From 85ecd5ebb24481b0a5f1311ede1f62bdeb7e36b0 Mon Sep 17 00:00:00 2001 From: negue Date: Sun, 12 May 2024 17:50:46 +0200 Subject: [PATCH 01/64] split component prepare new views / states --- website/client/src/pages/private-messages.vue | 658 +++++++++--------- .../pm-conversation-item.vue} | 6 +- .../pm-conversations-list.vue | 64 ++ .../pages/private-messages/pm-empty-state.vue | 19 + .../pm-new-message-started.vue | 19 + 5 files changed, 440 insertions(+), 326 deletions(-) rename website/client/src/{components/messages/conversationItem.vue => pages/private-messages/pm-conversation-item.vue} (97%) create mode 100644 website/client/src/pages/private-messages/pm-conversations-list.vue create mode 100644 website/client/src/pages/private-messages/pm-empty-state.vue create mode 100644 website/client/src/pages/private-messages/pm-new-message-started.vue diff --git a/website/client/src/pages/private-messages.vue b/website/client/src/pages/private-messages.vue index 581970297be..fca463f5f67 100644 --- a/website/client/src/pages/private-messages.vue +++ b/website/client/src/pages/private-messages.vue @@ -52,25 +52,11 @@ @change="toggleOpt()" /> -
- -
+
-
-
-
-

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

-

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

-
-
+ + +
-
-

{{ disabledTexts.title }}

-

{{ disabledTexts.description }}

