From e9e426554536ba33f30138856be5ea99828e21a7 Mon Sep 17 00:00:00 2001 From: SabreCat Date: Tue, 2 May 2023 09:51:33 -0500 Subject: [PATCH] Squashed commit of the following: commit 00affb306655a543f5d29b3af6361e686b577a97 Author: SabreCat Date: Tue May 2 09:47:25 2023 -0500 fix(tests): account for invite limit changes commit 47661117f9fd661b8bc8f63b7cc7c8d5f8fa0fd7 Author: SabreCat Date: Mon May 1 17:39:29 2023 -0500 fix(lfp): final polish commit 6a1e5af1db0dd90be3ced7e223f53c9183a206f5 Merge: 728ed2ddad 9e0777bb42 Author: SabreCat Date: Mon May 1 16:54:12 2023 -0500 Merge branch 'release' into sabrecat/party-seeking commit 728ed2ddad7f0962d28f1ab0a271e3555b19296c Author: SabreCat Date: Thu Apr 27 16:51:06 2023 -0500 fix(lfp): loading layout and page visit event commit 8a56ab329bff922e05963e3ef78fbc26ff273924 Author: SabreCat Date: Wed Apr 26 16:54:46 2023 -0500 fix(faq): copy and style updates commit 6fd00d7f30150a1802e5a37edbb914ef120caf9a Author: SabreCat Date: Fri Apr 21 17:12:52 2023 -0500 feat(lfp): fixes, analytics, FAQ commit 4b5d7304ad7cfc5f72320b23456ed2898e53caac Author: SabreCat Date: Mon Apr 17 15:13:03 2023 -0500 fix(lfp): smol tweaks commit 9a5476a2558eb17a603f4aae1b5b2d35773be8b4 Author: SabreCat Date: Thu Apr 13 16:03:33 2023 -0500 feat(lfp): refresh button commit aa58f5018469f38a9a9d31c3bffa26bb88a8c672 Merge: bbb03d006e c8adf20804 Author: SabreCat Date: Tue Apr 11 17:44:56 2023 -0500 Merge branch 'release' into sabrecat/party-seeking commit bbb03d006e8b122bb7206bdc778a31de422167bb Author: SabreCat Date: Tue Apr 4 18:30:50 2023 -0500 fix(lint): whitespace and const commit 23683ad29a4cce0b0da061ad6c030982034c0a9c Author: SabreCat Date: Tue Apr 4 17:02:57 2023 -0500 chore(LFP): add analytics also re-fix loading state commit 4477d84f5266c87f5583368029b72153f00f0568 Author: SabreCat Date: Mon Apr 3 16:24:26 2023 -0500 fix(LFP): address issues with loading commit bdc5154f24bb5e50963376c3c0c9cc73c0b05ccc Merge: 81923eef6f 229ed46425 Author: SabreCat Date: Mon Apr 3 15:58:12 2023 -0500 Merge branch 'release' into sabrecat/party-seeking commit 81923eef6f0c627d079475a28f9d93d8e4628934 Author: SabreCat Date: Thu Mar 30 16:44:49 2023 -0500 feat(LFP): release candidate commit fe1f8939fc6b09d36cfaf0b6e5838df04e41009d Author: SabreCat Date: Wed Mar 29 17:35:54 2023 -0500 WIP(LFP): fixes commit afc361f5a9f806cbd814ad910d1274e3a6609efd Merge: d6b5cbdebc 7ede3acd01 Author: SabreCat Date: Wed Mar 29 16:24:39 2023 -0500 Merge branch 'release' into sabrecat/party-seeking commit d6b5cbdebc2829e9325ea57fb5deeccc128c1635 Author: SabreCat Date: Tue Mar 28 16:13:18 2023 -0500 WIP(LFP): change copy, add close X, fix API response commit 4274a4625862351ef0ecf33c8a3249ca5ebec7cb Author: SabreCat Date: Mon Mar 27 17:07:36 2023 -0500 fix(LFP): layout, unset when stopping commit 95abfcfa5f13c9cce6385206947a47f85b76d11d Merge: 4a360eedd8 53c536b525 Author: SabreCat Date: Mon Mar 27 16:32:46 2023 -0500 Merge branch 'release' into sabrecat/party-seeking commit 4a360eedd8b9cf41d3a0fe7a4cfaa72c5bd7bd26 Author: SabreCat Date: Thu Mar 23 16:54:49 2023 -0500 feat(LFP): completed style and infinite scroll commit bbc439d9d03c9631a450236eb33af66f0428fa50 Author: SabreCat Date: Tue Mar 21 10:40:26 2023 -0500 WIP(LFP): nicer layout, buffs fix commit 1658688597456663477ab19da61ae1b9bc85cf2a Merge: 664507434f 027e61a93e Author: SabreCat Date: Tue Mar 21 09:29:02 2023 -0500 Merge branch 'release' into sabrecat/party-seeking commit 664507434f2f76e6bf3b61cdc9e3daddb81204af Author: SabreCat Date: Fri Mar 17 17:13:08 2023 -0500 WIP(LFP): API and client adjustments commit 2f0b6f2517f9e2d634cb23ee303cfb4542e998ce Merge: 0db1704325 a16098ccda Author: SabreCat Date: Fri Mar 17 16:45:13 2023 -0500 Merge branch 'release' into sabrecat/party-seeking commit 0db1704325c3555f0b5d9c8d1dfc44177e90c093 Author: SabreCat Date: Mon Mar 13 14:48:10 2023 -0500 fix(modal): scrollbar for squashed viewports commit 733c35192e0a4e31e1bebfdd7488cfc1f7587f36 Author: SabreCat Date: Thu Mar 9 12:51:02 2023 -0600 WIP(party): seekers functional rough commit d4b854410b557db26eec6e6a26b6d174c02cee3a Merge: 7fe919825a 0b6b967753 Author: SabreCat Date: Thu Mar 9 10:07:28 2023 -0600 Merge branch 'release' into sabrecat/party-seeking commit 7fe919825abfb6d518cb93b91f5997d3831bd0b5 Author: SabreCat Date: Thu Mar 2 14:40:09 2023 -0600 feat(party): several adjustments to seeking feature commit c93900efcf925f7aaa4c4cb56b4451f19adfb1b3 Author: SabreCat Date: Wed Mar 1 20:37:11 2023 -0600 feat(party): initial Seeking API commit 8bb784daeceb14c23992a6f3af1054a900fc26c1 Merge: e19a661a21 f327795761 Author: SabreCat Date: Wed Mar 1 18:58:20 2023 -0600 Merge branch 'release' into sabrecat/party-seeking commit e19a661a2163a50307a286379bffb44201ed392e Author: SabreCat Date: Fri Feb 24 15:51:42 2023 -0600 WIP(parties): add seeking flag and modal toggle --- .../groups/POST-groups_groupId_join.test.js | 41 -- .../groups/POST-groups_invite.test.js | 48 +-- website/client/src/assets/scss/icon.scss | 21 +- website/client/src/assets/svg/sync-2.svg | 1 + .../components/groups/createPartyModal.vue | 175 ++++++--- .../client/src/components/groups/group.vue | 3 +- .../src/components/groups/lookingForParty.vue | 351 ++++++++++++++++++ .../src/components/groups/rightSidebar.vue | 6 +- .../client/src/components/header/index.vue | 10 +- website/client/src/components/header/menu.vue | 47 ++- .../header/notifications/partyInvitation.vue | 3 + website/client/src/components/static/faq.vue | 54 ++- .../src/components/static/staticWrapper.vue | 4 - website/client/src/components/ui/closeX.vue | 46 +++ website/client/src/components/userLink.vue | 15 +- website/client/src/router/index.js | 8 +- website/client/src/store/actions/party.js | 12 + website/common/locales/en/faq.json | 70 +++- website/common/locales/en/front.json | 2 +- website/common/locales/en/generic.json | 1 + website/common/locales/en/groups.json | 26 +- website/common/locales/en/npc.json | 2 +- website/common/script/content/faq.js | 53 ++- .../common/script/errors/apiErrorMessages.js | 1 + website/server/controllers/api-v3/groups.js | 131 +++++-- website/server/libs/invites/index.js | 50 ++- website/server/libs/user/index.js | 26 +- website/server/models/group.js | 4 + website/server/models/user/schema.js | 1 + 29 files changed, 999 insertions(+), 213 deletions(-) create mode 100644 website/client/src/assets/svg/sync-2.svg create mode 100644 website/client/src/components/groups/lookingForParty.vue create mode 100644 website/client/src/components/ui/closeX.vue diff --git a/test/api/v3/integration/groups/POST-groups_groupId_join.test.js b/test/api/v3/integration/groups/POST-groups_groupId_join.test.js index 92acf9023f..bbad7a8401 100644 --- a/test/api/v3/integration/groups/POST-groups_groupId_join.test.js +++ b/test/api/v3/integration/groups/POST-groups_groupId_join.test.js @@ -258,47 +258,6 @@ describe('POST /group/:groupId/join', () => { await expect(user.get('/user')).to.eventually.have.nested.property('items.quests.basilist', 2); }); - it('deletes previous party where the user was the only member', async () => { - const userToInvite = await generateUser(); - const oldParty = await userToInvite.post('/groups', { // add user to a party - name: 'Another Test Party', - type: 'party', - }); - - await expect(checkExistence('groups', oldParty._id)).to.eventually.equal(true); - await user.post(`/groups/${party._id}/invite`, { - uuids: [userToInvite._id], - }); - await userToInvite.post(`/groups/${party._id}/join`); - - await expect(user.get('/user')).to.eventually.have.nested.property('party._id', party._id); - await expect(checkExistence('groups', oldParty._id)).to.eventually.equal(false); - }); - - it('does not allow user to leave a party if a quest was active and they were the only member', async () => { - const userToInvite = await generateUser(); - const oldParty = await userToInvite.post('/groups', { // add user to a party - name: 'Another Test Party', - type: 'party', - }); - - await userToInvite.update({ - [`items.quests.${PET_QUEST}`]: 1, - }); - await userToInvite.post(`/groups/${oldParty._id}/quests/invite/${PET_QUEST}`); - - await expect(checkExistence('groups', oldParty._id)).to.eventually.equal(true); - await user.post(`/groups/${party._id}/invite`, { - uuids: [userToInvite._id], - }); - - await expect(userToInvite.post(`/groups/${party._id}/join`)).to.eventually.be.rejected.and.eql({ - code: 401, - error: 'NotAuthorized', - message: t('messageCannotLeaveWhileQuesting'), - }); - }); - it('invites joining member to active quest', async () => { await user.update({ [`items.quests.${PET_QUEST}`]: 1, diff --git a/test/api/v3/integration/groups/POST-groups_invite.test.js b/test/api/v3/integration/groups/POST-groups_invite.test.js index 02f8c5e20c..58a478be00 100644 --- a/test/api/v3/integration/groups/POST-groups_invite.test.js +++ b/test/api/v3/integration/groups/POST-groups_invite.test.js @@ -1,6 +1,7 @@ import { v4 as generateUUID } from 'uuid'; import nconf from 'nconf'; import { + createAndPopulateGroup, generateUser, generateGroup, translate as t, @@ -581,20 +582,7 @@ describe('Post /groups/:groupId/invite', () => { }); }); - it('allow inviting a user to a party if they are partying solo', async () => { - const userToInvite = await generateUser(); - await userToInvite.post('/groups', { // add user to a party - name: 'Another Test Party', - type: 'party', - }); - - await inviter.post(`/groups/${party._id}/invite`, { - uuids: [userToInvite._id], - }); - expect((await userToInvite.get('/user')).invitations.parties[0].id).to.equal(party._id); - }); - - it('allow inviting a user to 2 different parties', async () => { + it('allows inviting a user to 2 different parties', async () => { // Create another inviter const inviter2 = await generateUser(); @@ -635,29 +623,47 @@ describe('Post /groups/:groupId/invite', () => { }); expect((await userToInvite.get('/user')).invitations.parties[0].id).to.equal(party._id); }); + }); + + describe('party size limits', () => { + let party, partyLeader; + + beforeEach(async () => { + group = await createAndPopulateGroup({ + groupDetails: { + name: 'Test Party', + type: 'party', + privacy: 'private', + }, + // Generate party with 20 members + members: PARTY_LIMIT_MEMBERS - 10, + }); + party = group.group; + partyLeader = group.groupLeader; + }); it('allows 30 members in a party', async () => { const invitesToGenerate = []; - // Generate 29 users to invite (29 + leader = 30 members) - for (let i = 0; i < PARTY_LIMIT_MEMBERS - 1; i += 1) { + // Generate 10 new invites + for (let i = 1; i < 10; i += 1) { invitesToGenerate.push(generateUser()); } const generatedInvites = await Promise.all(invitesToGenerate); // Invite users - expect(await inviter.post(`/groups/${party._id}/invite`, { + expect(await partyLeader.post(`/groups/${party._id}/invite`, { uuids: generatedInvites.map(invite => invite._id), })).to.be.an('array'); }).timeout(10000); - it('does not allow 30+ members in a party', async () => { + it('does not allow >30 members in a party', async () => { const invitesToGenerate = []; - // Generate 30 users to invite (30 + leader = 31 members) - for (let i = 0; i < PARTY_LIMIT_MEMBERS; i += 1) { + // Generate 11 invites + for (let i = 1; i < 11; i += 1) { invitesToGenerate.push(generateUser()); } const generatedInvites = await Promise.all(invitesToGenerate); // Invite users - await expect(inviter.post(`/groups/${party._id}/invite`, { + await expect(partyLeader.post(`/groups/${party._id}/invite`, { uuids: generatedInvites.map(invite => invite._id), })) .to.eventually.be.rejected.and.eql({ diff --git a/website/client/src/assets/scss/icon.scss b/website/client/src/assets/scss/icon.scss index 92930f1124..3ab036333a 100644 --- a/website/client/src/assets/scss/icon.scss +++ b/website/client/src/assets/scss/icon.scss @@ -24,9 +24,9 @@ } } -.icon-16 { - width: 16px; - height: 16px; +.icon-10 { + width: 10px; + height: 10px; } .icon-12 { @@ -34,21 +34,26 @@ height: 12px; } +.icon-16 { + width: 16px; + height: 16px; +} + .icon-24 { width: 24px; height: 24px; } +.icon-32 { + width: 32px; + height: 32px; +} + .icon-48 { width: 48px; height: 48px; } -.icon-10 { - width: 10px; - height: 10px; -} - .inline { display: inline-block; } diff --git a/website/client/src/assets/svg/sync-2.svg b/website/client/src/assets/svg/sync-2.svg new file mode 100644 index 0000000000..61a77879df --- /dev/null +++ b/website/client/src/assets/svg/sync-2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/client/src/components/groups/createPartyModal.vue b/website/client/src/components/groups/createPartyModal.vue index 8b00d4e06c..17c0184331 100644 --- a/website/client/src/components/groups/createPartyModal.vue +++ b/website/client/src/components/groups/createPartyModal.vue @@ -11,9 +11,12 @@
-

+

{{ $t('playInPartyTitle') }} -

+

+
-
+
-

- {{ $t('wantToJoinPartyTitle') }} -

-

-
-
- -
-
- @ -
- {{ user.auth.local.username }} -
-
-
- {{ $t('copy') }} -
-
+ {{ $t('wantToJoinPartyTitle') }} + +

+

+
+
+ {{ $t('currentlyLookingForParty') }} +
+
+
+
- @@ -107,15 +134,27 @@ cursor: pointer; } + .green-bar { + height: 32px; + font-size: 14px; + font-weight: bold; + line-height: 1.71; + text-align: center; + color: $green-1; + background-color: $green-100; + border-radius: 2px; + padding: 4px 0px 4px 0px; + } + .grey-row { background-color: $gray-700; color: #4e4a57; padding: 2em; - border-radius: 0px 0px 2px 2px; + border-radius: 0px 0px 8px 8px; } - h2 { - color: $gray-100; + h1 { + color: $purple-300; } .header-wrap { @@ -132,10 +171,6 @@ border-radius: 2px 2px 0 0; image-rendering: optimizequality; } - - h2 { - color: $purple-200; - } } .heading { @@ -182,6 +217,21 @@ margin: 0.75rem auto 0.75rem 0.25rem; } + p { + line-height: 1.71; + } + + .red-link { + cursor: pointer; + font-size: 14px; + line-height: 1.71; + text-align: center; + color: $maroon-50; + &:hover { + text-decoration: underline; + } + } + .small { color: $gray-200; margin: auto 0.5rem auto 0.25rem; @@ -192,21 +242,35 @@ import { mapState } from '@/libs/store'; import * as Analytics from '@/libs/analytics'; import notifications from '@/mixins/notifications'; +import closeX from '../ui/closeX'; import copyIcon from '@/assets/svg/copy.svg'; export default { + components: { + closeX, + }, mixins: [notifications], data () { return { icons: Object.freeze({ copy: copyIcon, }), + seeking: false, }; }, computed: { ...mapState({ user: 'user.data' }), }, + mounted () { + this.seeking = Boolean(this.user.party.seeking); + Analytics.track({ + eventName: 'Start a Party button', + eventAction: 'Start a Party button', + eventCategory: 'behavior', + hitType: 'event', + }, { trackOnClient: true }); + }, methods: { async createParty () { const group = { @@ -223,7 +287,10 @@ export default { }); this.$root.$emit('bv::hide::modal', 'create-party-modal'); - this.$router.push('/party'); + await this.$router.push('/party'); + }, + close () { + this.$root.$emit('bv::hide::modal', 'create-party-modal'); }, copyUsername () { if (navigator.clipboard) { @@ -238,6 +305,12 @@ export default { } this.text(this.$t('usernameCopied')); }, + seekParty () { + this.$store.dispatch('user:set', { + 'party.seeking': !this.user.party.seeking ? new Date() : null, + }); + this.seeking = !this.seeking; + }, }, }; diff --git a/website/client/src/components/groups/group.vue b/website/client/src/components/groups/group.vue index 85dbf296af..a238ef5664 100644 --- a/website/client/src/components/groups/group.vue +++ b/website/client/src/components/groups/group.vue @@ -542,7 +542,8 @@ export default { await this.$store.dispatch('guilds:leave', data); if (this.isParty) { - this.$router.push({ name: 'tasks' }); + await this.$router.push({ name: 'tasks' }); + window.location.reload(true); } }, upgradeGroup () { diff --git a/website/client/src/components/groups/lookingForParty.vue b/website/client/src/components/groups/lookingForParty.vue new file mode 100644 index 0000000000..eda06f5759 --- /dev/null +++ b/website/client/src/components/groups/lookingForParty.vue @@ -0,0 +1,351 @@ + + + + + diff --git a/website/client/src/components/groups/rightSidebar.vue b/website/client/src/components/groups/rightSidebar.vue index f5eafdcaf6..cab95cea2e 100644 --- a/website/client/src/components/groups/rightSidebar.vue +++ b/website/client/src/components/groups/rightSidebar.vue @@ -2,7 +2,7 @@