From aa41cedd932e88b030ecc3cc54848b5aa300eec3 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Wed, 10 Aug 2022 02:19:07 +0300 Subject: initial prefs storage work --- test/unit/specs/modules/serverSideStorage.spec.js | 94 +++++++++++++++++++++++ 1 file changed, 94 insertions(+) (limited to 'test/unit/specs') diff --git a/test/unit/specs/modules/serverSideStorage.spec.js b/test/unit/specs/modules/serverSideStorage.spec.js index e06c6ada..6059dc84 100644 --- a/test/unit/specs/modules/serverSideStorage.spec.js +++ b/test/unit/specs/modules/serverSideStorage.spec.js @@ -7,6 +7,7 @@ import { _getRecentData, _getAllFlags, _mergeFlags, + _mergePrefs, _resetFlags, mutations, defaultState, @@ -28,6 +29,7 @@ describe('The serverSideStorage module', () => { expect(state.cache._version).to.eql(VERSION) expect(state.cache._timestamp).to.be.a('number') expect(state.cache.flagStorage).to.eql(defaultState.flagStorage) + expect(state.cache.prefsStorage).to.eql(defaultState.prefsStorage) }) it('should initialize storage with proper flags for new users if none present', () => { @@ -36,6 +38,7 @@ describe('The serverSideStorage module', () => { expect(state.cache._version).to.eql(VERSION) expect(state.cache._timestamp).to.be.a('number') expect(state.cache.flagStorage).to.eql(newUserFlags) + expect(state.cache.prefsStorage).to.eql(defaultState.prefsStorage) }) it('should merge flags even if remote timestamp is older', () => { @@ -57,6 +60,9 @@ describe('The serverSideStorage module', () => { flagStorage: { ...defaultState.flagStorage, updateCounter: 1 + }, + prefsStorage: { + ...defaultState.flagStorage, } } } @@ -157,6 +163,94 @@ describe('The serverSideStorage module', () => { }) }) + describe('_mergePrefs', () => { + it('should prefer recent and apply journal to it', () => { + expect( + _mergePrefs( + // RECENT + { + simple: { a: 1, b: 0, c: true }, + _journal: [ + { path: 'simple.b', operation: 'set', args: [0], timestamp: 2 }, + { path: 'simple.c', operation: 'set', args: [true], timestamp: 4 } + ] + }, + // STALE + { + simple: { a: 1, b: 1, c: false }, + _journal: [ + { path: 'simple.a', operation: 'set', args: [1], timestamp: 1 }, + { path: 'simple.b', operation: 'set', args: [1], timestamp: 3 } + ] + } + ) + ).to.eql({ + simple: { a: 1, b: 1, c: true }, + _journal: [ + { path: 'simple.a', operation: 'set', args: [1], timestamp: 1 }, + { path: 'simple.b', operation: 'set', args: [1], timestamp: 3 }, + { path: 'simple.c', operation: 'set', args: [true], timestamp: 4 } + ] + }) + }) + + it('should allow setting falsy values', () => { + expect( + _mergePrefs( + // RECENT + { + simple: { a: 1, b: 0, c: false }, + _journal: [ + { path: 'simple.b', operation: 'set', args: [0], timestamp: 2 }, + { path: 'simple.c', operation: 'set', args: [false], timestamp: 4 } + ] + }, + // STALE + { + simple: { a: 0, b: 0, c: true }, + _journal: [ + { path: 'simple.a', operation: 'set', args: [0], timestamp: 1 }, + { path: 'simple.b', operation: 'set', args: [0], timestamp: 3 } + ] + } + ) + ).to.eql({ + simple: { a: 0, b: 0, c: false }, + _journal: [ + { path: 'simple.a', operation: 'set', args: [0], timestamp: 1 }, + { path: 'simple.b', operation: 'set', args: [0], timestamp: 3 }, + { path: 'simple.c', operation: 'set', args: [false], timestamp: 4 } + ] + }) + }) + + it('should work with strings', () => { + expect( + _mergePrefs( + // RECENT + { + simple: { a: 'foo' }, + _journal: [ + { path: 'simple.a', operation: 'set', args: ['foo'], timestamp: 2 } + ] + }, + // STALE + { + simple: { a: 'bar' }, + _journal: [ + { path: 'simple.a', operation: 'set', args: ['bar'], timestamp: 4 } + ] + } + ) + ).to.eql({ + simple: { a: 'bar' }, + _journal: [ + { path: 'simple.a', operation: 'set', args: ['bar'], timestamp: 4 } + ] + }) + }) + }) + describe('_resetFlags', () => { it('should reset all known flags to 0 when reset flag is set to > 0 and < 9000', () => { const totalFlags = { a: 0, b: 3, reset: 1 } -- cgit v1.2.3-70-g09d2 From 2c0eb29b286edf57d75c6044855ea5be9187493b Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Wed, 10 Aug 2022 02:31:41 +0300 Subject: more prefs storage work + move dontShowUpdateNotifs to prefs --- .../update_notification/update_notification.js | 4 +-- src/modules/serverSideStorage.js | 23 ++++++++++++---- test/unit/specs/modules/serverSideStorage.spec.js | 32 ++++++++++++++++++++++ 3 files changed, 52 insertions(+), 7 deletions(-) (limited to 'test/unit/specs') diff --git a/src/components/update_notification/update_notification.js b/src/components/update_notification/update_notification.js index ba008d81..609842c4 100644 --- a/src/components/update_notification/update_notification.js +++ b/src/components/update_notification/update_notification.js @@ -38,7 +38,7 @@ const UpdateNotification = { return !this.$store.state.instance.disableUpdateNotification && this.$store.state.users.currentUser && this.$store.state.serverSideStorage.flagStorage.updateCounter < CURRENT_UPDATE_COUNTER && - !this.$store.state.serverSideStorage.flagStorage.dontShowUpdateNotifs + !this.$store.state.serverSideStorage.prefsStorage.simple.dontShowUpdateNotifs } }, methods: { @@ -48,7 +48,7 @@ const UpdateNotification = { neverShowAgain () { this.toggleShow() this.$store.commit('setFlag', { flag: 'updateCounter', value: CURRENT_UPDATE_COUNTER }) - this.$store.commit('setFlag', { flag: 'dontShowUpdateNotifs', value: 1 }) + this.$store.commit('setPreference', { path: 'simple.dontShowUpdateNotifs', value: true }) this.$store.dispatch('pushServerSideStorage') }, dismiss () { diff --git a/src/modules/serverSideStorage.js b/src/modules/serverSideStorage.js index 08ba48bb..bb647b97 100644 --- a/src/modules/serverSideStorage.js +++ b/src/modules/serverSideStorage.js @@ -14,9 +14,6 @@ export const defaultState = { // storage of flags - stuff that can only be set and incremented flagStorage: { updateCounter: 0, // Counter for most recent update notification seen - // TODO move to prefsStorage when that becomes a thing since only way - // this can be reset is by complete reset of all flags - dontShowUpdateNotifs: 0, // if user chose to not show update notifications ever again reset: 0 // special flag that can be used to force-reset all flags, debug purposes only // special reset codes: // 1000: trim keys to those known by currently running FE @@ -24,7 +21,9 @@ export const defaultState = { }, prefsStorage: { _journal: [], - simple: {} + simple: { + dontShowUpdateNotifs: false + } }, // raw data raw: null, @@ -248,6 +247,20 @@ export const mutations = { setFlag (state, { flag, value }) { state.flagStorage[flag] = value state.dirty = true + }, + setPreference (state, { path, value }) { + if (path.startsWith('_')) { + console.error(`tried to edit internal (starts with _) field '${path}', ignoring.`) + return + } + set(state.prefsStorage, path, value) + state.prefsStorage._journal = uniqBy( + [ + ...state.prefsStorage._journal, + { command: 'set', path, args: [value], timestamp: Date.now() } + ].sort((a, b) => a.timestamp > b.timestamp ? -1 : 1), + 'path' + ).reverse() } } @@ -262,7 +275,7 @@ const serverSideStorage = { if (!needPush) return state.cache = _wrapData({ flagStorage: toRaw(state.flagStorage), - prefsStorage: toRaw(state.flagStorage) + prefsStorage: toRaw(state.prefsStorage) }) const params = { pleroma_settings_store: { 'pleroma-fe': state.cache } } rootState.api.backendInteractor diff --git a/test/unit/specs/modules/serverSideStorage.spec.js b/test/unit/specs/modules/serverSideStorage.spec.js index 6059dc84..da790793 100644 --- a/test/unit/specs/modules/serverSideStorage.spec.js +++ b/test/unit/specs/modules/serverSideStorage.spec.js @@ -105,6 +105,38 @@ describe('The serverSideStorage module', () => { expect(state.cache.flagStorage).to.eql(defaultState.flagStorage) }) }) + describe('setPreference', () => { + const { setPreference } = mutations + + it('should set preference and update journal log accordingly', () => { + const state = cloneDeep(defaultState) + setPreference(state, { path: 'simple.testing', value: 1 }) + expect(state.prefsStorage.simple.testing).to.eql(1) + expect(state.prefsStorage._journal.length).to.eql(1) + expect(state.prefsStorage._journal[0]).to.eql({ + path: 'simple.testing', + command: 'set', + args: [1], + // should have A timestamp, we don't really care what it is + timestamp: state.prefsStorage._journal[0].timestamp + }) + }) + + it('should keep journal to a minimum (one entry per path)', () => { + const state = cloneDeep(defaultState) + setPreference(state, { path: 'simple.testing', value: 1 }) + setPreference(state, { path: 'simple.testing', value: 2 }) + expect(state.prefsStorage.simple.testing).to.eql(1) + expect(state.prefsStorage._journal.length).to.eql(1) + expect(state.prefsStorage._journal[0]).to.eql({ + path: 'simple.testing', + command: 'set', + args: [2], + // should have A timestamp, we don't really care what it is + timestamp: state.prefsStorage._journal[0].timestamp + }) + }) + }) }) describe('helper functions', () => { -- cgit v1.2.3-70-g09d2 From 72e238ceb34304cb023a01a84c3f453aadaa775c Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Thu, 11 Aug 2022 01:07:51 +0300 Subject: server side storage support for collections + fixes --- src/modules/serverSideStorage.js | 111 ++++++++++++++++++---- test/unit/specs/modules/serverSideStorage.spec.js | 20 +++- 2 files changed, 108 insertions(+), 23 deletions(-) (limited to 'test/unit/specs') diff --git a/src/modules/serverSideStorage.js b/src/modules/serverSideStorage.js index 1a7e02b3..11e66220 100644 --- a/src/modules/serverSideStorage.js +++ b/src/modules/serverSideStorage.js @@ -1,5 +1,5 @@ import { toRaw } from 'vue' -import { isEqual, uniqBy, cloneDeep, set } from 'lodash' +import { isEqual, uniqWith, cloneDeep, set, get, clamp } from 'lodash' import { CURRENT_UPDATE_COUNTER } from 'src/components/update_notification/update_notification.js' export const VERSION = 1 @@ -36,6 +36,17 @@ export const newUserFlags = { updateCounter: CURRENT_UPDATE_COUNTER // new users don't need to see update notification } +export const _moveItemInArray = (array, value, movement) => { + const oldIndex = array.indexOf(value) + const newIndex = oldIndex + movement + const newArray = [...array] + // remove old + newArray.splice(oldIndex, 1) + // add new + newArray.splice(clamp(newIndex, 0, newArray.length + 1), 0, value) + return newArray +} + const _wrapData = (data) => ({ ...data, _timestamp: Date.now(), @@ -98,6 +109,23 @@ export const _mergeFlags = (recent, stale, allFlagKeys) => { })) } +const _mergeJournal = (a, b) => uniqWith( + [ + ...(Array.isArray(a) ? a : []), + ...(Array.isArray(b) ? b : []) + ].sort((a, b) => a.timestamp > b.timestamp ? -1 : 1), + (a, b) => { + if (a.operation !== b.operation) return false + switch (a.operation) { + case 'set': + case 'arrangeSet': + return a.path === b.path + default: + return a.path === b.path && a.timestamp === b.timestamp + } + } +).reverse() + export const _mergePrefs = (recent, stale, allFlagKeys) => { if (!stale) return recent if (!recent) return stale @@ -114,13 +142,7 @@ export const _mergePrefs = (recent, stale, allFlagKeys) => { * shouldn't be used with collections! */ const resultOutput = { ...recentData } - const totalJournal = uniqBy( - [ - ...(Array.isArray(recentJournal) ? recentJournal : []), - ...(Array.isArray(staleJournal) ? staleJournal : []) - ].sort((a, b) => a.timestamp > b.timestamp ? -1 : 1), - 'path' - ).reverse() + const totalJournal = _mergeJournal(staleJournal, recentJournal) totalJournal.forEach(({ path, timestamp, operation, args }) => { if (path.startsWith('_')) { console.error(`journal contains entry to edit internal (starts with _) field '${path}', something is incorrect here, ignoring.`) @@ -130,6 +152,17 @@ export const _mergePrefs = (recent, stale, allFlagKeys) => { case 'set': set(resultOutput, path, args[0]) break + case 'addToCollection': + set(resultOutput, path, Array.from(new Set(get(resultOutput, path)).add(args[0]))) + break + case 'removeFromCollection': + set(resultOutput, path, Array.from(new Set(get(resultOutput, path)).remove(args[0]))) + break + case 'reorderCollection': { + const [value, movement] = args + set(resultOutput, path, _moveItemInArray(get(resultOutput, path), value, movement)) + break + } default: console.error(`Unknown journal operation: '${operation}', did we forget to run reverse migrations beforehand?`) } @@ -260,13 +293,56 @@ export const mutations = { return } set(state.prefsStorage, path, value) - state.prefsStorage._journal = uniqBy( - [ - ...state.prefsStorage._journal, - { command: 'set', path, args: [value], timestamp: Date.now() } - ].sort((a, b) => a.timestamp > b.timestamp ? -1 : 1), - 'path' - ).reverse() + state.prefsStorage._journal = [ + ...state.prefsStorage._journal, + { command: 'set', path, args: [value], timestamp: Date.now() } + ] + }, + addCollectionPreference (state, { path, value }) { + if (path.startsWith('_')) { + console.error(`tried to edit internal (starts with _) field '${path}', ignoring.`) + return + } + const collection = new Set(get(state.prefsStorage, path)) + collection.add(value) + set(state.prefsStorage, path, collection) + state.prefsStorage._journal = [ + ...state.prefsStorage._journal, + { command: 'addToCollection', path, args: [value], timestamp: Date.now() } + ] + }, + removeCollectionPreference (state, { path, value }) { + if (path.startsWith('_')) { + console.error(`tried to edit internal (starts with _) field '${path}', ignoring.`) + return + } + const collection = new Set(get(state.prefsStorage, path)) + collection.remove(value) + set(state.prefsStorage, path, collection) + state.prefsStorage._journal = [ + ...state.prefsStorage._journal, + { command: 'removeFromCollection', path, args: [value], timestamp: Date.now() } + ] + }, + reorderCollectionPreference (state, { path, value, movement }) { + if (path.startsWith('_')) { + console.error(`tried to edit internal (starts with _) field '${path}', ignoring.`) + return + } + const collection = get(state.prefsStorage, path) + const newCollection = _moveItemInArray(collection, value, movement) + set(state.prefsStorage, path, newCollection) + state.prefsStorage._journal = [ + ...state.prefsStorage._journal, + { command: 'arrangeCollection', path, args: [value], timestamp: Date.now() } + ] + }, + updateCache (state) { + state.prefsStorage._journal = _mergeJournal(state.prefsStorage._journal) + state.cache = _wrapData({ + flagStorage: toRaw(state.flagStorage), + prefsStorage: toRaw(state.prefsStorage) + }) } } @@ -279,10 +355,7 @@ const serverSideStorage = { pushServerSideStorage ({ state, rootState, commit }, { force = false } = {}) { const needPush = state.dirty || force if (!needPush) return - state.cache = _wrapData({ - flagStorage: toRaw(state.flagStorage), - prefsStorage: toRaw(state.prefsStorage) - }) + commit('updateCache') const params = { pleroma_settings_store: { 'pleroma-fe': state.cache } } rootState.api.backendInteractor .updateProfile({ params }) diff --git a/test/unit/specs/modules/serverSideStorage.spec.js b/test/unit/specs/modules/serverSideStorage.spec.js index da790793..ada3b7ca 100644 --- a/test/unit/specs/modules/serverSideStorage.spec.js +++ b/test/unit/specs/modules/serverSideStorage.spec.js @@ -4,6 +4,7 @@ import { VERSION, COMMAND_TRIM_FLAGS, COMMAND_TRIM_FLAGS_AND_RESET, + _moveItemInArray, _getRecentData, _getAllFlags, _mergeFlags, @@ -62,7 +63,7 @@ describe('The serverSideStorage module', () => { updateCounter: 1 }, prefsStorage: { - ...defaultState.flagStorage, + ...defaultState.prefsStorage } } } @@ -106,7 +107,7 @@ describe('The serverSideStorage module', () => { }) }) describe('setPreference', () => { - const { setPreference } = mutations + const { setPreference, updateCache } = mutations it('should set preference and update journal log accordingly', () => { const state = cloneDeep(defaultState) @@ -122,11 +123,12 @@ describe('The serverSideStorage module', () => { }) }) - it('should keep journal to a minimum (one entry per path)', () => { + it('should keep journal to a minimum (one entry per path for sets)', () => { const state = cloneDeep(defaultState) setPreference(state, { path: 'simple.testing', value: 1 }) setPreference(state, { path: 'simple.testing', value: 2 }) - expect(state.prefsStorage.simple.testing).to.eql(1) + updateCache(state) + expect(state.prefsStorage.simple.testing).to.eql(2) expect(state.prefsStorage._journal.length).to.eql(1) expect(state.prefsStorage._journal[0]).to.eql({ path: 'simple.testing', @@ -140,6 +142,16 @@ describe('The serverSideStorage module', () => { }) describe('helper functions', () => { + describe('_moveItemInArray', () => { + it('should move item according to movement value', () => { + expect(_moveItemInArray([1, 2, 3, 4], 4, -1)).to.eql([1, 2, 4, 3]) + expect(_moveItemInArray([1, 2, 3, 4], 1, 2)).to.eql([2, 3, 1, 4]) + }) + it('should clamp movement to within array', () => { + expect(_moveItemInArray([1, 2, 3, 4], 4, -10)).to.eql([4, 1, 2, 3]) + expect(_moveItemInArray([1, 2, 3, 4], 3, 99)).to.eql([1, 2, 4, 3]) + }) + }) describe('_getRecentData', () => { it('should handle nulls correctly', () => { expect(_getRecentData(null, null)).to.eql({ recent: null, stale: null, needUpload: true }) -- cgit v1.2.3-70-g09d2 From 0123872b56ccd2d534913706ae0f27ea8d6481de Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Fri, 12 Aug 2022 00:50:08 +0300 Subject: fixes + fixes for anon users --- src/components/mobile_nav/mobile_nav.vue | 2 +- src/components/nav_panel/nav_panel.js | 13 +++++++++++++ src/components/nav_panel/nav_panel.vue | 2 +- src/components/navigation/navigation.js | 1 - src/components/navigation/navigation_entry.vue | 2 +- src/components/navigation/navigation_pins.js | 7 +++++++ src/components/navigation/navigation_pins.vue | 1 + test/unit/specs/modules/serverSideStorage.spec.js | 4 ++-- 8 files changed, 26 insertions(+), 6 deletions(-) (limited to 'test/unit/specs') diff --git a/src/components/mobile_nav/mobile_nav.vue b/src/components/mobile_nav/mobile_nav.vue index 82e726a4..01a77589 100644 --- a/src/components/mobile_nav/mobile_nav.vue +++ b/src/components/mobile_nav/mobile_nav.vue @@ -86,6 +86,7 @@ grid-template-columns: 2fr auto; width: 100%; box-sizing: border-box; + a { color: var(--topBarLink, $fallback--link); } @@ -175,7 +176,6 @@ .pinned-item { flex-grow: 1; - text-align: center; } } diff --git a/src/components/nav_panel/nav_panel.js b/src/components/nav_panel/nav_panel.js index 7daa5e6a..b4e1ec0b 100644 --- a/src/components/nav_panel/nav_panel.js +++ b/src/components/nav_panel/nav_panel.js @@ -86,6 +86,19 @@ const NavPanel = { pinnedItems: state => new Set(state.serverSideStorage.prefsStorage.collections.pinnedNavItems), collapsed: state => state.serverSideStorage.prefsStorage.simple.collapseNav }), + timelinesItems () { + return filterNavigation( + Object + .entries({ ...TIMELINES }) + .map(([k, v]) => ({ ...v, name: k })), + { + hasChats: this.pleromaChatMessagesAvailable, + isFederating: this.federating, + isPrivate: this.private, + currentUser: this.currentUser + } + ) + }, rootItems () { return filterNavigation( Object diff --git a/src/components/nav_panel/nav_panel.vue b/src/components/nav_panel/nav_panel.vue index 6e89094a..84b000a4 100644 --- a/src/components/nav_panel/nav_panel.vue +++ b/src/components/nav_panel/nav_panel.vue @@ -37,7 +37,7 @@ class="timelines-background" >
    - +
diff --git a/src/components/navigation/navigation.js b/src/components/navigation/navigation.js index f37d37fe..57db2253 100644 --- a/src/components/navigation/navigation.js +++ b/src/components/navigation/navigation.js @@ -1,7 +1,6 @@ export const TIMELINES = { home: { route: 'friends', - anonRoute: 'public-timeline', icon: 'home', label: 'nav.home_timeline', criteria: ['!private'] diff --git a/src/components/navigation/navigation_entry.vue b/src/components/navigation/navigation_entry.vue index 7d761395..0dcf5d85 100644 --- a/src/components/navigation/navigation_entry.vue +++ b/src/components/navigation/navigation_entry.vue @@ -21,7 +21,7 @@ @click.stop.prevent="togglePin(item.name)" > new Set(state.serverSideStorage.prefsStorage.collections.pinnedNavItems) }), pinnedList () { + if (!this.currentUser) { + return [ + { ...TIMELINES.public, name: 'public' }, + { ...TIMELINES.twkn, name: 'twkn' }, + { ...ROOT_ITEMS.about, name: 'about' } + ] + } return filterNavigation( [ ...Object diff --git a/src/components/navigation/navigation_pins.vue b/src/components/navigation/navigation_pins.vue index 754aad7a..f421b2be 100644 --- a/src/components/navigation/navigation_pins.vue +++ b/src/components/navigation/navigation_pins.vue @@ -46,6 +46,7 @@ position: relative; flex: 0 0 3em; min-width: 2em; + text-align: center; & .svg-inline--fa, & .iconLetter { diff --git a/test/unit/specs/modules/serverSideStorage.spec.js b/test/unit/specs/modules/serverSideStorage.spec.js index ada3b7ca..edb23e8a 100644 --- a/test/unit/specs/modules/serverSideStorage.spec.js +++ b/test/unit/specs/modules/serverSideStorage.spec.js @@ -116,7 +116,7 @@ describe('The serverSideStorage module', () => { expect(state.prefsStorage._journal.length).to.eql(1) expect(state.prefsStorage._journal[0]).to.eql({ path: 'simple.testing', - command: 'set', + operation: 'set', args: [1], // should have A timestamp, we don't really care what it is timestamp: state.prefsStorage._journal[0].timestamp @@ -132,7 +132,7 @@ describe('The serverSideStorage module', () => { expect(state.prefsStorage._journal.length).to.eql(1) expect(state.prefsStorage._journal[0]).to.eql({ path: 'simple.testing', - command: 'set', + operation: 'set', args: [2], // should have A timestamp, we don't really care what it is timestamp: state.prefsStorage._journal[0].timestamp -- cgit v1.2.3-70-g09d2 From 8d3d8fffab0106a8aff5822044a8c3c30bd6e057 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Fri, 12 Aug 2022 01:19:19 +0300 Subject: fixes, clear cache on logout --- src/components/nav_panel/nav_panel.js | 4 ++-- src/components/navigation/navigation_entry.js | 1 + src/components/navigation/navigation_pins.js | 2 +- src/modules/serverSideStorage.js | 17 +++++++++++++---- src/modules/users.js | 1 + .../entity_normalizer/entity_normalizer.service.js | 1 + test/unit/specs/modules/serverSideStorage.spec.js | 2 +- 7 files changed, 20 insertions(+), 8 deletions(-) (limited to 'test/unit/specs') diff --git a/src/components/nav_panel/nav_panel.js b/src/components/nav_panel/nav_panel.js index 0d71a924..26e8440a 100644 --- a/src/components/nav_panel/nav_panel.js +++ b/src/components/nav_panel/nav_panel.js @@ -95,7 +95,7 @@ const NavPanel = { { hasChats: this.pleromaChatMessagesAvailable, isFederating: this.federating, - isPrivate: this.private, + isPrivate: this.privateMode, currentUser: this.currentUser } ) @@ -108,7 +108,7 @@ const NavPanel = { { hasChats: this.pleromaChatMessagesAvailable, isFederating: this.federating, - isPrivate: this.private, + isPrivate: this.privateMode, currentUser: this.currentUser } ) diff --git a/src/components/navigation/navigation_entry.js b/src/components/navigation/navigation_entry.js index 09c216ed..e17e9436 100644 --- a/src/components/navigation/navigation_entry.js +++ b/src/components/navigation/navigation_entry.js @@ -16,6 +16,7 @@ const NavigationEntry = { } else { this.$store.commit('addCollectionPreference', { path: 'collections.pinnedNavItems', value }) } + this.$store.dispatch('pushServerSideStorage') } }, computed: { diff --git a/src/components/navigation/navigation_pins.js b/src/components/navigation/navigation_pins.js index 8a892466..43be4275 100644 --- a/src/components/navigation/navigation_pins.js +++ b/src/components/navigation/navigation_pins.js @@ -64,7 +64,7 @@ const NavPanel = { { hasChats: this.pleromaChatMessagesAvailable, isFederating: this.federating, - isPrivate: this.private, + isPrivate: this.privateMode, currentUser: this.currentUser } ) diff --git a/src/modules/serverSideStorage.js b/src/modules/serverSideStorage.js index d95fbb8a..5581783f 100644 --- a/src/modules/serverSideStorage.js +++ b/src/modules/serverSideStorage.js @@ -51,8 +51,9 @@ export const _moveItemInArray = (array, value, movement) => { return newArray } -const _wrapData = (data) => ({ +const _wrapData = (data, userName) => ({ ...data, + _user: userName, _timestamp: Date.now(), _version: VERSION }) @@ -254,10 +255,17 @@ export const _doMigrations = (cache) => { } export const mutations = { + clearServerSideStorage (state, userData) { + state = { ...cloneDeep(defaultState) } + }, setServerSideStorage (state, userData) { const live = userData.storage state.raw = live let cache = state.cache + if (cache._user !== userData.fqn) { + console.warn('cache belongs to another user! reinitializing local cache!') + cache = null + } cache = _doMigrations(cache) @@ -371,12 +379,12 @@ export const mutations = { ] state.dirty = true }, - updateCache (state) { + updateCache (state, { username }) { state.prefsStorage._journal = _mergeJournal(state.prefsStorage._journal) state.cache = _wrapData({ flagStorage: toRaw(state.flagStorage), prefsStorage: toRaw(state.prefsStorage) - }) + }, username) } } @@ -388,8 +396,9 @@ const serverSideStorage = { actions: { pushServerSideStorage ({ state, rootState, commit }, { force = false } = {}) { const needPush = state.dirty || force + console.log(needPush) if (!needPush) return - commit('updateCache') + commit('updateCache', { username: rootState.users.currentUser.fqn }) const params = { pleroma_settings_store: { 'pleroma-fe': state.cache } } rootState.api.backendInteractor .updateProfile({ params }) diff --git a/src/modules/users.js b/src/modules/users.js index b6fb9746..fe92d697 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -509,6 +509,7 @@ const users = { store.dispatch('setLastTimeline', 'public-timeline') store.dispatch('setLayoutWidth', windowWidth()) store.dispatch('setLayoutHeight', windowHeight()) + store.commit('clearServerSideStorage') }) }, loginUser (store, accessToken) { diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js index e9cbcfe6..b1ad2691 100644 --- a/src/services/entity_normalizer/entity_normalizer.service.js +++ b/src/services/entity_normalizer/entity_normalizer.service.js @@ -48,6 +48,7 @@ export const parseUser = (data) => { if (masto) { output.screen_name = data.acct + output.fqn = data.fqn output.statusnet_profile_url = data.url // There's nothing else to get diff --git a/test/unit/specs/modules/serverSideStorage.spec.js b/test/unit/specs/modules/serverSideStorage.spec.js index edb23e8a..f10e21e6 100644 --- a/test/unit/specs/modules/serverSideStorage.spec.js +++ b/test/unit/specs/modules/serverSideStorage.spec.js @@ -127,7 +127,7 @@ describe('The serverSideStorage module', () => { const state = cloneDeep(defaultState) setPreference(state, { path: 'simple.testing', value: 1 }) setPreference(state, { path: 'simple.testing', value: 2 }) - updateCache(state) + updateCache(state, { username: 'test' }) expect(state.prefsStorage.simple.testing).to.eql(2) expect(state.prefsStorage._journal.length).to.eql(1) expect(state.prefsStorage._journal[0]).to.eql({ -- cgit v1.2.3-70-g09d2 From 840ce063971d68063d380fc5f3aacc0564363b50 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 16 Aug 2022 19:24:20 +0300 Subject: proper journal trimming + remove some old workaround to my local bad data --- src/modules/serverSideStorage.js | 46 +++++++++++++---------- test/unit/specs/modules/serverSideStorage.spec.js | 15 +++++++- 2 files changed, 39 insertions(+), 22 deletions(-) (limited to 'test/unit/specs') diff --git a/src/modules/serverSideStorage.js b/src/modules/serverSideStorage.js index eb089be6..08b11b0b 100644 --- a/src/modules/serverSideStorage.js +++ b/src/modules/serverSideStorage.js @@ -1,5 +1,5 @@ import { toRaw } from 'vue' -import { isEqual, uniqWith, cloneDeep, set, get, clamp } from 'lodash' +import { isEqual, cloneDeep, set, get, clamp, flatten, groupBy, findLastIndex, takeRight } from 'lodash' import { CURRENT_UPDATE_COUNTER } from 'src/components/update_notification/update_notification.js' export const VERSION = 1 @@ -131,25 +131,32 @@ export const _mergeFlags = (recent, stale, allFlagKeys) => { })) } -const _mergeJournal = (a, b) => uniqWith( - // TODO use groupBy to group by path, then trim them depending on operations, - // i.e. if field got removed in the end - no need to sort it beforehand, if field - // got re-added no need to remove it and add it etc. - [ - ...(Array.isArray(a) ? a : []), - ...(Array.isArray(b) ? b : []) - ].sort((a, b) => a.timestamp > b.timestamp ? -1 : 1), - (a, b) => { - if (a.operation !== b.operation) return false - switch (a.operation) { - case 'set': - case 'arrangeSet': - return a.path === b.path - default: - return a.path === b.path && a.timestamp === b.timestamp +const _mergeJournal = (...journals) => { + const allJournals = flatten(journals.map(j => Array.isArray(j) ? j : [])) + const grouped = groupBy(allJournals, 'path') + const trimmedGrouped = Object.entries(grouped).map(([path, journal]) => { + // side effect + journal.sort((a, b) => a.timestamp > b.timestamp ? 1 : -1) + + if (path.startsWith('collections')) { + const lastRemoveIndex = findLastIndex(journal, ({ operation }) => operation === 'removeFromCollection') + // everything before last remove is unimportant + if (lastRemoveIndex > 0) { + return journal.slice(lastRemoveIndex) + } else { + // everything else doesn't need trimming + return journal + } + } else if (path.startsWith('simple')) { + // Only the last record is important + return takeRight(journal) + } else { + return journal } - } -).reverse() + }) + return flatten(trimmedGrouped) + .sort((a, b) => a.timestamp > b.timestamp ? 1 : -1) +} export const _mergePrefs = (recent, stale, allFlagKeys) => { if (!stale) return recent @@ -169,7 +176,6 @@ export const _mergePrefs = (recent, stale, allFlagKeys) => { const resultOutput = { ...recentData } const totalJournal = _mergeJournal(staleJournal, recentJournal) totalJournal.forEach(({ path, timestamp, operation, command, args }) => { - operation = operation || command if (path.startsWith('_')) { console.error(`journal contains entry to edit internal (starts with _) field '${path}', something is incorrect here, ignoring.`) return diff --git a/test/unit/specs/modules/serverSideStorage.spec.js b/test/unit/specs/modules/serverSideStorage.spec.js index f10e21e6..7adce20d 100644 --- a/test/unit/specs/modules/serverSideStorage.spec.js +++ b/test/unit/specs/modules/serverSideStorage.spec.js @@ -107,7 +107,7 @@ describe('The serverSideStorage module', () => { }) }) describe('setPreference', () => { - const { setPreference, updateCache } = mutations + const { setPreference, updateCache, addToCollection, removeFromCollection } = mutations it('should set preference and update journal log accordingly', () => { const state = cloneDeep(defaultState) @@ -123,12 +123,15 @@ describe('The serverSideStorage module', () => { }) }) - it('should keep journal to a minimum (one entry per path for sets)', () => { + it('should keep journal to a minimum', () => { const state = cloneDeep(defaultState) setPreference(state, { path: 'simple.testing', value: 1 }) setPreference(state, { path: 'simple.testing', value: 2 }) + addToCollection(state, { path: 'collections.testing', value: 2 }) + removeFromCollection(state, { path: 'collections.testing', value: 2 }) updateCache(state, { username: 'test' }) expect(state.prefsStorage.simple.testing).to.eql(2) + expect(state.prefsStorage.collections.testing).to.eql([]) expect(state.prefsStorage._journal.length).to.eql(1) expect(state.prefsStorage._journal[0]).to.eql({ path: 'simple.testing', @@ -137,6 +140,14 @@ describe('The serverSideStorage module', () => { // should have A timestamp, we don't really care what it is timestamp: state.prefsStorage._journal[0].timestamp }) + expect(state.prefsStorage._journal[1]).to.eql({ + path: 'collection.testing', + operation: 'remove', + args: [2], + // should have A timestamp, we don't really care what it is + timestamp: state.prefsStorage._journal[1].timestamp + }) + }) }) }) }) -- cgit v1.2.3-70-g09d2 From 821a09109c4747fcec49059f03c8cc908bd07ac5 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 16 Aug 2022 20:00:29 +0300 Subject: fix list tests --- src/modules/lists.js | 5 +++-- test/unit/specs/modules/lists.spec.js | 16 ++++++++-------- test/unit/specs/modules/serverSideStorage.spec.js | 1 - 3 files changed, 11 insertions(+), 11 deletions(-) (limited to 'test/unit/specs') diff --git a/src/modules/lists.js b/src/modules/lists.js index d9fab969..22fed800 100644 --- a/src/modules/lists.js +++ b/src/modules/lists.js @@ -15,10 +15,11 @@ export const mutations = { } state.allListsObject[listId].title = title - if (!find(state.allLists, { id: listId })) { + const entry = find(state.allLists, { id: listId }) + if (!entry) { state.allLists.push({ id: listId, title }) } else { - find(state.allLists, { id: listId }).title = title + entry.title = title } }, setListAccounts (state, { listId, accountIds }) { diff --git a/test/unit/specs/modules/lists.spec.js b/test/unit/specs/modules/lists.spec.js index ac9af1b6..e43106ea 100644 --- a/test/unit/specs/modules/lists.spec.js +++ b/test/unit/specs/modules/lists.spec.js @@ -17,13 +17,13 @@ describe('The lists module', () => { const list = { id: '1', title: 'testList' } const modList = { id: '1', title: 'anotherTestTitle' } - mutations.setList(state, list) - expect(state.allListsObject[list.id]).to.eql({ title: list.title }) + mutations.setList(state, { listId: list.id, title: list.title }) + expect(state.allListsObject[list.id]).to.eql({ title: list.title, accountIds: [] }) expect(state.allLists).to.have.length(1) expect(state.allLists[0]).to.eql(list) - mutations.setList(state, modList) - expect(state.allListsObject[modList.id]).to.eql({ title: modList.title }) + mutations.setList(state, { listId: modList.id, title: modList.title }) + expect(state.allListsObject[modList.id]).to.eql({ title: modList.title, accountIds: [] }) expect(state.allLists).to.have.length(1) expect(state.allLists[0]).to.eql(modList) }) @@ -33,10 +33,10 @@ describe('The lists module', () => { const list = { id: '1', accountIds: ['1', '2', '3'] } const modList = { id: '1', accountIds: ['3', '4', '5'] } - mutations.setListAccounts(state, list) + mutations.setListAccounts(state, { listId: list.id, accountIds: list.accountIds }) expect(state.allListsObject[list.id]).to.eql({ accountIds: list.accountIds }) - mutations.setListAccounts(state, modList) + mutations.setListAccounts(state, { listId: modList.id, accountIds: modList.accountIds }) expect(state.allListsObject[modList.id]).to.eql({ accountIds: modList.accountIds }) }) @@ -47,9 +47,9 @@ describe('The lists module', () => { 1: { title: 'testList', accountIds: ['1', '2', '3'] } } } - const id = '1' + const listId = '1' - mutations.deleteList(state, { id }) + mutations.deleteList(state, { listId }) expect(state.allLists).to.have.length(0) expect(state.allListsObject).to.eql({}) }) diff --git a/test/unit/specs/modules/serverSideStorage.spec.js b/test/unit/specs/modules/serverSideStorage.spec.js index 7adce20d..69cad0a5 100644 --- a/test/unit/specs/modules/serverSideStorage.spec.js +++ b/test/unit/specs/modules/serverSideStorage.spec.js @@ -148,7 +148,6 @@ describe('The serverSideStorage module', () => { timestamp: state.prefsStorage._journal[1].timestamp }) }) - }) }) }) -- cgit v1.2.3-70-g09d2 From 38bd59ceb0182de15e2e97d750df59ad53dfa51a Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 16 Aug 2022 20:14:18 +0300 Subject: fix journal test --- test/unit/specs/modules/serverSideStorage.spec.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'test/unit/specs') diff --git a/test/unit/specs/modules/serverSideStorage.spec.js b/test/unit/specs/modules/serverSideStorage.spec.js index 69cad0a5..be249eed 100644 --- a/test/unit/specs/modules/serverSideStorage.spec.js +++ b/test/unit/specs/modules/serverSideStorage.spec.js @@ -107,7 +107,7 @@ describe('The serverSideStorage module', () => { }) }) describe('setPreference', () => { - const { setPreference, updateCache, addToCollection, removeFromCollection } = mutations + const { setPreference, updateCache, addCollectionPreference, removeCollectionPreference } = mutations it('should set preference and update journal log accordingly', () => { const state = cloneDeep(defaultState) @@ -127,12 +127,12 @@ describe('The serverSideStorage module', () => { const state = cloneDeep(defaultState) setPreference(state, { path: 'simple.testing', value: 1 }) setPreference(state, { path: 'simple.testing', value: 2 }) - addToCollection(state, { path: 'collections.testing', value: 2 }) - removeFromCollection(state, { path: 'collections.testing', value: 2 }) + addCollectionPreference(state, { path: 'collections.testing', value: 2 }) + removeCollectionPreference(state, { path: 'collections.testing', value: 2 }) updateCache(state, { username: 'test' }) expect(state.prefsStorage.simple.testing).to.eql(2) expect(state.prefsStorage.collections.testing).to.eql([]) - expect(state.prefsStorage._journal.length).to.eql(1) + expect(state.prefsStorage._journal.length).to.eql(2) expect(state.prefsStorage._journal[0]).to.eql({ path: 'simple.testing', operation: 'set', @@ -141,8 +141,8 @@ describe('The serverSideStorage module', () => { timestamp: state.prefsStorage._journal[0].timestamp }) expect(state.prefsStorage._journal[1]).to.eql({ - path: 'collection.testing', - operation: 'remove', + path: 'collections.testing', + operation: 'removeFromCollection', args: [2], // should have A timestamp, we don't really care what it is timestamp: state.prefsStorage._journal[1].timestamp -- cgit v1.2.3-70-g09d2 From 0a79a747730bb4a10eb2544412dab68a10602240 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Mon, 29 Aug 2022 18:46:41 -0400 Subject: Use dedicated indicator for non-ascii domain names --- src/components/basic_user_card/basic_user_card.js | 4 ++- src/components/basic_user_card/basic_user_card.vue | 8 ++--- src/components/emoji_input/emoji_input.js | 4 ++- src/components/emoji_input/emoji_input.vue | 16 ++++++++- src/components/emoji_input/suggestor.js | 11 ++++--- src/components/mention_link/mention_link.js | 2 ++ src/components/mention_link/mention_link.vue | 3 ++ src/components/notification/notification.js | 4 ++- src/components/notification/notification.vue | 21 ++++++------ src/components/status/status.js | 4 ++- src/components/status/status.vue | 16 ++++----- .../unicode_domain_indicator.vue | 26 +++++++++++++++ src/components/user_card/user_card.js | 4 ++- src/components/user_card/user_card.vue | 9 ++--- src/components/user_link/user_link.vue | 38 ++++++++++++++++++++++ .../user_list_popover/user_list_popover.js | 2 ++ .../user_list_popover/user_list_popover.vue | 2 +- .../user_reporting_modal/user_reporting_modal.js | 4 ++- .../user_reporting_modal/user_reporting_modal.vue | 10 ++++-- src/i18n/en.json | 3 ++ .../entity_normalizer/entity_normalizer.service.js | 6 ++-- .../entity_normalizer/entity_normalizer.spec.js | 3 +- 22 files changed, 151 insertions(+), 49 deletions(-) create mode 100644 src/components/unicode_domain_indicator/unicode_domain_indicator.vue create mode 100644 src/components/user_link/user_link.vue (limited to 'test/unit/specs') diff --git a/src/components/basic_user_card/basic_user_card.js b/src/components/basic_user_card/basic_user_card.js index 8b1a2c38..31de2d75 100644 --- a/src/components/basic_user_card/basic_user_card.js +++ b/src/components/basic_user_card/basic_user_card.js @@ -1,5 +1,6 @@ import UserPopover from '../user_popover/user_popover.vue' import UserAvatar from '../user_avatar/user_avatar.vue' +import UserLink from '../user_link/user_link.vue' import RichContent from 'src/components/rich_content/rich_content.jsx' import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' @@ -10,7 +11,8 @@ const BasicUserCard = { components: { UserPopover, UserAvatar, - RichContent + RichContent, + UserLink }, methods: { userProfileLink (user) { diff --git a/src/components/basic_user_card/basic_user_card.vue b/src/components/basic_user_card/basic_user_card.vue index 9cca7840..418de926 100644 --- a/src/components/basic_user_card/basic_user_card.vue +++ b/src/components/basic_user_card/basic_user_card.vue @@ -30,12 +30,10 @@ />
- - @{{ user.screen_name_ui }} - + :user="user" + />
diff --git a/src/components/emoji_input/emoji_input.js b/src/components/emoji_input/emoji_input.js index 5ba3907f..b664d6b3 100644 --- a/src/components/emoji_input/emoji_input.js +++ b/src/components/emoji_input/emoji_input.js @@ -1,5 +1,6 @@ import Completion from '../../services/completion/completion.js' import EmojiPicker from '../emoji_picker/emoji_picker.vue' +import UnicodeDomainIndicator from '../unicode_domain_indicator/unicode_domain_indicator.vue' import { take } from 'lodash' import { findOffset } from '../../services/offset_finder/offset_finder.service.js' @@ -120,7 +121,8 @@ const EmojiInput = { } }, components: { - EmojiPicker + EmojiPicker, + UnicodeDomainIndicator }, computed: { padEmoji () { diff --git a/src/components/emoji_input/emoji_input.vue b/src/components/emoji_input/emoji_input.vue index 7d95ab7e..81b81913 100644 --- a/src/components/emoji_input/emoji_input.vue +++ b/src/components/emoji_input/emoji_input.vue @@ -50,7 +50,21 @@ {{ suggestion.replacement }}
- {{ suggestion.displayText }} + + {{ suggestion.displayText }} + + + {{ suggestion.displayText }} + {{ suggestion.detailText }}
diff --git a/src/components/emoji_input/suggestor.js b/src/components/emoji_input/suggestor.js index e8efbd1e..0ddb4d68 100644 --- a/src/components/emoji_input/suggestor.js +++ b/src/components/emoji_input/suggestor.js @@ -116,11 +116,12 @@ export const suggestUsers = ({ dispatch, state }) => { return diff + nameAlphabetically + screenNameAlphabetically /* eslint-disable camelcase */ - }).map(({ screen_name, screen_name_ui, name, profile_image_url_original }) => ({ - displayText: screen_name_ui, - detailText: name, - imageUrl: profile_image_url_original, - replacement: '@' + screen_name + ' ' + }).map((user) => ({ + user, + displayText: user.screen_name_ui, + detailText: user.name, + imageUrl: user.profile_image_url_original, + replacement: '@' + user.screen_name + ' ' })) /* eslint-enable camelcase */ diff --git a/src/components/mention_link/mention_link.js b/src/components/mention_link/mention_link.js index 4a74fbe2..6515bd11 100644 --- a/src/components/mention_link/mention_link.js +++ b/src/components/mention_link/mention_link.js @@ -2,6 +2,7 @@ import generateProfileLink from 'src/services/user_profile_link_generator/user_p import { mapGetters, mapState } from 'vuex' import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js' import UserAvatar from '../user_avatar/user_avatar.vue' +import UnicodeDomainIndicator from '../unicode_domain_indicator/unicode_domain_indicator.vue' import { defineAsyncComponent } from 'vue' import { library } from '@fortawesome/fontawesome-svg-core' import { @@ -16,6 +17,7 @@ const MentionLink = { name: 'MentionLink', components: { UserAvatar, + UnicodeDomainIndicator, UserPopover: defineAsyncComponent(() => import('../user_popover/user_popover.vue')) }, props: { diff --git a/src/components/mention_link/mention_link.vue b/src/components/mention_link/mention_link.vue index 3af502ef..869a3257 100644 --- a/src/components/mention_link/mention_link.vue +++ b/src/components/mention_link/mention_link.vue @@ -47,6 +47,9 @@ class="serverName" :class="{ '-faded': shouldFadeDomain }" v-html="'@' + serverName" + /> - - {{ notification.from_profile.screen_name_ui }} - +
- - @{{ user.screen_name_ui }} - + :user="user" + /> + + diff --git a/src/components/user_list_popover/user_list_popover.js b/src/components/user_list_popover/user_list_popover.js index e24eb9f7..046e0abd 100644 --- a/src/components/user_list_popover/user_list_popover.js +++ b/src/components/user_list_popover/user_list_popover.js @@ -1,5 +1,6 @@ import { defineAsyncComponent } from 'vue' import RichContent from 'src/components/rich_content/rich_content.jsx' +import UnicodeDomainIndicator from '../unicode_domain_indicator/unicode_domain_indicator.vue' import { library } from '@fortawesome/fontawesome-svg-core' import { faCircleNotch } from '@fortawesome/free-solid-svg-icons' @@ -15,6 +16,7 @@ const UserListPopover = { ], components: { RichContent, + UnicodeDomainIndicator, Popover: defineAsyncComponent(() => import('../popover/popover.vue')), UserAvatar: defineAsyncComponent(() => import('../user_avatar/user_avatar.vue')) }, diff --git a/src/components/user_list_popover/user_list_popover.vue b/src/components/user_list_popover/user_list_popover.vue index a3ce54c3..635dc7f6 100644 --- a/src/components/user_list_popover/user_list_popover.vue +++ b/src/components/user_list_popover/user_list_popover.vue @@ -29,7 +29,7 @@ :emoji="user.emoji" /> - {{ user.screen_name_ui }} + {{ user.screen_name_ui }}
diff --git a/src/components/user_reporting_modal/user_reporting_modal.js b/src/components/user_reporting_modal/user_reporting_modal.js index 85ffc661..67fde084 100644 --- a/src/components/user_reporting_modal/user_reporting_modal.js +++ b/src/components/user_reporting_modal/user_reporting_modal.js @@ -2,13 +2,15 @@ import Status from '../status/status.vue' import List from '../list/list.vue' import Checkbox from '../checkbox/checkbox.vue' import Modal from '../modal/modal.vue' +import UserLink from '../user_link/user_link.vue' const UserReportingModal = { components: { Status, List, Checkbox, - Modal + Modal, + UserLink }, data () { return { diff --git a/src/components/user_reporting_modal/user_reporting_modal.vue b/src/components/user_reporting_modal/user_reporting_modal.vue index 429a66e2..8c42ab7b 100644 --- a/src/components/user_reporting_modal/user_reporting_modal.vue +++ b/src/components/user_reporting_modal/user_reporting_modal.vue @@ -5,9 +5,13 @@ >
-
- {{ $t('user_reporting.title', [user.screen_name_ui]) }} -
+ + +
diff --git a/src/i18n/en.json b/src/i18n/en.json index 91722d9a..4ce56678 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -1006,5 +1006,8 @@ "update_changelog": "For more details on what's changed, see {theFullChangelog}.", "update_changelog_here": "the full changelog", "art_by": "Art by {linkToArtist}" + }, + "unicode_domain_indicator": { + "tooltip": "This domain contains non-ascii characters." } } diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js index e9cbcfe6..ce316832 100644 --- a/src/services/entity_normalizer/entity_normalizer.service.js +++ b/src/services/entity_normalizer/entity_normalizer.service.js @@ -214,12 +214,14 @@ export const parseUser = (data) => { output.screen_name_ui = output.screen_name if (output.screen_name && output.screen_name.includes('@')) { const parts = output.screen_name.split('@') - let unicodeDomain = punycode.toUnicode(parts[1]) + const unicodeDomain = punycode.toUnicode(parts[1]) if (unicodeDomain !== parts[1]) { // Add some identifier so users can potentially spot spoofing attempts: // lain.com and xn--lin-6cd.com would appear identical otherwise. - unicodeDomain = '🌏' + unicodeDomain + output.screen_name_ui_contains_non_ascii = true output.screen_name_ui = [parts[0], unicodeDomain].join('@') + } else { + output.screen_name_ui_contains_non_ascii = false } } diff --git a/test/unit/specs/services/entity_normalizer/entity_normalizer.spec.js b/test/unit/specs/services/entity_normalizer/entity_normalizer.spec.js index 98bb05a8..3923596b 100644 --- a/test/unit/specs/services/entity_normalizer/entity_normalizer.spec.js +++ b/test/unit/specs/services/entity_normalizer/entity_normalizer.spec.js @@ -269,7 +269,8 @@ describe('API Entities normalizer', () => { it('converts IDN to unicode and marks it as internatonal', () => { const user = makeMockUserMasto({ acct: 'lain@xn--lin-6cd.com' }) - expect(parseUser(user)).to.have.property('screen_name_ui').that.equal('lain@🌏lаin.com') + expect(parseUser(user)).to.have.property('screen_name_ui').that.equal('lain@lаin.com') + expect(parseUser(user)).to.have.property('screen_name_ui_contains_non_ascii').that.equal(true) }) }) -- cgit v1.2.3-70-g09d2