-
+
@@ -305,39 +325,6 @@ $background: $white; border-bottom: 1px solid $gray-500; } -.empty-messages { - h3, p { - color: $gray-200; - margin: 0rem; - } - - h2 { - color: $gray-200; - margin-bottom: 1rem; - } - - p { - font-size: 12px; - } - - .no-messages-box { - display: flex; - flex-direction: column; - align-items: center; - width: 330px; - } - - .envelope { - color: $gray-400 !important; - margin-bottom: 1.5rem; - - ::v-deep svg { - width: 64px; - height: 48px; - } - } -} - h3 { margin: 0rem; @@ -456,25 +443,6 @@ h3 { } } -.pm-disabled-caption { - padding-top: 1em; - z-index: 2; - - h4, p { - color: $gray-200; - } - - h4 { - margin-top: 0; - margin-bottom: 0.4em; - } - - p { - font-size: 12px; - margin-bottom: 0; - } -} - .left-header { max-width: calc(330px - 2rem); // minus the left padding flex: 1; @@ -551,20 +519,25 @@ import faceAvatar from '@/components/faceAvatar'; import Avatar from '@/components/avatar'; import { EVENTS } from '@/libs/events'; import PmConversationsList from './private-messages/pm-conversations-list.vue'; +import PmEmptyState from './private-messages/pm-empty-state.vue'; +import PmDisabledState from '@/pages/private-messages/pm-disabled-state.vue'; // extract to a shared path const CONVERSATIONS_PER_PAGE = 10; const PM_PER_PAGE = 10; -const UI_STATES = { +const UI_STATES = Object.freeze({ NO_CONVERSATIONS: 'NO_CONVERSATIONS', NO_CONVERSATIONS_SELECTED: 'NO_CONVERSATIONS_SELECTED', START_NEW_CONVERSATION: 'START_NEW_CONVERSATION', CONVERSATION_SELECTED: 'CONVERSATION_SELECTED', -}; + DISABLED: 'DISABLED', +}); export default defineComponent({ components: { + PmDisabledState, + PmEmptyState, PmConversationsList, Avatar, messageList, @@ -601,6 +574,7 @@ export default defineComponent({ messageIcon, mail, }), + UI_STATES, loaded: false, showPopover: false, @@ -617,7 +591,6 @@ export default defineComponent({ messages: [], messagesLoading: false, MAX_MESSAGE_LENGTH: MAX_MESSAGE_LENGTH.toString(), - uiState: '', }; }, computed: { @@ -700,6 +673,7 @@ export default defineComponent({ return ordered; }, + // TODO might be deleted once all states are done placeholderTexts () { if (this.user.flags.chatRevoked) { return { @@ -767,6 +741,28 @@ export default defineComponent({ return !this.selectedConversation || !this.selectedConversation.key || this.disabledTexts !== null; }, + uiState () { + if (true) { + // testing states + return UI_STATES.NO_CONVERSATIONS; + } + + if (this.disabledTexts) { + return UI_STATES.DISABLED; + } + + if (this.loadedConversations.length === 0) { + return UI_STATES.NO_CONVERSATIONS; + } + + if (!this.selectedConversation) { + this.uiState = UI_STATES.NO_CONVERSATIONS_SELECTED; + } + + // TODO start new conversation + + return UI_STATES.CONVERSATION_SELECTED; + }, shouldShowInputPanel () { const currentUiState = this.uiState; @@ -843,8 +839,6 @@ export default defineComponent({ this.loadedConversations = []; this.selectedConversation = {}; - this.uiState = UI_STATES.NO_CONVERSATIONS; - await this.loadConversations(); await this.$store.dispatch('user:markPrivMessagesRead'); @@ -860,10 +854,6 @@ export default defineComponent({ const loadedConversations = conversationRes.data.data; this.canLoadMoreConversations = loadedConversations.length === CONVERSATIONS_PER_PAGE; this.loadedConversations.push(...loadedConversations); - - if (this.loadedConversations.length !== 0 && !this.selectedConversation) { - this.uiState = UI_STATES.NO_CONVERSATIONS_SELECTED; - } }, messageRemoved (message) { const messages = this.messagesByConversation[this.selectedConversation.key]; diff --git a/website/client/src/pages/private-messages/pm-disabled-state.vue b/website/client/src/pages/private-messages/pm-disabled-state.vue new file mode 100644 index 00000000000..b5f22b87bc0 --- /dev/null +++ b/website/client/src/pages/private-messages/pm-disabled-state.vue @@ -0,0 +1,37 @@ + + + + + diff --git a/website/client/src/pages/private-messages/pm-empty-state.vue b/website/client/src/pages/private-messages/pm-empty-state.vue index 53170abcb43..87f087d9577 100644 --- a/website/client/src/pages/private-messages/pm-empty-state.vue +++ b/website/client/src/pages/private-messages/pm-empty-state.vue @@ -1,19 +1,45 @@ - diff --git a/website/common/locales/en/generic.json b/website/common/locales/en/generic.json index bfaa2387278..48c4b4f8cf2 100644 --- a/website/common/locales/en/generic.json +++ b/website/common/locales/en/generic.json @@ -206,7 +206,7 @@ "dismissAll": "Dismiss All", "messages": "Messages", "emptyMessagesLine1": "You don't have any messages", - "emptyMessagesLine2": "You can send a new message to a user by visiting their profile and clicking the \"Message\" button.", + "emptyMessagesLine2": "Send a message to start a conversation with your Party members or another Habitica player", "userSentMessage": "<%- user %> sent you a message", "letsgo": "Let's Go!", "selected": "Selected", From 7ce1136eff8e9ba228e4b340fa2049dc2e666767 Mon Sep 17 00:00:00 2001 From: negue Date: Sun, 12 May 2024 23:08:04 +0200 Subject: [PATCH 03/64] fix empty state mail icon --- website/client/src/pages/private-messages.vue | 14 +++----------- .../pages/private-messages/pm-empty-state.vue | 18 ++++++++++++------ 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/website/client/src/pages/private-messages.vue b/website/client/src/pages/private-messages.vue index 3e9295de98c..f9a38e1dad8 100644 --- a/website/client/src/pages/private-messages.vue +++ b/website/client/src/pages/private-messages.vue @@ -255,11 +255,10 @@ $pmHeaderHeight: 56px; .envelope { color: $gray-400 !important; - margin-bottom: 1.5rem; - ::v-deep svg { - width: 64px; - height: 48px; + svg { + width: 86px; + height: 64px; } } } @@ -504,7 +503,6 @@ import Vue, { defineComponent } from 'vue'; import moment from 'moment'; import groupBy from 'lodash/groupBy'; import orderBy from 'lodash/orderBy'; -import habiticaMarkdown from 'habitica-markdown'; import axios from 'axios'; import { MAX_MESSAGE_LENGTH } from '@/../../common/script/constants'; import { mapState } from '@/libs/store'; @@ -882,8 +880,6 @@ export default defineComponent({ await this.loadMessages(); } - this.uiState = UI_STATES.CONVERSATION_SELECTED; - this.scrollToBottom(); }, sendPrivateMessage () { @@ -946,10 +942,6 @@ export default defineComponent({ chatscroll.scrollTop = chatscroll.scrollHeight; }); }, - parseMarkdown (text) { - if (!text) return null; - return habiticaMarkdown.render(String(text)); - }, infiniteScrollTrigger () { // show loading and wait until the loadMore debounced // or else it would trigger on every scrolling-pixel (while not loading) diff --git a/website/client/src/pages/private-messages/pm-empty-state.vue b/website/client/src/pages/private-messages/pm-empty-state.vue index 87f087d9577..365cdddb387 100644 --- a/website/client/src/pages/private-messages/pm-empty-state.vue +++ b/website/client/src/pages/private-messages/pm-empty-state.vue @@ -5,12 +5,12 @@
-

+ {{ $t('emptyMessagesLine1') }} -

+

{{ $t('emptyMessagesLine2') }}

@@ -25,10 +25,16 @@ diff --git a/website/client/src/pages/private-messages/pm-empty-state.vue b/website/client/src/pages/private-messages/pm-empty-state.vue index 365cdddb387..05cf8803b8f 100644 --- a/website/client/src/pages/private-messages/pm-empty-state.vue +++ b/website/client/src/pages/private-messages/pm-empty-state.vue @@ -18,6 +18,7 @@ diff --git a/website/client/src/pages/private-messages/start-new-conversation-input-header.vue b/website/client/src/pages/private-messages/start-new-conversation-input-header.vue new file mode 100644 index 00000000000..512f39e8261 --- /dev/null +++ b/website/client/src/pages/private-messages/start-new-conversation-input-header.vue @@ -0,0 +1,31 @@ + + + + + diff --git a/website/client/src/router/index.js b/website/client/src/router/index.js index f3e87d45845..ddd69d91109 100644 --- a/website/client/src/router/index.js +++ b/website/client/src/router/index.js @@ -48,7 +48,7 @@ const GroupPlanIndex = () => import(/* webpackChunkName: "group-plans" */ '@/com const GroupPlanTaskInformation = () => import(/* webpackChunkName: "group-plans" */ '@/components/group-plans/taskInformation'); const GroupPlanBilling = () => import(/* webpackChunkName: "group-plans" */ '@/components/group-plans/billing'); -const MessagesIndex = () => import(/* webpackChunkName: "private-messages" */ '@/pages/private-messages'); +const MessagesIndex = () => import(/* webpackChunkName: "private-messages" */ '@/pages/private-messages/index.vue'); // Challenges const ChallengeIndex = () => import(/* webpackChunkName: "challenges" */ '@/components/challenges/index'); From 17b27600636397b573d1ba1159219425f60e2161 Mon Sep 17 00:00:00 2001 From: negue Date: Sat, 25 May 2024 18:49:12 +0200 Subject: [PATCH 05/64] extract autoCompleteHelper.js --- website/client/src/components/groups/chat.vue | 105 ++---------------- .../client/src/mixins/autoCompleteHelper.js | 102 +++++++++++++++++ 2 files changed, 113 insertions(+), 94 deletions(-) create mode 100644 website/client/src/mixins/autoCompleteHelper.js diff --git a/website/client/src/components/groups/chat.vue b/website/client/src/components/groups/chat.vue index 3cca86a6270..150a84e3194 100644 --- a/website/client/src/components/groups/chat.vue +++ b/website/client/src/components/groups/chat.vue @@ -22,13 +22,13 @@ :placeholder="placeholder" :class="{'user-entry': newMessage}" :maxlength="MAX_MESSAGE_LENGTH" - @keydown="updateCarretPosition" + @keydown="autoCompleteMixinUpdateCarretPosition" @keyup.ctrl.enter="sendMessageShortcut()" - @keydown.tab="handleTab($event)" - @keydown.up="selectPreviousAutocomplete($event)" - @keydown.down="selectNextAutocomplete($event)" - @keypress.enter="selectAutocomplete($event)" - @keydown.esc="handleEscape($event)" + @keydown.tab="autoCompleteMixinHandleTab($event)" + @keydown.up="autoCompleteMixinSelectPreviousAutocomplete($event)" + @keydown.down="autoCompleteMixinSelectNextAutocomplete($event)" + @keypress.enter="autoCompleteMixinSelectAutocomplete($event)" + @keydown.esc="autoCompleteMixinHandleEscape($event)" @paste="disableMessageSendShortcut()" > {{ currentLength }} / {{ MAX_MESSAGE_LENGTH }} @@ -36,8 +36,8 @@ ref="autocomplete" :text="newMessage" :textbox="textbox" - :coords="coords" - :caret-position="caretPosition" + :coords="mixinData.autoComplete.coords" + :caret-position="mixinData.autoComplete.caretPosition" :chat="group.chat" @select="selectedAutocomplete" /> @@ -86,8 +86,6 @@ From 8b6a032d73eb295931d1faf67d537d508e752eb5 Mon Sep 17 00:00:00 2001 From: negue Date: Sat, 25 May 2024 19:29:59 +0200 Subject: [PATCH 07/64] style plus button + focus input --- .../src/pages/private-messages/index.vue | 18 ++++++++++++++---- .../start-new-conversation-input-header.vue | 8 +++++++- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/website/client/src/pages/private-messages/index.vue b/website/client/src/pages/private-messages/index.vue index c841dfdb8bc..dc77040e2c7 100644 --- a/website/client/src/pages/private-messages/index.vue +++ b/website/client/src/pages/private-messages/index.vue @@ -17,10 +17,14 @@
@@ -313,8 +317,12 @@ $background: $white; width: 32px; } - .btn { - width: 40px; + .plus-button { + padding: 10px 14px; + + &.new-message-mode { + color: $gray-200; + } } } @@ -539,6 +547,7 @@ import PmEmptyState from './pm-empty-state.vue'; import PmDisabledState from './pm-disabled-state.vue'; import PmNewMessageStarted from './pm-new-message-started.vue'; import StartNewConversationInputHeader from './start-new-conversation-input-header.vue'; +import positiveIcon from '@/assets/svg/positive.svg'; // extract to a shared path const CONVERSATIONS_PER_PAGE = 10; @@ -593,6 +602,7 @@ export default defineComponent({ icons: Object.freeze({ messageIcon, mail, + positive: positiveIcon, }), UI_STATES, showStartNewConversationInput: false, diff --git a/website/client/src/pages/private-messages/start-new-conversation-input-header.vue b/website/client/src/pages/private-messages/start-new-conversation-input-header.vue index 21329917cea..8edfb9492ac 100644 --- a/website/client/src/pages/private-messages/start-new-conversation-input-header.vue +++ b/website/client/src/pages/private-messages/start-new-conversation-input-header.vue @@ -6,12 +6,15 @@ > diff --git a/website/common/locales/en/generic.json b/website/common/locales/en/generic.json index 48c4b4f8cf2..0c55f2599e8 100644 --- a/website/common/locales/en/generic.json +++ b/website/common/locales/en/generic.json @@ -238,5 +238,6 @@ "reportPlayer": "Report Player", "whyReportingPlayer": "Why are you reporting this player?", "whyReportingPlayerPlaceholder": "Reason for report", - "playerReportModalBody": "You should only report a player who violates the <%= firstLinkStart %>Community Guidelines<%= linkEnd %> and/or <%= secondLinkStart %>Terms of Service<%= linkEnd %>. Submitting a false report is a violation of Habitica’s Community Guidelines." + "playerReportModalBody": "You should only report a player who violates the <%= firstLinkStart %>Community Guidelines<%= linkEnd %> and/or <%= secondLinkStart %>Terms of Service<%= linkEnd %>. Submitting a false report is a violation of Habitica’s Community Guidelines.", + "targetUserNotExist": "Target User: '<%= userName %>' not exists." } diff --git a/website/server/controllers/api-v4/inbox.js b/website/server/controllers/api-v4/inbox.js index 3fc868f43f5..7a8347cccda 100644 --- a/website/server/controllers/api-v4/inbox.js +++ b/website/server/controllers/api-v4/inbox.js @@ -93,6 +93,7 @@ api.clearMessages = { * {"success":true,"data":[ * { * "_id":"8a9d461b-f5eb-4a16-97d3-c03380c422a3", + * "uuid":"8a9d461b-f5eb-4a16-97d3-c03380c422a3", * "user":"user display name", * "username":"some_user_name", * "timestamp":"12315123123", From 3f9c673db5eff2c4f0045469195abc6320c2bfe3 Mon Sep 17 00:00:00 2001 From: negue Date: Fri, 14 Jun 2024 23:01:34 +0200 Subject: [PATCH 09/64] WIP PM new Message started --- website/client/src/components/avatar.vue | 8 +- .../src/pages/private-messages/index.vue | 246 +++++++++--------- .../pm-new-message-started.vue | 62 ++++- .../start-new-conversation-input-header.vue | 16 +- website/common/locales/en/generic.json | 3 +- website/common/locales/en/messages.json | 2 - website/server/controllers/api-v3/members.js | 18 +- 7 files changed, 208 insertions(+), 147 deletions(-) diff --git a/website/client/src/components/avatar.vue b/website/client/src/components/avatar.vue index ec4f892464a..46c1313ff70 100644 --- a/website/client/src/components/avatar.vue +++ b/website/client/src/components/avatar.vue @@ -160,12 +160,12 @@ export default { type: Object, }, width: { - type: Number, - default: 140, + type: String, + default: '140px', }, height: { - type: Number, - default: 147, + type: String, + default: '147px', }, spritesMargin: { type: String, diff --git a/website/client/src/pages/private-messages/index.vue b/website/client/src/pages/private-messages/index.vue index ae3afae1b07..05dad36ede3 100644 --- a/website/client/src/pages/private-messages/index.vue +++ b/website/client/src/pages/private-messages/index.vue @@ -90,11 +90,12 @@ :disabled-texts="disabledTexts" />
@@ -107,20 +108,7 @@

-
- -

{{ $t('beginningOfConversation', {userName: selectedConversation.name}) }}

-

{{ $t('beginningOfConversationReminder') }}

-
+ - {{ $t('send') }} + {{ $t('sendMessage') }}
@@ -175,112 +163,112 @@ - diff --git a/website/client/src/pages/private-messages/start-new-conversation-input-header.vue b/website/client/src/pages/private-messages/start-new-conversation-input-header.vue index 5485d0113dd..03aeae46301 100644 --- a/website/client/src/pages/private-messages/start-new-conversation-input-header.vue +++ b/website/client/src/pages/private-messages/start-new-conversation-input-header.vue @@ -10,12 +10,13 @@ class="mx-2" placeholder="@user" autofocus + @keyup.enter="!preventTrigger && triggerNewConversation()" > @@ -63,8 +64,19 @@ export default { targetUserInputValue: '', }; }, + computed: { + preventTrigger () { + return !this.targetUserInputValue.includes('@') + || this.targetUserInputValue.length < 2; + }, + }, mounted () { this.$refs.targetUserInput.focus(); }, + methods: { + triggerNewConversation () { + this.$emit('startNewConversation', this.$refs.targetUserInput.value.replace('@', '')); + }, + }, }; diff --git a/website/common/locales/en/generic.json b/website/common/locales/en/generic.json index 0c55f2599e8..55250e7dbcc 100644 --- a/website/common/locales/en/generic.json +++ b/website/common/locales/en/generic.json @@ -239,5 +239,6 @@ "whyReportingPlayer": "Why are you reporting this player?", "whyReportingPlayerPlaceholder": "Reason for report", "playerReportModalBody": "You should only report a player who violates the <%= firstLinkStart %>Community Guidelines<%= linkEnd %> and/or <%= secondLinkStart %>Terms of Service<%= linkEnd %>. Submitting a false report is a violation of Habitica’s Community Guidelines.", - "targetUserNotExist": "Target User: '<%= userName %>' not exists." + "targetUserNotExist": "Target User: '<%= userName %>' not exists.", + "rememberToBeKind": "Please remember to be kind, respectful, and follow the Community Guidelines." } diff --git a/website/common/locales/en/messages.json b/website/common/locales/en/messages.json index 86a263b1127..2359bdee8ed 100644 --- a/website/common/locales/en/messages.json +++ b/website/common/locales/en/messages.json @@ -51,8 +51,6 @@ "messageNotAbleToBuyInBulk": "This item cannot be purchased in quantities above 1.", "notificationsRequired": "Notification ids are required.", "unallocatedStatsPoints": "You have <%= points %> unallocated Stat Points", - "beginningOfConversation": "This is the beginning of your conversation with <%= userName %>.", - "beginningOfConversationReminder": "Remember to be kind, respectful, and follow the Community Guidelines!", "messageDeletedUser": "Sorry, this user has deleted their account.", "messageMissingDisplayName": "Missing display name.", "reportedMessage": "You have reported this message to moderators.", diff --git a/website/server/controllers/api-v3/members.js b/website/server/controllers/api-v3/members.js index e48cc994233..65bb7aa4153 100644 --- a/website/server/controllers/api-v3/members.js +++ b/website/server/controllers/api-v3/members.js @@ -105,7 +105,7 @@ const api = {}; api.getMember = { method: 'GET', url: '/members/:memberId', - middlewares: [], + middlewares: [authWithHeaders()], async handler (req, res) { req.checkParams('memberId', res.t('memberIdRequired')).notEmpty().isUUID(); @@ -134,7 +134,7 @@ api.getMember = { api.getMemberByUsername = { method: 'GET', url: '/members/username/:username', - middlewares: [], + middlewares: [authWithHeaders()], async handler (req, res) { req.checkParams('username', res.t('invalidReqParams')).notEmpty(); @@ -146,15 +146,25 @@ api.getMemberByUsername = { const member = await User .findOne({ 'auth.local.lowerCaseUsername': username, 'flags.verifiedUsername': true }) - .select(memberFields) + .select(`${memberFields} blocks`) .exec(); if (!member) throw new NotFound(res.t('userNotFound')); + const blocksArray = member.blocks || []; + + delete member.blocks; + // manually call toJSON with minimize: true so empty paths aren't returned const memberToJSON = member.toJSON({ minimize: true }); User.addComputedStatsToJSONObj(memberToJSON.stats, member); + const { user } = res.locals; + + const isRequestingUserBlocked = blocksArray.includes(user._id); + + memberToJSON.inbox.canReceive = !(memberToJSON.inbox.optOut || isRequestingUserBlocked) || user.hasPermission('moderator'); + res.respond(200, memberToJSON); }, }; @@ -253,7 +263,7 @@ api.getMemberByUsername = { api.getMemberAchievements = { method: 'GET', url: '/members/:memberId/achievements', - middlewares: [], + middlewares: [authWithHeaders()], async handler (req, res) { req.checkParams('memberId', res.t('memberIdRequired')).notEmpty().isUUID(); From b3c511b8f4b0eb6595125d98e5007cac2cb545e9 Mon Sep 17 00:00:00 2001 From: negue Date: Sat, 15 Jun 2024 19:58:44 +0200 Subject: [PATCH 10/64] add /members/username test --- .../members/GET-members_username.test.js | 56 +++++++++++++++++++ .../src/pages/private-messages/index.vue | 1 - 2 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 test/api/v3/integration/members/GET-members_username.test.js diff --git a/test/api/v3/integration/members/GET-members_username.test.js b/test/api/v3/integration/members/GET-members_username.test.js new file mode 100644 index 00000000000..35e58675913 --- /dev/null +++ b/test/api/v3/integration/members/GET-members_username.test.js @@ -0,0 +1,56 @@ +import { + generateUser, + translate as t, +} from '../../../../helpers/api-integration/v3'; +import common from '../../../../../website/common'; + +describe('GET /members/username/:username', () => { + let user; + + before(async () => { + user = await generateUser(); + }); + + it('validates req.params.username', async () => { + await expect(user.get('/members/username/')).to.eventually.be.rejected.and.eql({ + code: 400, + error: 'BadRequest', + message: t('invalidReqParams'), + }); + }); + + it('returns a member public data only', async () => { + // make sure user has all the fields that can be returned by the getMember call + const member = await generateUser({ + contributor: { level: 1 }, + backer: { tier: 3 }, + preferences: { + costume: false, + background: 'volcano', + }, + secret: { + text: 'Clark Kent', + }, + }); + const memberRes = await user.get(`/members/username/${member.auth.local.username}`); + expect(memberRes).to.have.all.keys([ // works as: object has all and only these keys + '_id', 'id', 'preferences', 'profile', 'stats', 'achievements', 'party', + 'backer', 'contributor', 'auth', 'items', 'inbox', 'loginIncentives', 'flags', + ]); + expect(Object.keys(memberRes.auth)).to.eql(['local', 'timestamps']); + expect(Object.keys(memberRes.preferences).sort()).to.eql([ + 'size', 'hair', 'skin', 'shirt', + 'chair', 'costume', 'sleep', 'background', 'tasks', 'disableClasses', + ].sort()); + + expect(memberRes.stats.maxMP).to.exist; + expect(memberRes.stats.maxHealth).to.equal(common.maxHealth); + expect(memberRes.stats.toNextLevel).to.equal(common.tnl(memberRes.stats.lvl)); + expect(memberRes.inbox.optOut).to.exist; + expect(memberRes.inbox.canReceive).to.exist; + expect(memberRes.inbox.messages).to.not.exist; + expect(memberRes.secret).to.not.exist; + + expect(memberRes.blocks).to.not.exist; + }); +}); diff --git a/website/client/src/pages/private-messages/index.vue b/website/client/src/pages/private-messages/index.vue index 05dad36ede3..ef2be6fedb5 100644 --- a/website/client/src/pages/private-messages/index.vue +++ b/website/client/src/pages/private-messages/index.vue @@ -704,7 +704,6 @@ export default defineComponent({ return ordered; }, - // TODO might be deleted once all states are done placeholderTexts () { if (this.user.flags.chatRevoked) { return { From 68f91d0a8469e1cc03593351b032c0f01ace5739 Mon Sep 17 00:00:00 2001 From: negue Date: Sun, 22 Oct 2023 23:07:07 +0200 Subject: [PATCH 11/64] first design changes to messageCard --- website/client/src/assets/scss/button.scss | 13 +- website/client/src/assets/scss/dropdown.scss | 2 +- .../src/components/chat/chatMessages.vue | 6 + .../src/components/messages/messageCard.vue | 289 ++++++++++++------ .../src/components/messages/messageList.vue | 12 +- 5 files changed, 217 insertions(+), 105 deletions(-) diff --git a/website/client/src/assets/scss/button.scss b/website/client/src/assets/scss/button.scss index 62eb95422fe..f6615356188 100644 --- a/website/client/src/assets/scss/button.scss +++ b/website/client/src/assets/scss/button.scss @@ -94,8 +94,7 @@ .btn-secondary, .dropdown > .btn-secondary.dropdown-toggle:not(.btn-success), -.show > .btn-secondary.dropdown-toggle:not(.btn-success) -{ +.show > .btn-secondary.dropdown-toggle:not(.btn-success) { background: $white; border: 1px solid transparent; color: $gray-50; @@ -238,6 +237,16 @@ box-shadow: none; } +.btn-flat, +.dropdown > .btn-flat.dropdown-toggle:not(.btn-success), +.show > .btn-flat.dropdown-toggle:not(.btn-success) { + &.with-icon { + .svg-icon.color { + color: var(--icon-color); + } + } +} + .btn-cancel { color: $blue-10; } diff --git a/website/client/src/assets/scss/dropdown.scss b/website/client/src/assets/scss/dropdown.scss index f619baf1128..908c76d73ad 100644 --- a/website/client/src/assets/scss/dropdown.scss +++ b/website/client/src/assets/scss/dropdown.scss @@ -37,7 +37,7 @@ border: none; border-radius: 2px; box-shadow: 0 3px 6px 0 rgba(26, 24, 29, 0.16), 0 3px 6px 0 rgba(26, 24, 29, 0.24); - + overflow: auto; } // shared dropdown-item styles diff --git a/website/client/src/components/chat/chatMessages.vue b/website/client/src/components/chat/chatMessages.vue index 70617b74969..c38e7b2f510 100644 --- a/website/client/src/components/chat/chatMessages.vue +++ b/website/client/src/components/chat/chatMessages.vue @@ -54,6 +54,10 @@ @show-member-modal="showMemberModal" @chat-card-mounted="itemWasMounted" /> +
-
- -

- @{{ msg.username }} - {{ msg.timestamp | timeAgo }}  - ({{ msg.client }}) -

+
-
- {{ $t('reportedMessage') }}
- {{ $t('canDeleteNow') }} -
-
-
-
+

+ @{{ msg.username }} + {{ msg.timestamp | timeAgo }}  + + ({{ msg.client }}) + +

+ + -
-
- {{ $t('report') }} -
-
+ + + + + + {{ $t('report') }} + + + + + + + + {{ $t('delete') }} + + + + + +
-
-
- {{ $t('delete') }} -
+ {{ $t('reportedMessage') }}
+ {{ $t('canDeleteNow') }}
diff --git a/website/client/src/components/messages/messageCard.vue b/website/client/src/components/messages/messageCard.vue index 3dbc9b7a392..fceb0487f67 100644 --- a/website/client/src/components/messages/messageCard.vue +++ b/website/client/src/components/messages/messageCard.vue @@ -106,6 +106,14 @@ {{ $t('reportedMessage') }}
{{ $t('canDeleteNow') }}
+ + @@ -137,23 +145,6 @@ @import '~@/assets/scss/colors.scss'; @import '~@/assets/scss/tiers.scss'; -.action { - display: inline-block; - color: $gray-200; - margin-right: 1em; - font-size: 12px; - - :hover { - cursor: pointer; - } - - .svg-icon { - color: $gray-300; - margin-right: .2em; - width: 16px; - } -} - .active { color: $purple-300; @@ -245,6 +236,7 @@ hr { import axios from 'axios'; import moment from 'moment'; +import cloneDeep from 'lodash/cloneDeep'; import externalLinks from '../../mixins/externalLinks'; import renderWithMentions from '@/libs/renderWithMentions'; @@ -258,9 +250,43 @@ import { userStateMixin } from '@/mixins/userState'; import copyIcon from '@/assets/svg/copy.svg'; import likeIcon from '@/assets/svg/like.svg'; import likedIcon from '@/assets/svg/liked.svg'; +import LikeButton from '@/components/messages/likeButton.vue'; + +const LikeLogicMixin = { + computed: { + likeCount () { + const message = this.msg; + if (!message.likes) return 0; + + let likeCount = 0; + for (const key of Object.keys(message.likes)) { + const like = message.likes[key]; + if (like) likeCount += 1; + } + return likeCount; + }, + }, + methods: { + + async like () { + const message = cloneDeep(this.msg); + + await this.$store.dispatch('chat:like', { + groupId: this.groupId, + chatId: message.id, + }); + + message.likes[this.user._id] = !message.likes[this.user._id]; + + this.$emit('message-liked', message); + this.$root.$emit('bv::hide::tooltip'); + }, + }, +}; export default { components: { + LikeButton, userLink, }, filters: { @@ -272,7 +298,7 @@ export default { return moment(value).toDate().toString(); }, }, - mixins: [externalLinks, userStateMixin], + mixins: [externalLinks, userStateMixin, LikeLogicMixin], props: { msg: { type: Object, From a3b778ca1c189b9073c76f3c294148da5055372f Mon Sep 17 00:00:00 2001 From: negue Date: Sun, 29 Oct 2023 20:10:13 +0100 Subject: [PATCH 17/64] likeButton styling --- .../client/src/components/chat/chatCard.vue | 286 ---------------- .../src/components/chat/chatMessages.vue | 31 +- .../src/components/messages/likeButton.vue | 34 +- .../src/components/messages/messageCard.vue | 318 +++++++++++------- .../src/components/messages/messageList.vue | 20 +- 5 files changed, 233 insertions(+), 456 deletions(-) delete mode 100644 website/client/src/components/chat/chatCard.vue diff --git a/website/client/src/components/chat/chatCard.vue b/website/client/src/components/chat/chatCard.vue deleted file mode 100644 index 5804537c2c7..00000000000 --- a/website/client/src/components/chat/chatCard.vue +++ /dev/null @@ -1,286 +0,0 @@ - - - - - - - diff --git a/website/client/src/components/chat/chatMessages.vue b/website/client/src/components/chat/chatMessages.vue index 53264b34f0e..d79974acbcd 100644 --- a/website/client/src/components/chat/chatMessages.vue +++ b/website/client/src/components/chat/chatMessages.vue @@ -45,26 +45,15 @@ :override-top-padding="'14px'" @click.native="showMemberModal(msg.uuid)" /> -
- - -
+
-
+
- -

- @{{ msg.username }} - {{ msg.timestamp | timeAgo }}  - - ({{ msg.client }}) - -

- -
+
- - + + - + + + + + + {{ $t('copyAsTodo') }} + - - - - - - - {{ $t('delete') }} + + + + + + {{ $t('report') }} + - - - - -
-
- {{ $t('reportedMessage') }}
- {{ $t('canDeleteNow') }} + + + + + + {{ $t('delete') }} + + + + + +
+
+ {{ $t('reportedMessage') }}
+ {{ $t('canDeleteNow') }} +
+ +
- -
@@ -128,4 +140,12 @@ export default { margin-bottom: 0; } +/* this removes safari "save username" UI, we only search for one, we dont want to save it */ +input::-webkit-contacts-auto-fill-button, +input::-webkit-credentials-auto-fill-button { + visibility: hidden; + position: absolute; + right: 0; +} + diff --git a/website/client/src/pages/private-messages/start-new-conversation-input-header.vue b/website/client/src/pages/private-messages/start-new-conversation-input-header.vue index 67dda7841ed..76c8cde0d8d 100644 --- a/website/client/src/pages/private-messages/start-new-conversation-input-header.vue +++ b/website/client/src/pages/private-messages/start-new-conversation-input-header.vue @@ -4,14 +4,19 @@ v-once v-html="$t('to')" > - + :is-valid="foundUser._id" + :only-show-invalid-state="foundUser._id === undefined" + + :hide-error-line="true" + :placeholder="$t('usernameOrUserId')" + :invalid-issues="userInputInvalidIssues" + @enter="triggerNewConversation" + />