From 4803fb07c85914fd0c9756a1d399e9803f48b8c7 Mon Sep 17 00:00:00 2001 From: Alexander Tumin Date: Mon, 8 Aug 2022 23:42:22 +0300 Subject: [PATCH 1/9] Allow opening profile when clicking an avatar inside of user popover --- src/components/settings_modal/tabs/general_tab.js | 5 +++++ src/components/settings_modal/tabs/general_tab.vue | 11 +++++++---- src/components/user_popover/user_popover.js | 4 ++-- src/components/user_popover/user_popover.vue | 2 +- src/i18n/en.json | 5 ++++- src/modules/config.js | 2 +- 6 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/components/settings_modal/tabs/general_tab.js b/src/components/settings_modal/tabs/general_tab.js index 1e11b9e0..d94a3be9 100644 --- a/src/components/settings_modal/tabs/general_tab.js +++ b/src/components/settings_modal/tabs/general_tab.js @@ -43,6 +43,11 @@ const GeneralTab = { value: mode, label: this.$t(`settings.third_column_mode_${mode}`) })), + userPopoverAvatarActionOptions: ['close', 'zoom', 'open'].map(mode => ({ + key: mode, + value: mode, + label: this.$t(`settings.user_popover_avatar_action_${mode}`) + })), loopSilentAvailable: // Firefox Object.getOwnPropertyDescriptor(HTMLVideoElement.prototype, 'mozHasAudio') || diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue index a2609200..7ac61b2e 100644 --- a/src/components/settings_modal/tabs/general_tab.vue +++ b/src/components/settings_modal/tabs/general_tab.vue @@ -75,12 +75,15 @@
  • - - {{ $t('settings.user_popover_avatar_zoom') }} - + {{ $t('settings.user_popover_avatar_action') }} +
  • import('../popover/popover.vue')) }, computed: { - userPopoverZoom () { - return this.$store.getters.mergedConfig.userPopoverZoom + userPopoverAvatarAction () { + return this.$store.getters.mergedConfig.userPopoverAvatarAction }, userPopoverOverlay () { return this.$store.getters.mergedConfig.userPopoverOverlay diff --git a/src/components/user_popover/user_popover.vue b/src/components/user_popover/user_popover.vue index 4e999672..53d51fc4 100644 --- a/src/components/user_popover/user_popover.vue +++ b/src/components/user_popover/user_popover.vue @@ -14,7 +14,7 @@ class="user-popover" :user-id="userId" :hide-bio="true" - :avatar-action="userPopoverZoom ? 'zoom' : close" + :avatar-action="userPopoverAvatarAction == 'close' ? close : userPopoverAvatarAction" :on-close="close" /> diff --git a/src/i18n/en.json b/src/i18n/en.json index c54d4750..140e569d 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -556,7 +556,10 @@ "mention_link_show_avatar_quick": "Show user avatar next to mentions", "mention_link_fade_domain": "Fade domains (e.g. {'@'}example.org in {'@'}foo{'@'}example.org)", "mention_link_bolden_you": "Highlight mention of you when you are mentioned", - "user_popover_avatar_zoom": "Clicking on user avatar in popover zooms it instead of closing the popover", + "user_popover_avatar_action": "Popover avatar click action", + "user_popover_avatar_action_zoom": "Zoom the avatar", + "user_popover_avatar_action_close": "Close the popover", + "user_popover_avatar_action_open": "Open profile", "user_popover_avatar_overlay": "Show user popover over user avatar", "fun": "Fun", "greentext": "Meme arrows", diff --git a/src/modules/config.js b/src/modules/config.js index eaf67a91..e1e0bdd7 100644 --- a/src/modules/config.js +++ b/src/modules/config.js @@ -81,7 +81,7 @@ export const defaultState = { useContainFit: true, disableStickyHeaders: false, showScrollbars: false, - userPopoverZoom: false, + userPopoverAvatarAction: 'close', userPopoverOverlay: true, greentext: undefined, // instance default useAtIcon: undefined, // instance default From a403f93b478eec67d779400b1fcfdc006bb787da Mon Sep 17 00:00:00 2001 From: Alexander Tumin Date: Tue, 9 Aug 2022 10:44:20 +0300 Subject: [PATCH 2/9] Allow opening profile: multiChoiceProprties record, anonymous access --- src/components/settings_modal/tabs/general_tab.vue | 1 - src/modules/config.js | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue index 7ac61b2e..c212a81a 100644 --- a/src/components/settings_modal/tabs/general_tab.vue +++ b/src/components/settings_modal/tabs/general_tab.vue @@ -76,7 +76,6 @@
  • Date: Tue, 9 Aug 2022 21:49:40 -0400 Subject: [PATCH 3/9] Use $ for id UserProfile routes --- src/boot/routes.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/boot/routes.js b/src/boot/routes.js index 95e47199..91588af2 100644 --- a/src/boot/routes.js +++ b/src/boot/routes.js @@ -61,7 +61,7 @@ export default (store) => { component: RemoteUserResolver, beforeEnter: validateAuthenticatedRoute }, - { name: 'external-user-profile', path: '/users/:id', component: UserProfile }, + { name: 'external-user-profile', path: '/users/$:id', component: UserProfile }, { name: 'interactions', path: '/users/:username/interactions', component: Interactions, beforeEnter: validateAuthenticatedRoute }, { name: 'dms', path: '/users/:username/dms', component: DMs, beforeEnter: validateAuthenticatedRoute }, { name: 'registration', path: '/registration', component: Registration }, @@ -75,7 +75,8 @@ export default (store) => { { name: 'search', path: '/search', component: Search, props: (route) => ({ query: route.query.query }) }, { name: 'who-to-follow', path: '/who-to-follow', component: WhoToFollow, beforeEnter: validateAuthenticatedRoute }, { name: 'about', path: '/about', component: About }, - { name: 'user-profile', path: '/:_(users)?/:name', component: UserProfile }, + { name: 'user-profile', path: '/users/:name', component: UserProfile }, + { name: 'legacy-user-profile', path: '/:name', component: UserProfile }, { name: 'lists', path: '/lists', component: Lists }, { name: 'lists-timeline', path: '/lists/:id', component: ListsTimeline }, { name: 'lists-edit', path: '/lists/:id/edit', component: ListsEdit } From 09f9640be168d82f39491831dd9f8be4c682afb2 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Tue, 9 Aug 2022 22:11:55 -0400 Subject: [PATCH 4/9] Use lookup endpoint to obtain users by nickname --- src/components/user_profile/user_profile.js | 15 ++++++++++----- src/modules/users.js | 7 +++++++ src/services/api/api.service.js | 21 +++++++++++++++++++++ 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/src/components/user_profile/user_profile.js b/src/components/user_profile/user_profile.js index f779b823..781fd601 100644 --- a/src/components/user_profile/user_profile.js +++ b/src/components/user_profile/user_profile.js @@ -45,7 +45,7 @@ const UserProfile = { }, created () { const routeParams = this.$route.params - this.load(routeParams.name || routeParams.id) + this.load({ name: routeParams.name, id: routeParams.id }) this.tab = get(this.$route, 'query.tab', defaultTabKey) }, unmounted () { @@ -106,12 +106,17 @@ const UserProfile = { this.userId = null this.error = false + const maybeId = userNameOrId.id + const maybeName = userNameOrId.name + // Check if user data is already loaded in store - const user = this.$store.getters.findUser(userNameOrId) + const user = this.$store.getters.findUser(maybeId || maybeName) if (user) { loadById(user.id) } else { - this.$store.dispatch('fetchUser', userNameOrId) + (maybeId + ? this.$store.dispatch('fetchUser', maybeId) + : this.$store.dispatch('fetchUserByName', maybeName)) .then(({ id }) => loadById(id)) .catch((reason) => { const errorMessage = get(reason, 'error.error') @@ -150,12 +155,12 @@ const UserProfile = { watch: { '$route.params.id': function (newVal) { if (newVal) { - this.switchUser(newVal) + this.switchUser({ id: newVal }) } }, '$route.params.name': function (newVal) { if (newVal) { - this.switchUser(newVal) + this.switchUser({ name: newVal }) } }, '$route.query': function (newVal) { diff --git a/src/modules/users.js b/src/modules/users.js index b6fb9746..47dc8493 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -285,6 +285,13 @@ const users = { return user }) }, + fetchUserByName (store, name) { + return store.rootState.api.backendInteractor.fetchUserByName({ name }) + .then((user) => { + store.commit('addNewUsers', [user]) + return user + }) + }, fetchUserRelationship (store, id) { if (store.state.currentUser) { store.rootState.api.backendInteractor.fetchUserRelationship({ id }) diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index 7b6f7d67..ce60d65a 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -50,6 +50,7 @@ const MASTODON_USER_HOME_TIMELINE_URL = '/api/v1/timelines/home' const MASTODON_STATUS_URL = id => `/api/v1/statuses/${id}` const MASTODON_STATUS_CONTEXT_URL = id => `/api/v1/statuses/${id}/context` const MASTODON_USER_URL = '/api/v1/accounts' +const MASTODON_USER_LOOKUP_URL = '/api/v1/accounts/lookup' const MASTODON_USER_RELATIONSHIPS_URL = '/api/v1/accounts/relationships' const MASTODON_USER_TIMELINE_URL = id => `/api/v1/accounts/${id}/statuses` const MASTODON_LIST_URL = id => `/api/v1/lists/${id}` @@ -318,6 +319,25 @@ const fetchUser = ({ id, credentials }) => { .then((data) => parseUser(data)) } +const fetchUserByName = ({ name, credentials }) => { + return promisedRequest({ + url: MASTODON_USER_LOOKUP_URL, + credentials, + params: { acct: name } + }) + .then(data => data.id) + .catch(error => { + if (error && error.statusCode === 404) { + // Either the backend does not support lookup endpoint, + // or there is no user with such name. Fallback and treat name as id. + return name + } else { + throw error + } + }) + .then(id => fetchUser({ id, credentials })) +} + const fetchUserRelationship = ({ id, credentials }) => { const url = `${MASTODON_USER_RELATIONSHIPS_URL}/?id=${id}` return fetch(url, { headers: authHeaders(credentials) }) @@ -1481,6 +1501,7 @@ const apiService = { blockUser, unblockUser, fetchUser, + fetchUserByName, fetchUserRelationship, favorite, unfavorite, From ab4a75bdd92aba7973b6c32eb8ff11280552d6c6 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Wed, 10 Aug 2022 12:17:18 -0400 Subject: [PATCH 5/9] Do not allow to find by name in findUser() --- src/components/user_profile/user_profile.js | 2 +- src/modules/users.js | 20 ++++----- .../specs/components/user_profile.spec.js | 5 ++- test/unit/specs/modules/users.spec.js | 44 ++++++++++++++++--- 4 files changed, 54 insertions(+), 17 deletions(-) diff --git a/src/components/user_profile/user_profile.js b/src/components/user_profile/user_profile.js index 781fd601..08adaeab 100644 --- a/src/components/user_profile/user_profile.js +++ b/src/components/user_profile/user_profile.js @@ -110,7 +110,7 @@ const UserProfile = { const maybeName = userNameOrId.name // Check if user data is already loaded in store - const user = this.$store.getters.findUser(maybeId || maybeName) + const user = maybeId ? this.$store.getters.findUser(maybeId) : this.$store.getters.findUserByName(maybeName) if (user) { loadById(user.id) } else { diff --git a/src/modules/users.js b/src/modules/users.js index 47dc8493..be0bc997 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -16,9 +16,6 @@ export const mergeOrAdd = (arr, obj, item) => { // This is a new item, prepare it arr.push(item) obj[item.id] = item - if (item.screen_name && !item.screen_name.includes('@')) { - obj[item.screen_name.toLowerCase()] = item - } return { item, new: true } } } @@ -162,7 +159,11 @@ export const mutations = { if (user.relationship) { state.relationships[user.relationship.id] = user.relationship } - mergeOrAdd(state.users, state.usersObject, user) + const res = mergeOrAdd(state.users, state.usersObject, user) + const item = res.item + if (res.new && item.screen_name && !item.screen_name.includes('@')) { + state.usersByNameObject[item.screen_name.toLowerCase()] = item + } }) }, updateUserRelationship (state, relationships) { @@ -239,12 +240,10 @@ export const mutations = { export const getters = { findUser: state => query => { - const result = state.usersObject[query] - // In case it's a screen_name, we can try searching case-insensitive - if (!result && typeof query === 'string') { - return state.usersObject[query.toLowerCase()] - } - return result + return state.usersObject[query] + }, + findUserByName: state => query => { + return state.usersByNameObject[query.toLowerCase()] }, findUserByUrl: state => query => { return state.users @@ -263,6 +262,7 @@ export const defaultState = { currentUser: false, users: [], usersObject: {}, + usersByNameObject: {}, signUpPending: false, signUpErrors: [], relationships: {} diff --git a/test/unit/specs/components/user_profile.spec.js b/test/unit/specs/components/user_profile.spec.js index 0fbab722..dc0b938a 100644 --- a/test/unit/specs/components/user_profile.spec.js +++ b/test/unit/specs/components/user_profile.spec.js @@ -15,6 +15,7 @@ const actions = { const testGetters = { findUser: state => getters.findUser(state.users), + findUserByName: state => getters.findUserByName(state.users), relationship: state => getters.relationship(state.users), mergedConfig: state => ({ colors: '', @@ -95,6 +96,7 @@ const externalProfileStore = createStore({ credentials: '' }, usersObject: { 100: extUser }, + usersByNameObject: {}, users: [extUser], relationships: {} } @@ -163,7 +165,8 @@ const localProfileStore = createStore({ currentUser: { credentials: '' }, - usersObject: { 100: localUser, testuser: localUser }, + usersObject: { 100: localUser }, + usersByNameObject: { testuser: localUser }, users: [localUser], relationships: {} } diff --git a/test/unit/specs/modules/users.spec.js b/test/unit/specs/modules/users.spec.js index dfa5684d..3073f507 100644 --- a/test/unit/specs/modules/users.spec.js +++ b/test/unit/specs/modules/users.spec.js @@ -57,24 +57,27 @@ describe('The users module', () => { }) describe('findUser', () => { - it('returns user with matching screen_name', () => { + it('does not return user with matching screen_name', () => { const user = { screen_name: 'Guy', id: '1' } const state = { usersObject: { - 1: user, + 1: user + }, + usersByNameObject: { guy: user } } const name = 'Guy' - const expected = { screen_name: 'Guy', id: '1' } - expect(getters.findUser(state)(name)).to.eql(expected) + expect(getters.findUser(state)(name)).to.eql(undefined) }) it('returns user with matching id', () => { const user = { screen_name: 'Guy', id: '1' } const state = { usersObject: { - 1: user, + 1: user + }, + usersByNameObject: { guy: user } } @@ -83,4 +86,35 @@ describe('The users module', () => { expect(getters.findUser(state)(id)).to.eql(expected) }) }) + + describe('findUserByName', () => { + it('returns user with matching screen_name', () => { + const user = { screen_name: 'Guy', id: '1' } + const state = { + usersObject: { + 1: user + }, + usersByNameObject: { + guy: user + } + } + const name = 'Guy' + const expected = { screen_name: 'Guy', id: '1' } + expect(getters.findUserByName(state)(name)).to.eql(expected) + }) + + it('does not return user with matching id', () => { + const user = { screen_name: 'Guy', id: '1' } + const state = { + usersObject: { + 1: user + }, + usersByNameObject: { + guy: user + } + } + const id = '1' + expect(getters.findUserByName(state)(id)).to.eql(undefined) + }) + }) }) From 0f51b0054019e876bdac929dc02e1ca7772c4861 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 23 Aug 2022 01:30:17 +0300 Subject: [PATCH 6/9] reduce indexes to be below 9999 so that develop error messages appear above --- src/App.scss | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/App.scss b/src/App.scss index e5bc4c54..ab1a656e 100644 --- a/src/App.scss +++ b/src/App.scss @@ -5,12 +5,12 @@ --navbar-height: 3.5rem; --post-line-height: 1.4; // Z-Index stuff - --ZI_media_modal: 90000; - --ZI_modals_popovers: 85000; - --ZI_modals: 80000; - --ZI_navbar_popovers: 75000; - --ZI_navbar: 70000; - --ZI_popovers: 60000; + --ZI_media_modal: 9000; + --ZI_modals_popovers: 8500; + --ZI_modals: 8000; + --ZI_navbar_popovers: 7500; + --ZI_navbar: 7000; + --ZI_popovers: 6000; } html { From ef4ea4965327d2e94cd70a56bb9572f00219a29a Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 23 Aug 2022 01:46:59 +0300 Subject: [PATCH 7/9] fix modals not having proper z index --- src/App.scss | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/App.scss b/src/App.scss index ab1a656e..dec187af 100644 --- a/src/App.scss +++ b/src/App.scss @@ -141,6 +141,11 @@ nav { grid-area: sidebar; } +#modal { + position: absolute; + z-index: var(--ZI_modals); +} + .column.-scrollable { top: var(--navbar-height); position: sticky; From 1a24f8b4c155edc3834ce901a167e463c609cf2f Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 23 Aug 2022 01:49:22 +0300 Subject: [PATCH 8/9] fix notices being under the navbar, also change offset to use variable --- src/components/global_notice_list/global_notice_list.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/global_notice_list/global_notice_list.vue b/src/components/global_notice_list/global_notice_list.vue index 09904761..d828b819 100644 --- a/src/components/global_notice_list/global_notice_list.vue +++ b/src/components/global_notice_list/global_notice_list.vue @@ -29,10 +29,10 @@ .global-notice-list { position: fixed; - top: 50px; + top: calc(var(--navbar-height) + 0.5em); width: 100%; pointer-events: none; - z-index: var(--ZI_popovers); + z-index: var(--ZI_navbar_popovers); display: flex; flex-direction: column; align-items: center; From defc622767b52b85d925511c03d82eba9a1087e4 Mon Sep 17 00:00:00 2001 From: Pleroma Renovate Bot Date: Tue, 23 Aug 2022 09:05:51 +0000 Subject: [PATCH 9/9] Update dependency opn to v5 --- package.json | 2 +- yarn.lock | 26 ++++++++++---------------- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/package.json b/package.json index a8cbaa3f..dfc9c4b1 100644 --- a/package.json +++ b/package.json @@ -101,7 +101,7 @@ "mini-css-extract-plugin": "2.6.1", "mocha": "10.0.0", "nightwatch": "2.3.3", - "opn": "4.0.2", + "opn": "5.5.0", "ora": "0.4.1", "postcss": "8.4.16", "postcss-loader": "7.0.1", diff --git a/yarn.lock b/yarn.lock index 15b139a7..d8215d9d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5188,6 +5188,11 @@ is-weakref@^1.0.1: dependencies: call-bind "^1.0.2" +is-wsl@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" + integrity sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw== + is-wsl@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" @@ -6551,13 +6556,12 @@ open@^8.4.0: is-docker "^2.1.1" is-wsl "^2.2.0" -opn@4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/opn/-/opn-4.0.2.tgz#7abc22e644dff63b0a96d5ab7f2790c0f01abc95" - integrity sha1-erwi5kTf9jsKltWrfyeQwPAavJU= +opn@5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc" + integrity sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA== dependencies: - object-assign "^4.0.1" - pinkie-promise "^2.0.0" + is-wsl "^1.1.0" optimist@^0.6.1: version "0.6.1" @@ -6835,16 +6839,6 @@ pify@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" -pinkie-promise@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" - dependencies: - pinkie "^2.0.0" - -pinkie@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" - pirates@^4.0.5: version "4.0.5" resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b"