New client guilds (#8736)
* add colors palette * add secondary menu component and style it * add box shadow to secondary menu * misc css, fixes for secondary menu * client: add equipment page with grouping, css: add some styles * add typography * more equipment * stable: fix linting * equipment: add styles (lots of general styles too) * remove duplicate google fonts loading * add dropdowns * design: white search input background, remove gray from items * start adding drawer and selected indicator * wip equipment * fix equipment * equipment: correctly bind new properties on items.gear.equipped * equipment: fix vue binding. version 2 * equipment: fix vue binding. version 3 * back to first fix for equip op, fix for sourcemaps, send http request when an item is equipped, load bootstrap-vue components where needed * checkboxes and radio buttons * correctly renders selected items in first postion during the first render * add search * general changes, constants part of app state, add popovers * add toggle switch, rename css * correct offset * upgrade deps * upgrade deps * drawer and lot of other work * update equipping mechanism * finish equipment * fix compilation and upgrade deps * use v-show in place of v-if to fix ui issues * v-show -> v-if * Start of guild syyles * fix linting in test/client * fix es6 compilation in test/client * fix babel compilation for tests * fix groupsUtilities mixin tests * More designs * Added public guild state * Added my guilds store * client: buttons * client: buttons: fix colors * Added join and leave * Began adding new guild form * Create form updates * Added search to local data * Added filtering * Added initial code for group create * Added more create checks * Added more guild routes * Added styles to guild page * Added more chat styles * Began porting over angular functions * Moved over group service functions * Added paging * Updated sidebar * Updated join/leave and minor text * Added new sidebar functions * Updated paging * Added some form updates * Added more translations and styles * Updated shrinkwrap * Removed features config * Lint cleanup * Added member modal * Added more member actions * Updated nav * Fixed filter toggling * Updated create guild * Added no guild page * Added sort select * Added more styles * Added update guild form * Removed extra css and other minor changes * Many css and syntax fixes * Fixed color and merge conflic * Removed paging from my guilds * Removed extra strings * Many requests updates * Small style fixes
969
npm-shrinkwrap.json
generated
3
website/client/assets/chat/copy.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||||
|
<path fill="#A5A1AC" fill-rule="evenodd" d="M6 14h8V4H6v10zm-4-2V2h4a2 2 0 0 0-2 2v8H2zM14 2h-2a2 2 0 0 0-2-2H2a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h2a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 290 B |
3
website/client/assets/chat/delete.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16">
|
||||||
|
<path fill="#A5A1AC" fill-rule="evenodd" d="M3 14h8V4H3v10zM14 4h-1v10a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V4H0V2h4V1a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v1h4v2zm-6 8h1V6H8v6zm-3 0h1V6H5v6z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 274 B |
3
website/client/assets/chat/like.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||||
|
<path fill="#A5A1AC" fill-rule="evenodd" d="M11 10c0 1.654-1.346 3-3 3s-3-1.346-3-3h6zm2-3.5a1.5 1.5 0 1 1-3.001-.001A1.5 1.5 0 0 1 13 6.5zm-7 0a1.5 1.5 0 1 1-3.001-.001A1.5 1.5 0 0 1 6 6.5zM2 14h12V2H2v12zM14 0H2a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 377 B |
3
website/client/assets/chat/liked.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||||
|
<path fill="#A5A1AC" fill-rule="evenodd" d="M11 10c0 1.654-1.346 3-3 3s-3-1.346-3-3h6zm2-3.5a1.5 1.5 0 1 1-3.001-.001A1.5 1.5 0 0 1 13 6.5zm-7 0a1.5 1.5 0 1 1-3.001-.001A1.5 1.5 0 0 1 6 6.5zM14 0H2a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 361 B |
3
website/client/assets/chat/report.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||||
|
<path fill="#A5A1AC" fill-rule="evenodd" d="M6 12h8V6H6v6zM4 9H2V3h7v1H6a2 2 0 0 0-2 2v3zm10-5h-3V3a2 2 0 0 0-2-2H2a1 1 0 0 0-2 0v15h2v-5h2v1a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 287 B |
18
website/client/assets/guilds/bronze-guild-badge.svg
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="78" height="76" viewBox="0 0 78 76">
|
||||||
|
<defs>
|
||||||
|
<path id="a" d="M35 0L0 15.235C0 45.705 4.255 62.941 35 76c30.745-13.059 35-30.294 35-60.765L35 0z"/>
|
||||||
|
<rect id="b" width="70" height="24" y="42" rx="12"/>
|
||||||
|
</defs>
|
||||||
|
<g fill="none" fill-rule="evenodd">
|
||||||
|
<g transform="translate(4)">
|
||||||
|
<use fill="#EA8C31" xlink:href="#a"/>
|
||||||
|
<path stroke="#B36213" stroke-width="10" d="M34.03 5.031l4.443 1.934 12.031 5.237 12.032 5.237 2.445 1.065c-.183 14.83-1.676 23.96-5.418 31.395C55.326 58.32 47.803 64.81 35 70.545 22.197 64.81 14.674 58.32 10.437 49.9c-3.742-7.435-5.235-16.565-5.418-31.395L35 5.454l-.97-.423z"/>
|
||||||
|
<path stroke="#D77A20" stroke-width="6" d="M34.421 3.02a506596810.815 506596810.815 0 0 0 16.882 7.348l12.03 5.237 3.66 1.593c-.111 15.9-1.621 25.609-5.643 33.6C56.795 59.848 48.69 66.734 35 72.733c-13.691-6-21.795-12.885-26.35-21.935-4.022-7.991-5.532-17.7-5.643-33.6L35 3.272l-.579-.252z"/>
|
||||||
|
</g>
|
||||||
|
<g transform="translate(4)">
|
||||||
|
<use fill="#FFF" xlink:href="#b"/>
|
||||||
|
<rect width="74" height="28" x="-2" y="40" stroke="#B36213" stroke-width="4" rx="14"/>
|
||||||
|
</g>
|
||||||
|
<path fill="#B36213" d="M31 30.667V34h16v-3.333C47 27.557 41.668 26 39 26c-2.666 0-8 1.556-8 4.667zM39.006 16A4.005 4.005 0 0 0 35 20c0 2.208 1.795 4 4.006 4A3.993 3.993 0 0 0 43 20c0-2.207-1.781-4-3.994-4"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
18
website/client/assets/guilds/gold-guild-badge.svg
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="78" height="76" viewBox="0 0 78 76">
|
||||||
|
<defs>
|
||||||
|
<path id="a" d="M35 0L0 15.235C0 45.705 4.255 62.941 35 76c30.745-13.059 35-30.294 35-60.765L35 0z"/>
|
||||||
|
<rect id="b" width="70" height="24" y="42" rx="12"/>
|
||||||
|
</defs>
|
||||||
|
<g fill="none" fill-rule="evenodd">
|
||||||
|
<g transform="translate(4)">
|
||||||
|
<use fill="#FFBC5A" xlink:href="#a"/>
|
||||||
|
<path stroke="#DF911E" stroke-width="10" d="M34.03 5.031l4.443 1.934 12.031 5.237 12.032 5.237 2.445 1.065c-.183 14.83-1.676 23.96-5.418 31.395C55.326 58.32 47.803 64.81 35 70.545 22.197 64.81 14.674 58.32 10.437 49.9c-3.742-7.435-5.235-16.565-5.418-31.395L35 5.454l-.97-.423z"/>
|
||||||
|
<path stroke="#FFA623" stroke-width="6" d="M34.421 3.02a506596810.815 506596810.815 0 0 0 16.882 7.348l12.03 5.237 3.66 1.593c-.111 15.9-1.621 25.609-5.643 33.6C56.795 59.848 48.69 66.734 35 72.733c-13.691-6-21.795-12.885-26.35-21.935-4.022-7.991-5.532-17.7-5.643-33.6L35 3.272l-.579-.252z"/>
|
||||||
|
</g>
|
||||||
|
<g transform="translate(4)">
|
||||||
|
<use fill="#FFF" xlink:href="#b"/>
|
||||||
|
<rect width="74" height="28" x="-2" y="40" stroke="#DF911E" stroke-width="4" rx="14"/>
|
||||||
|
</g>
|
||||||
|
<path fill="#DF911E" d="M31 30.667V34h16v-3.333C47 27.557 41.668 26 39 26c-2.666 0-8 1.556-8 4.667zM39.006 16A4.005 4.005 0 0 0 35 20c0 2.208 1.795 4 4.006 4A3.993 3.993 0 0 0 43 20c0-2.207-1.781-4-3.994-4"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
10
website/client/assets/guilds/green-gem.svg
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||||
|
<g fill="none" fill-rule="evenodd">
|
||||||
|
<path fill="#24CC8F" d="M0 9l5-7h14l5 7-12 13z"/>
|
||||||
|
<path fill="#FFF" d="M7 8.8L6 4h6zM17 8.8L18 4h-6z" opacity=".25"/>
|
||||||
|
<path fill="#FFF" d="M7 8.8L12 4l5 4.8zM2.6 8.8L6 4l1 4.8z" opacity=".5"/>
|
||||||
|
<path fill="#1B996B" d="M21.4 8.8L18 4l-1 4.8zM2.6 8.8H7l5 10.3z" opacity=".35"/>
|
||||||
|
<path fill="#FFF" d="M21.4 8.8H17l-5 10.3z" opacity=".5"/>
|
||||||
|
<path fill="#FFF" d="M7 8.8h10l-5 10.3z" opacity=".25"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 579 B |
13
website/client/assets/guilds/grey-badge.svg
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="76" height="88" viewBox="0 0 76 88">
|
||||||
|
<defs>
|
||||||
|
<path id="a" d="M38 0L0 17.64C0 52.924 4.62 72.88 38 88c33.38-15.12 38-35.077 38-70.36L38 0z"/>
|
||||||
|
</defs>
|
||||||
|
<g fill="none" fill-rule="evenodd">
|
||||||
|
<g>
|
||||||
|
<use fill="#C3C0C7" xlink:href="#a"/>
|
||||||
|
<path stroke="#F9F9F9" stroke-width="10" d="M36.975 5.037l4.857 2.255 13.063 6.064 13.062 6.064 3.028 1.405c-.168 17.685-1.8 28.545-5.99 37.422-4.69 9.939-12.968 17.548-26.995 24.24-14.027-6.692-22.306-14.301-26.996-24.24-4.189-8.877-5.821-19.737-5.99-37.422L38 5.513l-1.025-.476z"/>
|
||||||
|
<path stroke="#C3C0C7" stroke-width="6" d="M37.39 3.025a191742500.733 191742500.733 0 0 1 18.347 8.517l13.062 6.064 4.196 1.947c-.102 18.718-1.747 30.131-6.19 39.548-5 10.593-13.865 18.622-28.805 25.597-14.94-6.975-23.805-15.004-28.805-25.597-4.443-9.417-6.088-20.83-6.19-39.548L38 3.308l-.61-.283z"/>
|
||||||
|
</g>
|
||||||
|
<path fill="#F9F9F9" d="M26 51.001V56h24v-4.999C50 46.334 42.002 44 38 44c-3.998 0-12 2.334-12 7.001zM38.009 29A6.008 6.008 0 0 0 32 35.001C32 38.312 34.692 41 38.009 41A5.99 5.99 0 0 0 44 35.001 5.991 5.991 0 0 0 38.009 29"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.2 KiB |
3
website/client/assets/guilds/information.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||||
|
<path fill="#A5A1AC" fill-rule="evenodd" d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zM7 7h2v5H7V7zm0-3h2v2H7V4z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 238 B |
18
website/client/assets/guilds/silver-guild-badge.svg
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="78" height="76" viewBox="0 0 78 76">
|
||||||
|
<defs>
|
||||||
|
<path id="a" d="M35 0L0 15.235C0 45.705 4.255 62.941 35 76c30.745-13.059 35-30.294 35-60.765L35 0z"/>
|
||||||
|
<rect id="b" width="70" height="24" y="42" rx="12"/>
|
||||||
|
</defs>
|
||||||
|
<g fill="none" fill-rule="evenodd">
|
||||||
|
<g transform="translate(4)">
|
||||||
|
<use fill="#E1E0E3" xlink:href="#a"/>
|
||||||
|
<path stroke="#A5A1AC" stroke-width="10" d="M34.03 5.031l4.443 1.934 12.031 5.237 12.032 5.237 2.445 1.065c-.183 14.83-1.676 23.96-5.418 31.395C55.326 58.32 47.803 64.81 35 70.545 22.197 64.81 14.674 58.32 10.437 49.9c-3.742-7.435-5.235-16.565-5.418-31.395L35 5.454l-.97-.423z"/>
|
||||||
|
<path stroke="#C3C0C7" stroke-width="6" d="M34.421 3.02a506596810.815 506596810.815 0 0 0 16.882 7.348l12.03 5.237 3.66 1.593c-.111 15.9-1.621 25.609-5.643 33.6C56.795 59.848 48.69 66.734 35 72.733c-13.691-6-21.795-12.885-26.35-21.935-4.022-7.991-5.532-17.7-5.643-33.6L35 3.272l-.579-.252z"/>
|
||||||
|
</g>
|
||||||
|
<g transform="translate(4)">
|
||||||
|
<use fill="#FFF" xlink:href="#b"/>
|
||||||
|
<rect width="74" height="28" x="-2" y="40" stroke="#A5A1AC" stroke-width="4" rx="14"/>
|
||||||
|
</g>
|
||||||
|
<path fill="#A5A1AC" d="M31 30.667V34h16v-3.333C47 27.557 41.668 26 39 26c-2.666 0-8 1.556-8 4.667zM39.006 16A4.005 4.005 0 0 0 35 20c0 2.208 1.795 4 4.006 4A3.993 3.993 0 0 0 43 20c0-2.207-1.781-4-3.994-4"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -19,4 +19,3 @@
|
|||||||
@import './dropdown';
|
@import './dropdown';
|
||||||
@import './popover';
|
@import './popover';
|
||||||
@import './item';
|
@import './item';
|
||||||
|
|
||||||
|
|||||||
@@ -15,14 +15,18 @@ nav.navbar.navbar-inverse.fixed-top.navbar-toggleable-sm
|
|||||||
router-link.dropdown-item(:to="{name: 'stable'}") {{ $t('stable') }}
|
router-link.dropdown-item(:to="{name: 'stable'}") {{ $t('stable') }}
|
||||||
router-link.nav-item(tag="li", :to="{name: 'market'}", exact)
|
router-link.nav-item(tag="li", :to="{name: 'market'}", exact)
|
||||||
a.nav-link(v-once) {{ $t('market') }}
|
a.nav-link(v-once) {{ $t('market') }}
|
||||||
|
router-link.nav-item.dropdown(tag="li", :to="{name: 'tavern'}", :class="{'active': $route.path.startsWith('/guilds')}")
|
||||||
|
a.nav-link(v-once) {{ $t('guilds') }}
|
||||||
|
.dropdown-menu
|
||||||
|
router-link.dropdown-item(:to="{name: 'tavern'}") {{ $t('tavern') }}
|
||||||
|
router-link.dropdown-item(:to="{name: 'myGuilds'}") {{ $t('myGuilds') }}
|
||||||
|
router-link.dropdown-item(:to="{name: 'guildsDiscovery'}") {{ $t('guildsDiscovery') }}
|
||||||
router-link.nav-item.dropdown(tag="li", :to="{name: 'tavern'}", :class="{'active': $route.path.startsWith('/social')}")
|
router-link.nav-item.dropdown(tag="li", :to="{name: 'tavern'}", :class="{'active': $route.path.startsWith('/social')}")
|
||||||
a.nav-link(v-once) {{ $t('social') }}
|
a.nav-link(v-once) {{ $t('social') }}
|
||||||
.dropdown-menu
|
.dropdown-menu
|
||||||
router-link.dropdown-item(:to="{name: 'tavern'}") {{ $t('tavern') }}
|
|
||||||
router-link.dropdown-item(:to="{name: 'inbox'}") {{ $t('inbox') }}
|
router-link.dropdown-item(:to="{name: 'inbox'}") {{ $t('inbox') }}
|
||||||
router-link.dropdown-item(:to="{name: 'challenges'}") {{ $t('challenges') }}
|
router-link.dropdown-item(:to="{name: 'challenges'}") {{ $t('challenges') }}
|
||||||
router-link.dropdown-item(:to="{name: 'party'}") {{ $t('party') }}
|
router-link.dropdown-item(:to="{name: 'party'}") {{ $t('party') }}
|
||||||
router-link.dropdown-item(:to="{name: 'guildsDiscovery'}") {{ $t('guilds') }}
|
|
||||||
router-link.nav-item.dropdown(tag="li", to="/help", :class="{'active': $route.path.startsWith('/help')}")
|
router-link.nav-item.dropdown(tag="li", to="/help", :class="{'active': $route.path.startsWith('/help')}")
|
||||||
a.nav-link(v-once) {{ $t('help') }}
|
a.nav-link(v-once) {{ $t('help') }}
|
||||||
.dropdown-menu
|
.dropdown-menu
|
||||||
|
|||||||
104
website/client/components/guilds/discovery.vue
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
<template lang="pug">
|
||||||
|
.row
|
||||||
|
sidebar(@search="updateSearch", @filter="updateFilters")
|
||||||
|
|
||||||
|
.col-10.standard-page
|
||||||
|
.clearfix
|
||||||
|
h1.page-header.float-left(v-once) {{ $t('publicGuilds') }}
|
||||||
|
.float-right
|
||||||
|
span.dropdown-label {{ $t('sortBy') }}
|
||||||
|
b-dropdown(:text="$t('sort')", right=true)
|
||||||
|
b-dropdown-item(v-for='sortOption in sortOptions', @click='sort(sortOption.value)') {{sortOption.text}}
|
||||||
|
.col-md-12
|
||||||
|
public-guild-item(v-for="guild in filteredGuilds", :key='guild._id', :guild="guild", :display-leave='true')
|
||||||
|
mugen-scroll(
|
||||||
|
:handler="fetchGuilds",
|
||||||
|
:should-handle="!loading && !this.hasLoadedAllGuilds",
|
||||||
|
:handle-on-mount="false",
|
||||||
|
v-show="loading",
|
||||||
|
)
|
||||||
|
span(v-once) {{ $t('loading') }}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.sort-select {
|
||||||
|
margin: 2em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import MugenScroll from 'vue-mugen-scroll';
|
||||||
|
import PublicGuildItem from './publicGuildItem';
|
||||||
|
import Sidebar from './sidebar';
|
||||||
|
import groupUtilities from 'client/mixins/groupsUtilities';
|
||||||
|
|
||||||
|
import bFormSelect from 'bootstrap-vue/lib/components/form-select';
|
||||||
|
import bDropdown from 'bootstrap-vue/lib/components/dropdown';
|
||||||
|
import bDropdownItem from 'bootstrap-vue/lib/components/dropdown-item';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [groupUtilities],
|
||||||
|
components: { PublicGuildItem, MugenScroll, Sidebar, bFormSelect, bDropdown, bDropdownItem },
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
hasLoadedAllGuilds: false,
|
||||||
|
lastPageLoaded: 0,
|
||||||
|
search: '',
|
||||||
|
filters: {},
|
||||||
|
sort: 'none',
|
||||||
|
sortOptions: [
|
||||||
|
{
|
||||||
|
text: this.$t('none'),
|
||||||
|
value: 'none',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: this.$t('memberCount'),
|
||||||
|
value: 'member_count',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: this.$t('recentActivity'),
|
||||||
|
value: 'recent_activity',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
guilds: [],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
if (!this.$store.state.publicGuilds) this.fetchGuilds();
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
filteredGuilds () {
|
||||||
|
let search = this.search;
|
||||||
|
let filters = this.filters;
|
||||||
|
let user = this.$store.state.user.data;
|
||||||
|
let filterGuild = this.filterGuild;
|
||||||
|
// @TODO: Move this to the server
|
||||||
|
return this.guilds.filter((guild) => {
|
||||||
|
return filterGuild(guild, filters, search, user);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
updateSearch (eventData) {
|
||||||
|
this.search = eventData.searchTerm;
|
||||||
|
},
|
||||||
|
updateFilters (eventData) {
|
||||||
|
this.filters = eventData;
|
||||||
|
},
|
||||||
|
async fetchGuilds () {
|
||||||
|
// We have the data cached
|
||||||
|
if (this.lastPageLoaded === 0 && this.guilds.length > 0) return;
|
||||||
|
|
||||||
|
this.loading = true;
|
||||||
|
let guilds = await this.$store.dispatch('guilds:getPublicGuilds', {page: this.lastPageLoaded});
|
||||||
|
if (guilds.length === 0) this.hasLoadedAllGuilds = true;
|
||||||
|
|
||||||
|
this.guilds.push(...guilds);
|
||||||
|
|
||||||
|
this.lastPageLoaded++;
|
||||||
|
this.loading = false;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
340
website/client/components/guilds/groupFormModal.vue
Normal file
@@ -0,0 +1,340 @@
|
|||||||
|
<template lang="pug">
|
||||||
|
b-modal#guild-form(:title="title", :hide-footer="true")
|
||||||
|
form(@submit.stop.prevent="submit")
|
||||||
|
.form-group
|
||||||
|
label
|
||||||
|
strong(v-once) {{$t('name')}}*
|
||||||
|
b-form-input(type="text", placeholder="$t('newGuildPlaceHolder')", v-model="newGuild.name")
|
||||||
|
|
||||||
|
.form-group(v-if='newGuild.id')
|
||||||
|
label
|
||||||
|
strong(v-once) {{$t('guildLeader')}}*
|
||||||
|
b-form-select(v-model="newGuild.newLeader" :options="members")
|
||||||
|
|
||||||
|
.form-group
|
||||||
|
label
|
||||||
|
strong(v-once) {{$t('privacySettings')}}*
|
||||||
|
br
|
||||||
|
label.custom-control.custom-checkbox
|
||||||
|
input.custom-control-input(type="checkbox", v-model="newGuild.onlyLeaderCreatesChallenges")
|
||||||
|
span.custom-control-indicator
|
||||||
|
span.custom-control-description(v-once) {{ $t('onlyLeaderCreatesChallenges') }}
|
||||||
|
b-tooltip.icon(:content="$t('privateDescription')")
|
||||||
|
img(src='~assets/guilds/information.svg')
|
||||||
|
|
||||||
|
br
|
||||||
|
label.custom-control.custom-checkbox
|
||||||
|
input.custom-control-input(type="checkbox", v-model="newGuild.guildLeaderCantBeMessaged")
|
||||||
|
span.custom-control-indicator
|
||||||
|
span.custom-control-description(v-once) {{ $t('guildLeaderCantBeMessaged') }}
|
||||||
|
|
||||||
|
br
|
||||||
|
label.custom-control.custom-checkbox
|
||||||
|
input.custom-control-input(type="checkbox", v-model="newGuild.privateGuild")
|
||||||
|
span.custom-control-indicator
|
||||||
|
span.custom-control-description(v-once) {{ $t('privateGuild') }}
|
||||||
|
b-tooltip.icon(:content="$t('privateDescription')")
|
||||||
|
img(src='~assets/guilds/information.svg')
|
||||||
|
|
||||||
|
br
|
||||||
|
label.custom-control.custom-checkbox
|
||||||
|
input.custom-control-input(type="checkbox", v-model="newGuild.allowGuildInvationsFromNonMembers")
|
||||||
|
span.custom-control-indicator
|
||||||
|
span.custom-control-description(v-once) {{ $t('allowGuildInvationsFromNonMembers') }}
|
||||||
|
|
||||||
|
.form-group
|
||||||
|
label
|
||||||
|
strong(v-once) {{$t('description')}}*
|
||||||
|
div.description-count {{charactersRemaining}} {{ $t('charactersRemaining') }}
|
||||||
|
b-form-input(type="text", textarea :placeholder="$t('guildDescriptionPlaceHolder')", v-model="newGuild.description")
|
||||||
|
|
||||||
|
.form-group(v-if='newGuild.id')
|
||||||
|
label
|
||||||
|
strong(v-once) {{$t('guildInformation')}}*
|
||||||
|
b-form-input(type="text", textarea :placeholder="$t('guildInformationPlaceHolder')", v-model="newGuild.guildInformation")
|
||||||
|
|
||||||
|
.form-group(style='position: relative;')
|
||||||
|
label
|
||||||
|
strong(v-once) {{$t('categories')}}*
|
||||||
|
div.category-wrap(@click.prevent="toggleCategorySelect")
|
||||||
|
span.category-select(v-if='newGuild.categories.length === 0') {{$t('none')}}
|
||||||
|
.category-label(v-for='category in newGuild.categories') {{$t(categoriesHashByKey[category])}}
|
||||||
|
.category-box(v-if="showCategorySelect")
|
||||||
|
.form-check(
|
||||||
|
v-for="group in categoryOptions",
|
||||||
|
:key="group.key",
|
||||||
|
)
|
||||||
|
label.custom-control.custom-checkbox
|
||||||
|
input.custom-control-input(type="checkbox", :value='group.key' v-model="newGuild.categories")
|
||||||
|
span.custom-control-indicator
|
||||||
|
span.custom-control-description(v-once) {{ $t(group.label) }}
|
||||||
|
button.btn.btn-primary(@click.prevent="toggleCategorySelect") {{$t('close')}}
|
||||||
|
|
||||||
|
.form-group.text-center
|
||||||
|
div.item-with-icon
|
||||||
|
img(src="~assets/guilds/green-gem.svg")
|
||||||
|
span.count 4
|
||||||
|
button.btn.btn-primary.btn-md(v-if='!newGuild.id', :disabled='!newGuild.name || !newGuild.description') {{ $t('createGuild') }}
|
||||||
|
button.btn.btn-primary.btn-md(v-if='newGuild.id', :disabled='!newGuild.name || !newGuild.description') {{ $t('updateGuild') }}
|
||||||
|
.gem-description(v-once) {{ $t('guildGemCostInfo') }}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import '~client/assets/scss/colors.scss';
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
height: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description-count, .gem-description {
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.33;
|
||||||
|
text-align: center;
|
||||||
|
color: $gray-200;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description-count {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gem-description {
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-box {
|
||||||
|
padding: 1em;
|
||||||
|
max-width: 400px;
|
||||||
|
position: absolute;
|
||||||
|
top: -480px;
|
||||||
|
padding: 2em;
|
||||||
|
border-radius: 2px;
|
||||||
|
background-color: $white;
|
||||||
|
box-shadow: 0 2px 2px 0 rgba($black, 0.15), 0 1px 4px 0 rgba($black, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-label {
|
||||||
|
min-width: 100px;
|
||||||
|
border-radius: 100px;
|
||||||
|
background-color: $gray-600;
|
||||||
|
padding: .5em;
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: .5em;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1.33;
|
||||||
|
text-align: center;
|
||||||
|
color: $gray-300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-with-icon {
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
img {
|
||||||
|
height: 20px;
|
||||||
|
margin-right: .5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.count {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-right: 1em;
|
||||||
|
color: $green-10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.description-count {
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-select {
|
||||||
|
border-radius: 2px;
|
||||||
|
background-color: $white;
|
||||||
|
box-shadow: 0 2px 2px 0 rgba($black, 0.16), 0 1px 4px 0 rgba($black, 0.12);
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-select:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-wrap {
|
||||||
|
margin-top: .5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
margin-left: .5em;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import bModal from 'bootstrap-vue/lib/components/modal';
|
||||||
|
import bBtn from 'bootstrap-vue/lib/components/button';
|
||||||
|
import bFormInput from 'bootstrap-vue/lib/components/form-input';
|
||||||
|
import bFormCheckbox from 'bootstrap-vue/lib/components/form-checkbox';
|
||||||
|
import bFormSelect from 'bootstrap-vue/lib/components/form-select';
|
||||||
|
import bTooltip from 'bootstrap-vue/lib/components/tooltip';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
bModal,
|
||||||
|
bBtn,
|
||||||
|
bFormInput,
|
||||||
|
bFormCheckbox,
|
||||||
|
bFormSelect,
|
||||||
|
bTooltip,
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
let data = {
|
||||||
|
newGuild: {
|
||||||
|
id: '',
|
||||||
|
name: '',
|
||||||
|
type: 'guild',
|
||||||
|
privacy: 'private',
|
||||||
|
description: '',
|
||||||
|
guildInformation: '',
|
||||||
|
categories: [],
|
||||||
|
onlyLeaderCreatesChallenges: true,
|
||||||
|
guildLeaderCantBeMessaged: true,
|
||||||
|
privateGuild: true,
|
||||||
|
allowGuildInvationsFromNonMembers: true,
|
||||||
|
newLeader: '',
|
||||||
|
},
|
||||||
|
categoryOptions: [
|
||||||
|
{
|
||||||
|
label: 'animals',
|
||||||
|
key: 'animals',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'artDesign',
|
||||||
|
key: 'art_design',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'booksWriting',
|
||||||
|
key: 'books_writing',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'comicsHobbies',
|
||||||
|
key: 'comics_hobbies',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'diyCrafts',
|
||||||
|
key: 'diy_crafts',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'education',
|
||||||
|
key: 'education',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'foodCooking',
|
||||||
|
key: 'food_cooking',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'healthFitness',
|
||||||
|
key: 'health_fitness',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'music',
|
||||||
|
key: 'music',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'relationship',
|
||||||
|
key: 'relationship',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'scienceTech',
|
||||||
|
key: 'science_tech ',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
showCategorySelect: false,
|
||||||
|
members: ['one', 'two'],
|
||||||
|
};
|
||||||
|
|
||||||
|
let hashedCategories = {};
|
||||||
|
data.categoryOptions.forEach((category) => {
|
||||||
|
hashedCategories[category.key] = category.label;
|
||||||
|
});
|
||||||
|
data.categoriesHashByKey = hashedCategories;
|
||||||
|
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.$root.$on('shown::modal', () => {
|
||||||
|
let editingGroup = this.$store.state.editingGroup;
|
||||||
|
if (!editingGroup) return;
|
||||||
|
this.newGuild.name = editingGroup.name;
|
||||||
|
this.newGuild.type = editingGroup.type;
|
||||||
|
this.newGuild.privacy = editingGroup.privacy;
|
||||||
|
if (editingGroup.description) this.newGuild.description = editingGroup.description;
|
||||||
|
this.newGuild.id = editingGroup._id;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
charactersRemaining () {
|
||||||
|
return 500 - this.newGuild.description.length;
|
||||||
|
},
|
||||||
|
title () {
|
||||||
|
if (!this.newGuild.id) return this.$t('createGuild');
|
||||||
|
return this.$t('updateGuild');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
toggleCategorySelect () {
|
||||||
|
this.showCategorySelect = !this.showCategorySelect;
|
||||||
|
},
|
||||||
|
async submit () {
|
||||||
|
if (this.$store.state.user.data.balance < 1 && !this.newGuild.id) {
|
||||||
|
// @TODO: Add proper notifications
|
||||||
|
alert('Not enough gems');
|
||||||
|
return;
|
||||||
|
// @TODO return $rootScope.openModal('buyGems', {track:"Gems > Create Group"});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.newGuild.name || !this.newGuild.description) {
|
||||||
|
// @TODO: Add proper notifications
|
||||||
|
alert('Enter a name and description');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.newGuild.description.length > 500) {
|
||||||
|
// @TODO: Add proper notifications
|
||||||
|
alert('Description is too long');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @TODO: Add proper notifications
|
||||||
|
if (!confirm(this.$t('confirmGuild'))) return;
|
||||||
|
|
||||||
|
if (!this.newGuild.privateGuild) {
|
||||||
|
this.newGuild.privacy = 'public';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.newGuild.onlyLeaderCreatesChallenges) {
|
||||||
|
this.newGuild.leaderOnly = {
|
||||||
|
challenges: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.newGuild.id) {
|
||||||
|
await this.$store.dispatch('guilds:update', {group: this.newGuild});
|
||||||
|
} else {
|
||||||
|
await this.$store.dispatch('guilds:create', {group: this.newGuild});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$store.state.editingGroup = {};
|
||||||
|
|
||||||
|
this.newGuild = {
|
||||||
|
name: '',
|
||||||
|
type: 'guild',
|
||||||
|
privacy: 'private',
|
||||||
|
description: '',
|
||||||
|
categories: [],
|
||||||
|
onlyLeaderCreatesChallenges: true,
|
||||||
|
guildLeaderCantBeMessaged: true,
|
||||||
|
privateGuild: true,
|
||||||
|
allowGuildInvationsFromNonMembers: true,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
317
website/client/components/guilds/guild.vue
Normal file
@@ -0,0 +1,317 @@
|
|||||||
|
<template lang="pug">
|
||||||
|
// TODO this is necessary until we have a way to wait for data to be loaded from the server
|
||||||
|
.row(v-if="guild")
|
||||||
|
.clearfix.col-8
|
||||||
|
.row
|
||||||
|
.col-6
|
||||||
|
.float-left
|
||||||
|
h2 {{guild.name}}
|
||||||
|
strong.float-left(v-once) {{$t('groupLeader')}}
|
||||||
|
span.float-left : {{guild.leader.profile.name}}
|
||||||
|
.col-6
|
||||||
|
.float-right
|
||||||
|
.row.icon-row
|
||||||
|
.col-6
|
||||||
|
img.icon.shield(src="~assets/guilds/gold-guild-badge.svg")
|
||||||
|
span.number {{guild.memberCount}}
|
||||||
|
div(v-once) {{ $t('guildMembers') }}
|
||||||
|
.col-6
|
||||||
|
.item-with-icon
|
||||||
|
img.icon.gem(src="~assets/header/png/gem@3x.png")
|
||||||
|
span.number {{guild.memberCount}}
|
||||||
|
div(v-once) {{ $t('guildBank') }}
|
||||||
|
.row.chat-row
|
||||||
|
.col-12
|
||||||
|
h3(v-once) {{ $t('chat') }}
|
||||||
|
|
||||||
|
textarea(placeholder="$('chatPlaceHolder')")
|
||||||
|
button.btn.btn-secondary.send-chat.float-right(v-once) {{ $t('send') }}
|
||||||
|
|
||||||
|
.hr
|
||||||
|
.hr-middle(v-once) {{ $t('today') }}
|
||||||
|
|
||||||
|
.row
|
||||||
|
.col-md-2
|
||||||
|
img.icon(src="~assets/chat/like.svg")
|
||||||
|
.col-md-10
|
||||||
|
.card(v-for="msg in guild.chat", :key="msg.id")
|
||||||
|
.card-block
|
||||||
|
h3.leader Character name
|
||||||
|
span 2 hours ago
|
||||||
|
.clearfix
|
||||||
|
strong.float-left {{msg.user}}
|
||||||
|
.float-right {{msg.timestamp}}
|
||||||
|
.text {{msg.text}}
|
||||||
|
hr
|
||||||
|
span.action(v-once)
|
||||||
|
img.icon(src="~assets/chat/like.svg")
|
||||||
|
| {{$t('like')}}
|
||||||
|
span.action(v-once)
|
||||||
|
img.icon(src="~assets/chat/copy.svg")
|
||||||
|
| {{$t('copyAsTodo')}}
|
||||||
|
span.action(v-once)
|
||||||
|
img.icon(src="~assets/chat/report.svg")
|
||||||
|
| {{$t('report')}}
|
||||||
|
span.action(v-once)
|
||||||
|
img.icon(src="~assets/chat/delete.svg")
|
||||||
|
| {{$t('delete')}}
|
||||||
|
span.action.float-right
|
||||||
|
img.icon(src="~assets/chat/liked.svg")
|
||||||
|
| +3
|
||||||
|
|
||||||
|
.col-md-4.sidebar
|
||||||
|
.guild-background.row
|
||||||
|
.col-6
|
||||||
|
p Image here
|
||||||
|
.col-6
|
||||||
|
members-modal(:group='guild')
|
||||||
|
br
|
||||||
|
button.btn.btn-primary(v-once) {{$t('joinGuild')}}
|
||||||
|
br
|
||||||
|
button.btn.float-left(:class="[isMember ? 'btn-danger' : 'btn-success']") {{ isMember ? $t('leave') : $t('join') }}
|
||||||
|
br
|
||||||
|
button.btn.btn-primary(v-once) {{$t('inviteToGuild')}}
|
||||||
|
br
|
||||||
|
button.btn.btn-primary(v-once) {{$t('messageGuildLeader')}}
|
||||||
|
br
|
||||||
|
button.btn.btn-primary(v-once) {{$t('donateGems')}}
|
||||||
|
br
|
||||||
|
button.btn.btn-primary(b-btn, @click="updateGuild", v-once) {{ $t('updateGuild') }}
|
||||||
|
div
|
||||||
|
h3(v-once) {{ $t('description') }}
|
||||||
|
p(v-once) {{ guild.description }}
|
||||||
|
p Life hacks are tricks, shortcuts, or methods that help increase productivity, efficiency, health, and so on. Generally, they get you to a better state of life. Life hacking is the process of utilizing and implementing these secrets. And, in this guild, we want to help everyone discover these improved ways of doing things.
|
||||||
|
div
|
||||||
|
h3(v-once) {{$t('guildInformation')}}
|
||||||
|
h4 Welcome
|
||||||
|
p Below are some resources that some members might find useful. Consider checking them out before posting any questions, as they just might help answer some of them! Feel free to share your life hacks in the guild chat, or ask any questions that you might have. Please peruse at your leisure, and remember: this guild is meant to help guide you in the right direction. Only you will know what works best for you.
|
||||||
|
div
|
||||||
|
h3 Challenges
|
||||||
|
.card
|
||||||
|
h4 Challenge
|
||||||
|
.row
|
||||||
|
.col-8
|
||||||
|
p Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla scelerisque ultrices libero.
|
||||||
|
.col-4
|
||||||
|
.row
|
||||||
|
.col-md-12
|
||||||
|
span Tag
|
||||||
|
span 100
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import '~client/assets/scss/colors.scss';
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
background-color: $gray-600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
margin: 2em 0;
|
||||||
|
padding: 1em;
|
||||||
|
|
||||||
|
h3.leader {
|
||||||
|
color: $purple-200;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 1.43;
|
||||||
|
color: $gray-50;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.guild-background {
|
||||||
|
background-image: linear-gradient(to bottom, rgba($gray-600, 0), $gray-600);
|
||||||
|
height: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
height: 150px;
|
||||||
|
width: 100%;
|
||||||
|
border-radius: 2px;
|
||||||
|
background-color: $white;
|
||||||
|
border: solid 1px $gray-400;
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: italic;
|
||||||
|
line-height: 1.43;
|
||||||
|
color: $gray-300;
|
||||||
|
padding: .5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon.shield, .icon.gem {
|
||||||
|
width: 40px;
|
||||||
|
margin-right: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-row {
|
||||||
|
width: 200px;
|
||||||
|
margin-top: 3em;
|
||||||
|
margin-right: 3em;
|
||||||
|
|
||||||
|
.number {
|
||||||
|
font-size: 22px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-row {
|
||||||
|
margin-top: 2em;
|
||||||
|
|
||||||
|
.send-chat {
|
||||||
|
margin-top: -3.5em;
|
||||||
|
z-index: 10;
|
||||||
|
position: relative;
|
||||||
|
margin-right: 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.hr {
|
||||||
|
width: 100%;
|
||||||
|
height: 20px;
|
||||||
|
border-bottom: 1px solid $gray-500;
|
||||||
|
text-align: center;
|
||||||
|
margin: 2em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hr-middle {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
font-family: 'Roboto Condensed';
|
||||||
|
line-height: 1.5;
|
||||||
|
text-align: center;
|
||||||
|
color: $gray-200;
|
||||||
|
background-color: $gray-700;
|
||||||
|
padding: .2em;
|
||||||
|
margin-top: .2em;
|
||||||
|
display: inline-block;
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.action {
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.33;
|
||||||
|
color: $gray-200;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-right: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.action .icon {
|
||||||
|
margin-right: .3em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import groupUtilities from 'client/mixins/groupsUtilities';
|
||||||
|
import { mapState } from 'client/libs/store';
|
||||||
|
import membersModal from './membersModal';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [groupUtilities],
|
||||||
|
props: ['guildId'],
|
||||||
|
components: {
|
||||||
|
membersModal,
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
guild: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState({user: 'user.data'}),
|
||||||
|
isMember () {
|
||||||
|
return this.isMemberOfGroup(this.user, this.guild);
|
||||||
|
},
|
||||||
|
isMemberOfPendingQuest () {
|
||||||
|
let userid = this.user._id;
|
||||||
|
let group = this.guild;
|
||||||
|
if (!group.quest || !group.quest.members) return false;
|
||||||
|
if (group.quest.active) return false; // quest is started, not pending
|
||||||
|
return userid in group.quest.members && group.quest.members[userid] !== false;
|
||||||
|
},
|
||||||
|
isMemberOfRunningQuest () {
|
||||||
|
let userid = this.user._id;
|
||||||
|
let group = this.guild;
|
||||||
|
if (!group.quest || !group.quest.members) return false;
|
||||||
|
if (!group.quest.active) return false; // quest is pending, not started
|
||||||
|
return group.quest.members[userid];
|
||||||
|
},
|
||||||
|
memberProfileName (memberId) {
|
||||||
|
let foundMember = find(this.group.members, function findMember (member) {
|
||||||
|
return member._id === memberId;
|
||||||
|
});
|
||||||
|
return foundMember.profile.name;
|
||||||
|
},
|
||||||
|
isManager (memberId, group) {
|
||||||
|
return Boolean(group.managers[memberId]);
|
||||||
|
},
|
||||||
|
userCanApprove (userId, group) {
|
||||||
|
if (!group) return false;
|
||||||
|
let leader = group.leader._id === userId;
|
||||||
|
let userIsManager = Boolean(group.managers[userId]);
|
||||||
|
return leader || userIsManager;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
this.fetchGuild();
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
// call again the method if the route changes (when this route is already active)
|
||||||
|
$route: 'fetchGuild',
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
updateGuild () {
|
||||||
|
this.$store.state.editingGroup = this.guild;
|
||||||
|
this.$root.$emit('show::modal', 'guild-form');
|
||||||
|
},
|
||||||
|
async fetchGuild () {
|
||||||
|
this.guild = await this.$store.dispatch('guilds:getGroup', {groupId: this.guildId});
|
||||||
|
|
||||||
|
this.guild.chat = [
|
||||||
|
{
|
||||||
|
text: '@CharacterName Vestibulum ultricies, lorem non bibendum consequat, nisl lacus semper nulla, hendrerit dignissim ipsum erat eu odio. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at aliquet urna. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla non est ut nisl interdum tincidunt in eu dui. Proin condimentum a.',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
},
|
||||||
|
editGroup () {
|
||||||
|
// @TODO: Open up model
|
||||||
|
},
|
||||||
|
save () {
|
||||||
|
let newLeader = this.group._newLeader && this.group._newLeader._id;
|
||||||
|
|
||||||
|
if (newLeader) {
|
||||||
|
this.group.leader = newLeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Groups.Group.update(group);
|
||||||
|
},
|
||||||
|
deleteAllMessages () {
|
||||||
|
if (confirm(this.$t('confirmDeleteAllMessages'))) {
|
||||||
|
// User.clearPMs();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// inviteOrStartParty (group) {
|
||||||
|
// Analytics.track({'hitType':'event','eventCategory':'button','eventAction':'click','eventLabel':'Invite Friends'});
|
||||||
|
|
||||||
|
// var sendInviteText = window.env.t('sendInvitations');
|
||||||
|
// if (group.type !== 'party' && group.type !== 'guild') {
|
||||||
|
// $location.path("/options/groups/party");
|
||||||
|
// return console.log('Invalid group type.')
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (group.purchased && group.purchased.plan && group.purchased.plan.customerId) sendInviteText += window.env.t('groupAdditionalUserCost');
|
||||||
|
//
|
||||||
|
// group.sendInviteText = sendInviteText;
|
||||||
|
//
|
||||||
|
// $rootScope.openModal('invite-' + group.type, {
|
||||||
|
// controller:'InviteToGroupCtrl',
|
||||||
|
// resolve: {
|
||||||
|
// injectedGroup: function() {
|
||||||
|
// return group;
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
// },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
26
website/client/components/guilds/index.vue
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<template lang="pug">
|
||||||
|
.row
|
||||||
|
secondary-menu.col-12
|
||||||
|
router-link.nav-link(:to="{name: 'tavern'}", exact, :class="{'active': $route.name === 'tavern'}") {{ $t('tavern') }}
|
||||||
|
router-link.nav-link(:to="{name: 'myGuilds'}", :class="{'active': $route.name === 'myGuilds'}") {{ $t('myGuilds') }}
|
||||||
|
router-link.nav-link(:to="{name: 'guildsDiscovery'}", :class="{'active': $route.name === 'guildsDiscovery'}") {{ $t('guildsDiscovery') }}
|
||||||
|
|
||||||
|
.col-12
|
||||||
|
router-view
|
||||||
|
|
||||||
|
group-form-modal
|
||||||
|
</template>
|
||||||
|
<!-- .col-md-2
|
||||||
|
button.btn.btn-primary(b-btn, @click="$root.$emit('show::modal','guild-form')") {{ $t('createGuild') }} -->
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import groupFormModal from './groupFormModal';
|
||||||
|
import SecondaryMenu from 'client/components/secondaryMenu';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
groupFormModal,
|
||||||
|
SecondaryMenu,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
107
website/client/components/guilds/membersModal.vue
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
<template lang="pug">
|
||||||
|
div
|
||||||
|
button.btn.btn-primary(b-btn, @click="$root.$emit('show::modal','members-modal')") {{ $t('viewMembers') }}
|
||||||
|
|
||||||
|
b-modal#members-modal(:title="$t('createGuild')")
|
||||||
|
ul(v-for='member in members', :key='member')
|
||||||
|
li(@click='clickMember') {{member}}
|
||||||
|
button(@click='removeMember(member)', v-once) {{$t('remove')}}
|
||||||
|
button(@click='quickReply(member)', v-once) {{$t('message')}}
|
||||||
|
button(@click='addManager(member)', v-once) {{$t('addManager')}}
|
||||||
|
button(@click='removeManager(member)', v-once) {{$t('addManager')}}
|
||||||
|
|
||||||
|
b-modal#remove-member(:title="$t('confirmRemoveMember')")
|
||||||
|
button(@click='confirmRemoveMember(member)', v-once) {{$t('remove')}}
|
||||||
|
|
||||||
|
b-modal#private-message(:title="$t('confirmRemoveMember')")
|
||||||
|
button(@click='confirmRemoveMember(member)', v-once) {{$t('remove')}}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import bModal from 'bootstrap-vue/lib/components/modal';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: ['group'],
|
||||||
|
components: {
|
||||||
|
bModal,
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
members: ['one', 'two'],
|
||||||
|
memberToRemove: '',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getMembers () {
|
||||||
|
// We should get members here via store if they are not loaded
|
||||||
|
},
|
||||||
|
clickMember (uid, forceShow) {
|
||||||
|
let user = this.$store.state.user.data;
|
||||||
|
|
||||||
|
if (user._id === uid && !forceShow) {
|
||||||
|
if (this.$route.name === 'tasks') {
|
||||||
|
this.$route.router.go('options.profile.avatar');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$route.router.go('tasks');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// $root.$emit('show::modal','members-modal')
|
||||||
|
// We need the member information up top here, but then we pass it down to the modal controller
|
||||||
|
// down below. Better way of handling this?
|
||||||
|
// Members.selectMember(uid)
|
||||||
|
// .then(function () {
|
||||||
|
// $rootScope.openModal('member', {controller: 'MemberModalCtrl', windowClass: 'profile-modal', size: 'lg'});
|
||||||
|
// });
|
||||||
|
},
|
||||||
|
removeMember (member) {
|
||||||
|
this.memberToRemove = member;
|
||||||
|
this.$root.$emit('show::modal', 'remove-member');
|
||||||
|
},
|
||||||
|
confirmRemoveMember (confirmation) {
|
||||||
|
if (!confirmation) {
|
||||||
|
this.memberToRemove = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Groups.Group.removeMember(
|
||||||
|
// $scope.removeMemberData.group._id,
|
||||||
|
// $scope.removeMemberData.member._id,
|
||||||
|
// $scope.removeMemberData.message
|
||||||
|
// ).then(function (response) {
|
||||||
|
// if($scope.removeMemberData.isMember){
|
||||||
|
// _.pull($scope.removeMemberData.group.members, $scope.removeMemberData.member);
|
||||||
|
// }else{
|
||||||
|
// _.pull($scope.removeMemberData.group.invites, $scope.removeMemberData.member);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// $scope.removeMemberData = undefined;
|
||||||
|
// });
|
||||||
|
},
|
||||||
|
quickReply (uid) {
|
||||||
|
this.memberToReply = uid;
|
||||||
|
this.$root.$emit('show::modal', 'private-message');
|
||||||
|
// Members.selectMember(uid)
|
||||||
|
// .then(function (response) {
|
||||||
|
// $rootScope.openModal('private-message', {controller: 'MemberModalCtrl'});
|
||||||
|
// });
|
||||||
|
},
|
||||||
|
addManager () {
|
||||||
|
// Groups.Group.addManager(this.group._id, this.group._newManager)
|
||||||
|
// .then(function (response) {
|
||||||
|
// this.group._newManager = '';
|
||||||
|
// this.group.managers = response.data.data.managers;
|
||||||
|
// });
|
||||||
|
},
|
||||||
|
removeManager (memberId) {
|
||||||
|
this.memberToReply = memberId;
|
||||||
|
// Groups.Group.removeManager(this.group._id, memberId)
|
||||||
|
// .then(function (response) {
|
||||||
|
// this.group._newManager = '';
|
||||||
|
// this.group.managers = response.data.data.managers;
|
||||||
|
// });
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
117
website/client/components/guilds/myGuilds.vue
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
<template lang="pug">
|
||||||
|
.row
|
||||||
|
sidebar(v-on:search="updateSearch", v-on:filter="updateFilters")
|
||||||
|
|
||||||
|
.col-10.no-guilds.standard-page(v-if='filteredGuilds.length === 0')
|
||||||
|
.no-guilds-wrapper
|
||||||
|
img(src='~assets/guilds/grey-badge.svg')
|
||||||
|
h2 {{$t('noGuildsTitle')}}
|
||||||
|
p {{$t('noGuildsParagraph1')}}
|
||||||
|
p {{$t('noGuildsParagraph2')}}
|
||||||
|
span(v-if='loading') {{ $t('loading') }}
|
||||||
|
|
||||||
|
.col-10.standard-page(v-if='filteredGuilds.length > 0')
|
||||||
|
.row
|
||||||
|
.col-md-12
|
||||||
|
h1.page-header.float-left(v-once) {{ $t('myGuilds') }}
|
||||||
|
.float-right
|
||||||
|
span.dropdown-label {{ $t('sortBy') }}
|
||||||
|
b-dropdown(:text="$t('sort')", right=true)
|
||||||
|
b-dropdown-item(v-for='sortOption in sortOptions', @click='sort(sortOption.value)') {{sortOption.text}}
|
||||||
|
.row
|
||||||
|
.col-md-12
|
||||||
|
public-guild-item(v-for="guild in filteredGuilds", :key='guild._id', :guild="guild", :display-gem-bank='true')
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import '~client/assets/scss/colors.scss';
|
||||||
|
.sort-select {
|
||||||
|
margin: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-guilds {
|
||||||
|
text-align: center;
|
||||||
|
color: $gray-200;
|
||||||
|
margin-top: 15em;
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.43;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-guilds-wrapper {
|
||||||
|
width: 400px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapState } from 'client/libs/store';
|
||||||
|
import groupUtilities from 'client/mixins/groupsUtilities';
|
||||||
|
|
||||||
|
import MugenScroll from 'vue-mugen-scroll';
|
||||||
|
import bFormSelect from 'bootstrap-vue/lib/components/form-select';
|
||||||
|
import bDropdown from 'bootstrap-vue/lib/components/dropdown';
|
||||||
|
import bDropdownItem from 'bootstrap-vue/lib/components/dropdown-item';
|
||||||
|
|
||||||
|
import PublicGuildItem from './publicGuildItem';
|
||||||
|
import Sidebar from './sidebar';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [groupUtilities],
|
||||||
|
components: { PublicGuildItem, MugenScroll, Sidebar, bFormSelect, bDropdown, bDropdownItem },
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
search: '',
|
||||||
|
filters: {},
|
||||||
|
sort: 'none',
|
||||||
|
sortOptions: [
|
||||||
|
{
|
||||||
|
text: this.$t('none'),
|
||||||
|
value: 'none',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: this.$t('memberCount'),
|
||||||
|
value: 'member_count',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: this.$t('recentActivity'),
|
||||||
|
value: 'recent_activity',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
this.fetchGuilds();
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState({
|
||||||
|
guilds: 'myGuilds',
|
||||||
|
}),
|
||||||
|
filteredGuilds () {
|
||||||
|
let search = this.search;
|
||||||
|
let filters = this.filters;
|
||||||
|
let user = this.$store.state.user.data;
|
||||||
|
let filterGuild = this.filterGuild;
|
||||||
|
return this.guilds.filter((guild) => {
|
||||||
|
return filterGuild(guild, filters, search, user);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
updateSearch (eventData) {
|
||||||
|
this.search = eventData.searchTerm;
|
||||||
|
},
|
||||||
|
updateFilters (eventData) {
|
||||||
|
this.filters = eventData;
|
||||||
|
},
|
||||||
|
async fetchGuilds () {
|
||||||
|
this.loading = true;
|
||||||
|
await this.$store.dispatch('guilds:getMyGuilds');
|
||||||
|
this.loading = false;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
144
website/client/components/guilds/publicGuildItem.vue
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
<template lang="pug">
|
||||||
|
.card
|
||||||
|
.card-block
|
||||||
|
.row
|
||||||
|
.col-md-2
|
||||||
|
img.icon.shield(src="~assets/guilds/gold-guild-badge.svg")
|
||||||
|
.member-count {{guild.memberCount}}
|
||||||
|
.col-md-10
|
||||||
|
.row
|
||||||
|
.col-md-8
|
||||||
|
router-link(:to="{ name: 'guild', params: { guildId: guild._id } }")
|
||||||
|
h3 {{ guild.name }}
|
||||||
|
p {{ guild.description }}
|
||||||
|
.col-md-2.cta-container
|
||||||
|
button.btn.btn-danger(v-if='isMember && displayLeave' @click='leave()', v-once) {{ $t('leave') }}
|
||||||
|
button.btn.btn-success(v-if='!isMember' @click='join()', v-once) {{ $t('join') }}
|
||||||
|
div.item-with-icon(v-if='displayGemBank')
|
||||||
|
img(src="~assets/guilds/green-gem.svg")
|
||||||
|
span.count {{ guild.balance }}
|
||||||
|
div.guild-bank(v-if='displayGemBank', v-once) {{$t('guildBank')}}
|
||||||
|
.row
|
||||||
|
.col-md-12
|
||||||
|
.category-label(v-for="category in guild.categories")
|
||||||
|
| {{category}}
|
||||||
|
span.recommend-text Suggested because you’re new to Habitica.
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import '~client/assets/scss/colors.scss';
|
||||||
|
|
||||||
|
.card {
|
||||||
|
height: 260px;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: $white;
|
||||||
|
box-shadow: 0 2px 2px 0 rgba($black, 0.15), 0 1px 4px 0 rgba($black, 0.1);
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
|
||||||
|
.category-label {
|
||||||
|
min-width: 100px;
|
||||||
|
border-radius: 100px;
|
||||||
|
background-color: $gray-600;
|
||||||
|
padding: .5em;
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: .5em;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1.33;
|
||||||
|
text-align: center;
|
||||||
|
color: $gray-300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recommend-text {
|
||||||
|
font-size: 12px;
|
||||||
|
font-style: italic;
|
||||||
|
line-height: 2;
|
||||||
|
color: $gray-300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cta-container {
|
||||||
|
margin: 0 auto;
|
||||||
|
margin-top: 4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shield {
|
||||||
|
width: 70px;
|
||||||
|
height: 76px;
|
||||||
|
margin: auto;
|
||||||
|
margin: 4em auto;
|
||||||
|
display: block;
|
||||||
|
background-size: cover;
|
||||||
|
width: 100%;
|
||||||
|
height: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-with-icon {
|
||||||
|
img {
|
||||||
|
height: 37px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.count {
|
||||||
|
font-size: 20px;
|
||||||
|
height: 37px;
|
||||||
|
width: 37px;
|
||||||
|
margin-left: .2em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.guild-bank {
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.33;
|
||||||
|
color: $gray-300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.member-count {
|
||||||
|
position: relative;
|
||||||
|
top: -3.6em;
|
||||||
|
left: -.1em;
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: bold;
|
||||||
|
font-family: 'Roboto Condensed';
|
||||||
|
line-height: 1.2;
|
||||||
|
text-align: center;
|
||||||
|
color: #b36213;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapState } from 'client/libs/store';
|
||||||
|
import groupUtilities from 'client/mixins/groupsUtilities';
|
||||||
|
import findIndex from 'lodash/findIndex';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [groupUtilities],
|
||||||
|
props: ['guild', 'displayLeave', 'displayGemBank'],
|
||||||
|
computed: {
|
||||||
|
...mapState({user: 'user.data'}),
|
||||||
|
isMember () {
|
||||||
|
return this.isMemberOfGroup(this.user, this.guild);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async join () {
|
||||||
|
// @TODO: This needs to be in the notifications where users will now accept invites
|
||||||
|
if (this.guild.cancelledPlan && !confirm(window.env.t('aboutToJoinCancelledGroupPlan'))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await this.$store.dispatch('guilds:join', {guildId: this.guild._id, type: 'myGuilds'});
|
||||||
|
},
|
||||||
|
async leave () {
|
||||||
|
// @TODO: ask about challenges when we add challenges
|
||||||
|
await this.$store.dispatch('guilds:leave', {guildId: this.guild._id, type: 'myGuilds'});
|
||||||
|
},
|
||||||
|
async reject (invitationToReject) {
|
||||||
|
// @TODO: This needs to be in the notifications where users will now accept invites
|
||||||
|
let index = findIndex(this.user.invitations.guilds, function findInviteIndex (invite) {
|
||||||
|
return invite.id === invitationToReject.id;
|
||||||
|
});
|
||||||
|
this.user.invitations.guilds = this.user.invitations.guilds.splice(0, index);
|
||||||
|
await this.$store.dispatch('guilds:rejectInvite', {guildId: invitationToReject.id});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
152
website/client/components/guilds/sidebar.vue
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
<template lang="pug">
|
||||||
|
.col-2.standard-sidebar
|
||||||
|
.form-group
|
||||||
|
input.form-control.search(type="text", :placeholder="$t('search')", v-model='searchTerm')
|
||||||
|
|
||||||
|
form
|
||||||
|
h3(v-once) {{ $t('filter') }}
|
||||||
|
.form-group
|
||||||
|
h5 Category
|
||||||
|
.form-check(
|
||||||
|
v-for="group in categoryOptions",
|
||||||
|
:key="group.key",
|
||||||
|
)
|
||||||
|
label.custom-control.custom-checkbox
|
||||||
|
input.custom-control-input(type="checkbox", :value='group.key' v-model="categoryFilters")
|
||||||
|
span.custom-control-indicator
|
||||||
|
span.custom-control-description(v-once) {{ $t(group.label) }}
|
||||||
|
.form-group
|
||||||
|
h5 Role
|
||||||
|
.form-check(
|
||||||
|
v-for="group in roleOptions",
|
||||||
|
:key="group.key",
|
||||||
|
)
|
||||||
|
label.custom-control.custom-checkbox
|
||||||
|
input.custom-control-input(type="checkbox", :value='group.key' v-model="roleFilters")
|
||||||
|
span.custom-control-indicator
|
||||||
|
span.custom-control-description(v-once) {{ $t(group.label) }}
|
||||||
|
.form-group
|
||||||
|
h5 Guild Size
|
||||||
|
.form-check(
|
||||||
|
v-for="group in guildSizeOptions",
|
||||||
|
:key="group.key",
|
||||||
|
)
|
||||||
|
label.custom-control.custom-checkbox
|
||||||
|
input.custom-control-input(type="checkbox", :value='group.key' v-model="guildSizeFilters")
|
||||||
|
span.custom-control-indicator
|
||||||
|
span.custom-control-description(v-once) {{ $t(group.label) }}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import throttle from 'lodash/throttle';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
categoryFilters: [],
|
||||||
|
categoryOptions: [
|
||||||
|
{
|
||||||
|
label: 'habiticaOfficial',
|
||||||
|
key: 'official',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'animals',
|
||||||
|
key: 'animals',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'artDesign',
|
||||||
|
key: 'art_design',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'booksWriting',
|
||||||
|
key: 'books_writing',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'comicsHobbies',
|
||||||
|
key: 'comics_hobbies',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'diyCrafts',
|
||||||
|
key: 'diy_crafts',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'education',
|
||||||
|
key: 'education',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'foodCooking',
|
||||||
|
key: 'food_cooking',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'healthFitness',
|
||||||
|
key: 'health_fitness',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'music',
|
||||||
|
key: 'music',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'relationship',
|
||||||
|
key: 'relationship',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'scienceTech',
|
||||||
|
key: 'science_tech ',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
roleFilters: [],
|
||||||
|
roleOptions: [
|
||||||
|
{
|
||||||
|
label: 'guildLeader',
|
||||||
|
key: 'guild_leader',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'member',
|
||||||
|
key: 'member',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
guildSizeFilters: [],
|
||||||
|
guildSizeOptions: [
|
||||||
|
{
|
||||||
|
label: 'goldTier',
|
||||||
|
key: 'gold_tier',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'silverTier',
|
||||||
|
key: 'silver_tier',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'bronzeTier',
|
||||||
|
key: 'bronze_tier',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
searchTerm: '',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
categoryFilters: function categoryFilters () {
|
||||||
|
this.emitFilters();
|
||||||
|
},
|
||||||
|
roleFilters: function roleFilters () {
|
||||||
|
this.emitFilters();
|
||||||
|
},
|
||||||
|
guildSizeFilters: function guildSizeFilters () {
|
||||||
|
this.emitFilters();
|
||||||
|
},
|
||||||
|
searchTerm: throttle(function searchTerm (newSearch) {
|
||||||
|
this.$emit('search', {
|
||||||
|
searchTerm: newSearch,
|
||||||
|
});
|
||||||
|
}, 250),
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
emitFilters () {
|
||||||
|
this.$emit('filter', {
|
||||||
|
categories: this.categoryFilters,
|
||||||
|
roles: this.roleFilters,
|
||||||
|
guildSize: this.guildSizeFilters,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
<template lang="pug">
|
<template lang="pug">
|
||||||
.row
|
.row
|
||||||
h1.page-header.col-12 Tavern
|
.col-md-4
|
||||||
// TODO Example code based on Semantic UI .ui.grid
|
|
||||||
.four.wide.column
|
|
||||||
h2.ui.dividing.header SideMenu
|
h2.ui.dividing.header SideMenu
|
||||||
|
|
||||||
.ui.card
|
.ui.card
|
||||||
@@ -153,7 +151,7 @@
|
|||||||
ul
|
ul
|
||||||
li Challenge 1
|
li Challenge 1
|
||||||
|
|
||||||
.twelve.wide.column
|
.col-md-8
|
||||||
h2.ui.dividing.header Tavern Chat
|
h2.ui.dividing.header Tavern Chat
|
||||||
.ui.comments
|
.ui.comments
|
||||||
|
|
||||||
@@ -172,7 +170,6 @@
|
|||||||
span.date {{message.date}}
|
span.date {{message.date}}
|
||||||
.text
|
.text
|
||||||
| {{message.message}}
|
| {{message.message}}
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
<template lang="pug">
|
|
||||||
.row
|
|
||||||
.col-2.standard-sidebar
|
|
||||||
.form-group
|
|
||||||
input.form-control(type="text", :placeholder="$t('search')")
|
|
||||||
|
|
||||||
form
|
|
||||||
h3(v-once) {{ $t('filter') }}
|
|
||||||
|
|
||||||
.form-group
|
|
||||||
h5 Interests
|
|
||||||
.form-check
|
|
||||||
.label.form-check-label
|
|
||||||
input.form-check-input(type="checkbox")
|
|
||||||
span Habitica Official
|
|
||||||
.form-check
|
|
||||||
.label.form-check-label
|
|
||||||
input.form-check-input(type="checkbox")
|
|
||||||
span Nature
|
|
||||||
.form-check
|
|
||||||
.label.form-check-label
|
|
||||||
input.form-check-input(type="checkbox")
|
|
||||||
span Animals
|
|
||||||
|
|
||||||
.col-10.standard-page
|
|
||||||
h1.page-header(v-once) {{ $t('publicGuilds') }}
|
|
||||||
public-guild-item(v-for="guild in guilds", :key='guild._id', :guild="guild")
|
|
||||||
mugen-scroll(
|
|
||||||
:handler="fetchGuilds",
|
|
||||||
:should-handle="loading === false && hasLoadedAllGuilds === false",
|
|
||||||
:handle-on-mount="false",
|
|
||||||
v-show="hasLoadedAllGuilds === false",
|
|
||||||
)
|
|
||||||
span loading...
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import axios from 'axios';
|
|
||||||
import MugenScroll from 'vue-mugen-scroll';
|
|
||||||
import PublicGuildItem from './publicGuildItem';
|
|
||||||
import { mapState } from 'client/libs/store';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: { PublicGuildItem, MugenScroll },
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
loading: false,
|
|
||||||
hasLoadedAllGuilds: false,
|
|
||||||
lastPageLoaded: 0,
|
|
||||||
guilds: [],
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
...mapState({
|
|
||||||
GUILDS_PER_PAGE: 'constants.GUILDS_PER_PAGE',
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
created () {
|
|
||||||
this.fetchGuilds();
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
async fetchGuilds () {
|
|
||||||
this.loading = true;
|
|
||||||
let response = await axios.get('/api/v3/groups', {
|
|
||||||
params: {
|
|
||||||
type: 'publicGuilds',
|
|
||||||
paginate: true,
|
|
||||||
page: this.lastPageLoaded,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
let guilds = response.data.data;
|
|
||||||
this.guilds.push(...guilds);
|
|
||||||
if (guilds.length < this.GUILDS_PER_PAGE) this.hasLoadedAllGuilds = true;
|
|
||||||
this.lastPageLoaded++;
|
|
||||||
this.loading = false;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
<template lang="pug">
|
|
||||||
.card
|
|
||||||
.card-block
|
|
||||||
.clearfix
|
|
||||||
router-link.float-left(:to="{ name: 'guild', params: { guildId: guild._id } }")
|
|
||||||
h3 {{ guild.name }}
|
|
||||||
button.btn.float-right(:class="[isMember ? 'btn-danger' : 'btn-success']") {{ isMember ? $t('leave') : $t('join') }}
|
|
||||||
p {{ guild.description }}
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.card {
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { mapState } from 'client/libs/store';
|
|
||||||
import groupUtilities from 'client/mixins/groupsUtilities';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
mixins: [groupUtilities],
|
|
||||||
props: ['guild'],
|
|
||||||
computed: {
|
|
||||||
...mapState({user: 'user.data'}),
|
|
||||||
isMember () {
|
|
||||||
return this.isMemberOfGroup(this.user, this.guild);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
<template lang="pug">
|
|
||||||
// TODO this is necessary until we have a way to wait for data to be loaded from the server
|
|
||||||
.row(v-if="guild")
|
|
||||||
.clearfix.col-12
|
|
||||||
.float-left
|
|
||||||
h1.page-header {{guild.name}}
|
|
||||||
strong.float-left {{$t('groupLeader')}}
|
|
||||||
span.float-left : {{guild.leader.profile.name}}
|
|
||||||
.float-right
|
|
||||||
.clearfix
|
|
||||||
h3.float-left
|
|
||||||
span.badge.badge-default {{guild.memberCount}}
|
|
||||||
| {{$t('members')}}
|
|
||||||
button.btn.float-left(:class="[isMember ? 'btn-danger' : 'btn-success']") {{ isMember ? $t('leave') : $t('join') }}
|
|
||||||
.col-5
|
|
||||||
h4(v-once) {{ $t('description') }}
|
|
||||||
p {{ guild.description }}
|
|
||||||
.col-7
|
|
||||||
h4(v-once) {{ $t('chat') }}
|
|
||||||
.card(v-for="msg in guild.chat", :key="msg.id")
|
|
||||||
.card-block
|
|
||||||
.clearfix
|
|
||||||
strong.float-left {{msg.user}}
|
|
||||||
.float-right {{msg.timestamp}}
|
|
||||||
.text {{msg.text}}
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.card {
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
||||||
<script>
|
|
||||||
import axios from 'axios';
|
|
||||||
import groupUtilities from 'client/mixins/groupsUtilities';
|
|
||||||
import { mapState } from 'client/libs/store';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
mixins: [groupUtilities],
|
|
||||||
props: ['guildId'],
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
guild: null,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
...mapState({user: 'user.data'}),
|
|
||||||
isMember () {
|
|
||||||
return this.isMemberOfGroup(this.user, this.guild);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
created () {
|
|
||||||
this.fetchGuild();
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
// call again the method if the route changes (when this route is already active)
|
|
||||||
$route: 'fetchGuild',
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
fetchGuild () {
|
|
||||||
axios.get(`/api/v3/groups/${this.guildId}`).then(response => {
|
|
||||||
this.guild = response.data.data;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import intersection from 'lodash/intersection';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
methods: {
|
methods: {
|
||||||
isMemberOfGroup (user, group) {
|
isMemberOfGroup (user, group) {
|
||||||
@@ -16,5 +18,36 @@ export default {
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
isLeaderOfGroup (user, group) {
|
||||||
|
return user._id === group.leader._id;
|
||||||
|
},
|
||||||
|
filterGuild (group, filters, search, user) {
|
||||||
|
let passedSearch = true;
|
||||||
|
let hasCategories = true;
|
||||||
|
let isMember = true;
|
||||||
|
let isLeader = true;
|
||||||
|
|
||||||
|
if (search) {
|
||||||
|
passedSearch = group.name.toLowerCase().indexOf(search.toLowerCase()) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filters.categories && filters.categories.length > 0) {
|
||||||
|
let intersectingCats = intersection(filters.categories, group.categories);
|
||||||
|
hasCategories = intersectingCats.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let filteringRole = filters.roles && filters.roles.length > 0;
|
||||||
|
if (filteringRole && filters.roles.indexOf('member')) {
|
||||||
|
isMember = this.isMemberOfGroup(user, group);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filteringRole && filters.roles.indexOf('guild_leader')) {
|
||||||
|
isLeader = this.isLeaderOfGroup(user, group);
|
||||||
|
}
|
||||||
|
|
||||||
|
// @TODO: Tier filters
|
||||||
|
|
||||||
|
return passedSearch && hasCategories && isMember && isLeader;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -17,11 +17,15 @@ import StablePage from './components/inventory/stable';
|
|||||||
|
|
||||||
// Social
|
// Social
|
||||||
import SocialContainer from './components/social/index';
|
import SocialContainer from './components/social/index';
|
||||||
import TavernPage from './components/social/tavern';
|
|
||||||
import InboxPage from './components/social/inbox/index';
|
import InboxPage from './components/social/inbox/index';
|
||||||
import InboxConversationPage from './components/social/inbox/conversationPage';
|
import InboxConversationPage from './components/social/inbox/conversationPage';
|
||||||
import GuildsDiscoveryPage from './components/social/guilds/discovery/index';
|
|
||||||
import GuildPage from './components/social/guilds/guild';
|
// Guilds
|
||||||
|
import GuildIndex from './components/guilds/index';
|
||||||
|
import TavernPage from './components/guilds/tavern';
|
||||||
|
import MyGuilds from './components/guilds/myGuilds';
|
||||||
|
import GuildsDiscoveryPage from './components/guilds/discovery';
|
||||||
|
import GuildPage from './components/guilds/guild';
|
||||||
|
|
||||||
Vue.use(VueRouter);
|
Vue.use(VueRouter);
|
||||||
|
|
||||||
@@ -46,11 +50,33 @@ export default new VueRouter({
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{ name: 'market', path: '/market', component: Page },
|
{ name: 'market', path: '/market', component: Page },
|
||||||
|
{
|
||||||
|
path: '/guilds',
|
||||||
|
component: GuildIndex,
|
||||||
|
children: [
|
||||||
|
{ name: 'tavern', path: 'tavern', component: TavernPage },
|
||||||
|
{
|
||||||
|
name: 'myGuilds',
|
||||||
|
path: 'myGuilds',
|
||||||
|
component: MyGuilds,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'guildsDiscovery',
|
||||||
|
path: 'discovery',
|
||||||
|
component: GuildsDiscoveryPage,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'guild',
|
||||||
|
path: 'guild/:guildId',
|
||||||
|
component: GuildPage,
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/social',
|
path: '/social',
|
||||||
component: SocialContainer,
|
component: SocialContainer,
|
||||||
children: [
|
children: [
|
||||||
{ name: 'tavern', path: 'tavern', component: TavernPage },
|
|
||||||
{
|
{
|
||||||
path: 'inbox',
|
path: 'inbox',
|
||||||
component: EmptyView,
|
component: EmptyView,
|
||||||
@@ -69,23 +95,6 @@ export default new VueRouter({
|
|||||||
},
|
},
|
||||||
{ name: 'challenges', path: 'challenges', component: Page },
|
{ name: 'challenges', path: 'challenges', component: Page },
|
||||||
{ name: 'party', path: 'party', component: Page },
|
{ name: 'party', path: 'party', component: Page },
|
||||||
{
|
|
||||||
path: 'guilds',
|
|
||||||
component: EmptyView,
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
name: 'guildsDiscovery',
|
|
||||||
path: 'discovery',
|
|
||||||
component: GuildsDiscoveryPage,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'guild',
|
|
||||||
path: 'guild/:guildId',
|
|
||||||
component: GuildPage,
|
|
||||||
props: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
162
website/client/store/actions/guilds.js
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
import omit from 'lodash/omit';
|
||||||
|
import findIndex from 'lodash/findIndex';
|
||||||
|
|
||||||
|
export async function getPublicGuilds (store, payload) {
|
||||||
|
let response = await axios.get('/api/v3/groups', {
|
||||||
|
params: {
|
||||||
|
type: 'publicGuilds',
|
||||||
|
paginate: true,
|
||||||
|
page: payload.page,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return response.data.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getMyGuilds (store) {
|
||||||
|
let response = await axios.get('/api/v3/groups', {
|
||||||
|
params: {
|
||||||
|
type: 'privateGuilds',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let guilds = response.data.data;
|
||||||
|
store.state.myGuilds = guilds;
|
||||||
|
|
||||||
|
return response.data.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getGroup (store, payload) {
|
||||||
|
let response = await axios.get(`/api/v3/groups/${payload.groupId}`);
|
||||||
|
|
||||||
|
// @TODO: should we store the active group for modifying?
|
||||||
|
// let guilds = response.data.data;
|
||||||
|
// store.state.myGuilds = guilds;
|
||||||
|
|
||||||
|
// @TODO: Populate wiht members, challenges, and invites
|
||||||
|
|
||||||
|
return response.data.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export async function join (store, payload) {
|
||||||
|
let response = await axios.post(`/api/v3/groups/${payload.guildId}/join`);
|
||||||
|
|
||||||
|
// @TODO: abstract for parties as well
|
||||||
|
store.state.user.data.guilds.push(payload.guildId);
|
||||||
|
if (payload.type === 'myGuilds') {
|
||||||
|
store.state.myGuilds.push(response.data.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.data.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function leave (store, payload) {
|
||||||
|
// @TODO: is the dafault for keepChallenges 'remain-in-challenges'
|
||||||
|
let data = {
|
||||||
|
keep: payload.keep,
|
||||||
|
keepChallenges: payload.keepChallenges,
|
||||||
|
};
|
||||||
|
let response = await axios.post(`/api/v3/groups/${payload.guildId}/leave`, data);
|
||||||
|
|
||||||
|
// @TODO: update for party
|
||||||
|
let index = store.state.user.data.guilds.indexOf(payload.guildId);
|
||||||
|
store.state.user.data.guilds.splice(index, 1);
|
||||||
|
if (payload.type === 'myGuilds') {
|
||||||
|
let guildIndex = findIndex(store.state.myGuilds, (guild) => {
|
||||||
|
return guild._id === payload.guildId;
|
||||||
|
});
|
||||||
|
store.state.myGuilds.splice(guildIndex, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.data.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function create (store, payload) {
|
||||||
|
let response = await axios.post('/api/v3/groups/', payload.group);
|
||||||
|
let newGroup = response.data.data;
|
||||||
|
|
||||||
|
// @TODO: Add party
|
||||||
|
if (newGroup.privacy === 'public') {
|
||||||
|
store.state.publicGuilds.push(newGroup);
|
||||||
|
} else if (newGroup.privacy === 'private') {
|
||||||
|
store.state.myGuilds.push(newGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function update (store, payload) {
|
||||||
|
// Remove populated fields
|
||||||
|
let groupDetailsToSend = omit(payload.group, ['chat', 'challenges', 'members', 'invites']);
|
||||||
|
if (groupDetailsToSend.leader && groupDetailsToSend.leader._id) groupDetailsToSend.leader = groupDetailsToSend.leader._id;
|
||||||
|
|
||||||
|
let response = await axios.put(`/api/v3/groups/${payload.group.id}`, {
|
||||||
|
data: groupDetailsToSend,
|
||||||
|
});
|
||||||
|
|
||||||
|
let updatedGroup = response.data.data;
|
||||||
|
|
||||||
|
// @TODO: Replace old group
|
||||||
|
// store.state.publicGuilds.push(newGroup);
|
||||||
|
|
||||||
|
return updatedGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function rejectInvite (store, payload) {
|
||||||
|
let response = await axios.post(`/api/v3/groups/${payload.groupId}/reject-invite`);
|
||||||
|
|
||||||
|
// @TODO: refresh or add guild
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function removeMember (store, payload) {
|
||||||
|
let response = await axios.post(`/api/v3/groups/${payload.groupId}/removeMember/${payload.memberId}`, {
|
||||||
|
message: payload.message,
|
||||||
|
});
|
||||||
|
|
||||||
|
// @TODO: find guild and remove member
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function invite (store, payload) {
|
||||||
|
let response = await axios.post(`/api/v3/groups/${payload.groupId}/invite`, {
|
||||||
|
uuids: payload.invitationDetails.uuids,
|
||||||
|
emails: payload.invitationDetails.emails,
|
||||||
|
});
|
||||||
|
|
||||||
|
// @TODO: find guild and add invites
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function inviteToQuest (store, payload) {
|
||||||
|
let response = await axios.post(`/api/v3/groups/${payload.groupId}/quests/invite/${payload.key}`);
|
||||||
|
|
||||||
|
// @TODO: Any updates?
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function addManager (store, payload) {
|
||||||
|
let response = await axios.post(`/api/v3/groups/${payload.groupId}/add-manager/`, {
|
||||||
|
managerId: payload.memberId,
|
||||||
|
});
|
||||||
|
|
||||||
|
// @TODO: Add managers to group or does the component handle?
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function removeManager (store, payload) {
|
||||||
|
let response = await axios.post(`/api/v3/groups/${payload.groupId}/remove-manager/`, {
|
||||||
|
managerId: payload.memberId,
|
||||||
|
});
|
||||||
|
|
||||||
|
// @TODO: Add managers to group or does the component handle?
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ import { flattenAndNamespace } from 'client/libs/store/helpers/internals';
|
|||||||
import * as common from './common';
|
import * as common from './common';
|
||||||
import * as user from './user';
|
import * as user from './user';
|
||||||
import * as tasks from './tasks';
|
import * as tasks from './tasks';
|
||||||
|
import * as guilds from './guilds';
|
||||||
|
|
||||||
// Actions should be named as 'actionName' and can be accessed as 'namespace:actionName'
|
// Actions should be named as 'actionName' and can be accessed as 'namespace:actionName'
|
||||||
// Example: fetch in user.js -> 'user:fetch'
|
// Example: fetch in user.js -> 'user:fetch'
|
||||||
@@ -11,6 +12,7 @@ const actions = flattenAndNamespace({
|
|||||||
common,
|
common,
|
||||||
user,
|
user,
|
||||||
tasks,
|
tasks,
|
||||||
|
guilds,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default actions;
|
export default actions;
|
||||||
@@ -17,6 +17,9 @@ export default function () {
|
|||||||
title: 'Habitica',
|
title: 'Habitica',
|
||||||
user: asyncResourceFactory(),
|
user: asyncResourceFactory(),
|
||||||
tasks: asyncResourceFactory(), // user tasks
|
tasks: asyncResourceFactory(), // user tasks
|
||||||
|
publicGuilds: [],
|
||||||
|
myGuilds: [],
|
||||||
|
editingGroup: {},
|
||||||
// content data, frozen to prevent Vue from modifying it since it's static and never changes
|
// content data, frozen to prevent Vue from modifying it since it's static and never changes
|
||||||
// TODO apply freezing to the entire codebase (the server) and not only to the client side?
|
// TODO apply freezing to the entire codebase (the server) and not only to the client side?
|
||||||
// NOTE this takes about 10-15ms on a fast computer
|
// NOTE this takes about 10-15ms on a fast computer
|
||||||
|
|||||||
@@ -170,6 +170,5 @@
|
|||||||
"hideQuickAllocation": "Hide stat allocation",
|
"hideQuickAllocation": "Hide stat allocation",
|
||||||
"quickAllocationLevelPopover": "Each level earns you one point to assign to an attribute of your choice. You can do so manually, or let the game decide for you using one of the Automatic Allocation options found in User -> Stats.",
|
"quickAllocationLevelPopover": "Each level earns you one point to assign to an attribute of your choice. You can do so manually, or let the game decide for you using one of the Automatic Allocation options found in User -> Stats.",
|
||||||
"invalidAttribute": "\"<%= attr %>\" is not a valid attribute.",
|
"invalidAttribute": "\"<%= attr %>\" is not a valid attribute.",
|
||||||
"notEnoughAttrPoints": "You don't have enough attribute points.",
|
"notEnoughAttrPoints": "You don't have enough attribute points."
|
||||||
"gearNotOwned": "You do not own this item."
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,64 @@
|
|||||||
{
|
{
|
||||||
"costumePopoverText": "Select \"Use Costume\" to equip items to your avatar without affecting the stats from your Battle Gear! This means that you can dress up your avatar in whatever outfit you like while still having your best Battle Gear equipped.",
|
"costumePopoverText": "Select \"Use Costume\" to equip items to your avatar without affecting the stats from your Battle Gear! This means that you can dress up your avatar in whatever outfit you like while still having your best Battle Gear equipped.",
|
||||||
"autoEquipPopoverText": "Select this option to automatically equip gear as soon as you purchase it.",
|
"autoEquipPopoverText": "Select this option to automatically equip gear as soon as you purchase it.",
|
||||||
"showAllGearItems": "Show All <%= items %> <%= type %> Gear Items",
|
|
||||||
"showLessGearItems": "Show Less <%= type %> Gear Items",
|
|
||||||
"noGearItemsOfType": "You don't own any pieces of <%= type %>.",
|
|
||||||
"costumeDisabled": "You have disabled your costume.",
|
"costumeDisabled": "You have disabled your costume.",
|
||||||
|
"newGuildPlaceHolder": "Enter your name",
|
||||||
|
"guildMembers": "Guild Members",
|
||||||
|
"guildBank": "Guild Bank",
|
||||||
|
"chatPlaceHolder": "Type your message to Guild members here",
|
||||||
|
"today": "Today",
|
||||||
|
"like": "Like",
|
||||||
|
"copyAsTodo": "Copy as To-Do",
|
||||||
|
"report": "Report",
|
||||||
|
"joinGuild": "Join Guild",
|
||||||
|
"inviteToGuild": "Invite to Guild",
|
||||||
|
"messageGuildLeader": "Message Guild Leader",
|
||||||
|
"donateGems": "Donate Gems",
|
||||||
|
"guildInformation": "Guild Information",
|
||||||
|
"updateGuild": "Update Guild",
|
||||||
|
"viewMembers": "View Members",
|
||||||
"items": "Items",
|
"items": "Items",
|
||||||
"sortBy": "Sort By",
|
"sortBy": "Sort By",
|
||||||
"groupBy2": "Group By",
|
"groupBy2": "Group By",
|
||||||
"quantity": "Quantity",
|
"quantity": "Quantity",
|
||||||
"AZ": "A-Z"
|
"AZ": "A-Z",
|
||||||
|
"sort": "Sort",
|
||||||
|
"memberCount": "Member Count",
|
||||||
|
"recentActivity": "Recent Activity",
|
||||||
|
"gearNotOwned": "You do not own this item.",
|
||||||
|
"showAllGearItems": "Show All <%= items %> <%= type %> Gear Items",
|
||||||
|
"showLessGearItems": "Show Less <%= type %> Gear Items",
|
||||||
|
"noGearItemsOfType": "You don't own any pieces of <%= type %>.",
|
||||||
|
"myGuilds": "My Guilds",
|
||||||
|
"guildsDiscovery": "Discover Guilds",
|
||||||
|
"habiticaOfficial": "Habitica Official",
|
||||||
|
"animals": "Animals",
|
||||||
|
"artDesign": "Art & Design",
|
||||||
|
"booksWriting": "Books & Writing",
|
||||||
|
"comicsHobbies": "Comics & Hobbies",
|
||||||
|
"diyCrafts": "DIY & Crafts",
|
||||||
|
"education": "Education",
|
||||||
|
"foodCooking": "Food & Cooking",
|
||||||
|
"healthFitness": "Health & Fitness",
|
||||||
|
"music": "Music",
|
||||||
|
"relationship": "Relationships",
|
||||||
|
"scienceTech": "Science & Technology",
|
||||||
|
"guildLeader": "Guild Leader",
|
||||||
|
"member": "Member",
|
||||||
|
"goldTier": "Gold Tier",
|
||||||
|
"silverTier": "Silver Tier",
|
||||||
|
"bronzeTier": "Bronze Tier",
|
||||||
|
"privacySettings": "Privacy Settings",
|
||||||
|
"onlyLeaderCreatesChallenges": "Only the Guild Leader can create Guild Challenges",
|
||||||
|
"guildLeaderCantBeMessaged": "Guild Leader can not be messaged directly",
|
||||||
|
"privateGuild": "Private Guild",
|
||||||
|
"allowGuildInvationsFromNonMembers": "Allow Guild invitations from non-members",
|
||||||
|
"charactersRemaining": "characters remaining",
|
||||||
|
"guildDescriptionPlaceHolder": "Write a short description advertising your Guild to other Habiticans. What is the main purpose of your Guild and why should people join it? Try to include useful keywords in the description so that Habiticans can easily find it when they search!",
|
||||||
|
"guildGemCostInfo": "A Gem cost promotes high quality Guilds and is transferred into your Guild's bank.",
|
||||||
|
"categories": "Categories",
|
||||||
|
"noGuildsTitle": "You aren’t a member of any Guilds.",
|
||||||
|
"noGuildsParagraph1": "Guilds are social groups created by other players that can offer you support, accountability, and encouraging chat.",
|
||||||
|
"noGuildsParagraph2": "Click the Discover tab to see recommended Guilds based on your interests, browse Habitica’s public Guilds, or create your own Guild.",
|
||||||
|
"privateDescription": "A private Guild will not be displayed in Habitica’s Guild directory. New members can be added by invitation only."
|
||||||
}
|
}
|
||||||
|
|||||||