From b2dca4a3f6341ae15165f8d57f0d33955bd40591 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 4 Aug 2026 17:12:46 +0300 Subject: [PATCH 01/64] more low-hanging fruits --- build/update-emoji.js | 2 +- src/components/registration/registration.js | 2 +- src/components/rich_content/rich_content.jsx | 10 +++---- .../settings_modal/admin_tabs/emoji_tab.js | 2 +- .../settings_modal/helpers/setting.js | 4 +-- .../settings_modal/tabs/appearance_tab.js | 2 +- .../tabs/old_theme_tab/old_theme_tab.js | 2 +- src/components/status_body/status_body.js | 4 +-- src/components/user_card/user_card.js | 2 +- .../entity_normalizer.service.js | 4 +-- src/services/errors/errors.js | 2 +- src/services/style_setter/style_setter.js | 4 +-- src/services/sw/sw.js | 2 +- src/services/theme_data/theme_data.service.js | 2 +- .../user_highlighter/user_highlighter.js | 2 +- src/stores/emoji.js | 2 +- src/stores/interface.js | 2 +- .../specs/components/rich_content.spec.js | 26 +++++++++---------- test/unit/specs/modules/statuses.spec.js | 12 ++++----- tools/emoji_merger.js | 2 +- 20 files changed, 45 insertions(+), 45 deletions(-) diff --git a/build/update-emoji.js b/build/update-emoji.js index dd965cf66..b2631ec6e 100644 --- a/build/update-emoji.js +++ b/build/update-emoji.js @@ -3,7 +3,7 @@ import emojis from '@kazvmoe-infra/unicode-emoji-json/data-by-group.json' with { type: 'json', } -Object.keys(emojis).map((k) => { +Object.keys(emojis).forEach((k) => { emojis[k].forEach((e) => { delete e.unicode_version delete e.emoji_version diff --git a/src/components/registration/registration.js b/src/components/registration/registration.js index 60f0fd16b..7e0cf859c 100644 --- a/src/components/registration/registration.js +++ b/src/components/registration/registration.js @@ -154,7 +154,7 @@ const registration = { }) }, replaceNewlines(str) { - return str.replace(/\s*\n\s*/g, ' \n') + return str.replaceAll('\s*\n\s*', ' \n') }, }, } diff --git a/src/components/rich_content/rich_content.jsx b/src/components/rich_content/rich_content.jsx index 646df5bab..89a61be89 100644 --- a/src/components/rich_content/rich_content.jsx +++ b/src/components/rich_content/rich_content.jsx @@ -181,7 +181,7 @@ export default { } // Processor to use with html_tree_converter - const processItem = (item, index, array, what) => { + const processItem = (item, index, array) => { // Handle text nodes - just add emoji if (typeof item === 'string') { const emptyText = item.trim() === '' @@ -281,7 +281,7 @@ export default { // Processor for back direction (for finding "last" stuff, just easier this way) let encounteredTextReverse = false - const processItemReverse = (item, index, array, what) => { + const processItemReverse = (item, index, array) => { // Handle text nodes - just add emoji if (typeof item === 'string') { const emptyText = item.trim() === '' @@ -479,7 +479,7 @@ export default { > {this.collapse ? pass2.map((x) => { - if (typeof x === 'string') return x.replace(/\n/g, ' ') + if (typeof x === 'string') return x.replaceAll('\n', ' ') if (!Array.isArray(x)) return x return x.map((y) => (y.type === 'br' ? ' ' : y)) }) @@ -547,8 +547,8 @@ export const preProcessPerLine = (html, greentext) => { (string.includes('>') || string.includes('<')) ) { const cleanedString = string - .replace(/<[^>]+?>/gi, '') // remove all tags - .replace(/@\w+/gi, '') // remove mentions (even failed ones) + .replaceAll(/<[^>]+?>/gi, '') // remove all tags + .replaceAll(/@\w+/gi, '') // remove mentions (even failed ones) .trim() if (cleanedString.startsWith('>')) { return `${string}` diff --git a/src/components/settings_modal/admin_tabs/emoji_tab.js b/src/components/settings_modal/admin_tabs/emoji_tab.js index 56361587d..7dc9e91bf 100644 --- a/src/components/settings_modal/admin_tabs/emoji_tab.js +++ b/src/components/settings_modal/admin_tabs/emoji_tab.js @@ -300,7 +300,7 @@ const EmojiTab = { sortPackFiles(nameOfPack) { // Sort by key const sorted = Object.keys(this.knownPacks[nameOfPack].files) - .sort() + .sort((a, b) => a.localeCompare(b)) .reduce((acc, key) => { if (key.length === 0) return acc acc[key] = this.knownPacks[nameOfPack].files[key] diff --git a/src/components/settings_modal/helpers/setting.js b/src/components/settings_modal/helpers/setting.js index c2f5279f6..a2d33deff 100644 --- a/src/components/settings_modal/helpers/setting.js +++ b/src/components/settings_modal/helpers/setting.js @@ -179,7 +179,7 @@ export default { [ 'admin_dash', 'temp_overrides', - ...this.canonPath.map((p) => p.replace(/\./g, '_DOT_')), + ...this.canonPath.map((p) => p.replaceAll('\.', '_DOT_')), 'label', ].join('.'), ) @@ -198,7 +198,7 @@ export default { [ 'admin_dash', 'temp_overrides', - ...this.canonPath.map((p) => p.replace(/\./g, '_DOT_')), + ...this.canonPath.map((p) => p.replaceAll('\.', '_DOT_')), 'description', ].join('.'), ) diff --git a/src/components/settings_modal/tabs/appearance_tab.js b/src/components/settings_modal/tabs/appearance_tab.js index 518345d9e..d93758f9a 100644 --- a/src/components/settings_modal/tabs/appearance_tab.js +++ b/src/components/settings_modal/tabs/appearance_tab.js @@ -257,7 +257,7 @@ const AppearanceTab = { const result = { name: `${meta.directives.name || this.$t('settings.style.themes3.palette.imported')}: ${variant}`, - key: `style.${variant.toLowerCase().replace(/ /g, '_')}`, + key: `style.${variant.toLowerCase().replaceAll(' ', '_')}`, bg, fg, text, diff --git a/src/components/settings_modal/tabs/old_theme_tab/old_theme_tab.js b/src/components/settings_modal/tabs/old_theme_tab/old_theme_tab.js index 13bcc3ae6..7a2673621 100644 --- a/src/components/settings_modal/tabs/old_theme_tab/old_theme_tab.js +++ b/src/components/settings_modal/tabs/old_theme_tab/old_theme_tab.js @@ -319,7 +319,7 @@ export default { return useInterfaceStore().themeDataUsed }, shadowsAvailable() { - return Object.keys(DEFAULT_SHADOWS).sort() + return Object.keys(DEFAULT_SHADOWS).sort((a, b) => a.localeCompare(b)) }, currentShadowOverriden: { get() { diff --git a/src/components/status_body/status_body.js b/src/components/status_body/status_body.js index c76e04b9c..5e94e6fa4 100644 --- a/src/components/status_body/status_body.js +++ b/src/components/status_body/status_body.js @@ -147,7 +147,7 @@ const StatusBody = { return this.status.attachments.map((file) => file.type) }, collapsedStatus() { - return this.status.raw_html.replace(/(\n|)/g, ' ') + return this.status.raw_html.replaceAll('(\n|)', ' ') }, ...mapState(useMergedConfigStore, ['mergedConfig']), }, @@ -168,7 +168,7 @@ const StatusBody = { .filter((mention) => !mention.notifying) .forEach((mention) => { const { content, url } = mention - const cleanedString = content.replace(/<[^>]+?>/gi, '') // remove all tags + const cleanedString = content.replaceAll(/<[^>]+?>/gi, '') // remove all tags if (!cleanedString.startsWith('@')) return const handle = cleanedString.slice(1) const host = url.replace(/^https?:\/\//, '').replace(/\/.+?$/, '') diff --git a/src/components/user_card/user_card.js b/src/components/user_card/user_card.js index 9f6488b34..b6e720c6f 100644 --- a/src/components/user_card/user_card.js +++ b/src/components/user_card/user_card.js @@ -196,7 +196,7 @@ export default { }, computed: { escapedNewBio() { - return ldEscape(this.newBio).replace(/\n/g, '
') + return ldEscape(this.newBio).replaceAll('\n', '
') }, somethingToSave() { if (this.newName !== this.user.name_unescaped) return true diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js index d2df5e869..9a5a642fd 100644 --- a/src/services/entity_normalizer/entity_normalizer.service.js +++ b/src/services/entity_normalizer/entity_normalizer.service.js @@ -62,8 +62,8 @@ export const parseUser = (data) => { }) output.fields_text = data.fields.map((field) => { return { - name: unescape(field.name.replace(/<[^>]*>/g, '')), - value: unescape(field.value.replace(/<[^>]*>/g, '')), + name: unescape(field.name.replaceAll('<[^>]*>', '')), + value: unescape(field.value.replaceAll('<[^>]*>', '')), } }) diff --git a/src/services/errors/errors.js b/src/services/errors/errors.js index 5fbb8da11..0742de3f4 100644 --- a/src/services/errors/errors.js +++ b/src/services/errors/errors.js @@ -3,7 +3,7 @@ import { capitalize } from 'lodash' function humanizeErrors(errors) { return Object.entries(errors).reduce((errs, [k, val]) => { const message = val.reduce((acc, message) => { - const key = capitalize(k.replace(/_/g, ' ')) + const key = capitalize(k.replaceAll('_', ' ')) return acc + [key, message].join(' ') + '. ' }, '') return [...errs, message] diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index cb445d2c1..0cab385a3 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -29,7 +29,7 @@ export const createStyleSheet = (id, priority = 1000) => { addRule(rule) { let newRule = rule if (!CSS.supports?.('backdrop-filter', 'blur()')) { - newRule = newRule.replace(/backdrop-filter:[^;]+;/g, '') // Remove backdrop-filter + newRule = newRule.replaceAll('backdrop-filter:[^;]+;', '') // Remove backdrop-filter } if (newRule.startsWith('::-webkit')) { @@ -44,7 +44,7 @@ export const createStyleSheet = (id, priority = 1000) => { } this.rules.push( - newRule.replace(/var\(--shadowFilter\)[^;]*;/g, ''), // Remove shadowFilter references + newRule.replaceAll('var\(--shadowFilter\)[^;]*;', ''), // Remove shadowFilter references ) }, } diff --git a/src/services/sw/sw.js b/src/services/sw/sw.js index b45409b28..b114cb0de 100644 --- a/src/services/sw/sw.js +++ b/src/services/sw/sw.js @@ -1,7 +1,7 @@ /* global process */ function urlBase64ToUint8Array(base64String) { const padding = '='.repeat((4 - (base64String.length % 4)) % 4) - const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/') + const base64 = (base64String + padding).replaceAll('-', '+').replace(/_/g, '/') const rawData = window.atob(base64) return Uint8Array.from([...rawData].map((char) => char.codePointAt(0))) diff --git a/src/services/theme_data/theme_data.service.js b/src/services/theme_data/theme_data.service.js index cdce2cf57..4747c22c3 100644 --- a/src/services/theme_data/theme_data.service.js +++ b/src/services/theme_data/theme_data.service.js @@ -460,7 +460,7 @@ export const generatePreset = (input) => { return composePreset( colors, generateRadii(input), - generateShadows(input, colors.theme.colors, colors.mod), + generateShadows(input, colors.theme.colors), generateFonts(input), ) } diff --git a/src/services/user_highlighter/user_highlighter.js b/src/services/user_highlighter/user_highlighter.js index 697496c91..9724595c0 100644 --- a/src/services/user_highlighter/user_highlighter.js +++ b/src/services/user_highlighter/user_highlighter.js @@ -47,7 +47,7 @@ const highlightStyle = (prefs) => { const highlightClass = (user) => { return ( - 'USER____' + user.screen_name?.replace(/\./g, '_').replace(/@/g, '_AT_') + 'USER____' + user.screen_name?.replaceAll('\.', '_').replace(/@/g, '_AT_') ) } diff --git a/src/stores/emoji.js b/src/stores/emoji.js index 3316f8328..aea8e5985 100644 --- a/src/stores/emoji.js +++ b/src/stores/emoji.js @@ -231,7 +231,7 @@ export const useEmojiStore = defineStore('emoji', { .then((allPacks) => { // Sort by key return Object.keys(allPacks) - .sort() + .sort((a, b) => a.localeCompare(b)) .reduce((acc, key) => { if (key.length === 0) return acc acc[key] = allPacks[key] diff --git a/src/stores/interface.js b/src/stores/interface.js index 87ef65865..dc92d6067 100644 --- a/src/stores/interface.js +++ b/src/stores/interface.js @@ -578,7 +578,7 @@ export const useInterfaceStore = defineStore('interface', { return { name: x.variant, ...cleanDirectives } }) .forEach((palette) => { - const key = 'style.' + palette.name.toLowerCase().replace(/ /g, '_') + const key = 'style.' + palette.name.toLowerCase().replaceAll(' ', '_') if (!firstStylePaletteName) firstStylePaletteName = key palettesIndex[key] = () => Promise.resolve(palette) }) diff --git a/test/unit/specs/components/rich_content.spec.js b/test/unit/specs/components/rich_content.spec.js index fdf6c7f8f..48cd5eb91 100644 --- a/test/unit/specs/components/rich_content.spec.js +++ b/test/unit/specs/components/rich_content.spec.js @@ -50,7 +50,7 @@ describe('RichContent', () => { }, }) - expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(html)) + expect(wrapper.html().replaceAll('\n', '')).to.eql(compwrap(html)) }) it('unescapes everything as needed', () => { @@ -67,7 +67,7 @@ describe('RichContent', () => { }, }) - expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(expected)) + expect(wrapper.html().replaceAll('\n', '')).to.eql(compwrap(expected)) }) it('replaces mention with mentionsline', () => { @@ -83,7 +83,7 @@ describe('RichContent', () => { }, }) - expect(wrapper.html().replace(/\n/g, '')).to.eql( + expect(wrapper.html().replaceAll('\n', '')).to.eql( compwrap(p(mentionsLine(1), ' how are you doing today?')), ) }) @@ -116,7 +116,7 @@ describe('RichContent', () => { }, }) - expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(expected)) + expect(wrapper.html().replaceAll('\n', '')).to.eql(compwrap(expected)) }) it('Does not touch links if link handling is disabled', () => { @@ -211,7 +211,7 @@ describe('RichContent', () => { }, }) - expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(expected)) + expect(wrapper.html().replaceAll('\n', '')).to.eql(compwrap(expected)) }) it("Doesn't add nonexistent emoji to post", () => { @@ -228,7 +228,7 @@ describe('RichContent', () => { }, }) - expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(html)) + expect(wrapper.html().replaceAll('\n', '')).to.eql(compwrap(html)) }) it('Greentext + last mentions', () => { @@ -279,7 +279,7 @@ describe('RichContent', () => { }, }) - expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(expected)) + expect(wrapper.html().replaceAll('\n', '')).to.eql(compwrap(expected)) }) it('buggy example/hashtags', () => { @@ -315,7 +315,7 @@ describe('RichContent', () => { }, }) - expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(expected)) + expect(wrapper.html().replaceAll('\n', '')).to.eql(compwrap(expected)) }) it('rich contents of a mention are handled properly', () => { @@ -365,8 +365,8 @@ describe('RichContent', () => { expect( wrapper .html() - .replace(/\n/g, '') - .replace(//g, ''), + .replaceAll('\n', '') + .replaceAll('', ''), ).to.eql(compwrap(expected)) }) @@ -438,8 +438,8 @@ describe('RichContent', () => { expect( wrapper .html() - .replace(/\n/g, '') - .replace(//g, ''), + .replaceAll('\n', '') + .replaceAll('', ''), ).to.eql(compwrap(expected)) }) @@ -484,7 +484,7 @@ describe('RichContent', () => { }, }) - expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(expected)) + expect(wrapper.html().replaceAll('\n', '')).to.eql(compwrap(expected)) }) it.skip('[INFORMATIVE] Performance testing, 10 000 simple posts', () => { diff --git a/test/unit/specs/modules/statuses.spec.js b/test/unit/specs/modules/statuses.spec.js index 1315724da..cd43496a9 100644 --- a/test/unit/specs/modules/statuses.spec.js +++ b/test/unit/specs/modules/statuses.spec.js @@ -107,7 +107,7 @@ describe('Statuses module', () => { showImmediately: true, timeline: 'public', }) - expect(state.timelines.public.maxId).to.eql('1') + expect(state.timelines.public.maxId).to.equal('1') mutations.addNewStatuses(state, { statuses: [secondStatus], @@ -120,7 +120,7 @@ describe('Statuses module', () => { secondStatus, status, ]) - expect(state.timelines.public.maxId).to.eql('1') + expect(state.timelines.public.maxId).to.equal('1') }) it('keeps a descending by id order in timeline.visibleStatuses and timeline.statuses', () => { @@ -340,7 +340,7 @@ describe('Statuses module', () => { expect(state.allStatusesObject['1'].emoji_reactions[0].me).to.eql(true) expect( state.allStatusesObject['1'].emoji_reactions[0].accounts[0].id, - ).to.eql('me') + ).to.equal('me') }) it('adds a new reaction', () => { @@ -362,7 +362,7 @@ describe('Statuses module', () => { expect(state.allStatusesObject['1'].emoji_reactions[0].me).to.eql(true) expect( state.allStatusesObject['1'].emoji_reactions[0].accounts[0].id, - ).to.eql('me') + ).to.equal('me') }) it('decreases count in existing reaction', () => { @@ -429,8 +429,8 @@ describe('Statuses module', () => { mutations.showNewStatuses(state, { timeline: 'public' }) expect(state.timelines.public.visibleStatuses.length).to.eql(2) - expect(state.timelines.public.minVisibleId).to.eql('10') - expect(state.timelines.public.minId).to.eql('10') + expect(state.timelines.public.minVisibleId).to.equal('10') + expect(state.timelines.public.minId).to.equal('10') }) }) diff --git a/tools/emoji_merger.js b/tools/emoji_merger.js index b49ead471..1e37134a7 100644 --- a/tools/emoji_merger.js +++ b/tools/emoji_merger.js @@ -54,7 +54,7 @@ const run = () => { // Sort by key const sorted = Object.keys(emojisObject) - .sort() + .sort((a, b) => a.localeCompare(b)) .reduce((acc, key) => { if (key.length === 0) return acc acc[key] = emojisObject[key] From 74a82f484a83922fb76ec1cf938c616ebb48b538 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 4 Aug 2026 17:17:01 +0300 Subject: [PATCH 02/64] more --- src/components/tab_switcher/tab_switcher.jsx | 2 +- src/stores/emoji.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/tab_switcher/tab_switcher.jsx b/src/components/tab_switcher/tab_switcher.jsx index 2c86983d8..f9a35ad1c 100644 --- a/src/components/tab_switcher/tab_switcher.jsx +++ b/src/components/tab_switcher/tab_switcher.jsx @@ -111,7 +111,7 @@ export default { type="button" role="tab" > - + {props['image-tooltip']} {props.label ? '' : props.label} diff --git a/src/stores/emoji.js b/src/stores/emoji.js index aea8e5985..2d77954f1 100644 --- a/src/stores/emoji.js +++ b/src/stores/emoji.js @@ -146,7 +146,7 @@ export const useEmojiStore = defineStore('emoji', { async getStaticEmoji() { try { // See build/emojis_plugin for more details - const values = (await import('/src/assets/emoji.json')).default + const values = (await import('src/assets/emoji.json')).default const emoji = Object.keys(values).reduce((res, groupId) => { res[groupId] = values[groupId].map((e) => ({ From f59cd5afa3726a954fe636c76c505fc729f9dde1 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 4 Aug 2026 17:17:05 +0300 Subject: [PATCH 03/64] cherry-pick this into 2.11.3 fixes --- src/components/moderation_tools/moderation_tools.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/moderation_tools/moderation_tools.js b/src/components/moderation_tools/moderation_tools.js index ca4852ab3..ba13afc8b 100644 --- a/src/components/moderation_tools/moderation_tools.js +++ b/src/components/moderation_tools/moderation_tools.js @@ -405,7 +405,7 @@ const ModerationTools = { ) }, isAdmin() { - this.$store.state.users.currentUser.role === 'admin' + return this.$store.state.users.currentUser.role === 'admin' }, }, methods: { From 6c108d7704fc818aa00f1eaa1b93f738b46132e8 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 4 Aug 2026 17:22:33 +0300 Subject: [PATCH 04/64] also cherry-pick? --- .../notifications_fetcher/notifications_fetcher.service.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/services/notifications_fetcher/notifications_fetcher.service.js b/src/services/notifications_fetcher/notifications_fetcher.service.js index 8530c468c..0e0bc0277 100644 --- a/src/services/notifications_fetcher/notifications_fetcher.service.js +++ b/src/services/notifications_fetcher/notifications_fetcher.service.js @@ -66,7 +66,8 @@ const fetchAndUpdate = ({ store, credentials, older = false, sinceId }) => { const unreadNotifsIds = notifications .filter((n) => !n.seen) .map((n) => n.id) - if (readNotifsIds.length > 0 && readNotifsIds.length > 0) { + + if (readNotifsIds.length > 0 && unreadNotifsIds.length > 0) { const minId = Math.min(...unreadNotifsIds) // Oldest known unread notification if (minId !== Infinity) { args.sinceId = null // Don't use since_id since it sorta conflicts with min_id From 0fc8eec293714e749525d60ce5ea7968fe138426 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 4 Aug 2026 17:26:30 +0300 Subject: [PATCH 05/64] tentative fix --- src/services/theme_data/iss_utils.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/services/theme_data/iss_utils.js b/src/services/theme_data/iss_utils.js index e5aa5cd15..3dc3aa65d 100644 --- a/src/services/theme_data/iss_utils.js +++ b/src/services/theme_data/iss_utils.js @@ -110,14 +110,13 @@ export const genericRuleToSelector = let arraySelector = Array.isArray(selector) ? selector : [selector] if (ignoreOutOfTreeSelector || liteMode) arraySelector = [arraySelector[0]] - arraySelector + return arraySelector .sort((a) => { if (a.startsWith(':')) return 1 if (/^[a-z]/.exec(a)) return -1 else return 0 }) .join('') - return arraySelector }) const statesSelectorsFlat = statesSelectors.reduce((acc, s) => { From 5ea3d462b9d6c952ab5e77f120df489c049baf25 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 4 Aug 2026 18:04:20 +0300 Subject: [PATCH 06/64] optional chaining --- src/components/announcement/announcement.js | 3 +-- .../announcements_page/announcements_page.js | 3 +-- src/components/attachment/attachment.js | 8 +++---- src/components/conversation/conversation.js | 2 +- src/components/desktop_nav/desktop_nav.js | 3 +-- .../edit_status_modal/edit_status_modal.js | 2 +- src/components/emoji_input/suggestor.js | 2 +- src/components/flash/flash.js | 2 +- src/components/image_cropper/image_cropper.js | 2 +- src/components/mention_link/mention_link.js | 8 +++---- .../mrf_transparency_panel.js | 2 +- src/components/navigation/filter.js | 2 +- src/components/poll/poll.js | 6 ++--- src/components/popover/popover.js | 23 +++++++++---------- .../post_status_form/post_status_form.js | 8 +++---- .../post_status_modal/post_status_modal.js | 2 +- src/components/quote/quote_form.js | 2 +- src/components/rich_content/rich_content.jsx | 7 +++--- src/components/search/search.js | 2 +- .../tabs/old_theme_tab/old_theme_tab.js | 6 ++--- src/components/side_drawer/side_drawer.js | 2 +- src/components/status/status.js | 2 +- src/components/status_body/status_body.js | 9 ++++---- src/components/still-image/still-image.js | 4 ++-- src/lib/persisted_state.js | 2 +- src/modules/api.js | 2 +- src/modules/statuses.js | 2 +- src/services/chat_utils/chat_utils.js | 3 +-- .../entity_normalizer.service.js | 2 +- .../notification_utils/notification_utils.js | 5 +--- src/services/theme_data/theme_data.service.js | 4 ++-- src/stores/chats.js | 4 ++-- src/stores/interface.js | 3 +-- src/stores/sync_config.js | 6 +++-- 34 files changed, 68 insertions(+), 77 deletions(-) diff --git a/src/components/announcement/announcement.js b/src/components/announcement/announcement.js index ee427533f..6f6190ebb 100644 --- a/src/components/announcement/announcement.js +++ b/src/components/announcement/announcement.js @@ -30,8 +30,7 @@ const Announcement = { }), canEditAnnouncement() { return ( - this.currentUser && - this.currentUser.privileges.has('announcements_manage_announcements') + this.currentUser?.privileges.has('announcements_manage_announcements') ) }, content() { diff --git a/src/components/announcements_page/announcements_page.js b/src/components/announcements_page/announcements_page.js index 0f7933e5f..a8d498075 100644 --- a/src/components/announcements_page/announcements_page.js +++ b/src/components/announcements_page/announcements_page.js @@ -34,8 +34,7 @@ const AnnouncementsPage = { }, canPostAnnouncement() { return ( - this.currentUser && - this.currentUser.privileges.has('announcements_manage_announcements') + this.currentUser?.privileges.has('announcements_manage_announcements') ) }, }, diff --git a/src/components/attachment/attachment.js b/src/components/attachment/attachment.js index f7d6ebf10..6556b07a0 100644 --- a/src/components/attachment/attachment.js +++ b/src/components/attachment/attachment.js @@ -165,16 +165,16 @@ const Attachment = { useMediaViewerStore().setCurrentMedia(this.attachment) }, onEdit(event) { - this.edit && this.edit(this.attachment, event) + this.edit?.(this.attachment, event) }, onRemove() { - this.remove && this.remove(this.attachment) + this.remove?.(this.attachment) }, onShiftUp() { - this.shiftUp && this.shiftUp(this.attachment) + this.shiftUp?.(this.attachment) }, onShiftDn() { - this.shiftDn && this.shiftDn(this.attachment) + this.shiftDn?.(this.attachment) }, stopFlash() { this.$refs.flash.closePlayer() diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js index 0bbf01e6a..fcde9477c 100644 --- a/src/components/conversation/conversation.js +++ b/src/components/conversation/conversation.js @@ -375,7 +375,7 @@ const conversation = { return !!(this.expanded || this.isPage) }, hiddenStyle() { - const height = (this.status && this.status.virtualHeight) || '120px' + const height = this.status?.virtualHeight || '120px' return this.virtualHidden ? { height } : {} }, threadDisplayStatus() { diff --git a/src/components/desktop_nav/desktop_nav.js b/src/components/desktop_nav/desktop_nav.js index fb23299d2..cc2c5aca9 100644 --- a/src/components/desktop_nav/desktop_nav.js +++ b/src/components/desktop_nav/desktop_nav.js @@ -45,8 +45,7 @@ export default { data: () => ({ searchBarHidden: true, supportsMask: - window.CSS && - window.CSS.supports && + window.CSS?.supports && (window.CSS.supports('mask-size', 'contain') || window.CSS.supports('-webkit-mask-size', 'contain') || window.CSS.supports('-moz-mask-size', 'contain') || diff --git a/src/components/edit_status_modal/edit_status_modal.js b/src/components/edit_status_modal/edit_status_modal.js index c3ba7e4cb..78c5c51aa 100644 --- a/src/components/edit_status_modal/edit_status_modal.js +++ b/src/components/edit_status_modal/edit_status_modal.js @@ -43,7 +43,7 @@ const EditStatusModal = { isFormVisible(val) { if (val) { this.$nextTick( - () => this.$el && this.$el.querySelector('textarea').focus(), + () => this.$el?.querySelector('textarea').focus(), ) } }, diff --git a/src/components/emoji_input/suggestor.js b/src/components/emoji_input/suggestor.js index d5e83ecb2..c31cb6717 100644 --- a/src/components/emoji_input/suggestor.js +++ b/src/components/emoji_input/suggestor.js @@ -79,7 +79,7 @@ export const suggestUsers = ({ dispatch, state }) => { const userSearch = (query) => dispatch('searchUsers', { query }) const debounceUserSearch = (query) => { - cancelUserSearch && cancelUserSearch() + cancelUserSearch?.() return new Promise((resolve, reject) => { timeout = setTimeout(() => { userSearch(query).then(resolve).catch(reject) diff --git a/src/components/flash/flash.js b/src/components/flash/flash.js index 3c25abea1..48d342d2f 100644 --- a/src/components/flash/flash.js +++ b/src/components/flash/flash.js @@ -44,7 +44,7 @@ const Flash = { }) }, closePlayer() { - this.ruffleInstance && this.ruffleInstance.remove() + this.ruffleInstance?.remove() this.player = false this.$emit('playerClosed') }, diff --git a/src/components/image_cropper/image_cropper.js b/src/components/image_cropper/image_cropper.js index c8529c8e6..b85ef6626 100644 --- a/src/components/image_cropper/image_cropper.js +++ b/src/components/image_cropper/image_cropper.js @@ -50,7 +50,7 @@ const ImageCropper = { }, readFile() { const fileInput = this.$refs.input - if (fileInput.files != null && fileInput.files[0] != null) { + if (fileInput?.files?.[0]) { this.file = fileInput.files[0] const reader = new window.FileReader() reader.onload = (e) => { diff --git a/src/components/mention_link/mention_link.js b/src/components/mention_link/mention_link.js index f1861748b..ff4612395 100644 --- a/src/components/mention_link/mention_link.js +++ b/src/components/mention_link/mention_link.js @@ -76,12 +76,12 @@ const MentionLink = { computed: { user() { return ( - this.url && this.$store && this.$store.getters.findUserByUrl(this.url) + this.url && this.$store?.getters.findUserByUrl(this.url) ) }, isYou() { // FIXME why user !== currentUser??? - return this.user && this.user.id === this.currentUser.id + return this.user?.id === this.currentUser.id }, userName() { return this.user && this.userNameFullUi.split('@')[0] @@ -94,10 +94,10 @@ const MentionLink = { ) }, userNameFull() { - return this.user && this.user.screen_name + return this.user?.screen_name }, userNameFullUi() { - return this.user && this.user.screen_name_ui + return this.user?.screen_name_ui }, highlightData() { return this.highlight[this.user?.screen_name] diff --git a/src/components/mrf_transparency_panel/mrf_transparency_panel.js b/src/components/mrf_transparency_panel/mrf_transparency_panel.js index b2048984d..7f2a16186 100644 --- a/src/components/mrf_transparency_panel/mrf_transparency_panel.js +++ b/src/components/mrf_transparency_panel/mrf_transparency_panel.js @@ -11,7 +11,7 @@ import { useInstanceStore } from 'src/stores/instance.js' */ const toInstanceReasonObject = (instances, info, key) => { return instances.map((instance) => { - if (info[key] && info[key][instance] && info[key][instance].reason) { + if (info[key]?.[instance]?.reason) { return { instance, reason: info[key][instance].reason } } return { instance, reason: '' } diff --git a/src/components/navigation/filter.js b/src/components/navigation/filter.js index 0255db6aa..ff91d97d9 100644 --- a/src/components/navigation/filter.js +++ b/src/components/navigation/filter.js @@ -15,7 +15,7 @@ export const filterNavigation = ( if (!isFederating && set.has('federating')) return false if (!currentUser && isPrivate && set.has('!private')) return false if (!currentUser && !(anon || anonRoute)) return false - if ((!currentUser || !currentUser.locked) && set.has('lockedUser')) + if ((!currentUser?.locked) && set.has('lockedUser')) return false if (!hasChats && set.has('chats')) return false if (!hasAnnouncements && set.has('announcements')) return false diff --git a/src/components/poll/poll.js b/src/components/poll/poll.js index b93a6699d..f2d4b0ee5 100644 --- a/src/components/poll/poll.js +++ b/src/components/poll/poll.js @@ -39,13 +39,13 @@ export default { return storePoll || {} }, options() { - return (this.poll && this.poll.options) || [] + return (this.poll?.options) || [] }, expiresAt() { - return (this.poll && this.poll.expires_at) || null + return (this.poll?.expires_at) || null }, expired() { - return (this.poll && this.poll.expired) || false + return (this.poll?.expired) || false }, expirationLabel() { if (useMergedConfigStore().mergedConfig.useAbsoluteTimeFormat) { diff --git a/src/components/popover/popover.js b/src/components/popover/popover.js index 9a06c4e69..857caadcb 100644 --- a/src/components/popover/popover.js +++ b/src/components/popover/popover.js @@ -130,7 +130,7 @@ const Popover = { // its children are what are inside the slot. Expect only one v-slot:trigger. const anchorEl = this.anchorEl || - (this.$refs.trigger && this.$refs.trigger.children[0]) || + (this.$refs.trigger?.children[0]) || this.$el // SVGs don't have offsetWidth/Height, use fallback const anchorHeight = anchorEl.offsetHeight || anchorEl.clientHeight @@ -155,8 +155,7 @@ const Popover = { // Minor optimization, don't call a slow reflow call if we don't have to const parentScreenBox = - this.boundTo && - (this.boundTo.x === 'container' || this.boundTo.y === 'container') && + (this.boundTo?.x === 'container' || this.boundTo?.y === 'container') && this.containerBoundingClientRect() const margin = this.margin || {} @@ -164,7 +163,7 @@ const Popover = { // What are the screen bounds for the popover? Viewport vs container // when using viewport, using default margin values to dodge the navbar const xBounds = - this.boundTo && this.boundTo.x === 'container' + this.boundTo?.x === 'container' ? { min: parentScreenBox.left + (margin.left || 0), max: parentScreenBox.right - (margin.right || 0), @@ -175,7 +174,7 @@ const Popover = { } const yBounds = - this.boundTo && this.boundTo.y === 'container' + this.boundTo?.y === 'container' ? { min: parentScreenBox.top + (margin.top || 0), max: parentScreenBox.bottom - (margin.bottom || 0), @@ -247,12 +246,12 @@ const Popover = { if (bottomBoundary + content.offsetHeight > yBounds.max) usingTop = true if (topBoundary - content.offsetHeight < yBounds.min) usingTop = false - const yOffset = (this.offset && this.offset.y) || 0 + const yOffset = (this.offset?.y) || 0 translateY = usingTop ? topBoundary - yOffset - content.offsetHeight : bottomBoundary + yOffset - const xOffset = (this.offset && this.offset.x) || 0 + const xOffset = (this.offset?.x) || 0 translateX = origin.x + horizOffset + xOffset } else { // Default to whatever user wished with placement prop @@ -268,12 +267,12 @@ const Popover = { if (rightBoundary + content.offsetWidth > xBounds.max) usingLeft = true if (leftBoundary - content.offsetWidth < xBounds.min) usingLeft = false - const xOffset = (this.offset && this.offset.x) || 0 + const xOffset = (this.offset?.x) || 0 translateX = usingLeft ? leftBoundary - xOffset - content.offsetWidth : rightBoundary + xOffset - const yOffset = (this.offset && this.offset.y) || 0 + const yOffset = (this.offset?.y) || 0 translateY = origin.y + vertOffset + yOffset } @@ -298,7 +297,7 @@ const Popover = { }, 0) const wasHidden = this.hidden this.hidden = false - this.parentPopover && this.parentPopover.onChildPopoverState(this, true) + this.parentPopover?.onChildPopoverState(this, true) if (this.trigger === 'click' || this.stayOnClick) { document.addEventListener('click', this.onClickOutside) } @@ -316,7 +315,7 @@ const Popover = { if (this.disabled) return if (!this.hidden) this.$emit('close') this.hidden = true - this.parentPopover && this.parentPopover.onChildPopoverState(this, false) + this.parentPopover?.onChildPopoverState(this, false) if (this.trigger === 'click') { document.removeEventListener('click', this.onClickOutside) } @@ -366,7 +365,7 @@ const Popover = { onClickOutside(e) { if (this.disableClickOutside) return if (this.hidden) return - if (this.$refs.content && this.$refs.content.contains(e.target)) return + if (this.$refs.content?.contains(e.target)) return if (this.$el.contains(e.target)) return if (this.childrenShown.size > 0) return this.hidePopover() diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index 02a70e388..dacca5b3c 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -316,7 +316,7 @@ const PostStatusForm = { }, // -Edit isEdit() { - return typeof this.statusId !== 'undefined' && this.statusId.trim() !== '' + return this.statusId !== undefined && this.statusId.trim() !== '' }, // -Reply isReply() { @@ -619,7 +619,7 @@ const PostStatusForm = { this.newStatus.quote = null this.newStatus.nsfw = this.defaultNewStatus.nsfw this.newStatus.mediaDescriptions = {} - this.$refs.mediaUpload && this.$refs.mediaUpload.clearFile() + this.$refs.mediaUpload?.clearFile() if (this.preserveFocus) { this.$nextTick(() => { this.$refs.textarea.focus() @@ -809,7 +809,7 @@ const PostStatusForm = { } }, fileDrop(e) { - if (e.dataTransfer && e.dataTransfer.types.includes('Files')) { + if (e.dataTransfer?.types.includes('Files')) { e.preventDefault() // allow dropping text like before this.dropFiles = e.dataTransfer.files clearTimeout(this.dropStopTimeout) @@ -826,7 +826,7 @@ const PostStatusForm = { }, fileDrag(e) { e.dataTransfer.dropEffect = this.uploadFileLimitReached ? 'none' : 'copy' - if (e.dataTransfer && e.dataTransfer.types.includes('Files')) { + if (e.dataTransfer?.types.includes('Files')) { clearTimeout(this.dropStopTimeout) this.showDropIcon = 'show' } diff --git a/src/components/post_status_modal/post_status_modal.js b/src/components/post_status_modal/post_status_modal.js index 973c2b1a5..7e9f1a4f8 100644 --- a/src/components/post_status_modal/post_status_modal.js +++ b/src/components/post_status_modal/post_status_modal.js @@ -41,7 +41,7 @@ const PostStatusModal = { isFormVisible(val) { if (val) { this.$nextTick( - () => this.$el && this.$el.querySelector('textarea').focus(), + () => this.$el?.querySelector('textarea').focus(), ) } }, diff --git a/src/components/quote/quote_form.js b/src/components/quote/quote_form.js index cd6f9a709..b350fb0e3 100644 --- a/src/components/quote/quote_form.js +++ b/src/components/quote/quote_form.js @@ -102,7 +102,7 @@ export default { type: 'statuses', }) .then((data) => { - if (data?.statuses && data.statuses.length === 1) { + if (data?.statuses?.length === 1) { this.$emit('update:id', data.statuses[0].id) } else { this.handleError(true) diff --git a/src/components/rich_content/rich_content.jsx b/src/components/rich_content/rich_content.jsx index 89a61be89..090253d91 100644 --- a/src/components/rich_content/rich_content.jsx +++ b/src/components/rich_content/rich_content.jsx @@ -251,7 +251,7 @@ export default { return ['', [mentionsLinePadding, renderImage(opener)], ''] } else if (Tag === 'a' && this.handleLinks) { // replace mentions with MentionLink - if (fullAttrs.class && fullAttrs.class.includes('mention')) { + if (fullAttrs.class?.includes('mention')) { // Handling mentions here return renderMention(attrs, children) } else { @@ -260,8 +260,7 @@ export default { } else if (Tag === 'span') { if ( this.handleLinks && - fullAttrs.class && - fullAttrs.class.includes('h-card') + fullAttrs.class?.includes('h-card') ) { return ['', children.map(processItem), ''] } @@ -300,7 +299,7 @@ export default { const attrs = getAttrs(opener, () => true) // should only be this if ( - (fullAttrs.class && fullAttrs.class.includes('hashtag')) || // Pleroma style + (fullAttrs.class?.includes('hashtag')) || // Pleroma style fullAttrs.rel === 'tag' // Mastodon style ) { return renderHashtag(attrs, children, encounteredTextReverse) diff --git a/src/components/search/search.js b/src/components/search/search.js index 0a1f779bb..1a5f8b51d 100644 --- a/src/components/search/search.js +++ b/src/components/search/search.js @@ -122,7 +122,7 @@ const Search = { return 'statuses' }, lastHistoryRecord(hashtag) { - return hashtag.history && hashtag.history[0] + return hashtag.history?.[0] }, }, } diff --git a/src/components/settings_modal/tabs/old_theme_tab/old_theme_tab.js b/src/components/settings_modal/tabs/old_theme_tab/old_theme_tab.js index 7a2673621..40df1b98e 100644 --- a/src/components/settings_modal/tabs/old_theme_tab/old_theme_tab.js +++ b/src/components/settings_modal/tabs/old_theme_tab/old_theme_tab.js @@ -346,7 +346,7 @@ export default { }, }, currentShadowFallback() { - return (this.previewTheme.shadows || {})[this.shadowSelected] + return this.previewTheme.shadows?.[this.shadowSelected] }, currentShadow: { get() { @@ -425,8 +425,8 @@ export default { this.dismissWarning() const version = origin === 'localStorage' && !theme.colors ? 'l1' : fileVersion - const snapshotEngineVersion = (theme || {}).themeEngineVersion - const themeEngineVersion = (source || {}).themeEngineVersion || 2 + const snapshotEngineVersion = theme?.themeEngineVersion + const themeEngineVersion = source?.themeEngineVersion || 2 const versionsMatch = themeEngineVersion === CURRENT_VERSION const sourceSnapshotMismatch = theme !== undefined && diff --git a/src/components/side_drawer/side_drawer.js b/src/components/side_drawer/side_drawer.js index 369177c7a..a1dca8161 100644 --- a/src/components/side_drawer/side_drawer.js +++ b/src/components/side_drawer/side_drawer.js @@ -61,7 +61,7 @@ const SideDrawer = { this.toggleDrawer, ) - if (this.currentUser && this.currentUser.locked) { + if (this.currentUser?.locked) { this.$store.dispatch('startFetchingFollowRequests') } }, diff --git a/src/components/status/status.js b/src/components/status/status.js index 61a553ef6..cd3ae44d0 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -129,7 +129,7 @@ const Status = { showReasonMutedThread() { return ( (this.status.thread_muted || - (this.status.reblog && this.status.reblog.thread_muted)) && + (this.status.reblog?.thread_muted)) && !this.inConversation ) }, diff --git a/src/components/status_body/status_body.js b/src/components/status_body/status_body.js index 5e94e6fa4..dc74426ca 100644 --- a/src/components/status_body/status_body.js +++ b/src/components/status_body/status_body.js @@ -152,11 +152,10 @@ const StatusBody = { ...mapState(useMergedConfigStore, ['mergedConfig']), }, mounted() { - this.status.attentions && - this.status.attentions.forEach((attn) => { - const { id } = attn - this.$store.dispatch('fetchUserIfMissing', id) - }) + this.status.attentions?.forEach((attn) => { + const { id } = attn + this.$store.dispatch('fetchUserIfMissing', id) + }) }, methods: { onParseReady(event) { diff --git a/src/components/still-image/still-image.js b/src/components/still-image/still-image.js index 29a4ed1de..809bd8c7d 100644 --- a/src/components/still-image/still-image.js +++ b/src/components/still-image/still-image.js @@ -51,7 +51,7 @@ const StillImage = { } const image = this.$refs.src if (!image) return - this.imageLoadHandler && this.imageLoadHandler(image) + this.imageLoadHandler?.(image) const canvas = this.$refs.canvas if (!canvas) return const width = image.naturalWidth @@ -61,7 +61,7 @@ const StillImage = { canvas.getContext('2d').drawImage(image, 0, 0, width, height) }, onError() { - this.imageLoadError && this.imageLoadError() + this.imageLoadError?.() }, }, watch: { diff --git a/src/lib/persisted_state.js b/src/lib/persisted_state.js index f6375dfed..aef1cc8dc 100644 --- a/src/lib/persisted_state.js +++ b/src/lib/persisted_state.js @@ -185,7 +185,7 @@ export const piniaPersistPlugin = } const fallbackValue = await storage.getItem(vuexKey) - if (fallbackValue && fallbackValue[id]) { + if (fallbackValue?.[id]) { console.info(`Migrating ${id} store data from vuex to pinia`) const res = fallbackValue[id] await storage.setItem(key, res) diff --git a/src/modules/api.js b/src/modules/api.js index e26336c05..92b849a2b 100644 --- a/src/modules/api.js +++ b/src/modules/api.js @@ -336,7 +336,7 @@ const api = { } }, disconnectFromSocket({ commit, state }) { - state.socket && state.socket.disconnect() + state.socket?.disconnect() commit('setSocket', null) }, }, diff --git a/src/modules/statuses.js b/src/modules/statuses.js index ee5e50a39..e3878e85c 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -134,7 +134,7 @@ const sortById = (a, b) => { const sortTimeline = (timeline) => { timeline.visibleStatuses = timeline.visibleStatuses.sort(sortById) timeline.statuses = timeline.statuses.sort(sortById) - timeline.minVisibleId = (last(timeline.visibleStatuses) || {}).id + timeline.minVisibleId = last(timeline.visibleStatuses)?.id return timeline } diff --git a/src/services/chat_utils/chat_utils.js b/src/services/chat_utils/chat_utils.js index ccf91e85c..05ba80bc3 100644 --- a/src/services/chat_utils/chat_utils.js +++ b/src/services/chat_utils/chat_utils.js @@ -13,8 +13,7 @@ export const maybeShowChatNotification = (chat) => { } if ( - chat.lastMessage.attachment && - chat.lastMessage.attachment.type === 'image' + chat.lastMessage.attachment?.type === 'image' ) { opts.image = chat.lastMessage.attachment.preview_url } diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js index 9a5a642fd..9eea7e459 100644 --- a/src/services/entity_normalizer/entity_normalizer.service.js +++ b/src/services/entity_normalizer/entity_normalizer.service.js @@ -191,7 +191,7 @@ export const parseUser = (data) => { // Convert punycode to unicode for UI output.screen_name_ui = output.screen_name - if (output.screen_name && output.screen_name.includes('@')) { + if (output.screen_name?.includes('@')) { const parts = output.screen_name.split('@') const unicodeDomain = punycode.toUnicode(parts[1]) if (unicodeDomain !== parts[1]) { diff --git a/src/services/notification_utils/notification_utils.js b/src/services/notification_utils/notification_utils.js index 6cb3dbc19..2339f1431 100644 --- a/src/services/notification_utils/notification_utils.js +++ b/src/services/notification_utils/notification_utils.js @@ -176,11 +176,8 @@ export const prepareNotificationObject = (notification, i18n) => { // Shows first attached non-nsfw image, if any. Should add configuration for this somehow... if ( - status && - status.attachments && - status.attachments.length > 0 && !status.nsfw && - status.attachments[0].mimetype.startsWith('image/') + status?.attachments?.[0]?.mimetype.startsWith('image/') ) { notifObj.image = status.attachments[0].url } diff --git a/src/services/theme_data/theme_data.service.js b/src/services/theme_data/theme_data.service.js index 4747c22c3..4f1fe0a76 100644 --- a/src/services/theme_data/theme_data.service.js +++ b/src/services/theme_data/theme_data.service.js @@ -245,7 +245,7 @@ export const OPACITIES = Object.entries(SLOT_INHERITANCE).reduce((acc, [k]) => { [opacity]: { defaultValue: DEFAULT_OPACITY[opacity] || 1, affectedSlots: [ - ...((acc[opacity] && acc[opacity].affectedSlots) || []), + ...((acc[opacity]?.affectedSlots) || []), k, ], }, @@ -413,7 +413,7 @@ export const getColors = (sourceColors, sourceOpacity) => outputColor.a = Number( opacityOverriden ? sourceOpacity[opacitySlot] - : (OPACITIES[opacitySlot] || {}).defaultValue, + : OPACITIES[opacitySlot]?.defaultValue, ) } } diff --git a/src/stores/chats.js b/src/stores/chats.js index bc5b7f101..c66bc2658 100644 --- a/src/stores/chats.js +++ b/src/stores/chats.js @@ -69,8 +69,8 @@ export const useChatsStore = defineStore('chats', { if (chat) { const isNewMessage = - (chat.lastMessage && chat.lastMessage.id) !== - (updatedChat.lastMessage && updatedChat.lastMessage.id) + (chat.lastMessage?.id) !== + (updatedChat.lastMessage?.id) chat.lastMessage = updatedChat.lastMessage chat.unread = updatedChat.unread chat.updated_at = updatedChat.updated_at diff --git a/src/stores/interface.js b/src/stores/interface.js index dc92d6067..cc56e6a48 100644 --- a/src/stores/interface.js +++ b/src/stores/interface.js @@ -60,8 +60,7 @@ export const useInterfaceStore = defineStore('interface', { }, browserSupport: { cssFilter: - window.CSS && - window.CSS.supports && + window.CSS?.supports && (window.CSS.supports('filter', 'drop-shadow(0 0)') || window.CSS.supports('-webkit-filter', 'drop-shadow(0 0)')), localFonts: typeof window.queryLocalFonts === 'function', diff --git a/src/stores/sync_config.js b/src/stores/sync_config.js index 87083d850..723cd9397 100644 --- a/src/stores/sync_config.js +++ b/src/stores/sync_config.js @@ -196,9 +196,11 @@ export const _getRecentData = (cache, live, isTest) => { export const _getAllFlags = (recent, stale) => { return Array.from( + recentStorage = toRaw(recent?.flagStorage) + staleStorage = toRaw(stale?.flagStorage) new Set([ - ...Object.keys(toRaw((recent || {}).flagStorage || {})), - ...Object.keys(toRaw((stale || {}).flagStorage || {})), + ...Object.keys(recentStorage || {}), + ...Object.keys(staleStorage || {}), ]), ) } From 95bbe7383200c74eff5df1e6fdaaf73fc33fa005 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 4 Aug 2026 18:04:57 +0300 Subject: [PATCH 07/64] lint --- src/components/announcement/announcement.js | 4 ++-- .../announcements_page/announcements_page.js | 4 ++-- .../edit_status_modal/edit_status_modal.js | 4 +--- src/components/mention_link/mention_link.js | 4 +--- src/components/navigation/filter.js | 3 +-- src/components/poll/poll.js | 6 +++--- src/components/popover/popover.js | 12 +++++------- .../post_status_modal/post_status_modal.js | 4 +--- src/components/rich_content/rich_content.jsx | 7 ++----- src/components/status/status.js | 3 +-- src/components/tab_switcher/tab_switcher.jsx | 6 +++++- src/services/chat_utils/chat_utils.js | 4 +--- .../notification_utils/notification_utils.js | 5 +---- src/services/sw/sw.js | 4 +++- src/services/theme_data/theme_data.service.js | 5 +---- src/stores/chats.js | 3 +-- src/stores/interface.js | 3 ++- src/stores/sync_config.js | 5 +++-- test/unit/specs/components/rich_content.spec.js | 10 ++-------- 19 files changed, 38 insertions(+), 58 deletions(-) diff --git a/src/components/announcement/announcement.js b/src/components/announcement/announcement.js index 6f6190ebb..6b45b2b90 100644 --- a/src/components/announcement/announcement.js +++ b/src/components/announcement/announcement.js @@ -29,8 +29,8 @@ const Announcement = { currentUser: (state) => state.users.currentUser, }), canEditAnnouncement() { - return ( - this.currentUser?.privileges.has('announcements_manage_announcements') + return this.currentUser?.privileges.has( + 'announcements_manage_announcements', ) }, content() { diff --git a/src/components/announcements_page/announcements_page.js b/src/components/announcements_page/announcements_page.js index a8d498075..802e90cf6 100644 --- a/src/components/announcements_page/announcements_page.js +++ b/src/components/announcements_page/announcements_page.js @@ -33,8 +33,8 @@ const AnnouncementsPage = { return useAnnouncementsStore().announcements }, canPostAnnouncement() { - return ( - this.currentUser?.privileges.has('announcements_manage_announcements') + return this.currentUser?.privileges.has( + 'announcements_manage_announcements', ) }, }, diff --git a/src/components/edit_status_modal/edit_status_modal.js b/src/components/edit_status_modal/edit_status_modal.js index 78c5c51aa..59c142d08 100644 --- a/src/components/edit_status_modal/edit_status_modal.js +++ b/src/components/edit_status_modal/edit_status_modal.js @@ -42,9 +42,7 @@ const EditStatusModal = { }, isFormVisible(val) { if (val) { - this.$nextTick( - () => this.$el?.querySelector('textarea').focus(), - ) + this.$nextTick(() => this.$el?.querySelector('textarea').focus()) } }, }, diff --git a/src/components/mention_link/mention_link.js b/src/components/mention_link/mention_link.js index ff4612395..0309079e8 100644 --- a/src/components/mention_link/mention_link.js +++ b/src/components/mention_link/mention_link.js @@ -75,9 +75,7 @@ const MentionLink = { }, computed: { user() { - return ( - this.url && this.$store?.getters.findUserByUrl(this.url) - ) + return this.url && this.$store?.getters.findUserByUrl(this.url) }, isYou() { // FIXME why user !== currentUser??? diff --git a/src/components/navigation/filter.js b/src/components/navigation/filter.js index ff91d97d9..0cbefd3fa 100644 --- a/src/components/navigation/filter.js +++ b/src/components/navigation/filter.js @@ -15,8 +15,7 @@ export const filterNavigation = ( if (!isFederating && set.has('federating')) return false if (!currentUser && isPrivate && set.has('!private')) return false if (!currentUser && !(anon || anonRoute)) return false - if ((!currentUser?.locked) && set.has('lockedUser')) - return false + if (!currentUser?.locked && set.has('lockedUser')) return false if (!hasChats && set.has('chats')) return false if (!hasAnnouncements && set.has('announcements')) return false if (!supportsBubbleTimeline && set.has('supportsBubbleTimeline')) diff --git a/src/components/poll/poll.js b/src/components/poll/poll.js index f2d4b0ee5..0ce304a0a 100644 --- a/src/components/poll/poll.js +++ b/src/components/poll/poll.js @@ -39,13 +39,13 @@ export default { return storePoll || {} }, options() { - return (this.poll?.options) || [] + return this.poll?.options || [] }, expiresAt() { - return (this.poll?.expires_at) || null + return this.poll?.expires_at || null }, expired() { - return (this.poll?.expired) || false + return this.poll?.expired || false }, expirationLabel() { if (useMergedConfigStore().mergedConfig.useAbsoluteTimeFormat) { diff --git a/src/components/popover/popover.js b/src/components/popover/popover.js index 857caadcb..40344834b 100644 --- a/src/components/popover/popover.js +++ b/src/components/popover/popover.js @@ -129,9 +129,7 @@ const Popover = { // Popover will be anchored around this element, trigger ref is the container, so // its children are what are inside the slot. Expect only one v-slot:trigger. const anchorEl = - this.anchorEl || - (this.$refs.trigger?.children[0]) || - this.$el + this.anchorEl || this.$refs.trigger?.children[0] || this.$el // SVGs don't have offsetWidth/Height, use fallback const anchorHeight = anchorEl.offsetHeight || anchorEl.clientHeight const anchorWidth = anchorEl.offsetWidth || anchorEl.clientWidth @@ -246,12 +244,12 @@ const Popover = { if (bottomBoundary + content.offsetHeight > yBounds.max) usingTop = true if (topBoundary - content.offsetHeight < yBounds.min) usingTop = false - const yOffset = (this.offset?.y) || 0 + const yOffset = this.offset?.y || 0 translateY = usingTop ? topBoundary - yOffset - content.offsetHeight : bottomBoundary + yOffset - const xOffset = (this.offset?.x) || 0 + const xOffset = this.offset?.x || 0 translateX = origin.x + horizOffset + xOffset } else { // Default to whatever user wished with placement prop @@ -267,12 +265,12 @@ const Popover = { if (rightBoundary + content.offsetWidth > xBounds.max) usingLeft = true if (leftBoundary - content.offsetWidth < xBounds.min) usingLeft = false - const xOffset = (this.offset?.x) || 0 + const xOffset = this.offset?.x || 0 translateX = usingLeft ? leftBoundary - xOffset - content.offsetWidth : rightBoundary + xOffset - const yOffset = (this.offset?.y) || 0 + const yOffset = this.offset?.y || 0 translateY = origin.y + vertOffset + yOffset } diff --git a/src/components/post_status_modal/post_status_modal.js b/src/components/post_status_modal/post_status_modal.js index 7e9f1a4f8..e7001db9a 100644 --- a/src/components/post_status_modal/post_status_modal.js +++ b/src/components/post_status_modal/post_status_modal.js @@ -40,9 +40,7 @@ const PostStatusModal = { }, isFormVisible(val) { if (val) { - this.$nextTick( - () => this.$el?.querySelector('textarea').focus(), - ) + this.$nextTick(() => this.$el?.querySelector('textarea').focus()) } }, }, diff --git a/src/components/rich_content/rich_content.jsx b/src/components/rich_content/rich_content.jsx index 090253d91..a00bf2c47 100644 --- a/src/components/rich_content/rich_content.jsx +++ b/src/components/rich_content/rich_content.jsx @@ -258,10 +258,7 @@ export default { currentMentions = null } } else if (Tag === 'span') { - if ( - this.handleLinks && - fullAttrs.class?.includes('h-card') - ) { + if (this.handleLinks && fullAttrs.class?.includes('h-card')) { return ['', children.map(processItem), ''] } } @@ -299,7 +296,7 @@ export default { const attrs = getAttrs(opener, () => true) // should only be this if ( - (fullAttrs.class?.includes('hashtag')) || // Pleroma style + fullAttrs.class?.includes('hashtag') || // Pleroma style fullAttrs.rel === 'tag' // Mastodon style ) { return renderHashtag(attrs, children, encounteredTextReverse) diff --git a/src/components/status/status.js b/src/components/status/status.js index cd3ae44d0..3e10a24e1 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -128,8 +128,7 @@ const Status = { computed: { showReasonMutedThread() { return ( - (this.status.thread_muted || - (this.status.reblog?.thread_muted)) && + (this.status.thread_muted || this.status.reblog?.thread_muted) && !this.inConversation ) }, diff --git a/src/components/tab_switcher/tab_switcher.jsx b/src/components/tab_switcher/tab_switcher.jsx index f9a35ad1c..01913f709 100644 --- a/src/components/tab_switcher/tab_switcher.jsx +++ b/src/components/tab_switcher/tab_switcher.jsx @@ -111,7 +111,11 @@ export default { type="button" role="tab" > - {props['image-tooltip']} + {props['image-tooltip']} {props.label ? '' : props.label} diff --git a/src/services/chat_utils/chat_utils.js b/src/services/chat_utils/chat_utils.js index 05ba80bc3..bc02981cd 100644 --- a/src/services/chat_utils/chat_utils.js +++ b/src/services/chat_utils/chat_utils.js @@ -12,9 +12,7 @@ export const maybeShowChatNotification = (chat) => { body: chat.lastMessage.content, } - if ( - chat.lastMessage.attachment?.type === 'image' - ) { + if (chat.lastMessage.attachment?.type === 'image') { opts.image = chat.lastMessage.attachment.preview_url } diff --git a/src/services/notification_utils/notification_utils.js b/src/services/notification_utils/notification_utils.js index 2339f1431..46b81cc66 100644 --- a/src/services/notification_utils/notification_utils.js +++ b/src/services/notification_utils/notification_utils.js @@ -175,10 +175,7 @@ export const prepareNotificationObject = (notification, i18n) => { } // Shows first attached non-nsfw image, if any. Should add configuration for this somehow... - if ( - !status.nsfw && - status?.attachments?.[0]?.mimetype.startsWith('image/') - ) { + if (!status.nsfw && status?.attachments?.[0]?.mimetype.startsWith('image/')) { notifObj.image = status.attachments[0].url } diff --git a/src/services/sw/sw.js b/src/services/sw/sw.js index b114cb0de..2b7dabd41 100644 --- a/src/services/sw/sw.js +++ b/src/services/sw/sw.js @@ -1,7 +1,9 @@ /* global process */ function urlBase64ToUint8Array(base64String) { const padding = '='.repeat((4 - (base64String.length % 4)) % 4) - const base64 = (base64String + padding).replaceAll('-', '+').replace(/_/g, '/') + const base64 = (base64String + padding) + .replaceAll('-', '+') + .replace(/_/g, '/') const rawData = window.atob(base64) return Uint8Array.from([...rawData].map((char) => char.codePointAt(0))) diff --git a/src/services/theme_data/theme_data.service.js b/src/services/theme_data/theme_data.service.js index 4f1fe0a76..cc5553936 100644 --- a/src/services/theme_data/theme_data.service.js +++ b/src/services/theme_data/theme_data.service.js @@ -244,10 +244,7 @@ export const OPACITIES = Object.entries(SLOT_INHERITANCE).reduce((acc, [k]) => { ...acc, [opacity]: { defaultValue: DEFAULT_OPACITY[opacity] || 1, - affectedSlots: [ - ...((acc[opacity]?.affectedSlots) || []), - k, - ], + affectedSlots: [...(acc[opacity]?.affectedSlots || []), k], }, } } else { diff --git a/src/stores/chats.js b/src/stores/chats.js index c66bc2658..7908da9c8 100644 --- a/src/stores/chats.js +++ b/src/stores/chats.js @@ -69,8 +69,7 @@ export const useChatsStore = defineStore('chats', { if (chat) { const isNewMessage = - (chat.lastMessage?.id) !== - (updatedChat.lastMessage?.id) + chat.lastMessage?.id !== updatedChat.lastMessage?.id chat.lastMessage = updatedChat.lastMessage chat.unread = updatedChat.unread chat.updated_at = updatedChat.updated_at diff --git a/src/stores/interface.js b/src/stores/interface.js index cc56e6a48..edc879614 100644 --- a/src/stores/interface.js +++ b/src/stores/interface.js @@ -577,7 +577,8 @@ export const useInterfaceStore = defineStore('interface', { return { name: x.variant, ...cleanDirectives } }) .forEach((palette) => { - const key = 'style.' + palette.name.toLowerCase().replaceAll(' ', '_') + const key = + 'style.' + palette.name.toLowerCase().replaceAll(' ', '_') if (!firstStylePaletteName) firstStylePaletteName = key palettesIndex[key] = () => Promise.resolve(palette) }) diff --git a/src/stores/sync_config.js b/src/stores/sync_config.js index 723cd9397..3b71478c1 100644 --- a/src/stores/sync_config.js +++ b/src/stores/sync_config.js @@ -195,9 +195,10 @@ export const _getRecentData = (cache, live, isTest) => { } export const _getAllFlags = (recent, stale) => { + const recentStorage = toRaw(recent?.flagStorage) + const staleStorage = toRaw(stale?.flagStorage) + return Array.from( - recentStorage = toRaw(recent?.flagStorage) - staleStorage = toRaw(stale?.flagStorage) new Set([ ...Object.keys(recentStorage || {}), ...Object.keys(staleStorage || {}), diff --git a/test/unit/specs/components/rich_content.spec.js b/test/unit/specs/components/rich_content.spec.js index 48cd5eb91..6ea574304 100644 --- a/test/unit/specs/components/rich_content.spec.js +++ b/test/unit/specs/components/rich_content.spec.js @@ -363,10 +363,7 @@ describe('RichContent', () => { }) expect( - wrapper - .html() - .replaceAll('\n', '') - .replaceAll('', ''), + wrapper.html().replaceAll('\n', '').replaceAll('', ''), ).to.eql(compwrap(expected)) }) @@ -436,10 +433,7 @@ describe('RichContent', () => { }) expect( - wrapper - .html() - .replaceAll('\n', '') - .replaceAll('', ''), + wrapper.html().replaceAll('\n', '').replaceAll('', ''), ).to.eql(compwrap(expected)) }) From 665849615c5e0ef6f677788e9e0f7ab0b3359a43 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 4 Aug 2026 18:29:35 +0300 Subject: [PATCH 08/64] fixes --- src/components/registration/registration.js | 2 +- src/components/settings_modal/helpers/setting.js | 4 ++-- src/services/entity_normalizer/entity_normalizer.service.js | 4 ++-- src/services/style_setter/style_setter.js | 4 ++-- src/services/theme_data/iss_utils.js | 6 ------ src/services/user_highlighter/user_highlighter.js | 2 +- test/unit/specs/components/rich_content.spec.js | 4 ++-- 7 files changed, 10 insertions(+), 16 deletions(-) diff --git a/src/components/registration/registration.js b/src/components/registration/registration.js index 7e0cf859c..bb8de17b9 100644 --- a/src/components/registration/registration.js +++ b/src/components/registration/registration.js @@ -154,7 +154,7 @@ const registration = { }) }, replaceNewlines(str) { - return str.replaceAll('\s*\n\s*', ' \n') + return str.replaceAll(/\s*\n\s*/g, ' \n') }, }, } diff --git a/src/components/settings_modal/helpers/setting.js b/src/components/settings_modal/helpers/setting.js index a2d33deff..09d3ecc2d 100644 --- a/src/components/settings_modal/helpers/setting.js +++ b/src/components/settings_modal/helpers/setting.js @@ -179,7 +179,7 @@ export default { [ 'admin_dash', 'temp_overrides', - ...this.canonPath.map((p) => p.replaceAll('\.', '_DOT_')), + ...this.canonPath.map((p) => p.replaceAll('.', '_DOT_')), 'label', ].join('.'), ) @@ -198,7 +198,7 @@ export default { [ 'admin_dash', 'temp_overrides', - ...this.canonPath.map((p) => p.replaceAll('\.', '_DOT_')), + ...this.canonPath.map((p) => p.replaceAll('.', '_DOT_')), 'description', ].join('.'), ) diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js index 9eea7e459..5b8fa5b57 100644 --- a/src/services/entity_normalizer/entity_normalizer.service.js +++ b/src/services/entity_normalizer/entity_normalizer.service.js @@ -62,8 +62,8 @@ export const parseUser = (data) => { }) output.fields_text = data.fields.map((field) => { return { - name: unescape(field.name.replaceAll('<[^>]*>', '')), - value: unescape(field.value.replaceAll('<[^>]*>', '')), + name: unescape(field.name.replaceAll(/<[^>]*>/g, '')), + value: unescape(field.value.replaceAll(/<[^>]*>/g, '')), } }) diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index 0cab385a3..4eed080c1 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -29,7 +29,7 @@ export const createStyleSheet = (id, priority = 1000) => { addRule(rule) { let newRule = rule if (!CSS.supports?.('backdrop-filter', 'blur()')) { - newRule = newRule.replaceAll('backdrop-filter:[^;]+;', '') // Remove backdrop-filter + newRule = newRule.replaceAll(/backdrop-filter:[^;]+;/g, '') // Remove backdrop-filter } if (newRule.startsWith('::-webkit')) { @@ -44,7 +44,7 @@ export const createStyleSheet = (id, priority = 1000) => { } this.rules.push( - newRule.replaceAll('var\(--shadowFilter\)[^;]*;', ''), // Remove shadowFilter references + newRule.replaceAll(/var\(--shadowFilter\)[^;]*;/g, ''), // Remove shadowFilter references ) }, } diff --git a/src/services/theme_data/iss_utils.js b/src/services/theme_data/iss_utils.js index 3dc3aa65d..67579a118 100644 --- a/src/services/theme_data/iss_utils.js +++ b/src/services/theme_data/iss_utils.js @@ -111,12 +111,6 @@ export const genericRuleToSelector = if (ignoreOutOfTreeSelector || liteMode) arraySelector = [arraySelector[0]] return arraySelector - .sort((a) => { - if (a.startsWith(':')) return 1 - if (/^[a-z]/.exec(a)) return -1 - else return 0 - }) - .join('') }) const statesSelectorsFlat = statesSelectors.reduce((acc, s) => { diff --git a/src/services/user_highlighter/user_highlighter.js b/src/services/user_highlighter/user_highlighter.js index 9724595c0..e3f94ea1a 100644 --- a/src/services/user_highlighter/user_highlighter.js +++ b/src/services/user_highlighter/user_highlighter.js @@ -47,7 +47,7 @@ const highlightStyle = (prefs) => { const highlightClass = (user) => { return ( - 'USER____' + user.screen_name?.replaceAll('\.', '_').replace(/@/g, '_AT_') + 'USER____' + user.screen_name?.replaceAll('.', '_').replace(/@/g, '_AT_') ) } diff --git a/test/unit/specs/components/rich_content.spec.js b/test/unit/specs/components/rich_content.spec.js index 6ea574304..ebdf4ef45 100644 --- a/test/unit/specs/components/rich_content.spec.js +++ b/test/unit/specs/components/rich_content.spec.js @@ -363,7 +363,7 @@ describe('RichContent', () => { }) expect( - wrapper.html().replaceAll('\n', '').replaceAll('', ''), + wrapper.html().replaceAll('\n', '').replaceAll(//g, ''), ).to.eql(compwrap(expected)) }) @@ -433,7 +433,7 @@ describe('RichContent', () => { }) expect( - wrapper.html().replaceAll('\n', '').replaceAll('', ''), + wrapper.html().replaceAll('\n', '').replaceAll(//g, ''), ).to.eql(compwrap(expected)) }) From ef8a40764328cfefb5bcfc09bf2087c498423275 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 4 Aug 2026 19:20:51 +0300 Subject: [PATCH 09/64] lint --- src/components/chat_message/chat_message.vue | 25 +++++++++++++------ .../chat_message_list/chat_message_list.vue | 2 +- src/components/chat_view/chat_view.vue | 2 +- src/components/conversation/conversation.vue | 4 +-- .../post_status_form/post_status_form.vue | 8 +++--- .../status_action_buttons.vue | 2 +- .../specs/components/rich_content.spec.js | 10 ++++++-- 7 files changed, 35 insertions(+), 18 deletions(-) diff --git a/src/components/chat_message/chat_message.vue b/src/components/chat_message/chat_message.vue index 394cce4b7..88ab2fc0a 100644 --- a/src/components/chat_message/chat_message.vue +++ b/src/components/chat_message/chat_message.vue @@ -1,9 +1,9 @@ - + {{ $t('status.broken_reply') }} @@ -83,7 +86,10 @@ :user="author" /> -
+
@@ -98,7 +104,6 @@ @mouseenter="hovered = true" @mouseleave="hovered = false" > -
- +
@@ -232,7 +240,10 @@ v-else class="chat-message-date-separator" > - +
diff --git a/src/components/chat_message_list/chat_message_list.vue b/src/components/chat_message_list/chat_message_list.vue index 5cdbf6871..7833730e7 100644 --- a/src/components/chat_message_list/chat_message_list.vue +++ b/src/components/chat_message_list/chat_message_list.vue @@ -7,7 +7,7 @@ :previous-item="getPreviousItem(index)" :hovered-message-chain="chatItem.messageChainId === hoveredMessageChainId" :focused="chatItem.id === focusedId" - :repliedTo="chatItem.id === repliedId" + :replied-to="chatItem.id === repliedId" @hover="onMessageHover" @delete="onMessageDelete" @reply-requested="onReplyRequested" diff --git a/src/components/chat_view/chat_view.vue b/src/components/chat_view/chat_view.vue index e6bef0f84..06862ab3c 100644 --- a/src/components/chat_view/chat_view.vue +++ b/src/components/chat_view/chat_view.vue @@ -24,7 +24,7 @@ v-if="messages[0]?.summary_raw_html" :html="messages[0].summary_raw_html" :emoji="messages[0].emojis" - /> + /> diff --git a/src/components/conversation/conversation.vue b/src/components/conversation/conversation.vue index 170ab41d6..0833c4f89 100644 --- a/src/components/conversation/conversation.vue +++ b/src/components/conversation/conversation.vue @@ -14,9 +14,9 @@ v-if="conversation[0]?.summary_raw_html" :html="conversation[0].summary_raw_html" :emoji="conversation[0].emojis" - /> + />
Date: Wed, 5 Aug 2026 17:02:04 +0300 Subject: [PATCH 36/64] lint --- src/components/poll/poll_form.js | 6 +++--- src/components/post_status_form/post_status_form.js | 4 +--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/components/poll/poll_form.js b/src/components/poll/poll_form.js index 657e75b44..a9c915127 100644 --- a/src/components/poll/poll_form.js +++ b/src/components/poll/poll_form.js @@ -116,7 +116,7 @@ export default { if (this.options.length < this.maxOptions) { this.$emit('update:modelValue', { ...this.modelValue, - options: [...this.options, ''] + options: [...this.options, ''], }) return true @@ -130,7 +130,7 @@ export default { this.$emit('update:modelValue', { ...this.modelValue, - options + options, }) } }, @@ -140,7 +140,7 @@ export default { this.$emit('update:modelValue', { ...this.modelValue, - options + options, }) }, convertExpiryToUnit(unit, amount) { diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index 7ef59af3f..f36987efb 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -509,9 +509,7 @@ const PostStatusForm = { // Error handling pollContentError() { - return ( - this.pollFormVisible && this.newStatus.poll?.error - ) + return this.pollFormVisible && this.newStatus.poll?.error }, // Featureset detection From 24b715d676d9af4861fd3e0bf72f7f8bafc31f0d Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Wed, 5 Aug 2026 17:31:06 +0300 Subject: [PATCH 37/64] reorganize our CI stuff --- .woodpecker/build.yaml | 25 -------------------- .woodpecker/code-analisys.yaml | 42 ++++++++++++++++++++++++++++++++++ .woodpecker/test-e2e.yaml | 4 ++++ .woodpecker/test.yaml | 17 +++----------- package.json | 3 ++- 5 files changed, 51 insertions(+), 40 deletions(-) create mode 100644 .woodpecker/code-analisys.yaml diff --git a/.woodpecker/build.yaml b/.woodpecker/build.yaml index af0bb98e3..5353344d1 100644 --- a/.woodpecker/build.yaml +++ b/.woodpecker/build.yaml @@ -6,10 +6,6 @@ when: - event: manual branch: ${CI_REPO_DEFAULT_BRANCH} -depends_on: - - test - - test-e2e - labels: platform: linux/amd64 memory: 'high' @@ -18,26 +14,5 @@ steps: build: image: docker.io/node:20-alpine commands: - - apk add --no-cache zip git - yarn --frozen-lockfile - yarn build - - if [ "${CI_PIPELINE_EVENT}" = "push" ] || [ "${CI_PIPELINE_EVENT}" = "manual" ]; then zip -9qr ${CI_REPO_DEFAULT_BRANCH}.zip dist/; fi - - upload-artifacts: - image: docker.io/woodpeckercommunity/plugin-gitea-package:0.5.0 - when: - - event: push - branch: ${CI_REPO_DEFAULT_BRANCH} - - event: manual - branch: ${CI_REPO_DEFAULT_BRANCH} - settings: - user: - from_secret: pleroma-ci-user - password: - from_secret: pleroma-ci-password - update: true - owner: 'pleroma' - package_name: 'pleroma-fe-builds' - package_version: ${CI_REPO_DEFAULT_BRANCH} - file_source: ./${CI_REPO_DEFAULT_BRANCH}.zip - file_name: latest.zip diff --git a/.woodpecker/code-analisys.yaml b/.woodpecker/code-analisys.yaml new file mode 100644 index 000000000..deee51543 --- /dev/null +++ b/.woodpecker/code-analisys.yaml @@ -0,0 +1,42 @@ +when: + - event: push + branch: ${CI_REPO_DEFAULT_BRANCH} + - event: manual + branch: ${CI_REPO_DEFAULT_BRANCH} + +labels: + platform: linux/amd64 + memory: 'high' + +depends_on: + - build + - test + - test-e2e + +clone: + depth: 0 + +variables: + script_file_entrypoint: &script_file_entrypoint + - /bin/sh + - -c + - 'printf "%s" "$CI_SCRIPT" | base64 -d > /tmp/ci-script.sh && /bin/sh -xe /tmp/ci-script.sh' + +steps: + # Needed to generate coverage + test: + image: mcr.microsoft.com/playwright:v1.61.0-jammy + environment: + FF_NETWORK_PER_BUILD: "true" + PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "1" + entrypoint: *script_file_entrypoint + commands: + - yarn --frozen-lockfile + - yarn unit-ci-coverage + + code-analysis: + image: sonarsource/sonar-scanner-cli:11 + environment: + SONAR_TOKEN: + from_secret: sonarqube-token + entrypoint: sonar-scanner diff --git a/.woodpecker/test-e2e.yaml b/.woodpecker/test-e2e.yaml index 2a7d1511d..8f7e407f8 100644 --- a/.woodpecker/test-e2e.yaml +++ b/.woodpecker/test-e2e.yaml @@ -10,6 +10,10 @@ labels: platform: linux/amd64 memory: 'high' +depends_on: + - build + - test + variables: artifacts_uploader_settings: &artifacts_uploader_settings user: diff --git a/.woodpecker/test.yaml b/.woodpecker/test.yaml index 9c3c24a53..c3f2c3793 100644 --- a/.woodpecker/test.yaml +++ b/.woodpecker/test.yaml @@ -10,6 +10,9 @@ labels: platform: linux/amd64 memory: 'high' +depends_on: + - build + variables: artifacts_uploader_settings: &artifacts_uploader_settings user: @@ -45,20 +48,6 @@ steps: exit 1 fi - code-analysis: - image: sonarsource/sonar-scanner-cli:11 - when: - - event: push - branch: ${CI_REPO_DEFAULT_BRANCH} - - event: manual - branch: ${CI_REPO_DEFAULT_BRANCH} - environment: - SONAR_TOKEN: - from_secret: sonarqube-token - entrypoint: *script_file_entrypoint - commands: - - sonar-scanner - upload-artifacts: image: docker.io/woodpeckercommunity/plugin-gitea-package:0.5.0 when: diff --git a/package.json b/package.json index f8bacaf15..50138e6a2 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,8 @@ "dev": "node build/update-emoji.js && vite dev", "build": "node build/update-emoji.js && vite build", "unit": "node build/update-emoji.js && vitest --run --coverage", - "unit-ci": "node build/update-emoji.js && vitest --run --coverage --browser.headless", + "unit-ci": "node build/update-emoji.js && vitest --run --browser.headless", + "unit-ci-coverage": "node build/update-emoji.js && vitest --run --coverage --browser.headless", "unit:watch": "node build/update-emoji.js && vitest --coverage", "e2e:pw": "playwright test --config test/e2e-playwright/playwright.config.mjs", "e2e": "sh ./tools/e2e/run.sh", From 9c51ff467e8f0272f15bb3ff04a304179ee65080 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Wed, 5 Aug 2026 17:34:35 +0300 Subject: [PATCH 38/64] cleanup --- .woodpecker/build.yaml | 1 - .woodpecker/code-analisys.yaml | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.woodpecker/build.yaml b/.woodpecker/build.yaml index 5353344d1..d6bd36f3e 100644 --- a/.woodpecker/build.yaml +++ b/.woodpecker/build.yaml @@ -8,7 +8,6 @@ when: labels: platform: linux/amd64 - memory: 'high' steps: build: diff --git a/.woodpecker/code-analisys.yaml b/.woodpecker/code-analisys.yaml index deee51543..b0d295a39 100644 --- a/.woodpecker/code-analisys.yaml +++ b/.woodpecker/code-analisys.yaml @@ -23,8 +23,7 @@ variables: - 'printf "%s" "$CI_SCRIPT" | base64 -d > /tmp/ci-script.sh && /bin/sh -xe /tmp/ci-script.sh' steps: - # Needed to generate coverage - test: + generate-coverage: image: mcr.microsoft.com/playwright:v1.61.0-jammy environment: FF_NETWORK_PER_BUILD: "true" From 55816a0da4fbfbe2d90a39ed479f1d919aa358f0 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Wed, 5 Aug 2026 17:39:57 +0300 Subject: [PATCH 39/64] clone fix --- .woodpecker/code-analisys.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.woodpecker/code-analisys.yaml b/.woodpecker/code-analisys.yaml index b0d295a39..3f669b81e 100644 --- a/.woodpecker/code-analisys.yaml +++ b/.woodpecker/code-analisys.yaml @@ -14,7 +14,10 @@ depends_on: - test-e2e clone: - depth: 0 + - name: git + image: woodpeckerci/plugin-git + settings: + depth: 0 variables: script_file_entrypoint: &script_file_entrypoint From 4999ee10e543299d3aefa7c807f2ca24d7a4a977 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Wed, 5 Aug 2026 17:43:20 +0300 Subject: [PATCH 40/64] lost file --- .woodpecker/build-package.yaml | 43 ++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 .woodpecker/build-package.yaml diff --git a/.woodpecker/build-package.yaml b/.woodpecker/build-package.yaml new file mode 100644 index 000000000..af0bb98e3 --- /dev/null +++ b/.woodpecker/build-package.yaml @@ -0,0 +1,43 @@ +when: + - event: pull_request + evaluate: 'CI_COMMIT_SOURCE_BRANCH != "weblate" && not(CI_COMMIT_SOURCE_BRANCH startsWith "renovate/")' + - event: push + branch: ${CI_REPO_DEFAULT_BRANCH} + - event: manual + branch: ${CI_REPO_DEFAULT_BRANCH} + +depends_on: + - test + - test-e2e + +labels: + platform: linux/amd64 + memory: 'high' + +steps: + build: + image: docker.io/node:20-alpine + commands: + - apk add --no-cache zip git + - yarn --frozen-lockfile + - yarn build + - if [ "${CI_PIPELINE_EVENT}" = "push" ] || [ "${CI_PIPELINE_EVENT}" = "manual" ]; then zip -9qr ${CI_REPO_DEFAULT_BRANCH}.zip dist/; fi + + upload-artifacts: + image: docker.io/woodpeckercommunity/plugin-gitea-package:0.5.0 + when: + - event: push + branch: ${CI_REPO_DEFAULT_BRANCH} + - event: manual + branch: ${CI_REPO_DEFAULT_BRANCH} + settings: + user: + from_secret: pleroma-ci-user + password: + from_secret: pleroma-ci-password + update: true + owner: 'pleroma' + package_name: 'pleroma-fe-builds' + package_version: ${CI_REPO_DEFAULT_BRANCH} + file_source: ./${CI_REPO_DEFAULT_BRANCH}.zip + file_name: latest.zip From 23934a30033b61ce941549172444ca4be59a99bb Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Wed, 5 Aug 2026 17:45:28 +0300 Subject: [PATCH 41/64] cleanup --- .woodpecker/build-package.yaml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.woodpecker/build-package.yaml b/.woodpecker/build-package.yaml index af0bb98e3..d06a267cd 100644 --- a/.woodpecker/build-package.yaml +++ b/.woodpecker/build-package.yaml @@ -1,6 +1,4 @@ when: - - event: pull_request - evaluate: 'CI_COMMIT_SOURCE_BRANCH != "weblate" && not(CI_COMMIT_SOURCE_BRANCH startsWith "renovate/")' - event: push branch: ${CI_REPO_DEFAULT_BRANCH} - event: manual @@ -25,11 +23,6 @@ steps: upload-artifacts: image: docker.io/woodpeckercommunity/plugin-gitea-package:0.5.0 - when: - - event: push - branch: ${CI_REPO_DEFAULT_BRANCH} - - event: manual - branch: ${CI_REPO_DEFAULT_BRANCH} settings: user: from_secret: pleroma-ci-user From 9f5bb82174ba95b3c657c7390c28e9e0369527d5 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Wed, 5 Aug 2026 17:49:33 +0300 Subject: [PATCH 42/64] status badges for readme.md --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 16d32dcd2..01bb30d8a 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,15 @@ -# Pleroma-FE +# Pleroma-FE > Highly-customizable frontend designed for Pleroma. ![screenshot](./image-1.png) +[![Pipeline Status](https://ci.pleroma.com/api/badges/2/status.svg)](https://ci.pleroma.com/repos/2) [![Coverage](https://sonarqube.pleroma.dev/api/project_badges/measure?project=Pleroma-FE&metric=coverage&token=sqb_f8a5ec836caede0e2c6c62a9bfe66e62a89e2b01)](https://sonarqube.pleroma.dev/dashboard?id=Pleroma-FE) [![Maintainability Rating](https://sonarqube.pleroma.dev/api/project_badges/measure?project=Pleroma-FE&metric=software_quality_maintainability_rating&token=sqb_f8a5ec836caede0e2c6c62a9bfe66e62a89e2b01)](https://sonarqube.pleroma.dev/dashboard?id=Pleroma-FE) [![Reliability Rating](https://sonarqube.pleroma.dev/api/project_badges/measure?project=Pleroma-FE&metric=software_quality_reliability_rating&token=sqb_f8a5ec836caede0e2c6c62a9bfe66e62a89e2b01)](https://sonarqube.pleroma.dev/dashboard?id=Pleroma-FE) [![Security Rating](https://sonarqube.pleroma.dev/api/project_badges/measure?project=Pleroma-FE&metric=software_quality_security_rating&token=sqb_f8a5ec836caede0e2c6c62a9bfe66e62a89e2b01)](https://sonarqube.pleroma.dev/dashboard?id=Pleroma-FE) + + # For Translators -To translate Pleroma-FE, use our weblate server: https://translate.pleroma.social/. If you need to add your language it should be added as a json file in [src/i18n/](https://git.pleroma.social/pleroma/pleroma-fe/src/src/i18n/) folder and added in a list within [src/i18n/languages.js](https://git.pleroma.social/pleroma/pleroma-fe/src/src/i18n/languages.js). +To translate Pleroma-FE, use our weblate server: https://translate.pleroma.social/. If you need to add your language it should be added as a json file in [src/i18n/](https://git.pleroma.social/pleroma/pleroma-fe/src/src/i18n/) folder and added in a list within [src/i18n/languages.js](https://git.pleroma.social/pleroma/pleroma-fe/src/src/i18n/languages.js). Pleroma-FE will set your language by your browser locale, but you can change language in settings. From 9112155f814944bfe8dd062742984cfb49f0ef41 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Wed, 5 Aug 2026 17:52:38 +0300 Subject: [PATCH 43/64] small update for a better way of setting up proxy --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 01bb30d8a..6d62ca26a 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,6 @@ [![Pipeline Status](https://ci.pleroma.com/api/badges/2/status.svg)](https://ci.pleroma.com/repos/2) [![Coverage](https://sonarqube.pleroma.dev/api/project_badges/measure?project=Pleroma-FE&metric=coverage&token=sqb_f8a5ec836caede0e2c6c62a9bfe66e62a89e2b01)](https://sonarqube.pleroma.dev/dashboard?id=Pleroma-FE) [![Maintainability Rating](https://sonarqube.pleroma.dev/api/project_badges/measure?project=Pleroma-FE&metric=software_quality_maintainability_rating&token=sqb_f8a5ec836caede0e2c6c62a9bfe66e62a89e2b01)](https://sonarqube.pleroma.dev/dashboard?id=Pleroma-FE) [![Reliability Rating](https://sonarqube.pleroma.dev/api/project_badges/measure?project=Pleroma-FE&metric=software_quality_reliability_rating&token=sqb_f8a5ec836caede0e2c6c62a9bfe66e62a89e2b01)](https://sonarqube.pleroma.dev/dashboard?id=Pleroma-FE) [![Security Rating](https://sonarqube.pleroma.dev/api/project_badges/measure?project=Pleroma-FE&metric=software_quality_security_rating&token=sqb_f8a5ec836caede0e2c6c62a9bfe66e62a89e2b01)](https://sonarqube.pleroma.dev/dashboard?id=Pleroma-FE) - # For Translators To translate Pleroma-FE, use our weblate server: https://translate.pleroma.social/. If you need to add your language it should be added as a json file in [src/i18n/](https://git.pleroma.social/pleroma/pleroma-fe/src/src/i18n/) folder and added in a list within [src/i18n/languages.js](https://git.pleroma.social/pleroma/pleroma-fe/src/src/i18n/languages.js). @@ -35,9 +34,14 @@ yarn unit # For Contributors: +You can make local build proxy requests to specific instance with an environment variable `VITE_PROXY_TARGET`, generally it is a good idea to also set port for that specific proxy, if you intend on testing multiple instances, i.e.: + +```bash +yarn && PORT=8080 VITE_PROXY_TARGET=https://coolinstance.tld yarn dev +``` + You can create file `/config/local.json` (see [example](https://git.pleroma.social/pleroma/pleroma-fe/src/config/local.example.json)) to enable some convenience dev options: -* `target`: makes local dev server redirect to some existing instance's BE instead of local BE, useful for testing things in near-production environment and searching for real-life use-cases. * `staticConfigPreference`: makes FE's `/static/config.json` take preference of BE-served `/api/pleroma/frontend_configurations`. Only works in dev mode. FE Build process also leaves current commit hash in global variable `___pleromafe_commit_hash` so that you can easily see which pleroma-fe commit instance is running, also helps pinpointing which commit was used when FE was bundled into BE. From 7dce6130f8529bee17a3b416f23bc6b9e6422f0c Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Wed, 5 Aug 2026 18:01:47 +0300 Subject: [PATCH 44/64] more badges --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6d62ca26a..324bb2e7a 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ![screenshot](./image-1.png) -[![Pipeline Status](https://ci.pleroma.com/api/badges/2/status.svg)](https://ci.pleroma.com/repos/2) [![Coverage](https://sonarqube.pleroma.dev/api/project_badges/measure?project=Pleroma-FE&metric=coverage&token=sqb_f8a5ec836caede0e2c6c62a9bfe66e62a89e2b01)](https://sonarqube.pleroma.dev/dashboard?id=Pleroma-FE) [![Maintainability Rating](https://sonarqube.pleroma.dev/api/project_badges/measure?project=Pleroma-FE&metric=software_quality_maintainability_rating&token=sqb_f8a5ec836caede0e2c6c62a9bfe66e62a89e2b01)](https://sonarqube.pleroma.dev/dashboard?id=Pleroma-FE) [![Reliability Rating](https://sonarqube.pleroma.dev/api/project_badges/measure?project=Pleroma-FE&metric=software_quality_reliability_rating&token=sqb_f8a5ec836caede0e2c6c62a9bfe66e62a89e2b01)](https://sonarqube.pleroma.dev/dashboard?id=Pleroma-FE) [![Security Rating](https://sonarqube.pleroma.dev/api/project_badges/measure?project=Pleroma-FE&metric=software_quality_security_rating&token=sqb_f8a5ec836caede0e2c6c62a9bfe66e62a89e2b01)](https://sonarqube.pleroma.dev/dashboard?id=Pleroma-FE) +[![Pipeline Status](https://ci.pleroma.com/api/badges/2/status.svg)](https://ci.pleroma.com/repos/2) [![Coverage](https://sonarqube.pleroma.dev/api/project_badges/measure?project=Pleroma-FE&metric=coverage&token=sqb_f8a5ec836caede0e2c6c62a9bfe66e62a89e2b01)](https://sonarqube.pleroma.dev/dashboard?id=Pleroma-FE) [![Maintainability Rating](https://sonarqube.pleroma.dev/api/project_badges/measure?project=Pleroma-FE&metric=software_quality_maintainability_rating&token=sqb_f8a5ec836caede0e2c6c62a9bfe66e62a89e2b01)](https://sonarqube.pleroma.dev/dashboard?id=Pleroma-FE) [![Reliability Rating](https://sonarqube.pleroma.dev/api/project_badges/measure?project=Pleroma-FE&metric=software_quality_reliability_rating&token=sqb_f8a5ec836caede0e2c6c62a9bfe66e62a89e2b01)](https://sonarqube.pleroma.dev/dashboard?id=Pleroma-FE) [![Security Rating](https://sonarqube.pleroma.dev/api/project_badges/measure?project=Pleroma-FE&metric=software_quality_security_rating&token=sqb_f8a5ec836caede0e2c6c62a9bfe66e62a89e2b01)](https://sonarqube.pleroma.dev/dashboard?id=Pleroma-FE)Translation status # For Translators From d915bb2f9f6a791f6d95d5f19490bd99e4b69f21 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Wed, 5 Aug 2026 18:02:45 +0300 Subject: [PATCH 45/64] space --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 324bb2e7a..a1dabba35 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ![screenshot](./image-1.png) -[![Pipeline Status](https://ci.pleroma.com/api/badges/2/status.svg)](https://ci.pleroma.com/repos/2) [![Coverage](https://sonarqube.pleroma.dev/api/project_badges/measure?project=Pleroma-FE&metric=coverage&token=sqb_f8a5ec836caede0e2c6c62a9bfe66e62a89e2b01)](https://sonarqube.pleroma.dev/dashboard?id=Pleroma-FE) [![Maintainability Rating](https://sonarqube.pleroma.dev/api/project_badges/measure?project=Pleroma-FE&metric=software_quality_maintainability_rating&token=sqb_f8a5ec836caede0e2c6c62a9bfe66e62a89e2b01)](https://sonarqube.pleroma.dev/dashboard?id=Pleroma-FE) [![Reliability Rating](https://sonarqube.pleroma.dev/api/project_badges/measure?project=Pleroma-FE&metric=software_quality_reliability_rating&token=sqb_f8a5ec836caede0e2c6c62a9bfe66e62a89e2b01)](https://sonarqube.pleroma.dev/dashboard?id=Pleroma-FE) [![Security Rating](https://sonarqube.pleroma.dev/api/project_badges/measure?project=Pleroma-FE&metric=software_quality_security_rating&token=sqb_f8a5ec836caede0e2c6c62a9bfe66e62a89e2b01)](https://sonarqube.pleroma.dev/dashboard?id=Pleroma-FE)Translation status +[![Pipeline Status](https://ci.pleroma.com/api/badges/2/status.svg)](https://ci.pleroma.com/repos/2) [![Coverage](https://sonarqube.pleroma.dev/api/project_badges/measure?project=Pleroma-FE&metric=coverage&token=sqb_f8a5ec836caede0e2c6c62a9bfe66e62a89e2b01)](https://sonarqube.pleroma.dev/dashboard?id=Pleroma-FE) [![Maintainability Rating](https://sonarqube.pleroma.dev/api/project_badges/measure?project=Pleroma-FE&metric=software_quality_maintainability_rating&token=sqb_f8a5ec836caede0e2c6c62a9bfe66e62a89e2b01)](https://sonarqube.pleroma.dev/dashboard?id=Pleroma-FE) [![Reliability Rating](https://sonarqube.pleroma.dev/api/project_badges/measure?project=Pleroma-FE&metric=software_quality_reliability_rating&token=sqb_f8a5ec836caede0e2c6c62a9bfe66e62a89e2b01)](https://sonarqube.pleroma.dev/dashboard?id=Pleroma-FE) [![Security Rating](https://sonarqube.pleroma.dev/api/project_badges/measure?project=Pleroma-FE&metric=software_quality_security_rating&token=sqb_f8a5ec836caede0e2c6c62a9bfe66e62a89e2b01)](https://sonarqube.pleroma.dev/dashboard?id=Pleroma-FE) Translation status # For Translators From 22a27741e85c03bb86fb84279a7aaf446137e7ff Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Wed, 5 Aug 2026 18:08:29 +0300 Subject: [PATCH 46/64] fix switching main PSF scope to direct locking scope --- src/components/post_status_form/post_status_form.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue index f282f4947..5c2e073c2 100644 --- a/src/components/post_status_form/post_status_form.vue +++ b/src/components/post_status_form/post_status_form.vue @@ -234,7 +234,7 @@ ref="scopeSelector" :show-all="showAllScopes" :user-default="userDefaultScope" - :original-scope="newStatus.visibility" + :original-scope="repliedStatus?.visibility" :initial-scope="newStatus.visibility" @change="changeVis" /> From d23ec577a02aeef3997c6697f254126593aab086 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Wed, 5 Aug 2026 18:13:48 +0300 Subject: [PATCH 47/64] partial: false --- .woodpecker/code-analisys.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.woodpecker/code-analisys.yaml b/.woodpecker/code-analisys.yaml index 3f669b81e..11ee853ee 100644 --- a/.woodpecker/code-analisys.yaml +++ b/.woodpecker/code-analisys.yaml @@ -18,6 +18,7 @@ clone: image: woodpeckerci/plugin-git settings: depth: 0 + partial: false variables: script_file_entrypoint: &script_file_entrypoint From a4cd10aa2e005a5c5b4b466bee7e17c6e5cc3851 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Wed, 5 Aug 2026 18:39:49 +0300 Subject: [PATCH 48/64] fix #3525 --- src/components/image_cropper/image_cropper.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/image_cropper/image_cropper.js b/src/components/image_cropper/image_cropper.js index b85ef6626..599c95caf 100644 --- a/src/components/image_cropper/image_cropper.js +++ b/src/components/image_cropper/image_cropper.js @@ -70,6 +70,9 @@ const ImageCropper = { ) }, onCropperSelectionChange(event) { + if (!this.$refs.cropperCanvas) { + return // Cropper sends even before component is fully initialized + } const cropperCanvas = this.$refs.cropperCanvas const cropperCanvasRect = cropperCanvas.getBoundingClientRect() const selection = event.detail From c20d47ee31b9b4f0a889415209475832e1dc3fc0 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Wed, 5 Aug 2026 18:40:53 +0300 Subject: [PATCH 49/64] changelog --- changelog.d/avatar_upload.fix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/avatar_upload.fix diff --git a/changelog.d/avatar_upload.fix b/changelog.d/avatar_upload.fix new file mode 100644 index 000000000..b5dd9ee56 --- /dev/null +++ b/changelog.d/avatar_upload.fix @@ -0,0 +1 @@ +fix image cropper error preventing avatar upload From d202b94104972e17a3d158530b738603e8510779 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Wed, 5 Aug 2026 19:13:07 +0300 Subject: [PATCH 50/64] fix third column setting change not affecting UI immideately --- src/App.js | 24 ++++++++++++------- src/components/notifications/notifications.js | 6 +++-- .../settings_modal/helpers/setting.js | 5 +++- .../settings_modal/tabs/layout_tab.js | 6 +++++ .../settings_modal/tabs/layout_tab.vue | 1 + 5 files changed, 30 insertions(+), 12 deletions(-) diff --git a/src/App.js b/src/App.js index 4ce6f1abc..c5b38d958 100644 --- a/src/App.js +++ b/src/App.js @@ -74,6 +74,8 @@ export default { }, data: () => ({ mobileActivePanel: 'timeline', + updateMobileState: null, + updateScrollState: null, }), provide() { return { @@ -211,22 +213,26 @@ export default { hideShoutbox() { return this.isChats || useMergedConfigStore().mergedConfig.hideShoutbox }, + thirdColumnMode() { + return this.mergedConfig.thirdColumnMode + }, + reverseSetting() { + return this.mergedConfig.sidebarRight + }, reverseLayout() { - const { thirdColumnMode, sidebarRight: reverseSetting } = - useMergedConfigStore().mergedConfig if (this.layoutType !== 'wide') { - return reverseSetting + return this.reverseSetting } else { - return thirdColumnMode === 'notifications' - ? reverseSetting - : !reverseSetting + return this.thirdColumnMode === 'notifications' + ? this.reverseSetting + : !this.reverseSetting } }, noSticky() { - return useMergedConfigStore().mergedConfig.disableStickyHeaders + return this.mergedConfig.disableStickyHeaders }, showScrollbars() { - return useMergedConfigStore().mergedConfig.showScrollbars + return this.mergedConfig.showScrollbars }, scrollParent() { return window /* this.$refs.appContentRef */ @@ -234,7 +240,7 @@ export default { showInstanceSpecificPanel() { return ( this.instanceSpecificPanelPresent && - !useMergedConfigStore().mergedConfig.hideISP + !this.mergedConfig.hideISP ) }, ...mapState(useMergedConfigStore, ['mergedConfig']), diff --git a/src/components/notifications/notifications.js b/src/components/notifications/notifications.js index 393aef639..5ae6014bc 100644 --- a/src/components/notifications/notifications.js +++ b/src/components/notifications/notifications.js @@ -134,12 +134,11 @@ const Notifications = { return this.minimalMode || layoutType === 'mobile' }, teleportTarget() { - const { layoutType } = useInterfaceStore() const map = { wide: '#notifs-column', mobile: '#mobile-notifications', } - return map[layoutType] || '#notifs-sidebar' + return map[this.layoutType] || '#notifs-sidebar' }, popoversZLayer() { const { layoutType } = useInterfaceStore() @@ -162,6 +161,9 @@ const Notifications = { }, ...mapState(useAnnouncementsStore, ['unreadAnnouncementCount']), ...mapState(useChatsStore, ['unreadChatsCount']), + ...mapState(useInterfaceStore, [ + 'layoutType', + ]), }, mounted() { this.scrollerRef = this.$refs.root.closest('.column.-scrollable') diff --git a/src/components/settings_modal/helpers/setting.js b/src/components/settings_modal/helpers/setting.js index 09d3ecc2d..4e0f43875 100644 --- a/src/components/settings_modal/helpers/setting.js +++ b/src/components/settings_modal/helpers/setting.js @@ -113,6 +113,7 @@ export default { localDraft: null, } }, + emits: ['update:modelValue'], created() { if ( this.realDraftMode && @@ -244,7 +245,7 @@ export default { }, configSink() { if (this.path == null) { - return (k, v) => this.$emit('update:modelValue', v) + return () => {} } switch (this.realSource) { @@ -385,11 +386,13 @@ export default { if (this.realDraftMode) { this.draft = this.getValue(e) } else { + this.$emit('update:modelValue', this.getValue(e)) this.configSink(this.path, this.getValue(e)) } }, commitDraft() { if (this.realDraftMode) { + this.$emit('update:modelValue', v) this.configSink(this.path, this.draft) } }, diff --git a/src/components/settings_modal/tabs/layout_tab.js b/src/components/settings_modal/tabs/layout_tab.js index fc56e1392..c1a3cd3b4 100644 --- a/src/components/settings_modal/tabs/layout_tab.js +++ b/src/components/settings_modal/tabs/layout_tab.js @@ -7,6 +7,7 @@ import UnitSetting from '../helpers/unit_setting.vue' import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js' import { useMergedConfigStore } from 'src/stores/merged_config.js' +import { useInterfaceStore } from 'src/stores/interface.js' const GeneralTab = { data() { @@ -46,6 +47,11 @@ const GeneralTab = { }, ...SharedComputedObject(), }, + methods: { + updateLayout() { + useInterfaceStore().setLayoutWidth() + }, + }, } export default GeneralTab diff --git a/src/components/settings_modal/tabs/layout_tab.vue b/src/components/settings_modal/tabs/layout_tab.vue index 0035f3a11..b649572fb 100644 --- a/src/components/settings_modal/tabs/layout_tab.vue +++ b/src/components/settings_modal/tabs/layout_tab.vue @@ -105,6 +105,7 @@ id="thirdColumnMode" path="thirdColumnMode" :options="thirdColumnModeOptions" + @change="updateLayout" > {{ $t('settings.third_column_mode') }} From 8e19fbf23ed8785deed69c042033ce9d48350c10 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Wed, 5 Aug 2026 19:13:58 +0300 Subject: [PATCH 51/64] changelog --- changelog.d/third_column.fix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/third_column.fix diff --git a/changelog.d/third_column.fix b/changelog.d/third_column.fix new file mode 100644 index 000000000..191e914d5 --- /dev/null +++ b/changelog.d/third_column.fix @@ -0,0 +1 @@ +Fixed layout selector (third column/reverse) not working immideately upon change From 92b8d1607f8da7b2df30ef209555255ffe130037 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Wed, 5 Aug 2026 19:15:37 +0300 Subject: [PATCH 52/64] lint --- src/App.js | 5 +---- src/components/notifications/notifications.js | 4 +--- src/components/settings_modal/helpers/setting.js | 4 ++-- src/components/settings_modal/tabs/layout_tab.js | 2 +- 4 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/App.js b/src/App.js index c5b38d958..d7a63bd8a 100644 --- a/src/App.js +++ b/src/App.js @@ -238,10 +238,7 @@ export default { return window /* this.$refs.appContentRef */ }, showInstanceSpecificPanel() { - return ( - this.instanceSpecificPanelPresent && - !this.mergedConfig.hideISP - ) + return this.instanceSpecificPanelPresent && !this.mergedConfig.hideISP }, ...mapState(useMergedConfigStore, ['mergedConfig']), ...mapState(useInterfaceStore, [ diff --git a/src/components/notifications/notifications.js b/src/components/notifications/notifications.js index 5ae6014bc..feb59857e 100644 --- a/src/components/notifications/notifications.js +++ b/src/components/notifications/notifications.js @@ -161,9 +161,7 @@ const Notifications = { }, ...mapState(useAnnouncementsStore, ['unreadAnnouncementCount']), ...mapState(useChatsStore, ['unreadChatsCount']), - ...mapState(useInterfaceStore, [ - 'layoutType', - ]), + ...mapState(useInterfaceStore, ['layoutType']), }, mounted() { this.scrollerRef = this.$refs.root.closest('.column.-scrollable') diff --git a/src/components/settings_modal/helpers/setting.js b/src/components/settings_modal/helpers/setting.js index 4e0f43875..05e1027f3 100644 --- a/src/components/settings_modal/helpers/setting.js +++ b/src/components/settings_modal/helpers/setting.js @@ -245,7 +245,7 @@ export default { }, configSink() { if (this.path == null) { - return () => {} + return () => {/* no-op */} } switch (this.realSource) { @@ -392,7 +392,7 @@ export default { }, commitDraft() { if (this.realDraftMode) { - this.$emit('update:modelValue', v) + this.$emit('update:modelValue', this.draft) this.configSink(this.path, this.draft) } }, diff --git a/src/components/settings_modal/tabs/layout_tab.js b/src/components/settings_modal/tabs/layout_tab.js index c1a3cd3b4..50761fa43 100644 --- a/src/components/settings_modal/tabs/layout_tab.js +++ b/src/components/settings_modal/tabs/layout_tab.js @@ -6,8 +6,8 @@ import SharedComputedObject from '../helpers/shared_computed_object.js' import UnitSetting from '../helpers/unit_setting.vue' import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js' -import { useMergedConfigStore } from 'src/stores/merged_config.js' import { useInterfaceStore } from 'src/stores/interface.js' +import { useMergedConfigStore } from 'src/stores/merged_config.js' const GeneralTab = { data() { From a3bc98589e65ab347d48edb812af160fe1287cb8 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Wed, 5 Aug 2026 19:18:33 +0300 Subject: [PATCH 53/64] lint --- src/components/settings_modal/helpers/setting.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/settings_modal/helpers/setting.js b/src/components/settings_modal/helpers/setting.js index 05e1027f3..a1a946fca 100644 --- a/src/components/settings_modal/helpers/setting.js +++ b/src/components/settings_modal/helpers/setting.js @@ -245,7 +245,9 @@ export default { }, configSink() { if (this.path == null) { - return () => {/* no-op */} + return () => { + /* no-op */ + } } switch (this.realSource) { From 2f207a274a871bee4461b3cbf211090c8839718f Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Wed, 5 Aug 2026 19:27:50 +0300 Subject: [PATCH 54/64] fix security issue sonarqube detected --- src/sw.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sw.js b/src/sw.js index b7629b5f2..908e86567 100644 --- a/src/sw.js +++ b/src/sw.js @@ -198,6 +198,10 @@ self.addEventListener('push', async (event) => { self.addEventListener('message', async (event) => { await setSettings() const { type, content } = event.data + if (self.location.origin !== event.origin) { + console.error('SW Message with strange origin received', event) + return + } if (type === 'desktopNotification') { const { title, ...rest } = content From 621966d812f5313f73f8f053d310ce6b2def914a Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Wed, 5 Aug 2026 19:35:58 +0300 Subject: [PATCH 55/64] another security issue found by sonarqube (might need to extend later) --- build/commit_hash.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/commit_hash.js b/build/commit_hash.js index 8225817ee..3719f5906 100644 --- a/build/commit_hash.js +++ b/build/commit_hash.js @@ -7,7 +7,7 @@ export const getCommitHash = () => { } else { try { return childProcess - .execSync('git rev-parse --short HEAD') + .execSync('PATH=/usr/bin:/bin:/usr/local/bin:/sbin:/usr/sbin git rev-parse --short HEAD') .toString() .trim() } catch (e) { From 2d52ce8017bd1b6cc3380e02b3b7a48f83b6f216 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Wed, 5 Aug 2026 19:40:46 +0300 Subject: [PATCH 56/64] lint --- build/commit_hash.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/build/commit_hash.js b/build/commit_hash.js index 3719f5906..2b13afb93 100644 --- a/build/commit_hash.js +++ b/build/commit_hash.js @@ -7,7 +7,9 @@ export const getCommitHash = () => { } else { try { return childProcess - .execSync('PATH=/usr/bin:/bin:/usr/local/bin:/sbin:/usr/sbin git rev-parse --short HEAD') + .execSync( + 'PATH=/usr/bin:/bin:/usr/local/bin:/sbin:/usr/sbin git rev-parse --short HEAD', + ) .toString() .trim() } catch (e) { From 4107f45a6f12e7ff32d0953998f7ef0bdb5025f5 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Wed, 5 Aug 2026 19:56:57 +0300 Subject: [PATCH 57/64] fix cjk pinned display --- src/components/status/status.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/status/status.scss b/src/components/status/status.scss index 1214d8b12..1b0e3cf3e 100644 --- a/src/components/status/status.scss +++ b/src/components/status/status.scss @@ -119,6 +119,7 @@ display: flex; flex-shrink: 0; align-self: baseline; + word-break: keep-all; .button-unstyled { padding: 0.2em; From 77ff4bdd4a244f8e347086962444c910a542f133 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Wed, 5 Aug 2026 19:57:34 +0300 Subject: [PATCH 58/64] changelog --- changelog.d/cjk.fix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/cjk.fix diff --git a/changelog.d/cjk.fix b/changelog.d/cjk.fix new file mode 100644 index 000000000..861c6cb91 --- /dev/null +++ b/changelog.d/cjk.fix @@ -0,0 +1 @@ +fix pinned indicator looking wrong on CJK locales From 92b1ac85073b3692719bf694a48cc4b15f36cce4 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Wed, 5 Aug 2026 20:07:41 +0300 Subject: [PATCH 59/64] don't overflow panel-heading --- src/panel.scss | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/panel.scss b/src/panel.scss index 8d9c2896b..6af2c9e76 100644 --- a/src/panel.scss +++ b/src/panel.scss @@ -148,6 +148,14 @@ background-image: linear-gradient(to bottom, var(--background), var(--background)), linear-gradient(to bottom, var(--__panel-background), var(--__panel-background)); + overflow: hidden; + text-overflow: ellipsis; + + .title { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } --_shadow: var(--shadow); From 326a1ea9bd387b6c32c8c8a7606770e465d1dac9 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Fri, 7 Aug 2026 15:55:57 +0300 Subject: [PATCH 60/64] fix timelines --- src/modules/statuses.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/modules/statuses.js b/src/modules/statuses.js index ef68a5237..cc0a46db7 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -49,8 +49,8 @@ const emptyTl = (userId = 0) => ({ visibleStatuses: [], visibleStatusesObject: {}, newStatusCount: 0, - maxId: '0', - minId: '0', + maxId: '', + minId: '', minVisibleId: 0, loading: false, followers: [], @@ -64,7 +64,7 @@ export const defaultState = () => ({ scrobblesNextFetch: {}, allStatusesObject: {}, conversationsObject: {}, - maxId: '0', + maxId: '', favorites: new Set(), timelines: { mentions: emptyTl(), @@ -222,13 +222,15 @@ const addNewStatuses = ( const newer = timeline && - (maxNew > timelineObject.maxId || timelineObject.maxId === 0) && + (maxNew > timelineObject.maxId || timelineObject.maxId === '') && statuses.length > 0 const older = timeline && - (minNew < timelineObject.minId || timelineObject.minId === 0) && + (minNew < timelineObject.minId || timelineObject.minId === '') && statuses.length > 0 + console.log(minNew, maxNew) + if (!noIdUpdate && newer) { timelineObject.maxId = maxNew } From d0689612b0c2efb942073a0ae198396020d8303f Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Fri, 7 Aug 2026 15:56:48 +0300 Subject: [PATCH 61/64] console log --- src/modules/statuses.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/modules/statuses.js b/src/modules/statuses.js index cc0a46db7..f39da5b3f 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -229,8 +229,6 @@ const addNewStatuses = ( (minNew < timelineObject.minId || timelineObject.minId === '') && statuses.length > 0 - console.log(minNew, maxNew) - if (!noIdUpdate && newer) { timelineObject.maxId = maxNew } From 58fdd50838f00f1f9594079680e1e60d3572ef30 Mon Sep 17 00:00:00 2001 From: Alexander Tumin Date: Sat, 1 Aug 2026 23:36:31 +0300 Subject: [PATCH 62/64] fix status index approxmiation in timeline rendering for dynamically changing viewport geometries --- changelog.d/timeline_rendering_status_index_approx.fix | 1 + src/components/timeline/timeline.js | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 changelog.d/timeline_rendering_status_index_approx.fix diff --git a/changelog.d/timeline_rendering_status_index_approx.fix b/changelog.d/timeline_rendering_status_index_approx.fix new file mode 100644 index 000000000..717464c58 --- /dev/null +++ b/changelog.d/timeline_rendering_status_index_approx.fix @@ -0,0 +1 @@ +Fixed status index approxmiation in timeline rendering for dynamically changing viewport geometries diff --git a/src/components/timeline/timeline.js b/src/components/timeline/timeline.js index 17065a9ef..c3f4d381d 100644 --- a/src/components/timeline/timeline.js +++ b/src/components/timeline/timeline.js @@ -263,7 +263,10 @@ const Timeline = { // Start from approximating the index of some visible status by using the // the center of the screen on the timeline. - let approxIndex = Math.floor(statuses.length * (centerOfScreen / height)) + let approxIndex = Math.min( + Math.floor(statuses.length * (centerOfScreen / height)), + statuses.length - 1, + ) let err = statuses[approxIndex].getBoundingClientRect().y // if we have a previous scroll index that can be used, test if it's From 54b36e8f8e8fa8afd2517e92dee6dfb7b262ebf4 Mon Sep 17 00:00:00 2001 From: Alexander Tumin Date: Thu, 6 Aug 2026 07:14:22 +0300 Subject: [PATCH 63/64] fix server-side domain mutes rendering/api calls --- changelog.d/server-mute-render-modify.fix | 1 + src/components/settings_modal/tabs/mutes_and_blocks_tab.vue | 1 - src/modules/users.js | 6 ++++-- 3 files changed, 5 insertions(+), 3 deletions(-) create mode 100644 changelog.d/server-mute-render-modify.fix diff --git a/changelog.d/server-mute-render-modify.fix b/changelog.d/server-mute-render-modify.fix new file mode 100644 index 000000000..cb94bb826 --- /dev/null +++ b/changelog.d/server-mute-render-modify.fix @@ -0,0 +1 @@ +Fixed server-side domain mutes rendering/api calls diff --git a/src/components/settings_modal/tabs/mutes_and_blocks_tab.vue b/src/components/settings_modal/tabs/mutes_and_blocks_tab.vue index f89253133..a5395ae36 100644 --- a/src/components/settings_modal/tabs/mutes_and_blocks_tab.vue +++ b/src/components/settings_modal/tabs/mutes_and_blocks_tab.vue @@ -153,7 +153,6 @@