diff --git a/src/components/rich_content/rich_content.jsx b/src/components/rich_content/rich_content.jsx index ca075270c..7881e365c 100644 --- a/src/components/rich_content/rich_content.jsx +++ b/src/components/rich_content/rich_content.jsx @@ -150,6 +150,7 @@ export default { if (Array.isArray(item)) { const [opener, children, closer] = item const Tag = getTagName(opener) + const fullAttrs = getAttrs(opener, () => true) const attrs = getAttrs(opener) const previouslyMentions = currentMentions !== null /* During grouping of mentions we trim all the empty text elements @@ -171,7 +172,7 @@ export default { return ['', [mentionsLinePadding, renderImage(opener)], ''] case 'a': // replace mentions with MentionLink if (!this.handleLinks) break - if (attrs['class'] && attrs['class'].includes('mention')) { + if (fullAttrs.class && fullAttrs.class.includes('mention')) { // Handling mentions here return renderMention(attrs, children) } else { @@ -179,7 +180,7 @@ export default { break } case 'span': - if (this.handleLinks && attrs['class'] && attrs['class'].includes('h-card')) { + if (this.handleLinks && fullAttrs.class && fullAttrs.class.includes('h-card')) { return ['', children.map(processItem), ''] } } @@ -213,13 +214,14 @@ export default { const [opener, children] = item const Tag = opener === '' ? '' : getTagName(opener) switch (Tag) { - case 'a': // replace mentions with MentionLink + case 'a': { // replace mentions with MentionLink if (!this.handleLinks) break - const attrs = getAttrs(opener) + const fullAttrs = getAttrs(opener, () => true) + const attrs = getAttrs(opener, () => true) // should only be this if ( - (attrs['class'] && attrs['class'].includes('hashtag')) || // Pleroma style - (attrs['rel'] === 'tag') // Mastodon style + (fullAttrs.class && fullAttrs.class.includes('hashtag')) || // Pleroma style + (fullAttrs.rel === 'tag') // Mastodon style ) { return renderHashtag(attrs, children, encounteredTextReverse) } else { @@ -230,6 +232,7 @@ export default { { newChildren } } + } case '': return [...children].reverse().map(processItemReverse).reverse() } diff --git a/src/modules/config.js b/src/modules/config.js index c966602ec..3cd6888f0 100644 --- a/src/modules/config.js +++ b/src/modules/config.js @@ -83,8 +83,8 @@ export const defaultState = { useContainFit: true, disableStickyHeaders: false, showScrollbars: false, - userPopoverAvatarAction: 'close', - userPopoverOverlay: true, + userPopoverAvatarAction: 'open', + userPopoverOverlay: false, sidebarColumnWidth: '25rem', contentColumnWidth: '45rem', notifsColumnWidth: '25rem', diff --git a/src/modules/serverSideStorage.js b/src/modules/serverSideStorage.js index 56164be7a..c933ce8df 100644 --- a/src/modules/serverSideStorage.js +++ b/src/modules/serverSideStorage.js @@ -1,5 +1,5 @@ import { toRaw } from 'vue' -import { isEqual, cloneDeep, set, get, clamp, flatten, groupBy, findLastIndex, takeRight } from 'lodash' +import { isEqual, cloneDeep, set, get, clamp, flatten, groupBy, findLastIndex, takeRight, uniqWith } from 'lodash' import { CURRENT_UPDATE_COUNTER } from 'src/components/update_notification/update_notification.js' export const VERSION = 1 @@ -149,12 +149,21 @@ const _mergeJournal = (...journals) => { if (path.startsWith('collections')) { const lastRemoveIndex = findLastIndex(journal, ({ operation }) => operation === 'removeFromCollection') // everything before last remove is unimportant + let remainder if (lastRemoveIndex > 0) { - return journal.slice(lastRemoveIndex) + remainder = journal.slice(lastRemoveIndex) } else { // everything else doesn't need trimming - return journal + remainder = journal } + return uniqWith(remainder, (a, b) => { + if (a.path !== b.path) { return false } + if (a.operation !== b.operation) { return false } + if (a.operation === 'addToCollection') { + return a.args[0] === b.args[0] + } + return false + }) } else if (path.startsWith('simple')) { // Only the last record is important return takeRight(journal) diff --git a/src/services/html_converter/utility.service.js b/src/services/html_converter/utility.service.js index 583ccca5f..f10429718 100644 --- a/src/services/html_converter/utility.service.js +++ b/src/services/html_converter/utility.service.js @@ -16,7 +16,7 @@ export const getTagName = (tag) => { * @return {Object} - map of attributes key = attribute name, value = attribute value * attributes without values represented as boolean true */ -export const getAttrs = tag => { +export const getAttrs = (tag, filter) => { const innertag = tag .substring(1, tag.length - 1) .replace(new RegExp('^' + getTagName(tag)), '') @@ -28,7 +28,15 @@ export const getAttrs = tag => { if (!v) return [k, true] return [k, v.substring(1, v.length - 1)] }) - return Object.fromEntries(attrs) + const defaultFilter = ([k, v]) => { + const attrKey = k.toLowerCase() + if (attrKey === 'style') return false + if (attrKey === 'class') { + return v === 'greentext' || v === 'cyantext' + } + return true + } + return Object.fromEntries(attrs.filter(filter || defaultFilter)) } /** diff --git a/test/unit/specs/components/rich_content.spec.js b/test/unit/specs/components/rich_content.spec.js index 616df6a08..427eb5ed0 100644 --- a/test/unit/specs/components/rich_content.spec.js +++ b/test/unit/specs/components/rich_content.spec.js @@ -19,9 +19,11 @@ const global = { } } -const makeMention = (who) => { +const makeMention = (who, noClass) => { attentions.push({ statusnet_profile_url: `https://fake.tld/@${who}` }) - return `@${who}` + return noClass + ? `@${who}` + : `@${who}` } const p = (...data) => `

${data.join('')}

` const compwrap = (...data) => `${data.join('')}` @@ -142,6 +144,17 @@ describe('RichContent', () => { makeMention('Josh'), makeMention('Jeremy') ].join('') ].join('\n') + const strippedHtml = [ + [ + makeMention('Jack', true), + 'let\'s meet up with ', + makeMention('Janet', true) + ].join(''), + [ + makeMention('John', true), + makeMention('Josh', true), makeMention('Jeremy', true) + ].join('') + ].join('\n') const wrapper = shallowMount(RichContent, { global, @@ -154,7 +167,7 @@ describe('RichContent', () => { } }) - expect(wrapper.html()).to.eql(compwrap(html)) + expect(wrapper.html()).to.eql(compwrap(strippedHtml)) }) it('Adds greentext and cyantext to the post', () => { @@ -412,7 +425,7 @@ describe('RichContent', () => { 'Testing' ].join('') const expected = [ - '', + '', '', '', '', diff --git a/test/unit/specs/modules/serverSideStorage.spec.js b/test/unit/specs/modules/serverSideStorage.spec.js index be249eede..2e43263ae 100644 --- a/test/unit/specs/modules/serverSideStorage.spec.js +++ b/test/unit/specs/modules/serverSideStorage.spec.js @@ -148,6 +148,18 @@ describe('The serverSideStorage module', () => { timestamp: state.prefsStorage._journal[1].timestamp }) }) + + it('should remove duplicate entries from journal', () => { + const state = cloneDeep(defaultState) + setPreference(state, { path: 'simple.testing', value: 1 }) + setPreference(state, { path: 'simple.testing', value: 1 }) + addCollectionPreference(state, { path: 'collections.testing', value: 2 }) + addCollectionPreference(state, { path: 'collections.testing', value: 2 }) + updateCache(state, { username: 'test' }) + expect(state.prefsStorage.simple.testing).to.eql(1) + expect(state.prefsStorage.collections.testing).to.eql([2]) + expect(state.prefsStorage._journal.length).to.eql(2) + }) }) })