From b9a77cc61d179069e5578f00f6c9e8e67ef4b35e Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 6 Jan 2026 16:19:03 +0200 Subject: [PATCH 01/17] add biome --- biome.json | 266 +++++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 5 +- yarn.lock | 54 +++++++++++ 3 files changed, 323 insertions(+), 2 deletions(-) create mode 100644 biome.json diff --git a/biome.json b/biome.json new file mode 100644 index 000000000..3c44b3354 --- /dev/null +++ b/biome.json @@ -0,0 +1,266 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.3.11/schema.json", + "vcs": { + "enabled": true, + "clientKind": "git", + "useIgnoreFile": true + }, + "files": { + "includes": ["**", "!!**/dist"] + }, + "formatter": { + "enabled": true, + "indentStyle": "space" + }, + "linter": { + "enabled": true, + "domains": { + "vue": "recommended" + }, + "rules": { + "recommended": false, + "complexity": { + "noAdjacentSpacesInRegex": "error", + "noExtraBooleanCast": "error", + "noUselessCatch": "error", + "noUselessEscapeInRegex": "error" + }, + "correctness": { + "noConstAssign": "error", + "noConstantCondition": "error", + "noEmptyCharacterClassInRegex": "error", + "noEmptyPattern": "error", + "noGlobalObjectCalls": "error", + "noInvalidBuiltinInstantiation": "error", + "noInvalidConstructorSuper": "error", + "noNonoctalDecimalEscape": "error", + "noPrecisionLoss": "error", + "noSelfAssign": "error", + "noSetterReturn": "error", + "noSwitchDeclarations": "error", + "noUndeclaredVariables": "error", + "noUnreachable": "error", + "noUnreachableSuper": "error", + "noUnsafeFinally": "error", + "noUnsafeOptionalChaining": "error", + "noUnusedLabels": "error", + "noUnusedPrivateClassMembers": "error", + "noUnusedVariables": "error", + "useIsNan": "error", + "useValidForDirection": "error", + "useValidTypeof": "error", + "useYield": "error" + }, + "suspicious": { + "noAsyncPromiseExecutor": "error", + "noCatchAssign": "error", + "noClassAssign": "error", + "noCompareNegZero": "error", + "noConstantBinaryExpressions": "error", + "noControlCharactersInRegex": "error", + "noDebugger": "error", + "noDuplicateCase": "error", + "noDuplicateClassMembers": "error", + "noDuplicateElseIf": "error", + "noDuplicateObjectKeys": "error", + "noDuplicateParameters": "error", + "noEmptyBlockStatements": "error", + "noFallthroughSwitchClause": "error", + "noFunctionAssign": "error", + "noGlobalAssign": "error", + "noImportAssign": "error", + "noIrregularWhitespace": "error", + "noMisleadingCharacterClass": "error", + "noPrototypeBuiltins": "error", + "noRedeclare": "error", + "noShadowRestrictedNames": "error", + "noSparseArray": "error", + "noUnsafeNegation": "error", + "noUselessRegexBackrefs": "error", + "noWith": "error", + "useGetterReturn": "error" + } + } + }, + "javascript": { + "formatter": { + "quoteStyle": "single", + "semicolons": "asNeeded" + }, + "globals": [] + }, + "overrides": [ + { + "includes": ["*.vue", "**/*.vue"], + "javascript": { "globals": [] }, + "linter": { "rules": {} } + }, + { + "includes": [ + "**/*.js", + "**/*.mjs", + "**/*.vue", + "!build/*.js", + "!config/*.js" + ], + "javascript": { + "globals": [ + "onanimationend", + "onabortpayment", + "exports", + "ongamepadconnected", + "onlostpointercapture", + "onanimationiteration", + "onkeyup", + "onmousedown", + "onanimationstart", + "onslotchange", + "onprogress", + "ontransitionstart", + "onpause", + "onended", + "onpointerover", + "onscrollend", + "onformdata", + "ontransitionrun", + "onanimationcancel", + "ondrag", + "oninstall", + "onbeforeinstallprompt", + "onbeforexrselect", + "onmessage", + "ontransitioncancel", + "onpointerdown", + "should", + "onchange", + "onabort", + "onpointerout", + "oncuechange", + "ongotpointercapture", + "onscrollsnapchanging", + "onsearch", + "onsubmit", + "onstalled", + "onsuspend", + "onreset", + "onerror", + "onmouseenter", + "ongamepaddisconnected", + "onresize", + "ondragover", + "onbeforetoggle", + "onmouseover", + "onpagehide", + "onmousemove", + "onratechange", + "oncommand", + "onmessageerror", + "onwheel", + "ondevicemotion", + "onauxclick", + "ontransitionend", + "onpaste", + "onpageswap", + "ononline", + "ondeviceorientationabsolute", + "onkeydown", + "onclose", + "onselect", + "onpaymentrequest", + "onpageshow", + "onactivate", + "onpointercancel", + "onbeforematch", + "onpointerrawupdate", + "ondragleave", + "onscrollsnapchange", + "onseeked", + "onwaiting", + "assert", + "onbeforeunload", + "onplaying", + "onvolumechange", + "onnotificationclose", + "onbackgroundfetchfail", + "ondragend", + "onstorage", + "onloadeddata", + "onfocus", + "onoffline", + "onplay", + "onbackgroundfetchclick", + "onafterprint", + "onclick", + "oncut", + "onmouseout", + "ondblclick", + "oncanplay", + "onloadstart", + "onpushsubscriptionchange", + "onappinstalled", + "onpointermove", + "ontoggle", + "oncontextmenu", + "onblur", + "onperiodicsync", + "oncookiechange", + "oncancel", + "onbeforeprint", + "oncontextrestored", + "onnotificationclick", + "onloadedmetadata", + "onpointerup", + "onlanguagechange", + "oncopy", + "onselectstart", + "onscroll", + "onload", + "ondragstart", + "onbeforeinput", + "oncanplaythrough", + "oninput", + "oninvalid", + "ontimeupdate", + "ondurationchange", + "onselectionchange", + "onmouseup", + "onkeypress", + "onpointerleave", + "oncontextlost", + "ondrop", + "onsecuritypolicyviolation", + "oncontentvisibilityautostatechange", + "onsync", + "onpush", + "expect", + "ondeviceorientation", + "onbackgroundfetchabort", + "onseeking", + "onbackgroundfetchsuccess", + "onfetch", + "oncanmakepayment", + "onrejectionhandled", + "onunload", + "onmouseleave", + "onhashchange", + "onpointerenter", + "onmousewheel", + "onunhandledrejection", + "ondragenter", + "onpopstate", + "onpagereveal", + "onemptied" + ] + }, + "linter": { + "rules": { + "suspicious": { "noDebugger": "off" } + } + } + } + ], + "assist": { + "enabled": true, + "actions": { "source": { "organizeImports": "on" } } + } +} diff --git a/package.json b/package.json index 21d75f0fb..b6f16bd60 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,7 @@ "@babel/plugin-transform-runtime": "7.28.3", "@babel/preset-env": "7.28.3", "@babel/register": "7.28.3", + "@biomejs/biome": "2.3.11", "@ungap/event-target": "0.2.4", "@vitejs/plugin-vue": "^5.2.1", "@vitejs/plugin-vue-jsx": "^4.1.1", @@ -77,7 +78,6 @@ "cross-spawn": "7.0.6", "custom-event-polyfill": "1.0.7", "eslint": "9.37.0", - "vue-eslint-parser": "10.2.0", "eslint-config-standard": "17.1.0", "eslint-formatter-friendly": "7.0.0", "eslint-plugin-import": "2.32.0", @@ -112,7 +112,8 @@ "vite": "^6.1.0", "vite-plugin-eslint2": "^5.0.3", "vite-plugin-stylelint": "^6.0.0", - "vitest": "^3.0.7" + "vitest": "^3.0.7", + "vue-eslint-parser": "10.2.0" }, "type": "module", "engines": { diff --git a/yarn.lock b/yarn.lock index 32a3b93c8..9ce5558e6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1049,6 +1049,60 @@ resolved "https://registry.yarnpkg.com/@bazel/runfiles/-/runfiles-6.3.1.tgz#3f8824b2d82853377799d42354b4df78ab0ace0b" integrity sha512-1uLNT5NZsUVIGS4syuHwTzZ8HycMPyr6POA3FCE4GbMtc4rhoJk8aZKtNIRthJYfL+iioppi+rTfH3olMPr9nA== +"@biomejs/biome@2.3.11": + version "2.3.11" + resolved "https://registry.yarnpkg.com/@biomejs/biome/-/biome-2.3.11.tgz#a8f3682b3b2c0112e2728f6d51d9c67a6c5521f8" + integrity sha512-/zt+6qazBWguPG6+eWmiELqO+9jRsMZ/DBU3lfuU2ngtIQYzymocHhKiZRyrbra4aCOoyTg/BmY+6WH5mv9xmQ== + optionalDependencies: + "@biomejs/cli-darwin-arm64" "2.3.11" + "@biomejs/cli-darwin-x64" "2.3.11" + "@biomejs/cli-linux-arm64" "2.3.11" + "@biomejs/cli-linux-arm64-musl" "2.3.11" + "@biomejs/cli-linux-x64" "2.3.11" + "@biomejs/cli-linux-x64-musl" "2.3.11" + "@biomejs/cli-win32-arm64" "2.3.11" + "@biomejs/cli-win32-x64" "2.3.11" + +"@biomejs/cli-darwin-arm64@2.3.11": + version "2.3.11" + resolved "https://registry.yarnpkg.com/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.3.11.tgz#3aa71d119e7216d66620282121673b95257fcc35" + integrity sha512-/uXXkBcPKVQY7rc9Ys2CrlirBJYbpESEDme7RKiBD6MmqR2w3j0+ZZXRIL2xiaNPsIMMNhP1YnA+jRRxoOAFrA== + +"@biomejs/cli-darwin-x64@2.3.11": + version "2.3.11" + resolved "https://registry.yarnpkg.com/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.3.11.tgz#3003d7c24e4fbba9693fba98692c6386884fedcf" + integrity sha512-fh7nnvbweDPm2xEmFjfmq7zSUiox88plgdHF9OIW4i99WnXrAC3o2P3ag9judoUMv8FCSUnlwJCM1B64nO5Fbg== + +"@biomejs/cli-linux-arm64-musl@2.3.11": + version "2.3.11" + resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.3.11.tgz#a17fb411ba8146ba60f3592857b7c49c8f0fe936" + integrity sha512-XPSQ+XIPZMLaZ6zveQdwNjbX+QdROEd1zPgMwD47zvHV+tCGB88VH+aynyGxAHdzL+Tm/+DtKST5SECs4iwCLg== + +"@biomejs/cli-linux-arm64@2.3.11": + version "2.3.11" + resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.3.11.tgz#643830d1e53071d594e16b919e88a46fbc5c0b55" + integrity sha512-l4xkGa9E7Uc0/05qU2lMYfN1H+fzzkHgaJoy98wO+b/7Gl78srbCRRgwYSW+BTLixTBrM6Ede5NSBwt7rd/i6g== + +"@biomejs/cli-linux-x64-musl@2.3.11": + version "2.3.11" + resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.3.11.tgz#e9165c6d05f085829ab73a7ea0123a6a9b45dc89" + integrity sha512-vU7a8wLs5C9yJ4CB8a44r12aXYb8yYgBn+WeyzbMjaCMklzCv1oXr8x+VEyWodgJt9bDmhiaW/I0RHbn7rsNmw== + +"@biomejs/cli-linux-x64@2.3.11": + version "2.3.11" + resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-x64/-/cli-linux-x64-2.3.11.tgz#3571049b1310132f1d12d67596ea5f39419836d0" + integrity sha512-/1s9V/H3cSe0r0Mv/Z8JryF5x9ywRxywomqZVLHAoa/uN0eY7F8gEngWKNS5vbbN/BsfpCG5yeBT5ENh50Frxg== + +"@biomejs/cli-win32-arm64@2.3.11": + version "2.3.11" + resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.3.11.tgz#6769907655cd06938b00d040c6576c2fdf6c34a2" + integrity sha512-PZQ6ElCOnkYapSsysiTy0+fYX+agXPlWugh6+eQ6uPKI3vKAqNp6TnMhoM3oY2NltSB89hz59o8xIfOdyhi9Iw== + +"@biomejs/cli-win32-x64@2.3.11": + version "2.3.11" + resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-x64/-/cli-win32-x64-2.3.11.tgz#71ba2fb5505b3b01dd3cf551ef329e0094636125" + integrity sha512-43VrG813EW+b5+YbDbz31uUsheX+qFKCpXeY9kfdAx+ww3naKxeVkTD9zLIWxUPfJquANMHrmW3wbe/037G0Qg== + "@bundled-es-modules/cookie@^2.0.1": version "2.0.1" resolved "https://registry.yarnpkg.com/@bundled-es-modules/cookie/-/cookie-2.0.1.tgz#b41376af6a06b3e32a15241d927b840a9b4de507" From 8372348148e77ecf5c032cf08004a4d2934a127f Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 6 Jan 2026 16:20:14 +0200 Subject: [PATCH 02/17] biome lint --write --- .../settings_modal/admin_tabs/mailer_tab.js | 2 +- src/components/status/status.js | 2 +- src/modules/notifications.js | 2 +- .../entity_normalizer/entity_normalizer.service.js | 12 ++++++------ src/services/errors/errors.js | 2 +- src/services/style_setter/style_setter.js | 2 +- src/stores/interface.js | 10 +++++----- src/stores/serverSideStorage.js | 8 ++++---- test/unit/specs/boot/routes.spec.js | 12 ++++++------ .../specs/services/theme_data/sanity_checks.spec.js | 2 +- 10 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/components/settings_modal/admin_tabs/mailer_tab.js b/src/components/settings_modal/admin_tabs/mailer_tab.js index 44b2f33df..f6cd854b4 100644 --- a/src/components/settings_modal/admin_tabs/mailer_tab.js +++ b/src/components/settings_modal/admin_tabs/mailer_tab.js @@ -57,7 +57,7 @@ const MailerTab = { const descriptions = this.$store.state.adminSettings.descriptions const mailerStuff = descriptions[':pleroma']['Pleroma.Emails.Mailer'] const adapterStuff = mailerStuff[':subgroup,' + this.adapter] - return Object.prototype.hasOwnProperty.call(adapterStuff, key) + return Object.hasOwn(adapterStuff, key) } } } diff --git a/src/components/status/status.js b/src/components/status/status.js index eebae459b..4112ed44e 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -397,7 +397,7 @@ const Status = { }, tags () { // eslint-disable-next-line no-prototype-builtins - return this.status.tags.filter(tagObj => tagObj.hasOwnProperty('name')).map(tagObj => tagObj.name).join(' ') + return this.status.tags.filter(tagObj => Object.hasOwn(tagObj, 'name')).map(tagObj => tagObj.name).join(' ') }, hidePostStats () { return this.mergedConfig.hidePostStats diff --git a/src/modules/notifications.js b/src/modules/notifications.js index c5c607922..f1c39b180 100644 --- a/src/modules/notifications.js +++ b/src/modules/notifications.js @@ -109,7 +109,7 @@ export const notifications = { // Only add a new notification if we don't have one for the same action // eslint-disable-next-line no-prototype-builtins - if (!state.idStore.hasOwnProperty(notification.id)) { + if (!Object.hasOwn(state.idStore, notification.id)) { commit('updateNotificationsMinMaxId', notification.id) commit('addNewNotifications', { notifications: [notification] }) diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js index 28f1bc2aa..1e60e2272 100644 --- a/src/services/entity_normalizer/entity_normalizer.service.js +++ b/src/services/entity_normalizer/entity_normalizer.service.js @@ -39,9 +39,9 @@ const qvitterStatusType = (status) => { export const parseUser = (data) => { const output = {} - const masto = Object.prototype.hasOwnProperty.call(data, 'acct') + const masto = Object.hasOwn(data, 'acct') // case for users in "mentions" property for statuses in MastoAPI - const mastoShort = masto && !Object.prototype.hasOwnProperty.call(data, 'avatar') + const mastoShort = masto && !Object.hasOwn(data, 'avatar') output.inLists = null output.id = String(data.id) @@ -269,7 +269,7 @@ export const parseUser = (data) => { export const parseAttachment = (data) => { const output = {} - const masto = !Object.prototype.hasOwnProperty.call(data, 'oembed') + const masto = !Object.hasOwn(data, 'oembed') if (masto) { // Not exactly same... @@ -300,7 +300,7 @@ export const parseSource = (data) => { export const parseStatus = (data) => { const output = {} - const masto = Object.prototype.hasOwnProperty.call(data, 'account') + const masto = Object.hasOwn(data, 'account') if (masto) { output.favorited = data.favourited @@ -432,7 +432,7 @@ export const parseStatus = (data) => { output.favoritedBy = [] output.rebloggedBy = [] - if (Object.prototype.hasOwnProperty.call(data, 'originalStatus')) { + if (Object.hasOwn(data, 'originalStatus')) { Object.assign(output, data.originalStatus) } @@ -444,7 +444,7 @@ export const parseNotification = (data) => { favourite: 'like', reblog: 'repeat' } - const masto = !Object.prototype.hasOwnProperty.call(data, 'ntype') + const masto = !Object.hasOwn(data, 'ntype') const output = {} if (masto) { diff --git a/src/services/errors/errors.js b/src/services/errors/errors.js index f0cc6dad7..ef18c36ed 100644 --- a/src/services/errors/errors.js +++ b/src/services/errors/errors.js @@ -37,7 +37,7 @@ export class RegistrationError extends Error { if (typeof error === 'string') { error = JSON.parse(error) // eslint-disable-next-line - if (error.hasOwnProperty('error')) { + if (Object.hasOwn(error, 'error')) { error = JSON.parse(error.error) } } diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index 90c6180f4..29392610a 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -254,7 +254,7 @@ export const applyConfig = (input) => { styleSheet.addRule(`:root { ${rules} }`) // TODO find a way to make this not apply to theme previews - if (Object.prototype.hasOwnProperty.call(config, 'forcedRoundness')) { + if (Object.hasOwn(config, 'forcedRoundness')) { styleSheet.addRule(` *:not(.preview-block) { --roundness: var(--forcedRoundness) !important; }`) diff --git a/src/stores/interface.js b/src/stores/interface.js index 5148daa4c..a5631ab17 100644 --- a/src/stores/interface.js +++ b/src/stores/interface.js @@ -668,16 +668,16 @@ export const normalizeThemeData = (input) => { // this might not be even used at all, some leftover of unimplemented code in V2 editor return generatePreset(input).theme } else if ( - Object.prototype.hasOwnProperty.call(input, '_pleroma_theme_version') || - Object.prototype.hasOwnProperty.call(input, 'source') || - Object.prototype.hasOwnProperty.call(input, 'theme') + Object.hasOwn(input, '_pleroma_theme_version') || + Object.hasOwn(input, 'source') || + Object.hasOwn(input, 'theme') ) { // We got passed a full theme file themeData = input.theme themeSource = input.source } else if ( - Object.prototype.hasOwnProperty.call(input, 'themeEngineVersion') || - Object.prototype.hasOwnProperty.call(input, 'colors') + Object.hasOwn(input, 'themeEngineVersion') || + Object.hasOwn(input, 'colors') ) { // We got passed a source/snapshot themeData = input diff --git a/src/stores/serverSideStorage.js b/src/stores/serverSideStorage.js index 40f69c7c2..78bac1259 100644 --- a/src/stores/serverSideStorage.js +++ b/src/stores/serverSideStorage.js @@ -170,10 +170,10 @@ const _mergeJournal = (...journals) => { const allJournals = flatten( journals.map(j => Array.isArray(j) ? j : []) ).filter(entry => - Object.prototype.hasOwnProperty.call(entry, 'path') && - Object.prototype.hasOwnProperty.call(entry, 'operation') && - Object.prototype.hasOwnProperty.call(entry, 'args') && - Object.prototype.hasOwnProperty.call(entry, 'timestamp') + Object.hasOwn(entry, 'path') && + Object.hasOwn(entry, 'operation') && + Object.hasOwn(entry, 'args') && + Object.hasOwn(entry, 'timestamp') ) const grouped = groupBy(allJournals, 'path') const trimmedGrouped = Object.entries(grouped).map(([path, journal]) => { diff --git a/test/unit/specs/boot/routes.spec.js b/test/unit/specs/boot/routes.spec.js index ff246d2b3..2029eca10 100644 --- a/test/unit/specs/boot/routes.spec.js +++ b/test/unit/specs/boot/routes.spec.js @@ -20,7 +20,7 @@ describe('routes', () => { const matchedComponents = router.currentRoute.value.matched // eslint-disable-next-line no-prototype-builtins - expect(matchedComponents[0].components.default.components.hasOwnProperty('Timeline')).to.eql(true) + expect(Object.hasOwn(matchedComponents[0].components.default.components, 'Timeline')).to.eql(true) }) it('user\'s profile', async () => { @@ -29,7 +29,7 @@ describe('routes', () => { const matchedComponents = router.currentRoute.value.matched // eslint-disable-next-line no-prototype-builtins - expect(matchedComponents[0].components.default.components.hasOwnProperty('UserCard')).to.eql(true) + expect(Object.hasOwn(matchedComponents[0].components.default.components, 'UserCard')).to.eql(true) }) it('user\'s profile at /users', async () => { @@ -38,7 +38,7 @@ describe('routes', () => { const matchedComponents = router.currentRoute.value.matched // eslint-disable-next-line no-prototype-builtins - expect(matchedComponents[0].components.default.components.hasOwnProperty('UserCard')).to.eql(true) + expect(Object.hasOwn(matchedComponents[0].components.default.components, 'UserCard')).to.eql(true) }) it('list view', async () => { @@ -46,7 +46,7 @@ describe('routes', () => { const matchedComponents = router.currentRoute.value.matched - expect(Object.prototype.hasOwnProperty.call(matchedComponents[0].components.default.components, 'ListsCard')).to.eql(true) + expect(Object.hasOwn(matchedComponents[0].components.default.components, 'ListsCard')).to.eql(true) }) it('list timeline', async () => { @@ -54,7 +54,7 @@ describe('routes', () => { const matchedComponents = router.currentRoute.value.matched - expect(Object.prototype.hasOwnProperty.call(matchedComponents[0].components.default.components, 'Timeline')).to.eql(true) + expect(Object.hasOwn(matchedComponents[0].components.default.components, 'Timeline')).to.eql(true) }) it('list edit', async () => { @@ -62,6 +62,6 @@ describe('routes', () => { const matchedComponents = router.currentRoute.value.matched - expect(Object.prototype.hasOwnProperty.call(matchedComponents[0].components.default.components, 'BasicUserCard')).to.eql(true) + expect(Object.hasOwn(matchedComponents[0].components.default.components, 'BasicUserCard')).to.eql(true) }) }) diff --git a/test/unit/specs/services/theme_data/sanity_checks.spec.js b/test/unit/specs/services/theme_data/sanity_checks.spec.js index b9053f377..bb47653db 100644 --- a/test/unit/specs/services/theme_data/sanity_checks.spec.js +++ b/test/unit/specs/services/theme_data/sanity_checks.spec.js @@ -6,7 +6,7 @@ const checkColors = (output) => { expect(v, key).to.be.an('object') expect(v, key).to.include.all.keys('r', 'g', 'b') 'rgba'.split('').forEach(k => { - if ((k === 'a' && Object.prototype.hasOwnProperty.call(v, 'a')) || k !== 'a') { + if ((k === 'a' && Object.hasOwn(v, 'a')) || k !== 'a') { expect(v[k], key + '.' + k).to.be.a('number') expect(v[k], key + '.' + k).to.be.least(0) expect(v[k], key + '.' + k).to.be.most(k === 'a' ? 1 : 255) From 9262e803ec8036a2776bc73069b7f2bdd091fe0c Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 6 Jan 2026 16:22:52 +0200 Subject: [PATCH 03/17] biome format --write --- build/check-versions.mjs | 19 +- build/commit_hash.js | 8 +- build/copy_plugin.js | 47 +- build/emojis_plugin.js | 27 +- build/msw_plugin.js | 6 +- build/service_worker_messages.js | 18 +- build/sw_plugin.js | 151 +- build/update-emoji.js | 27 +- eslint.config.mjs | 23 +- postcss.config.js | 4 +- public/static/palettes/index.json | 72 +- public/static/splash.css | 21 +- renovate.json | 4 +- src/App.js | 161 +- src/boot/after_store.js | 355 +- src/boot/routes.js | 170 +- src/components/about/about.js | 16 +- .../account_actions/account_actions.js | 62 +- src/components/alert.style.js | 38 +- src/components/announcement/announcement.js | 87 +- .../announcement_editor.js | 6 +- .../announcements_page/announcements_page.js | 38 +- .../async_component_error.vue | 6 +- src/components/attachment/attachment.js | 83 +- src/components/auth_form/auth_form.js | 18 +- src/components/autosuggest/autosuggest.js | 40 +- src/components/avatar_list/avatar_list.js | 18 +- src/components/badge.style.js | 23 +- .../basic_user_card/basic_user_card.js | 18 +- src/components/block_card/block_card.js | 28 +- .../bookmark_folder_card.js | 19 +- .../bookmark_folder_edit.js | 57 +- .../bookmark_folders/bookmark_folders.js | 18 +- .../bookmark_folders_menu_content.js | 12 +- .../bookmark_timeline/bookmark_timeline.js | 28 +- src/components/border.style.js | 8 +- .../bubble_timeline/bubble_timeline.js | 13 +- src/components/button.style.js | 99 +- src/components/button_unstyled.style.js | 59 +- src/components/chat/chat.js | 260 +- src/components/chat/chat.style.js | 16 +- src/components/chat/chat_layout_utils.js | 7 +- src/components/chat_list/chat_list.js | 20 +- .../chat_list_item/chat_list_item.js | 40 +- src/components/chat_message/chat_message.js | 65 +- .../chat_message/chat_message.style.js | 21 +- .../chat_message_date/chat_message_date.vue | 11 +- src/components/chat_new/chat_new.js | 57 +- src/components/chat_title/chat_title.js | 16 +- src/components/checkbox/checkbox.vue | 19 +- src/components/color_input/color_input.vue | 49 +- .../component_preview/component_preview.js | 51 +- src/components/confirm_modal/confirm_modal.js | 21 +- src/components/confirm_modal/mute_confirm.js | 35 +- .../contrast_ratio/contrast_ratio.vue | 50 +- .../conversation-page/conversation-page.js | 8 +- src/components/conversation/conversation.js | 370 +- src/components/desktop_nav/desktop_nav.js | 100 +- src/components/dialog_modal/dialog_modal.js | 12 +- src/components/dm_timeline/dm_timeline.js | 8 +- .../domain_mute_card/domain_mute_card.js | 16 +- src/components/draft/draft.js | 65 +- src/components/draft_closer/draft_closer.js | 29 +- src/components/drafts/drafts.js | 8 +- .../edit_status_form/edit_status_form.js | 21 +- .../edit_status_modal/edit_status_modal.js | 34 +- src/components/emoji_input/emoji_input.js | 226 +- src/components/emoji_input/suggestor.js | 86 +- src/components/emoji_picker/emoji_picker.js | 194 +- .../emoji_reactions/emoji_reactions.js | 71 +- src/components/exporter/exporter.js | 48 +- .../extra_notifications.js | 65 +- .../features_panel/features_panel.js | 36 +- src/components/flash/flash.js | 36 +- src/components/follow_button/follow_button.js | 48 +- src/components/follow_card/follow_card.js | 17 +- .../follow_request_card.js | 47 +- .../follow_requests/follow_requests.js | 8 +- src/components/font_control/font_control.js | 36 +- .../friends_timeline/friends_timeline.js | 8 +- src/components/fun_text.style.js | 24 +- src/components/gallery/gallery.js | 105 +- .../global_notice_list/global_notice_list.js | 18 +- src/components/hashtag_link/hashtag_link.js | 16 +- src/components/icon.style.js | 8 +- src/components/image_cropper/image_cropper.js | 44 +- src/components/importer/importer.js | 40 +- src/components/input.style.js | 77 +- .../instance_specific_panel.js | 6 +- src/components/interactions/interactions.js | 19 +- .../interface_language_switcher.js | 36 +- src/components/link-preview/link-preview.js | 26 +- src/components/link.style.js | 14 +- src/components/list/list.vue | 12 +- src/components/lists/lists.js | 18 +- src/components/lists_card/lists_card.js | 12 +- src/components/lists_edit/lists_edit.js | 83 +- .../lists_menu/lists_menu_content.js | 18 +- .../lists_timeline/lists_timeline.js | 28 +- .../lists_user_search/lists_user_search.js | 37 +- src/components/login_form/login_form.js | 104 +- src/components/media_modal/media_modal.js | 82 +- src/components/media_upload/media_upload.js | 98 +- src/components/mention_link/mention_link.js | 108 +- src/components/mentions/mentions.js | 8 +- src/components/mentions_line/mentions_line.js | 20 +- src/components/menu_item.style.js | 64 +- src/components/mfa_form/recovery_form.js | 28 +- src/components/mfa_form/totp_form.js | 28 +- src/components/mobile_drawer.style.js | 12 +- src/components/mobile_nav/mobile_nav.js | 88 +- .../mobile_post_status_button.js | 84 +- src/components/modal/modal.vue | 16 +- src/components/modal/modals.style.js | 6 +- .../moderation_tools/moderation_tools.js | 130 +- .../mrf_transparency_panel.js | 110 +- src/components/mute_card/mute_card.js | 26 +- src/components/nav_panel/nav_panel.js | 95 +- src/components/navigation/filter.js | 65 +- src/components/navigation/navigation.js | 41 +- src/components/navigation/navigation_entry.js | 29 +- src/components/navigation/navigation_pins.js | 92 +- src/components/notification/notification.js | 116 +- .../notification/notification.style.js | 4 +- .../notifications/notification_filters.vue | 18 +- src/components/notifications/notifications.js | 158 +- .../oauth_callback/oauth_callback.js | 26 +- .../opacity_input/opacity_input.vue | 12 +- .../optional_router_link.vue | 2 +- .../palette_editor/palette_editor.vue | 39 +- src/components/panel.style.js | 50 +- src/components/panel_header.style.js | 44 +- .../panel_loading/panel_loading.vue | 4 +- .../password_reset/password_reset.js | 64 +- src/components/pinch_zoom/pinch_zoom.js | 10 +- src/components/poll/poll.js | 72 +- src/components/poll/poll_form.js | 96 +- src/components/poll/poll_graph.style.js | 8 +- src/components/popover.style.js | 30 +- src/components/popover/popover.js | 149 +- .../post_status_form/post_status_form.js | 466 +- .../post_status_modal/post_status_modal.js | 34 +- .../progress_button/progress_button.vue | 23 +- .../public_and_external_timeline.js | 16 +- .../public_timeline/public_timeline.js | 13 +- .../quick_filter_settings.js | 109 +- .../quick_view_settings.js | 98 +- .../quotes_timeline/quotes_timeline.js | 30 +- src/components/range_input/range_input.vue | 17 +- src/components/registration/registration.js | 83 +- src/components/remote_follow/remote_follow.js | 6 +- .../remote_user_resolver.js | 18 +- .../remove_follower_button.js | 32 +- src/components/report/report.js | 32 +- src/components/rich_content/rich_content.jsx | 202 +- src/components/root.style.js | 8 +- .../roundness_input/roundness_input.vue | 12 +- .../scope_selector/scope_selector.js | 62 +- .../screen_reader_notice.js | 18 +- .../scroll_top_button/scroll_top_button.js | 10 +- src/components/scrollbar.style.js | 14 +- src/components/scrollbar_element.style.js | 50 +- src/components/search/search.js | 65 +- src/components/search_bar/search_bar.js | 22 +- src/components/select/select.js | 16 +- src/components/select/select_motion.vue | 15 +- .../selectable_list/selectable_list.js | 36 +- .../settings_modal/admin_tabs/auth_tab.js | 14 +- .../settings_modal/admin_tabs/emoji_tab.js | 290 +- .../admin_tabs/federation_tab.js | 10 +- .../admin_tabs/frontends_tab.js | 54 +- .../settings_modal/admin_tabs/http_tab.js | 25 +- .../settings_modal/admin_tabs/instance_tab.js | 49 +- .../admin_tabs/job_queues_tab.js | 10 +- .../settings_modal/admin_tabs/limits_tab.js | 6 +- .../settings_modal/admin_tabs/links_tab.js | 155 +- .../settings_modal/admin_tabs/mailer_tab.js | 42 +- .../admin_tabs/media_proxy_tab.js | 22 +- .../admin_tabs/monitoring_tab.js | 19 +- .../settings_modal/admin_tabs/other_tab.js | 10 +- .../settings_modal/admin_tabs/posts_tab.js | 10 +- .../settings_modal/admin_tabs/rates_tab.js | 8 +- .../admin_tabs/registrations_tab.js | 10 +- .../settings_modal/admin_tabs/uploads_tab.js | 48 +- .../helpers/attachment_setting.js | 22 +- .../settings_modal/helpers/boolean_setting.js | 14 +- .../settings_modal/helpers/choice_setting.js | 26 +- .../settings_modal/helpers/color_setting.js | 8 +- .../settings_modal/helpers/draft_buttons.vue | 6 +- .../helpers/emoji_editing_popover.vue | 137 +- .../settings_modal/helpers/float_setting.vue | 4 +- .../settings_modal/helpers/help_indicator.vue | 6 +- .../helpers/integer_setting.vue | 4 +- .../settings_modal/helpers/list_setting.js | 42 +- .../helpers/list_tuple_setting.js | 10 +- .../settings_modal/helpers/map_setting.js | 57 +- .../helpers/modified_indicator.vue | 10 +- .../settings_modal/helpers/number_setting.js | 16 +- .../helpers/profile_setting_indicator.vue | 6 +- .../settings_modal/helpers/proxy_setting.js | 27 +- .../helpers/pwa_manifest_icons_setting.js | 40 +- .../settings_modal/helpers/rate_setting.js | 24 +- .../settings_modal/helpers/setting.js | 226 +- .../helpers/shared_computed_object.js | 12 +- .../settings_modal/helpers/string_setting.js | 2 +- .../settings_modal/helpers/tuple_setting.js | 10 +- .../settings_modal/helpers/unit_setting.js | 54 +- .../helpers/vertical_tab_switcher.jsx | 133 +- .../settings_modal/settings_modal.js | 124 +- .../settings_modal_admin_content.js | 42 +- .../settings_modal_user_content.js | 38 +- .../settings_modal/tabs/appearance_tab.js | 370 +- .../settings_modal/tabs/clutter_tab.js | 114 +- .../settings_modal/tabs/composing_tab.js | 142 +- .../tabs/data_import_export_tab.js | 106 +- .../settings_modal/tabs/developer_tab.js | 23 +- .../settings_modal/tabs/filtering_tab.js | 156 +- .../settings_modal/tabs/general_tab.js | 45 +- .../settings_modal/tabs/layout_tab.js | 35 +- .../tabs/mutes_and_blocks_tab.js | 91 +- .../settings_modal/tabs/notifications_tab.js | 30 +- .../tabs/old_theme_tab/old_theme_tab.js | 489 +- .../tabs/old_theme_tab/theme_preview.vue | 13 +- .../settings_modal/tabs/posts_tab.js | 75 +- .../settings_modal/tabs/profile_tab.js | 28 +- .../tabs/security_tab/confirm.js | 10 +- .../settings_modal/tabs/security_tab/mfa.js | 114 +- .../tabs/security_tab/mfa_backup_codes.js | 20 +- .../tabs/security_tab/mfa_totp.js | 32 +- .../tabs/security_tab/security_tab.js | 73 +- .../tabs/style_tab/style_tab.js | 529 +- .../tabs/style_tab/virtual_directives_tab.js | 35 +- .../shadow_control/shadow_control.js | 102 +- src/components/shout_panel/shout_panel.js | 43 +- src/components/side_drawer/side_drawer.js | 64 +- src/components/staff_panel/staff_panel.js | 28 +- src/components/status/post.style.js | 16 +- src/components/status/status.js | 423 +- .../status_action_buttons/action_button.js | 64 +- .../action_button_container.js | 50 +- .../buttons_definitions.js | 509 +- .../status_action_buttons.js | 98 +- src/components/status_body/status_body.js | 96 +- .../status_bookmark_folder_menu.js | 26 +- .../status_content/status_content.js | 103 +- .../status_history_modal.js | 44 +- .../status_popover/status_popover.js | 31 +- .../sticker_picker/sticker_picker.js | 55 +- .../still-image/still-image-emoji-popover.vue | 172 +- src/components/still-image/still-image.js | 37 +- src/components/swipe_click/swipe_click.js | 36 +- src/components/tab_switcher/tab.style.js | 49 +- src/components/tab_switcher/tab_switcher.jsx | 139 +- src/components/tag_timeline/tag_timeline.js | 30 +- .../terms_of_service_panel.js | 8 +- src/components/text.style.js | 14 +- src/components/thread_tree/thread_tree.js | 55 +- src/components/timeago/timeago.vue | 70 +- src/components/timeline/timeline.js | 219 +- src/components/timeline_menu/timeline_menu.js | 56 +- src/components/top_bar.style.js | 56 +- src/components/underlay.style.js | 12 +- .../unicode_domain_indicator.vue | 12 +- .../update_notification.js | 68 +- src/components/user_avatar/avatar.style.js | 26 +- src/components/user_avatar/user_avatar.js | 38 +- src/components/user_card/user_card.js | 309 +- src/components/user_card/user_card.style.js | 8 +- src/components/user_link/user_link.vue | 17 +- .../user_list_menu/user_list_menu.js | 119 +- .../user_list_popover/user_list_popover.js | 18 +- src/components/user_note/user_note.js | 29 +- src/components/user_panel/user_panel.js | 10 +- src/components/user_popover/user_popover.js | 14 +- src/components/user_profile/user_profile.js | 93 +- .../user_reporting_modal.js | 65 +- .../user_timed_filter_modal.js | 46 +- .../video_attachment/video_attachment.js | 18 +- src/components/who_to_follow/who_to_follow.js | 26 +- .../who_to_follow_panel.js | 55 +- src/directives/body_scroll_lock.js | 26 +- src/hocs/with_load_more/with_load_more.jsx | 186 +- .../with_subscription/with_subscription.jsx | 149 +- src/i18n/en.json | 2 +- src/i18n/languages.js | 8 +- src/i18n/messages.js | 8 +- src/i18n/oc.json | 22 +- src/i18n/service_worker_messages.js | 2 +- src/lib/notification-i18n-loader.js | 2 +- src/lib/persisted_state.js | 265 +- src/lib/push_notifications_plugin.js | 24 +- src/main.js | 44 +- src/modules/adminSettings.js | 179 +- src/modules/api.js | 250 +- src/modules/chats.js | 139 +- src/modules/config.js | 86 +- src/modules/config_declaration.js | 12 +- src/modules/default_config_state.js | 16 +- src/modules/drafts.js | 36 +- src/modules/index.js | 2 +- src/modules/instance.js | 208 +- src/modules/notifications.js | 107 +- src/modules/profileConfig.js | 69 +- src/modules/statuses.js | 471 +- src/modules/users.js | 423 +- src/services/api/api.service.js | 1105 +- .../attributes_helper.service.js | 9 +- .../backend_interactor_service.js | 46 +- .../bookmark_folders_fetcher.service.js | 14 +- src/services/chat_service/chat_service.js | 63 +- src/services/chat_utils/chat_utils.js | 23 +- src/services/color_convert/color_convert.js | 25 +- src/services/completion/completion.js | 36 +- .../component_utils/component_utils.js | 8 +- src/services/date_utils/date_utils.js | 25 +- .../desktop_notification_utils.js | 26 +- .../entity_normalizer.service.js | 112 +- src/services/errors/errors.js | 12 +- src/services/export_import/export_import.js | 14 +- .../favicon_service/favicon_service.js | 59 +- .../file_size_format/file_size_format.js | 7 +- src/services/file_type/file_type.service.js | 14 +- .../follow_manipulate/follow_manipulate.js | 102 +- .../follow_request_fetcher.service.js | 16 +- .../gesture_service/gesture_service.js | 62 +- .../html_line_converter.service.js | 73 +- .../html_tree_converter.service.js | 25 +- .../html_converter/utility.service.js | 9 +- .../lists_fetcher/lists_fetcher.service.js | 14 +- src/services/locale/locale.service.js | 23 +- src/services/matcher/matcher.service.js | 8 +- src/services/new_api/mfa.js | 38 +- src/services/new_api/oauth.js | 128 +- src/services/new_api/password_reset.js | 14 +- .../notification_utils/notification_utils.js | 66 +- .../notifications_fetcher.service.js | 33 +- .../offset_finder/offset_finder.service.js | 25 +- src/services/poll/poll.service.js | 15 +- .../promise_interval/promise_interval.js | 4 +- src/services/resettable_async_component.js | 19 +- src/services/ruffle_service/ruffle_service.js | 65 +- src/services/status_parser/status_parser.js | 101 +- .../status_poster/status_poster.service.js | 64 +- src/services/style_setter/style_setter.js | 130 +- src/services/sw/sw.js | 158 +- src/services/theme_data/css_utils.js | 258 +- src/services/theme_data/iss_deserializer.js | 188 +- src/services/theme_data/iss_serializer.js | 76 +- src/services/theme_data/iss_utils.js | 176 +- src/services/theme_data/pleromafe.js | 254 +- src/services/theme_data/theme2_keys.js | 2 +- src/services/theme_data/theme2_to_theme3.js | 410 +- .../theme_data/theme3_slot_functions.js | 133 +- src/services/theme_data/theme_data.service.js | 781 +- .../theme_data/theme_data_3.service.js | 441 +- .../timeline_fetcher.service.js | 79 +- .../user_highlighter/user_highlighter.js | 23 +- .../user_profile_link_generator.js | 11 +- src/stores/announcements.js | 66 +- src/stores/auth_flow.js | 26 +- src/stores/bookmark_folders.js | 32 +- src/stores/editStatus.js | 10 +- src/stores/i18n.js | 10 +- src/stores/interface.js | 308 +- src/stores/lists.js | 63 +- src/stores/media_viewer.js | 14 +- src/stores/oauth.js | 52 +- src/stores/oauth_tokens.js | 34 +- src/stores/polls.js | 44 +- src/stores/post_status.js | 12 +- src/stores/reports.js | 48 +- src/stores/serverSideStorage.js | 316 +- src/stores/shout.js | 8 +- src/stores/statusHistory.js | 10 +- src/sw.js | 202 +- test/e2e/custom-assertions/elementCount.js | 14 +- test/e2e/nightwatch.conf.js | 21 +- test/e2e/runner.js | 4 +- test/e2e/specs/test.js | 2 +- test/fixtures/mastoapi.json | 2059 +- test/fixtures/mock_api.js | 42 +- test/fixtures/mock_store.js | 2 +- test/fixtures/setup_test.js | 74 +- test/fixtures/statuses.json | 36 +- test/unit/specs/boot/routes.spec.js | 52 +- test/unit/specs/components/draft.spec.js | 115 +- .../unit/specs/components/emoji_input.spec.js | 36 +- test/unit/specs/components/gallery.spec.js | 133 +- .../specs/components/rich_content.spec.js | 225 +- .../specs/components/user_profile.spec.js | 88 +- test/unit/specs/lib/persisted_state.spec.js | 90 +- .../specs/modules/serverSideStorage.spec.js | 304 +- test/unit/specs/modules/statuses.spec.js | 231 +- test/unit/specs/modules/users.spec.js | 42 +- .../chat_service/chat_service.spec.js | 24 +- .../services/completion/completion.spec.js | 41 +- .../services/date_utils/date_utils.spec.js | 43 +- .../entity_normalizer.spec.js | 432 +- .../file_size_format/file_size_format.spec.js | 12 +- .../gesture_service/gesture_service.spec.js | 59 +- .../html_line_converter.spec.js | 65 +- .../html_tree_converter.spec.js | 101 +- .../services/html_converter/utility.spec.js | 13 +- .../specs/services/matcher/matcher.spec.js | 4 +- .../notification_utils.spec.js | 56 +- .../theme_data/iss_deserializer.spec.js | 13 +- .../services/theme_data/sanity_checks.spec.js | 7 +- .../services/theme_data/theme_data.spec.js | 17 +- .../services/theme_data/theme_data3.spec.js | 158 +- .../user_profile_link_generator.spec.js | 9 +- test/unit/specs/stores/lists.spec.js | 35 +- test/unit/specs/stores/oauth.spec.js | 49 +- tools/emoji_merger.js | 31 +- tools/emojis.json | 40326 ++++++++++++++-- vite.config.js | 103 +- 415 files changed, 54076 insertions(+), 17419 deletions(-) diff --git a/build/check-versions.mjs b/build/check-versions.mjs index 73c1eeb15..61f750e7b 100644 --- a/build/check-versions.mjs +++ b/build/check-versions.mjs @@ -7,8 +7,8 @@ var versionRequirements = [ { name: 'node', currentVersion: semver.clean(process.version), - versionRequirement: packageConfig.engines.node - } + versionRequirement: packageConfig.engines.node, + }, ] export default function () { @@ -16,15 +16,22 @@ export default function () { for (let i = 0; i < versionRequirements.length; i++) { const mod = versionRequirements[i] if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { - warnings.push(mod.name + ': ' + - chalk.red(mod.currentVersion) + ' should be ' + - chalk.green(mod.versionRequirement) + warnings.push( + mod.name + + ': ' + + chalk.red(mod.currentVersion) + + ' should be ' + + chalk.green(mod.versionRequirement), ) } } if (warnings.length) { - console.warn(chalk.yellow('\nTo use this template, you must update following to modules:\n')) + console.warn( + chalk.yellow( + '\nTo use this template, you must update following to modules:\n', + ), + ) for (let i = 0; i < warnings.length; i++) { const warning = warnings[i] console.warn(' ' + warning) diff --git a/build/commit_hash.js b/build/commit_hash.js index c104af5d9..c60355804 100644 --- a/build/commit_hash.js +++ b/build/commit_hash.js @@ -1,8 +1,8 @@ import childProcess from 'child_process' -export const getCommitHash = (() => { - const subst = "$Format:%h$" - if(!subst.match(/Format:/)) { +export const getCommitHash = () => { + const subst = '$Format:%h$' + if (!subst.match(/Format:/)) { return subst } else { try { @@ -15,4 +15,4 @@ export const getCommitHash = (() => { return 'UNKNOWN' } } -}) +} diff --git a/build/copy_plugin.js b/build/copy_plugin.js index 694048a36..ad4d9617a 100644 --- a/build/copy_plugin.js +++ b/build/copy_plugin.js @@ -2,7 +2,7 @@ import serveStatic from 'serve-static' import { resolve } from 'node:path' import { cp } from 'node:fs/promises' -const getPrefix = s => { +const getPrefix = (s) => { const padEnd = s.endsWith('/') ? s : s + '/' return padEnd.startsWith('/') ? padEnd : '/' + padEnd } @@ -13,28 +13,31 @@ const copyPlugin = ({ inUrl, inFs }) => { let copyTarget const handler = serveStatic(inFs) - return [{ - name: 'copy-plugin-serve', - apply: 'serve', - configureServer (server) { - server.middlewares.use(prefix, handler) - } - }, { - name: 'copy-plugin-build', - apply: 'build', - configResolved (config) { - copyTarget = resolve(config.root, config.build.outDir, subdir) + return [ + { + name: 'copy-plugin-serve', + apply: 'serve', + configureServer(server) { + server.middlewares.use(prefix, handler) + }, }, - closeBundle: { - order: 'post', - sequential: true, - async handler () { - console.info(`Copying '${inFs}' to ${copyTarget}...`) - await cp(inFs, copyTarget, { recursive: true }) - console.info('Done.') - } - } - }] + { + name: 'copy-plugin-build', + apply: 'build', + configResolved(config) { + copyTarget = resolve(config.root, config.build.outDir, subdir) + }, + closeBundle: { + order: 'post', + sequential: true, + async handler() { + console.info(`Copying '${inFs}' to ${copyTarget}...`) + await cp(inFs, copyTarget, { recursive: true }) + console.info('Done.') + }, + }, + }, + ] } export default copyPlugin diff --git a/build/emojis_plugin.js b/build/emojis_plugin.js index aed52066d..056da056e 100644 --- a/build/emojis_plugin.js +++ b/build/emojis_plugin.js @@ -4,18 +4,19 @@ import { languages, langCodeToCldrName } from '../src/i18n/languages.js' const annotationsImportPrefix = '@kazvmoe-infra/unicode-emoji-json/annotations/' const specialAnnotationsLocale = { - ja_easy: 'ja' + ja_easy: 'ja', } -const internalToAnnotationsLocale = (internal) => specialAnnotationsLocale[internal] || internal +const internalToAnnotationsLocale = (internal) => + specialAnnotationsLocale[internal] || internal // This gets all the annotations that are accessible (whose language // can be chosen in the settings). Data for other languages are // discarded because there is no way for it to be fetched. const getAllAccessibleAnnotations = async (projectRoot) => { - const imports = (await Promise.all( - languages - .map(async lang => { + const imports = ( + await Promise.all( + languages.map(async (lang) => { const destLang = internalToAnnotationsLocale(lang) const importModule = `${annotationsImportPrefix}${destLang}.json` const importFile = resolve(projectRoot, 'node_modules', importModule) @@ -25,9 +26,11 @@ const getAllAccessibleAnnotations = async (projectRoot) => { } catch (e) { return } - }))) - .filter(k => k) - .join(',\n') + }), + ) + ) + .filter((k) => k) + .join(',\n') return ` export const annotationsLoader = { @@ -43,21 +46,21 @@ const emojisPlugin = () => { let projectRoot return { name: 'emojis-plugin', - configResolved (conf) { + configResolved(conf) { projectRoot = conf.root }, - resolveId (id) { + resolveId(id) { if (id === emojiAnnotationsId) { return emojiAnnotationsIdResolved } return null }, - async load (id) { + async load(id) { if (id === emojiAnnotationsIdResolved) { return await getAllAccessibleAnnotations(projectRoot) } return null - } + }, } } diff --git a/build/msw_plugin.js b/build/msw_plugin.js index f544348fc..009546945 100644 --- a/build/msw_plugin.js +++ b/build/msw_plugin.js @@ -8,10 +8,10 @@ const mswPlugin = () => { return { name: 'msw-plugin', apply: 'serve', - configResolved (conf) { + configResolved(conf) { projectRoot = conf.root }, - configureServer (server) { + configureServer(server) { server.middlewares.use(async (req, res, next) => { if (req.path === '/mockServiceWorker.js') { const file = await readFile(resolve(projectRoot, target)) @@ -21,7 +21,7 @@ const mswPlugin = () => { next() } }) - } + }, } } diff --git a/build/service_worker_messages.js b/build/service_worker_messages.js index c078e8563..852963245 100644 --- a/build/service_worker_messages.js +++ b/build/service_worker_messages.js @@ -5,7 +5,7 @@ import { fileURLToPath } from 'node:url' const i18nDir = resolve( dirname(dirname(fileURLToPath(import.meta.url))), - 'src/i18n' + 'src/i18n', ) export const i18nFiles = languages.reduce((acc, lang) => { @@ -16,13 +16,15 @@ export const i18nFiles = languages.reduce((acc, lang) => { }, {}) export const generateServiceWorkerMessages = async () => { - const msgArray = await Promise.all(Object.entries(i18nFiles).map(async ([lang, file]) => { - const fileContent = await readFile(file, 'utf-8') - const msg = { - notifications: JSON.parse(fileContent).notifications || {} - } - return [lang, msg] - })) + const msgArray = await Promise.all( + Object.entries(i18nFiles).map(async ([lang, file]) => { + const fileContent = await readFile(file, 'utf-8') + const msg = { + notifications: JSON.parse(fileContent).notifications || {}, + } + return [lang, msg] + }), + ) return msgArray.reduce((acc, [lang, msg]) => { acc[lang] = msg return acc diff --git a/build/sw_plugin.js b/build/sw_plugin.js index d970c5c09..c7f543e10 100644 --- a/build/sw_plugin.js +++ b/build/sw_plugin.js @@ -3,7 +3,10 @@ import { dirname, resolve } from 'node:path' import { readFile } from 'node:fs/promises' import { build } from 'vite' import * as esbuild from 'esbuild' -import { generateServiceWorkerMessages, i18nFiles } from './service_worker_messages.js' +import { + generateServiceWorkerMessages, + i18nFiles, +} from './service_worker_messages.js' const getSWMessagesAsText = async () => { const messages = await generateServiceWorkerMessages() @@ -14,14 +17,10 @@ const projectRoot = dirname(dirname(fileURLToPath(import.meta.url))) const swEnvName = 'virtual:pleroma-fe/service_worker_env' const swEnvNameResolved = '\0' + swEnvName const getDevSwEnv = () => `self.serviceWorkerOption = { assets: [] };` -const getProdSwEnv = ({ assets }) => `self.serviceWorkerOption = { assets: ${JSON.stringify(assets)} };` +const getProdSwEnv = ({ assets }) => + `self.serviceWorkerOption = { assets: ${JSON.stringify(assets)} };` -export const devSwPlugin = ({ - swSrc, - swDest, - transformSW, - alias -}) => { +export const devSwPlugin = ({ swSrc, swDest, transformSW, alias }) => { const swFullSrc = resolve(projectRoot, swSrc) const esbuildAlias = {} Object.entries(alias).forEach(([source, dest]) => { @@ -31,9 +30,8 @@ export const devSwPlugin = ({ return { name: 'dev-sw-plugin', apply: 'serve', - configResolved (conf) { - }, - resolveId (id) { + configResolved(conf) {}, + resolveId(id) { const name = id.startsWith('/') ? id.slice(1) : id if (name === swDest) { return swFullSrc @@ -42,7 +40,7 @@ export const devSwPlugin = ({ } return null }, - async load (id) { + async load(id) { if (id === swFullSrc) { return readFile(swFullSrc, 'utf-8') } else if (id === swEnvNameResolved) { @@ -55,7 +53,7 @@ export const devSwPlugin = ({ * during dev, and firefox does not support ESM as service worker * https://bugzilla.mozilla.org/show_bug.cgi?id=1360870 */ - async transform (code, id) { + async transform(code, id) { if (id === swFullSrc && transformSW) { const res = await esbuild.build({ entryPoints: [swSrc], @@ -63,52 +61,54 @@ export const devSwPlugin = ({ write: false, outfile: 'sw-pleroma.js', alias: esbuildAlias, - plugins: [{ - name: 'vite-like-root-resolve', - setup (b) { - b.onResolve( - { filter: new RegExp(/^\//) }, - args => ({ - path: resolve(projectRoot, args.path.slice(1)) - }) - ) - } - }, { - name: 'sw-messages', - setup (b) { - b.onResolve( - { filter: new RegExp('^' + swMessagesName + '$') }, - args => ({ - path: args.path, - namespace: 'sw-messages' + plugins: [ + { + name: 'vite-like-root-resolve', + setup(b) { + b.onResolve({ filter: new RegExp(/^\//) }, (args) => ({ + path: resolve(projectRoot, args.path.slice(1)), })) - b.onLoad( - { filter: /.*/, namespace: 'sw-messages' }, - async () => ({ - contents: await getSWMessagesAsText() + }, + }, + { + name: 'sw-messages', + setup(b) { + b.onResolve( + { filter: new RegExp('^' + swMessagesName + '$') }, + (args) => ({ + path: args.path, + namespace: 'sw-messages', + }), + ) + b.onLoad( + { filter: /.*/, namespace: 'sw-messages' }, + async () => ({ + contents: await getSWMessagesAsText(), + }), + ) + }, + }, + { + name: 'sw-env', + setup(b) { + b.onResolve( + { filter: new RegExp('^' + swEnvName + '$') }, + (args) => ({ + path: args.path, + namespace: 'sw-env', + }), + ) + b.onLoad({ filter: /.*/, namespace: 'sw-env' }, () => ({ + contents: getDevSwEnv(), })) - } - }, { - name: 'sw-env', - setup (b) { - b.onResolve( - { filter: new RegExp('^' + swEnvName + '$') }, - args => ({ - path: args.path, - namespace: 'sw-env' - })) - b.onLoad( - { filter: /.*/, namespace: 'sw-env' }, - () => ({ - contents: getDevSwEnv() - })) - } - }] + }, + }, + ], }) const text = res.outputFiles[0].text return text } - } + }, } } @@ -118,16 +118,13 @@ export const devSwPlugin = ({ // however, we must compile the service worker to iife because of browser support. // Run another vite build just for the service worker targeting iife at // the end of the build. -export const buildSwPlugin = ({ - swSrc, - swDest, -}) => { +export const buildSwPlugin = ({ swSrc, swDest }) => { let config return { name: 'build-sw-plugin', enforce: 'post', apply: 'build', - configResolved (resolvedConfig) { + configResolved(resolvedConfig) { config = { define: resolvedConfig.define, resolve: resolvedConfig.resolve, @@ -138,50 +135,50 @@ export const buildSwPlugin = ({ lib: { entry: swSrc, formats: ['iife'], - name: 'sw_pleroma' + name: 'sw_pleroma', }, emptyOutDir: false, rollupOptions: { output: { - entryFileNames: swDest - } - } + entryFileNames: swDest, + }, + }, }, - configFile: false + configFile: false, } }, generateBundle: { order: 'post', sequential: true, - async handler (_, bundle) { + async handler(_, bundle) { const assets = Object.keys(bundle) - .filter(name => !/\.map$/.test(name)) - .map(name => '/' + name) + .filter((name) => !/\.map$/.test(name)) + .map((name) => '/' + name) config.plugins.push({ name: 'build-sw-env-plugin', - resolveId (id) { + resolveId(id) { if (id === swEnvName) { return swEnvNameResolved } return null }, - load (id) { + load(id) { if (id === swEnvNameResolved) { return getProdSwEnv({ assets }) } return null - } + }, }) - } + }, }, closeBundle: { order: 'post', sequential: true, - async handler () { + async handler() { console.info('Building service worker for production') await build(config) - } - } + }, + }, } } @@ -191,9 +188,9 @@ const swMessagesNameResolved = '\0' + swMessagesName export const swMessagesPlugin = () => { return { name: 'sw-messages-plugin', - resolveId (id) { + resolveId(id) { if (id === swMessagesName) { - Object.values(i18nFiles).forEach(f => { + Object.values(i18nFiles).forEach((f) => { this.addWatchFile(f) }) return swMessagesNameResolved @@ -201,11 +198,11 @@ export const swMessagesPlugin = () => { return null } }, - async load (id) { + async load(id) { if (id === swMessagesNameResolved) { return await getSWMessagesAsText() } return null - } + }, } } diff --git a/build/update-emoji.js b/build/update-emoji.js index 5d578ba61..4ff7e1de8 100644 --- a/build/update-emoji.js +++ b/build/update-emoji.js @@ -1,22 +1,21 @@ - -import emojis from '@kazvmoe-infra/unicode-emoji-json/data-by-group.json' with { type: 'json' } +import emojis from '@kazvmoe-infra/unicode-emoji-json/data-by-group.json' with { + type: 'json', +} import fs from 'fs' -Object.keys(emojis) - .map(k => { - emojis[k].map(e => { - delete e.unicode_version - delete e.emoji_version - delete e.skin_tone_support_unicode_version - }) +Object.keys(emojis).map((k) => { + emojis[k].map((e) => { + delete e.unicode_version + delete e.emoji_version + delete e.skin_tone_support_unicode_version }) +}) const res = {} -Object.keys(emojis) - .map(k => { - const groupId = k.replace('&', 'and').replace(/ /g, '-').toLowerCase() - res[groupId] = emojis[k] - }) +Object.keys(emojis).map((k) => { + const groupId = k.replace('&', 'and').replace(/ /g, '-').toLowerCase() + res[groupId] = emojis[k] +}) console.info('Updating emojis...') fs.writeFileSync('src/assets/emoji.json', JSON.stringify(res)) diff --git a/eslint.config.mjs b/eslint.config.mjs index 01bdb2038..9a670ccf9 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,29 +1,28 @@ -import vue from "eslint-plugin-vue"; -import js from "@eslint/js"; -import globals from "globals"; - +import vue from 'eslint-plugin-vue' +import js from '@eslint/js' +import globals from 'globals' export default [ ...vue.configs['flat/recommended'], js.configs.recommended, { - files: ["**/*.js", "**/*.mjs", "**/*.vue"], - ignores: ["build/*.js", "config/*.js"], + files: ['**/*.js', '**/*.mjs', '**/*.vue'], + ignores: ['build/*.js', 'config/*.js'], languageOptions: { ecmaVersion: 2024, - sourceType: "module", + sourceType: 'module', parserOptions: { - parser: "@babel/eslint-parser", + parser: '@babel/eslint-parser', }, globals: { ...globals.browser, ...globals.vitest, ...globals.chai, ...globals.commonjs, - ...globals.serviceworker - } + ...globals.serviceworker, + }, }, rules: { @@ -32,6 +31,6 @@ export default [ 'no-debugger': 0, 'vue/require-prop-types': 0, 'vue/multi-word-component-names': 0, - } - } + }, + }, ] diff --git a/postcss.config.js b/postcss.config.js index 95ebbf2a6..b7fc12838 100644 --- a/postcss.config.js +++ b/postcss.config.js @@ -1,7 +1,5 @@ import autoprefixer from 'autoprefixer' export default { - plugins: [ - autoprefixer - ] + plugins: [autoprefixer], } diff --git a/public/static/palettes/index.json b/public/static/palettes/index.json index 2cd110d1e..3c4e37e44 100644 --- a/public/static/palettes/index.json +++ b/public/static/palettes/index.json @@ -1,6 +1,26 @@ { - "pleroma-dark": [ "Pleroma Dark", "#121a24", "#182230", "#b9b9ba", "#d8a070", "#d31014", "#0fa00f", "#0095ff", "#ffa500" ], - "pleroma-light": [ "Pleroma Light", "#f2f4f6", "#dbe0e8", "#304055", "#f86f0f", "#d31014", "#0fa00f", "#0095ff", "#ffa500" ], + "pleroma-dark": [ + "Pleroma Dark", + "#121a24", + "#182230", + "#b9b9ba", + "#d8a070", + "#d31014", + "#0fa00f", + "#0095ff", + "#ffa500" + ], + "pleroma-light": [ + "Pleroma Light", + "#f2f4f6", + "#dbe0e8", + "#304055", + "#f86f0f", + "#d31014", + "#0fa00f", + "#0095ff", + "#ffa500" + ], "classic-dark": { "name": "Classic Dark", "bg": "#161c20", @@ -12,8 +32,28 @@ "cBlue": "#0095ff", "cOrange": "#ffa500" }, - "bird": [ "Bird", "#f8fafd", "#e6ecf0", "#14171a", "#0084b8", "#e0245e", "#17bf63", "#1b95e0", "#fab81e"], - "pleroma-amoled": [ "Pleroma Dark AMOLED", "#000000", "#111111", "#b0b0b1", "#d8a070", "#aa0000", "#0fa00f", "#0095ff", "#d59500"], + "bird": [ + "Bird", + "#f8fafd", + "#e6ecf0", + "#14171a", + "#0084b8", + "#e0245e", + "#17bf63", + "#1b95e0", + "#fab81e" + ], + "pleroma-amoled": [ + "Pleroma Dark AMOLED", + "#000000", + "#111111", + "#b0b0b1", + "#d8a070", + "#aa0000", + "#0fa00f", + "#0095ff", + "#d59500" + ], "tomorrow-night": { "name": "Tomorrow Night", "bg": "#1d1f21", @@ -36,8 +76,28 @@ "cGreen": "#50FA7B", "cOrange": "#FFB86C" }, - "ir-black": [ "Ir Black", "#000000", "#242422", "#b5b3aa", "#ff6c60", "#FF6C60", "#A8FF60", "#96CBFE", "#FFFFB6" ], - "monokai": [ "Monokai", "#272822", "#383830", "#f8f8f2", "#f92672", "#F92672", "#a6e22e", "#66d9ef", "#f4bf75" ], + "ir-black": [ + "Ir Black", + "#000000", + "#242422", + "#b5b3aa", + "#ff6c60", + "#FF6C60", + "#A8FF60", + "#96CBFE", + "#FFFFB6" + ], + "monokai": [ + "Monokai", + "#272822", + "#383830", + "#f8f8f2", + "#f92672", + "#F92672", + "#a6e22e", + "#66d9ef", + "#f4bf75" + ], "purple-stream": { "name": "Purple stream", "bg": "#17171A", diff --git a/public/static/splash.css b/public/static/splash.css index f56f33d07..caf57dacb 100644 --- a/public/static/splash.css +++ b/public/static/splash.css @@ -65,16 +65,17 @@ body { z-index: 2; grid-template-rows: repeat(8, 1fr); grid-template-columns: repeat(5, 1fr); - grid-template-areas: "P P . L L" - "P P . L L" - "P P . L L" - "P P . L L" - "P P . . ." - "P P . . ." - "P P . E E" - "P P . E E"; + grid-template-areas: + "P P . L L" + "P P . L L" + "P P . L L" + "P P . L L" + "P P . . ." + "P P . . ." + "P P . E E" + "P P . E E"; - --logoChunkSize: calc(2em * 0.5 * var(--scale)) + --logoChunkSize: calc(2em * 0.5 * var(--scale)); } .chunk { @@ -84,7 +85,7 @@ body { #chunk-P { grid-area: P; - border-top-left-radius: calc(var(--logoChunkSize) / 2); + border-top-left-radius: calc(var(--logoChunkSize) / 2); } #chunk-L { diff --git a/renovate.json b/renovate.json index 39a2b6e9a..4bd832f5f 100644 --- a/renovate.json +++ b/renovate.json @@ -1,6 +1,4 @@ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": [ - "config:base" - ] + "extends": ["config:base"] } diff --git a/src/App.js b/src/App.js index 0027d908a..91e6cbc4c 100644 --- a/src/App.js +++ b/src/App.js @@ -28,7 +28,9 @@ export default { components: { UserPanel, NavPanel, - Notifications: defineAsyncComponent(() => import('./components/notifications/notifications.vue')), + Notifications: defineAsyncComponent( + () => import('./components/notifications/notifications.vue'), + ), InstanceSpecificPanel, FeaturesPanel, WhoToFollowPanel, @@ -38,29 +40,33 @@ export default { MobilePostStatusButton, MobileNav, DesktopNav, - SettingsModal: defineAsyncComponent(() => import('./components/settings_modal/settings_modal.vue')), - UpdateNotification: defineAsyncComponent(() => import('./components/update_notification/update_notification.vue')), + SettingsModal: defineAsyncComponent( + () => import('./components/settings_modal/settings_modal.vue'), + ), + UpdateNotification: defineAsyncComponent( + () => import('./components/update_notification/update_notification.vue'), + ), UserReportingModal, PostStatusModal, EditStatusModal, StatusHistoryModal, - GlobalNoticeList + GlobalNoticeList, }, data: () => ({ - mobileActivePanel: 'timeline' + mobileActivePanel: 'timeline', }), watch: { - themeApplied () { + themeApplied() { this.removeSplash() }, - currentTheme () { + currentTheme() { this.setThemeBodyClass() }, - layoutType () { + layoutType() { document.getElementById('modal').classList = ['-' + this.layoutType] - } + }, }, - created () { + created() { // Load the locale from the storage const val = this.$store.getters.mergedConfig.interfaceLanguage this.$store.dispatch('setOption', { name: 'interfaceLanguage', value: val }) @@ -70,7 +76,7 @@ export default { this.updateScrollState = throttle(this.scrollHandler, 200) this.updateMobileState = throttle(this.resizeHandler, 200) }, - mounted () { + mounted() { window.addEventListener('resize', this.updateMobileState) this.scrollParent.addEventListener('scroll', this.updateScrollState) @@ -80,108 +86,145 @@ export default { } getOrCreateServiceWorker() }, - unmounted () { + unmounted() { window.removeEventListener('resize', this.updateMobileState) this.scrollParent.removeEventListener('scroll', this.updateScrollState) }, computed: { - themeApplied () { + themeApplied() { return useInterfaceStore().themeApplied }, - currentTheme () { + currentTheme() { if (useInterfaceStore().styleDataUsed) { - const styleMeta = useInterfaceStore().styleDataUsed.find(x => x.component === '@meta') + const styleMeta = useInterfaceStore().styleDataUsed.find( + (x) => x.component === '@meta', + ) if (styleMeta !== undefined) { - return styleMeta.directives.name.replaceAll(" ", "-").toLowerCase() + return styleMeta.directives.name.replaceAll(' ', '-').toLowerCase() } } return 'stock' }, - layoutModalClass () { + layoutModalClass() { return '-' + this.layoutType }, - classes () { + classes() { return [ { '-reverse': this.reverseLayout, '-no-sticky-headers': this.noSticky, - '-has-new-post-button': this.newPostButtonShown + '-has-new-post-button': this.newPostButtonShown, }, - '-' + this.layoutType + '-' + this.layoutType, ] }, - navClasses () { + navClasses() { const { navbarColumnStretch } = this.$store.getters.mergedConfig return [ '-' + this.layoutType, - ...(navbarColumnStretch ? ['-column-stretch'] : []) + ...(navbarColumnStretch ? ['-column-stretch'] : []), ] }, - currentUser () { return this.$store.state.users.currentUser }, - userBackground () { return this.currentUser.background_image }, - instanceBackground () { + currentUser() { + return this.$store.state.users.currentUser + }, + userBackground() { + return this.currentUser.background_image + }, + instanceBackground() { return this.mergedConfig.hideInstanceWallpaper ? null : this.$store.state.instance.background }, - background () { return this.userBackground || this.instanceBackground }, - bgStyle () { + background() { + return this.userBackground || this.instanceBackground + }, + bgStyle() { if (this.background) { return { - '--body-background-image': `url(${this.background})` + '--body-background-image': `url(${this.background})`, } } }, - shout () { return useShoutStore().joined }, - suggestionsEnabled () { return this.$store.state.instance.suggestionsEnabled }, - showInstanceSpecificPanel () { - return this.$store.state.instance.showInstanceSpecificPanel && + shout() { + return useShoutStore().joined + }, + suggestionsEnabled() { + return this.$store.state.instance.suggestionsEnabled + }, + showInstanceSpecificPanel() { + return ( + this.$store.state.instance.showInstanceSpecificPanel && !this.$store.getters.mergedConfig.hideISP && this.$store.state.instance.instanceSpecificPanelContent + ) }, - isChats () { + isChats() { return this.$route.name === 'chat' || this.$route.name === 'chats' }, - isListEdit () { + isListEdit() { return this.$route.name === 'lists-edit' }, - newPostButtonShown () { + newPostButtonShown() { if (this.isChats) return false if (this.isListEdit) return false - return this.$store.getters.mergedConfig.alwaysShowNewPostButton || this.layoutType === 'mobile' + return ( + this.$store.getters.mergedConfig.alwaysShowNewPostButton || + this.layoutType === 'mobile' + ) }, - showFeaturesPanel () { return this.$store.state.instance.showFeaturesPanel }, - editingAvailable () { return this.$store.state.instance.editingAvailable }, - shoutboxPosition () { + showFeaturesPanel() { + return this.$store.state.instance.showFeaturesPanel + }, + editingAvailable() { + return this.$store.state.instance.editingAvailable + }, + shoutboxPosition() { return this.$store.getters.mergedConfig.alwaysShowNewPostButton || false }, - hideShoutbox () { + hideShoutbox() { return this.$store.getters.mergedConfig.hideShoutbox }, - layoutType () { return useInterfaceStore().layoutType }, - privateMode () { return this.$store.state.instance.private }, - reverseLayout () { - const { thirdColumnMode, sidebarRight: reverseSetting } = this.$store.getters.mergedConfig + layoutType() { + return useInterfaceStore().layoutType + }, + privateMode() { + return this.$store.state.instance.private + }, + reverseLayout() { + const { thirdColumnMode, sidebarRight: reverseSetting } = + this.$store.getters.mergedConfig if (this.layoutType !== 'wide') { return reverseSetting } else { - return thirdColumnMode === 'notifications' ? reverseSetting : !reverseSetting + return thirdColumnMode === 'notifications' + ? reverseSetting + : !reverseSetting } }, - noSticky () { return this.$store.getters.mergedConfig.disableStickyHeaders }, - showScrollbars () { return this.$store.getters.mergedConfig.showScrollbars }, - scrollParent () { return window; /* this.$refs.appContentRef */ }, - ...mapGetters(['mergedConfig']) + noSticky() { + return this.$store.getters.mergedConfig.disableStickyHeaders + }, + showScrollbars() { + return this.$store.getters.mergedConfig.showScrollbars + }, + scrollParent() { + return window /* this.$refs.appContentRef */ + }, + ...mapGetters(['mergedConfig']), }, methods: { - resizeHandler () { + resizeHandler() { useInterfaceStore().setLayoutWidth(windowWidth()) useInterfaceStore().setLayoutHeight(windowHeight()) }, - scrollHandler () { - const scrollPosition = this.scrollParent === window ? window.scrollY : this.scrollParent.scrollTop + scrollHandler() { + const scrollPosition = + this.scrollParent === window + ? window.scrollY + : this.scrollParent.scrollTop if (scrollPosition != 0) { this.$refs.appContentRef.classList.add(['-scrolled']) @@ -189,10 +232,10 @@ export default { this.$refs.appContentRef.classList.remove(['-scrolled']) } }, - setThemeBodyClass () { + setThemeBodyClass() { const themeName = this.currentTheme const classList = Array.from(document.body.classList) - const oldTheme = classList.filter(c => c.startsWith('theme-')) + const oldTheme = classList.filter((c) => c.startsWith('theme-')) if (themeName !== null && themeName !== '') { const newTheme = `theme-${themeName.toLowerCase()}` @@ -208,8 +251,10 @@ export default { document.body.classList.remove(...oldTheme) } }, - removeSplash () { - document.querySelector('#status').textContent = this.$t('splash.fun_' + Math.ceil(Math.random() * 4)) + removeSplash() { + document.querySelector('#status').textContent = this.$t( + 'splash.fun_' + Math.ceil(Math.random() * 4), + ) const splashscreenRoot = document.querySelector('#splash') splashscreenRoot.addEventListener('transitionend', () => { splashscreenRoot.remove() @@ -219,6 +264,6 @@ export default { }, 600) splashscreenRoot.classList.add('hidden') document.querySelector('#app').classList.remove('hidden') - } - } + }, + }, } diff --git a/src/boot/after_store.js b/src/boot/after_store.js index ad994af6a..c05fb7e55 100644 --- a/src/boot/after_store.js +++ b/src/boot/after_store.js @@ -5,15 +5,21 @@ import vClickOutside from 'click-outside-vue3' import VueVirtualScroller from 'vue-virtual-scroller' import 'vue-virtual-scroller/dist/vue-virtual-scroller.css' -import { FontAwesomeIcon, FontAwesomeLayers } from '@fortawesome/vue-fontawesome' -import { config } from '@fortawesome/fontawesome-svg-core'; +import { + FontAwesomeIcon, + FontAwesomeLayers, +} from '@fortawesome/vue-fontawesome' +import { config } from '@fortawesome/fontawesome-svg-core' config.autoAddCss = false import App from '../App.vue' import routes from './routes' import VBodyScrollLock from 'src/directives/body_scroll_lock' -import { windowWidth, windowHeight } from '../services/window_utils/window_utils' +import { + windowWidth, + windowHeight, +} from '../services/window_utils/window_utils' import backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js' import { applyConfig } from '../services/style_setter/style_setter.js' import FaviconService from '../services/favicon_service/favicon_service.js' @@ -24,7 +30,10 @@ import { useI18nStore } from 'src/stores/i18n' import { useInterfaceStore } from 'src/stores/interface' import { useAnnouncementsStore } from 'src/stores/announcements' import { useAuthFlowStore } from 'src/stores/auth_flow' -import { staticOrApiConfigDefault, instanceDefaultConfig } from 'src/modules/default_config_state.js' +import { + staticOrApiConfigDefault, + instanceDefaultConfig, +} from 'src/modules/default_config_state.js' let staticInitialResults = null @@ -33,7 +42,9 @@ const parsedInitialResults = () => { return null } if (!staticInitialResults) { - staticInitialResults = JSON.parse(document.getElementById('initial-results').textContent) + staticInitialResults = JSON.parse( + document.getElementById('initial-results').textContent, + ) } return staticInitialResults } @@ -55,7 +66,7 @@ const preloadFetch = async (request) => { return { ok: true, json: () => requestData, - text: () => requestData + text: () => requestData, } } @@ -67,17 +78,35 @@ const getInstanceConfig = async ({ store }) => { const textlimit = data.max_toot_chars const vapidPublicKey = data.pleroma.vapid_public_key - store.dispatch('setInstanceOption', { name: 'pleromaExtensionsAvailable', value: data.pleroma }) - store.dispatch('setInstanceOption', { name: 'textlimit', value: textlimit }) - store.dispatch('setInstanceOption', { name: 'accountApprovalRequired', value: data.approval_required }) - store.dispatch('setInstanceOption', { name: 'birthdayRequired', value: !!data.pleroma?.metadata.birthday_required }) - store.dispatch('setInstanceOption', { name: 'birthdayMinAge', value: data.pleroma?.metadata.birthday_min_age || 0 }) + store.dispatch('setInstanceOption', { + name: 'pleromaExtensionsAvailable', + value: data.pleroma, + }) + store.dispatch('setInstanceOption', { + name: 'textlimit', + value: textlimit, + }) + store.dispatch('setInstanceOption', { + name: 'accountApprovalRequired', + value: data.approval_required, + }) + store.dispatch('setInstanceOption', { + name: 'birthdayRequired', + value: !!data.pleroma?.metadata.birthday_required, + }) + store.dispatch('setInstanceOption', { + name: 'birthdayMinAge', + value: data.pleroma?.metadata.birthday_min_age || 0, + }) if (vapidPublicKey) { - store.dispatch('setInstanceOption', { name: 'vapidPublicKey', value: vapidPublicKey }) + store.dispatch('setInstanceOption', { + name: 'vapidPublicKey', + value: vapidPublicKey, + }) } } else { - throw (res) + throw res } } catch (error) { console.error('Could not load instance config, potentially fatal') @@ -94,10 +123,12 @@ const getBackendProvidedConfig = async () => { const data = await res.json() return data.pleroma_fe } else { - throw (res) + throw res } } catch (error) { - console.error('Could not load backend-provided frontend config, potentially fatal') + console.error( + 'Could not load backend-provided frontend config, potentially fatal', + ) console.error(error) } } @@ -108,7 +139,7 @@ const getStaticConfig = async () => { if (res.ok) { return res.json() } else { - throw (res) + throw res } } catch (error) { console.warn('Failed to load static/config.json, continuing without it.') @@ -149,7 +180,7 @@ const getTOS = async ({ store }) => { const html = await res.text() store.dispatch('setInstanceOption', { name: 'tos', value: html }) } else { - throw (res) + throw res } } catch (e) { console.warn("Can't load TOS\n", e) @@ -161,9 +192,12 @@ const getInstancePanel = async ({ store }) => { const res = await preloadFetch('/instance/panel.html') if (res.ok) { const html = await res.text() - store.dispatch('setInstanceOption', { name: 'instanceSpecificPanelContent', value: html }) + store.dispatch('setInstanceOption', { + name: 'instanceSpecificPanelContent', + value: html, + }) } else { - throw (res) + throw res } } catch (e) { console.warn("Can't load instance panel\n", e) @@ -175,25 +209,27 @@ const getStickers = async ({ store }) => { const res = await window.fetch('/static/stickers.json') if (res.ok) { const values = await res.json() - const stickers = (await Promise.all( - Object.entries(values).map(async ([name, path]) => { - const resPack = await window.fetch(path + 'pack.json') - let meta = {} - if (resPack.ok) { - meta = await resPack.json() - } - return { - pack: name, - path, - meta - } - }) - )).sort((a, b) => { + const stickers = ( + await Promise.all( + Object.entries(values).map(async ([name, path]) => { + const resPack = await window.fetch(path + 'pack.json') + let meta = {} + if (resPack.ok) { + meta = await resPack.json() + } + return { + pack: name, + path, + meta, + } + }), + ) + ).sort((a, b) => { return a.meta.title.localeCompare(b.meta.title) }) store.dispatch('setInstanceOption', { name: 'stickers', value: stickers }) } else { - throw (res) + throw res } } catch (e) { console.warn("Can't load stickers\n", e) @@ -203,13 +239,19 @@ const getStickers = async ({ store }) => { const getAppSecret = async ({ store }) => { const oauth = useOAuthStore() if (oauth.userToken) { - store.commit('setBackendInteractor', backendInteractorService(oauth.getToken)) + store.commit( + 'setBackendInteractor', + backendInteractorService(oauth.getToken), + ) } } const resolveStaffAccounts = ({ store, accounts }) => { - const nicknames = accounts.map(uri => uri.split('/').pop()) - store.dispatch('setInstanceOption', { name: 'staffAccounts', value: nicknames }) + const nicknames = accounts.map((uri) => uri.split('/').pop()) + store.dispatch('setInstanceOption', { + name: 'staffAccounts', + value: nicknames, + }) } const getNodeInfo = async ({ store }) => { @@ -220,77 +262,167 @@ const getNodeInfo = async ({ store }) => { const data = await res.json() const metadata = data.metadata const features = metadata.features - store.dispatch('setInstanceOption', { name: 'name', value: metadata.nodeName }) - store.dispatch('setInstanceOption', { name: 'registrationOpen', value: data.openRegistrations }) - store.dispatch('setInstanceOption', { name: 'mediaProxyAvailable', value: features.includes('media_proxy') }) - store.dispatch('setInstanceOption', { name: 'safeDM', value: features.includes('safe_dm_mentions') }) - store.dispatch('setInstanceOption', { name: 'shoutAvailable', value: features.includes('chat') }) - store.dispatch('setInstanceOption', { name: 'pleromaChatMessagesAvailable', value: features.includes('pleroma_chat_messages') }) + store.dispatch('setInstanceOption', { + name: 'name', + value: metadata.nodeName, + }) + store.dispatch('setInstanceOption', { + name: 'registrationOpen', + value: data.openRegistrations, + }) + store.dispatch('setInstanceOption', { + name: 'mediaProxyAvailable', + value: features.includes('media_proxy'), + }) + store.dispatch('setInstanceOption', { + name: 'safeDM', + value: features.includes('safe_dm_mentions'), + }) + store.dispatch('setInstanceOption', { + name: 'shoutAvailable', + value: features.includes('chat'), + }) + store.dispatch('setInstanceOption', { + name: 'pleromaChatMessagesAvailable', + value: features.includes('pleroma_chat_messages'), + }) store.dispatch('setInstanceOption', { name: 'pleromaCustomEmojiReactionsAvailable', value: features.includes('pleroma_custom_emoji_reactions') || - features.includes('custom_emoji_reactions') + features.includes('custom_emoji_reactions'), + }) + store.dispatch('setInstanceOption', { + name: 'pleromaBookmarkFoldersAvailable', + value: features.includes('pleroma:bookmark_folders'), + }) + store.dispatch('setInstanceOption', { + name: 'gopherAvailable', + value: features.includes('gopher'), + }) + store.dispatch('setInstanceOption', { + name: 'pollsAvailable', + value: features.includes('polls'), + }) + store.dispatch('setInstanceOption', { + name: 'editingAvailable', + value: features.includes('editing'), + }) + store.dispatch('setInstanceOption', { + name: 'pollLimits', + value: metadata.pollLimits, + }) + store.dispatch('setInstanceOption', { + name: 'mailerEnabled', + value: metadata.mailerEnabled, + }) + store.dispatch('setInstanceOption', { + name: 'quotingAvailable', + value: features.includes('quote_posting'), + }) + store.dispatch('setInstanceOption', { + name: 'groupActorAvailable', + value: features.includes('pleroma:group_actors'), + }) + store.dispatch('setInstanceOption', { + name: 'blockExpiration', + value: features.includes('pleroma:block_expiration'), + }) + store.dispatch('setInstanceOption', { + name: 'localBubbleInstances', + value: metadata.localBubbleInstances ?? [], }) - store.dispatch('setInstanceOption', { name: 'pleromaBookmarkFoldersAvailable', value: features.includes('pleroma:bookmark_folders') }) - store.dispatch('setInstanceOption', { name: 'gopherAvailable', value: features.includes('gopher') }) - store.dispatch('setInstanceOption', { name: 'pollsAvailable', value: features.includes('polls') }) - store.dispatch('setInstanceOption', { name: 'editingAvailable', value: features.includes('editing') }) - store.dispatch('setInstanceOption', { name: 'pollLimits', value: metadata.pollLimits }) - store.dispatch('setInstanceOption', { name: 'mailerEnabled', value: metadata.mailerEnabled }) - store.dispatch('setInstanceOption', { name: 'quotingAvailable', value: features.includes('quote_posting') }) - store.dispatch('setInstanceOption', { name: 'groupActorAvailable', value: features.includes('pleroma:group_actors') }) - store.dispatch('setInstanceOption', { name: 'blockExpiration', value: features.includes('pleroma:block_expiration') }) - store.dispatch('setInstanceOption', { name: 'localBubbleInstances', value: metadata.localBubbleInstances ?? [] }) const uploadLimits = metadata.uploadLimits - store.dispatch('setInstanceOption', { name: 'uploadlimit', value: parseInt(uploadLimits.general) }) - store.dispatch('setInstanceOption', { name: 'avatarlimit', value: parseInt(uploadLimits.avatar) }) - store.dispatch('setInstanceOption', { name: 'backgroundlimit', value: parseInt(uploadLimits.background) }) - store.dispatch('setInstanceOption', { name: 'bannerlimit', value: parseInt(uploadLimits.banner) }) - store.dispatch('setInstanceOption', { name: 'fieldsLimits', value: metadata.fieldsLimits }) + store.dispatch('setInstanceOption', { + name: 'uploadlimit', + value: parseInt(uploadLimits.general), + }) + store.dispatch('setInstanceOption', { + name: 'avatarlimit', + value: parseInt(uploadLimits.avatar), + }) + store.dispatch('setInstanceOption', { + name: 'backgroundlimit', + value: parseInt(uploadLimits.background), + }) + store.dispatch('setInstanceOption', { + name: 'bannerlimit', + value: parseInt(uploadLimits.banner), + }) + store.dispatch('setInstanceOption', { + name: 'fieldsLimits', + value: metadata.fieldsLimits, + }) - store.dispatch('setInstanceOption', { name: 'restrictedNicknames', value: metadata.restrictedNicknames }) - store.dispatch('setInstanceOption', { name: 'postFormats', value: metadata.postFormats }) + store.dispatch('setInstanceOption', { + name: 'restrictedNicknames', + value: metadata.restrictedNicknames, + }) + store.dispatch('setInstanceOption', { + name: 'postFormats', + value: metadata.postFormats, + }) const suggestions = metadata.suggestions - store.dispatch('setInstanceOption', { name: 'suggestionsEnabled', value: suggestions.enabled }) - store.dispatch('setInstanceOption', { name: 'suggestionsWeb', value: suggestions.web }) + store.dispatch('setInstanceOption', { + name: 'suggestionsEnabled', + value: suggestions.enabled, + }) + store.dispatch('setInstanceOption', { + name: 'suggestionsWeb', + value: suggestions.web, + }) const software = data.software - store.dispatch('setInstanceOption', { name: 'backendVersion', value: software.version }) - store.dispatch('setInstanceOption', { name: 'backendRepository', value: software.repository }) + store.dispatch('setInstanceOption', { + name: 'backendVersion', + value: software.version, + }) + store.dispatch('setInstanceOption', { + name: 'backendRepository', + value: software.repository, + }) const priv = metadata.private store.dispatch('setInstanceOption', { name: 'private', value: priv }) const frontendVersion = window.___pleromafe_commit_hash - store.dispatch('setInstanceOption', { name: 'frontendVersion', value: frontendVersion }) + store.dispatch('setInstanceOption', { + name: 'frontendVersion', + value: frontendVersion, + }) const federation = metadata.federation store.dispatch('setInstanceOption', { name: 'tagPolicyAvailable', - value: typeof federation.mrf_policies === 'undefined' - ? false - : metadata.federation.mrf_policies.includes('TagPolicy') + value: + typeof federation.mrf_policies === 'undefined' + ? false + : metadata.federation.mrf_policies.includes('TagPolicy'), }) - store.dispatch('setInstanceOption', { name: 'federationPolicy', value: federation }) + store.dispatch('setInstanceOption', { + name: 'federationPolicy', + value: federation, + }) store.dispatch('setInstanceOption', { name: 'federating', - value: typeof federation.enabled === 'undefined' - ? true - : federation.enabled + value: + typeof federation.enabled === 'undefined' ? true : federation.enabled, }) const accountActivationRequired = metadata.accountActivationRequired - store.dispatch('setInstanceOption', { name: 'accountActivationRequired', value: accountActivationRequired }) + store.dispatch('setInstanceOption', { + name: 'accountActivationRequired', + value: accountActivationRequired, + }) const accounts = metadata.staffAccounts resolveStaffAccounts({ store, accounts }) } else { - throw (res) + throw res } } catch (e) { console.warn('Could not load nodeinfo') @@ -300,7 +432,10 @@ const getNodeInfo = async ({ store }) => { const setConfig = async ({ store }) => { // apiConfig, staticConfig - const configInfos = await Promise.all([getBackendProvidedConfig({ store }), getStaticConfig()]) + const configInfos = await Promise.all([ + getBackendProvidedConfig({ store }), + getStaticConfig(), + ]) const apiConfig = configInfos[0] const staticConfig = configInfos[1] @@ -331,29 +466,37 @@ const afterStoreSetup = async ({ pinia, store, storageError, i18n }) => { if (process.env.NODE_ENV === 'development') { // do some checks to avoid common errors if (!Object.keys(allStores).length) { - throw new Error('No stores are available. Check the code in src/boot/after_store.js') + throw new Error( + 'No stores are available. Check the code in src/boot/after_store.js', + ) } } await Promise.all( - Object.entries(allStores) - .map(async ([name, mod]) => { - const isStoreName = name => name.startsWith('use') - if (process.env.NODE_ENV === 'development') { - if (Object.keys(mod).filter(isStoreName).length !== 1) { - throw new Error('Each store file must export exactly one store as a named export. Check your code in src/stores/') - } + Object.entries(allStores).map(async ([name, mod]) => { + const isStoreName = (name) => name.startsWith('use') + if (process.env.NODE_ENV === 'development') { + if (Object.keys(mod).filter(isStoreName).length !== 1) { + throw new Error( + 'Each store file must export exactly one store as a named export. Check your code in src/stores/', + ) } - const storeFuncName = Object.keys(mod).find(isStoreName) - if (storeFuncName && typeof mod[storeFuncName] === 'function') { - const p = mod[storeFuncName]().$persistLoaded - if (!(p instanceof Promise)) { - throw new Error(`${name} store's $persistLoaded is not a Promise. The persist plugin is not applied.`) - } - await p - } else { - throw new Error(`Store module ${name} does not export a 'use...' function`) + } + const storeFuncName = Object.keys(mod).find(isStoreName) + if (storeFuncName && typeof mod[storeFuncName] === 'function') { + const p = mod[storeFuncName]().$persistLoaded + if (!(p instanceof Promise)) { + throw new Error( + `${name} store's $persistLoaded is not a Promise. The persist plugin is not applied.`, + ) } - })) + await p + } else { + throw new Error( + `Store module ${name} does not export a 'use...' function`, + ) + } + }), + ) } try { @@ -364,7 +507,10 @@ const afterStoreSetup = async ({ pinia, store, storageError, i18n }) => { } if (storageError) { - useInterfaceStore().pushGlobalNotice({ messageKey: 'errors.storage_unavailable', level: 'error' }) + useInterfaceStore().pushGlobalNotice({ + messageKey: 'errors.storage_unavailable', + level: 'error', + }) } useInterfaceStore().setLayoutWidth(windowWidth()) @@ -376,12 +522,19 @@ const afterStoreSetup = async ({ pinia, store, storageError, i18n }) => { window.addEventListener('focus', () => updateFocus()) const overrides = window.___pleromafe_dev_overrides || {} - const server = (typeof overrides.target !== 'undefined') ? overrides.target : window.location.origin + const server = + typeof overrides.target !== 'undefined' + ? overrides.target + : window.location.origin store.dispatch('setInstanceOption', { name: 'server', value: server }) await setConfig({ store }) try { - await useInterfaceStore().applyTheme().catch((e) => { console.error('Error setting theme', e) }) + await useInterfaceStore() + .applyTheme() + .catch((e) => { + console.error('Error setting theme', e) + }) } catch (e) { window.splashError(e) return Promise.reject(e) @@ -395,8 +548,8 @@ const afterStoreSetup = async ({ pinia, store, storageError, i18n }) => { checkOAuthToken({ store }), getInstancePanel({ store }), getNodeInfo({ store }), - getInstanceConfig({ store }) - ]).catch(e => Promise.reject(e)) + getInstanceConfig({ store }), + ]).catch((e) => Promise.reject(e)) // Start fetching things that don't need to block the UI store.dispatch('fetchMutes') @@ -409,11 +562,11 @@ const afterStoreSetup = async ({ pinia, store, storageError, i18n }) => { history: createWebHistory(), routes: routes(store), scrollBehavior: (to, _from, savedPosition) => { - if (to.matched.some(m => m.meta.dontScroll)) { + if (to.matched.some((m) => m.meta.dontScroll)) { return false } return savedPosition || { left: 0, top: 0 } - } + }, }) useI18nStore().setI18n(i18n) diff --git a/src/boot/routes.js b/src/boot/routes.js index 02abf8ce6..b0b90a8d7 100644 --- a/src/boot/routes.js +++ b/src/boot/routes.js @@ -45,46 +45,124 @@ export default (store) => { name: 'root', path: '/', redirect: () => { - return (store.state.users.currentUser - ? store.state.instance.redirectRootLogin - : store.state.instance.redirectRootNoLogin) || '/main/all' - } + return ( + (store.state.users.currentUser + ? store.state.instance.redirectRootLogin + : store.state.instance.redirectRootNoLogin) || '/main/all' + ) + }, + }, + { + name: 'public-external-timeline', + path: '/main/all', + component: PublicAndExternalTimeline, + }, + { + name: 'public-timeline', + path: '/main/public', + component: PublicTimeline, + }, + { + name: 'friends', + path: '/main/friends', + component: FriendsTimeline, + beforeEnter: validateAuthenticatedRoute, }, - { name: 'public-external-timeline', path: '/main/all', component: PublicAndExternalTimeline }, - { name: 'public-timeline', path: '/main/public', component: PublicTimeline }, - { name: 'friends', path: '/main/friends', component: FriendsTimeline, beforeEnter: validateAuthenticatedRoute }, { name: 'tag-timeline', path: '/tag/:tag', component: TagTimeline }, { name: 'bookmarks', path: '/bookmarks', component: BookmarkTimeline }, { name: 'bubble', path: '/bubble', component: BubbleTimeline }, - { name: 'conversation', path: '/notice/:id', component: ConversationPage, meta: { dontScroll: true } }, + { + name: 'conversation', + path: '/notice/:id', + component: ConversationPage, + meta: { dontScroll: true }, + }, { name: 'quotes', path: '/notice/:id/quotes', component: QuotesTimeline }, { name: 'remote-user-profile-acct', path: '/remote-users/:_(@)?:username([^/@]+)@:hostname([^/@]+)', component: RemoteUserResolver, - beforeEnter: validateAuthenticatedRoute + beforeEnter: validateAuthenticatedRoute, }, { name: 'remote-user-profile', path: '/remote-users/:hostname/:username', component: RemoteUserResolver, - beforeEnter: validateAuthenticatedRoute + beforeEnter: validateAuthenticatedRoute, + }, + { + name: 'external-user-profile', + path: '/users/$:id', + component: UserProfile, + }, + { + name: 'interactions', + path: '/users/:username/interactions', + component: Interactions, + beforeEnter: validateAuthenticatedRoute, + }, + { + name: 'dms', + path: '/users/:username/dms', + component: DMs, + beforeEnter: validateAuthenticatedRoute, }, - { name: 'external-user-profile', path: '/users/$:id', component: UserProfile }, - { name: 'interactions', path: '/users/:username/interactions', component: Interactions, beforeEnter: validateAuthenticatedRoute }, - { name: 'dms', path: '/users/:username/dms', component: DMs, beforeEnter: validateAuthenticatedRoute }, { name: 'registration', path: '/registration', component: Registration }, - { name: 'password-reset', path: '/password-reset', component: PasswordReset, props: true }, - { name: 'registration-token', path: '/registration/:token', component: Registration }, - { name: 'friend-requests', path: '/friend-requests', component: FollowRequests, beforeEnter: validateAuthenticatedRoute }, - { name: 'notifications', path: '/:username/notifications', component: Notifications, props: () => ({ disableTeleport: true }), beforeEnter: validateAuthenticatedRoute }, + { + name: 'password-reset', + path: '/password-reset', + component: PasswordReset, + props: true, + }, + { + name: 'registration-token', + path: '/registration/:token', + component: Registration, + }, + { + name: 'friend-requests', + path: '/friend-requests', + component: FollowRequests, + beforeEnter: validateAuthenticatedRoute, + }, + { + name: 'notifications', + path: '/:username/notifications', + component: Notifications, + props: () => ({ disableTeleport: true }), + beforeEnter: validateAuthenticatedRoute, + }, { name: 'login', path: '/login', component: AuthForm }, - { name: 'shout-panel', path: '/shout-panel', component: ShoutPanel, props: () => ({ floating: false }) }, - { name: 'oauth-callback', path: '/oauth-callback', component: OAuthCallback, props: (route) => ({ code: route.query.code }) }, - { name: 'search', path: '/search', component: Search, props: (route) => ({ query: route.query.query }) }, - { name: 'who-to-follow', path: '/who-to-follow', component: WhoToFollow, beforeEnter: validateAuthenticatedRoute }, + { + name: 'shout-panel', + path: '/shout-panel', + component: ShoutPanel, + props: () => ({ floating: false }), + }, + { + name: 'oauth-callback', + path: '/oauth-callback', + component: OAuthCallback, + props: (route) => ({ code: route.query.code }), + }, + { + name: 'search', + path: '/search', + component: Search, + props: (route) => ({ query: route.query.query }), + }, + { + name: 'who-to-follow', + path: '/who-to-follow', + component: WhoToFollow, + beforeEnter: validateAuthenticatedRoute, + }, { name: 'about', path: '/about', component: About }, - { name: 'announcements', path: '/announcements', component: AnnouncementsPage }, + { + name: 'announcements', + path: '/announcements', + component: AnnouncementsPage, + }, { name: 'drafts', path: '/drafts', component: Drafts }, { name: 'user-profile', path: '/users/:name', component: UserProfile }, { name: 'legacy-user-profile', path: '/:name', component: UserProfile }, @@ -92,17 +170,51 @@ export default (store) => { { name: 'lists-timeline', path: '/lists/:id', component: ListsTimeline }, { name: 'lists-edit', path: '/lists/:id/edit', component: ListsEdit }, { name: 'lists-new', path: '/lists/new', component: ListsEdit }, - { name: 'edit-navigation', path: '/nav-edit', component: NavPanel, props: () => ({ forceExpand: true, forceEditMode: true }), beforeEnter: validateAuthenticatedRoute }, - { name: 'bookmark-folders', path: '/bookmark_folders', component: BookmarkFolders }, - { name: 'bookmark-folder-new', path: '/bookmarks/new-folder', component: BookmarkFolderEdit }, - { name: 'bookmark-folder', path: '/bookmarks/:id', component: BookmarkTimeline }, - { name: 'bookmark-folder-edit', path: '/bookmarks/:id/edit', component: BookmarkFolderEdit } + { + name: 'edit-navigation', + path: '/nav-edit', + component: NavPanel, + props: () => ({ forceExpand: true, forceEditMode: true }), + beforeEnter: validateAuthenticatedRoute, + }, + { + name: 'bookmark-folders', + path: '/bookmark_folders', + component: BookmarkFolders, + }, + { + name: 'bookmark-folder-new', + path: '/bookmarks/new-folder', + component: BookmarkFolderEdit, + }, + { + name: 'bookmark-folder', + path: '/bookmarks/:id', + component: BookmarkTimeline, + }, + { + name: 'bookmark-folder-edit', + path: '/bookmarks/:id/edit', + component: BookmarkFolderEdit, + }, ] if (store.state.instance.pleromaChatMessagesAvailable) { routes = routes.concat([ - { name: 'chat', path: '/users/:username/chats/:recipient_id', component: Chat, meta: { dontScroll: false }, beforeEnter: validateAuthenticatedRoute }, - { name: 'chats', path: '/users/:username/chats', component: ChatList, meta: { dontScroll: false }, beforeEnter: validateAuthenticatedRoute } + { + name: 'chat', + path: '/users/:username/chats/:recipient_id', + component: Chat, + meta: { dontScroll: false }, + beforeEnter: validateAuthenticatedRoute, + }, + { + name: 'chats', + path: '/users/:username/chats', + component: ChatList, + meta: { dontScroll: false }, + beforeEnter: validateAuthenticatedRoute, + }, ]) } diff --git a/src/components/about/about.js b/src/components/about/about.js index 1df258450..19ac8a7b6 100644 --- a/src/components/about/about.js +++ b/src/components/about/about.js @@ -10,16 +10,20 @@ const About = { FeaturesPanel, TermsOfServicePanel, StaffPanel, - MRFTransparencyPanel + MRFTransparencyPanel, }, computed: { - showFeaturesPanel () { return this.$store.state.instance.showFeaturesPanel }, - showInstanceSpecificPanel () { - return this.$store.state.instance.showInstanceSpecificPanel && + showFeaturesPanel() { + return this.$store.state.instance.showFeaturesPanel + }, + showInstanceSpecificPanel() { + return ( + this.$store.state.instance.showInstanceSpecificPanel && !this.$store.getters.mergedConfig.hideISP && this.$store.state.instance.instanceSpecificPanelContent - } - } + ) + }, + }, } export default About diff --git a/src/components/account_actions/account_actions.js b/src/components/account_actions/account_actions.js index 2ac74ea76..93b906bc0 100644 --- a/src/components/account_actions/account_actions.js +++ b/src/components/account_actions/account_actions.js @@ -5,23 +5,17 @@ import UserListMenu from 'src/components/user_list_menu/user_list_menu.vue' import ConfirmModal from '../confirm_modal/confirm_modal.vue' import UserTimedFilterModal from 'src/components/user_timed_filter_modal/user_timed_filter_modal.vue' import { library } from '@fortawesome/fontawesome-svg-core' -import { - faEllipsisV -} from '@fortawesome/free-solid-svg-icons' +import { faEllipsisV } from '@fortawesome/free-solid-svg-icons' import { useReportsStore } from 'src/stores/reports' -library.add( - faEllipsisV -) +library.add(faEllipsisV) const AccountActions = { - props: [ - 'user', 'relationship' - ], - data () { + props: ['user', 'relationship'], + data() { return { showingConfirmBlock: false, - showingConfirmRemoveFollower: false + showingConfirmRemoveFollower: false, } }, components: { @@ -29,25 +23,25 @@ const AccountActions = { Popover, UserListMenu, ConfirmModal, - UserTimedFilterModal + UserTimedFilterModal, }, methods: { - showConfirmRemoveUserFromFollowers () { + showConfirmRemoveUserFromFollowers() { this.showingConfirmRemoveFollower = true }, - hideConfirmRemoveUserFromFollowers () { + hideConfirmRemoveUserFromFollowers() { this.showingConfirmRemoveFollower = false }, - hideConfirmBlock () { + hideConfirmBlock() { this.showingConfirmBlock = false }, - showRepeats () { + showRepeats() { this.$store.dispatch('showReblogs', this.user.id) }, - hideRepeats () { + hideRepeats() { this.$store.dispatch('hideReblogs', this.user.id) }, - blockUser () { + blockUser() { if (this.$refs.timedBlockDialog) { this.$refs.timedBlockDialog.optionallyPrompt() } else { @@ -58,46 +52,50 @@ const AccountActions = { } } }, - doBlockUser () { + doBlockUser() { this.$store.dispatch('blockUser', { id: this.user.id }) this.hideConfirmBlock() }, - unblockUser () { + unblockUser() { this.$store.dispatch('unblockUser', this.user.id) }, - removeUserFromFollowers () { + removeUserFromFollowers() { if (!this.shouldConfirmRemoveUserFromFollowers) { this.doRemoveUserFromFollowers() } else { this.showConfirmRemoveUserFromFollowers() } }, - doRemoveUserFromFollowers () { + doRemoveUserFromFollowers() { this.$store.dispatch('removeUserFromFollowers', this.user.id) this.hideConfirmRemoveUserFromFollowers() }, - reportUser () { + reportUser() { useReportsStore().openUserReportingModal({ userId: this.user.id }) }, - openChat () { + openChat() { this.$router.push({ name: 'chat', - params: { username: this.$store.state.users.currentUser.screen_name, recipient_id: this.user.id } + params: { + username: this.$store.state.users.currentUser.screen_name, + recipient_id: this.user.id, + }, }) - } + }, }, computed: { - shouldConfirmBlock () { + shouldConfirmBlock() { return this.$store.getters.mergedConfig.modalOnBlock }, - shouldConfirmRemoveUserFromFollowers () { + shouldConfirmRemoveUserFromFollowers() { return this.$store.getters.mergedConfig.modalOnRemoveUserFromFollowers }, ...mapState({ - blockExpirationSupported: state => state.instance.blockExpiration, - pleromaChatMessagesAvailable: state => state.instance.pleromaChatMessagesAvailable - }) - } + blockExpirationSupported: (state) => state.instance.blockExpiration, + pleromaChatMessagesAvailable: (state) => + state.instance.pleromaChatMessagesAvailable, + }), + }, } export default AccountActions diff --git a/src/components/alert.style.js b/src/components/alert.style.js index 868514764..8a6f842ed 100644 --- a/src/components/alert.style.js +++ b/src/components/alert.style.js @@ -1,57 +1,51 @@ export default { name: 'Alert', selector: '.alert', - validInnerComponents: [ - 'Text', - 'Icon', - 'Link', - 'Border', - 'ButtonUnstyled' - ], + validInnerComponents: ['Text', 'Icon', 'Link', 'Border', 'ButtonUnstyled'], variants: { normal: '.neutral', error: '.error', warning: '.warning', - success: '.success' + success: '.success', }, editor: { border: 1, - aspect: '3 / 1' + aspect: '3 / 1', }, defaultRules: [ { directives: { background: '--text', opacity: 0.5, - blur: '9px' - } + blur: '9px', + }, }, { parent: { - component: 'Alert' + component: 'Alert', }, component: 'Border', directives: { - textColor: '--parent' - } + textColor: '--parent', + }, }, { variant: 'error', directives: { - background: '--cRed' - } + background: '--cRed', + }, }, { variant: 'warning', directives: { - background: '--cOrange' - } + background: '--cOrange', + }, }, { variant: 'success', directives: { - background: '--cGreen' - } - } - ] + background: '--cGreen', + }, + }, + ], } diff --git a/src/components/announcement/announcement.js b/src/components/announcement/announcement.js index d1b8257d8..ffb646320 100644 --- a/src/components/announcement/announcement.js +++ b/src/components/announcement/announcement.js @@ -7,103 +7,122 @@ import { useAnnouncementsStore } from 'src/stores/announcements' const Announcement = { components: { AnnouncementEditor, - RichContent + RichContent, }, - data () { + data() { return { editing: false, editedAnnouncement: { content: '', startsAt: undefined, endsAt: undefined, - allDay: undefined + allDay: undefined, }, - editError: '' + editError: '', } }, props: { - announcement: Object + announcement: Object, }, computed: { ...mapState({ - currentUser: state => state.users.currentUser + currentUser: (state) => state.users.currentUser, }), - canEditAnnouncement () { - return this.currentUser && this.currentUser.privileges.includes('announcements_manage_announcements') + canEditAnnouncement() { + return ( + this.currentUser && + this.currentUser.privileges.includes( + 'announcements_manage_announcements', + ) + ) }, - content () { + content() { return this.announcement.content }, - isRead () { + isRead() { return this.announcement.read }, - publishedAt () { + publishedAt() { const time = this.announcement.published_at if (!time) { return } - return this.formatTimeOrDate(time, localeService.internalToBrowserLocale(this.$i18n.locale)) + return this.formatTimeOrDate( + time, + localeService.internalToBrowserLocale(this.$i18n.locale), + ) }, - startsAt () { + startsAt() { const time = this.announcement.starts_at if (!time) { return } - return this.formatTimeOrDate(time, localeService.internalToBrowserLocale(this.$i18n.locale)) + return this.formatTimeOrDate( + time, + localeService.internalToBrowserLocale(this.$i18n.locale), + ) }, - endsAt () { + endsAt() { const time = this.announcement.ends_at if (!time) { return } - return this.formatTimeOrDate(time, localeService.internalToBrowserLocale(this.$i18n.locale)) + return this.formatTimeOrDate( + time, + localeService.internalToBrowserLocale(this.$i18n.locale), + ) }, - inactive () { + inactive() { return this.announcement.inactive - } + }, }, methods: { - markAsRead () { + markAsRead() { if (!this.isRead) { - return useAnnouncementsStore().markAnnouncementAsRead(this.announcement.id) + return useAnnouncementsStore().markAnnouncementAsRead( + this.announcement.id, + ) } }, - deleteAnnouncement () { + deleteAnnouncement() { return useAnnouncementsStore().deleteAnnouncement(this.announcement.id) }, - formatTimeOrDate (time, locale) { + formatTimeOrDate(time, locale) { const d = new Date(time) - return this.announcement.all_day ? d.toLocaleDateString(locale) : d.toLocaleString(locale) + return this.announcement.all_day + ? d.toLocaleDateString(locale) + : d.toLocaleString(locale) }, - enterEditMode () { + enterEditMode() { this.editedAnnouncement.content = this.announcement.pleroma.raw_content this.editedAnnouncement.startsAt = this.announcement.starts_at this.editedAnnouncement.endsAt = this.announcement.ends_at this.editedAnnouncement.allDay = this.announcement.all_day this.editing = true }, - submitEdit () { - useAnnouncementsStore().editAnnouncement({ - id: this.announcement.id, - ...this.editedAnnouncement - }) + submitEdit() { + useAnnouncementsStore() + .editAnnouncement({ + id: this.announcement.id, + ...this.editedAnnouncement, + }) .then(() => { this.editing = false }) - .catch(error => { + .catch((error) => { this.editError = error.error }) }, - cancelEdit () { + cancelEdit() { this.editing = false }, - clearError () { + clearError() { this.editError = undefined - } - } + }, + }, } export default Announcement diff --git a/src/components/announcement_editor/announcement_editor.js b/src/components/announcement_editor/announcement_editor.js index 79a03afe1..6d22ac1fd 100644 --- a/src/components/announcement_editor/announcement_editor.js +++ b/src/components/announcement_editor/announcement_editor.js @@ -2,12 +2,12 @@ import Checkbox from '../checkbox/checkbox.vue' const AnnouncementEditor = { components: { - Checkbox + Checkbox, }, props: { announcement: Object, - disabled: Boolean - } + disabled: Boolean, + }, } export default AnnouncementEditor diff --git a/src/components/announcements_page/announcements_page.js b/src/components/announcements_page/announcements_page.js index 9ce0b45f5..6d1671e83 100644 --- a/src/components/announcements_page/announcements_page.js +++ b/src/components/announcements_page/announcements_page.js @@ -6,54 +6,60 @@ import { useAnnouncementsStore } from 'src/stores/announcements' const AnnouncementsPage = { components: { Announcement, - AnnouncementEditor + AnnouncementEditor, }, - data () { + data() { return { newAnnouncement: { content: '', startsAt: undefined, endsAt: undefined, - allDay: false + allDay: false, }, posting: false, - error: undefined + error: undefined, } }, - mounted () { + mounted() { useAnnouncementsStore().fetchAnnouncements() }, computed: { ...mapState({ - currentUser: state => state.users.currentUser + currentUser: (state) => state.users.currentUser, }), - announcements () { + announcements() { return useAnnouncementsStore().announcements }, - canPostAnnouncement () { - return this.currentUser && this.currentUser.privileges.includes('announcements_manage_announcements') - } + canPostAnnouncement() { + return ( + this.currentUser && + this.currentUser.privileges.includes( + 'announcements_manage_announcements', + ) + ) + }, }, methods: { - postAnnouncement () { + postAnnouncement() { this.posting = true - useAnnouncementsStore().postAnnouncement(this.newAnnouncement) + useAnnouncementsStore() + .postAnnouncement(this.newAnnouncement) .then(() => { this.newAnnouncement.content = '' this.startsAt = undefined this.endsAt = undefined }) - .catch(error => { + .catch((error) => { this.error = error.error }) .finally(() => { this.posting = false }) }, - clearError () { + clearError() { this.error = undefined - } - } + }, + }, } export default AnnouncementsPage diff --git a/src/components/async_component_error/async_component_error.vue b/src/components/async_component_error/async_component_error.vue index 2ff8974c1..baf430950 100644 --- a/src/components/async_component_error/async_component_error.vue +++ b/src/components/async_component_error/async_component_error.vue @@ -21,10 +21,10 @@ export default { emits: ['resetAsyncComponent'], methods: { - retry () { + retry() { this.$emit('resetAsyncComponent') - } - } + }, + }, } diff --git a/src/components/attachment/attachment.js b/src/components/attachment/attachment.js index 21d793930..22228d4af 100644 --- a/src/components/attachment/attachment.js +++ b/src/components/attachment/attachment.js @@ -16,7 +16,7 @@ import { faSearchPlus, faTrashAlt, faPencilAlt, - faAlignRight + faAlignRight, } from '@fortawesome/free-solid-svg-icons' import { useMediaViewerStore } from 'src/stores/media_viewer' @@ -31,7 +31,7 @@ library.add( faSearchPlus, faTrashAlt, faPencilAlt, - faAlignRight + faAlignRight, ) const Attachment = { @@ -46,72 +46,74 @@ const Attachment = { 'remove', 'shiftUp', 'shiftDn', - 'edit' + 'edit', ], - data () { + data() { return { localDescription: this.description || this.attachment.description, nsfwImage: this.$store.state.instance.nsfwCensorImage || nsfwImage, hideNsfwLocal: this.$store.getters.mergedConfig.hideNsfw, preloadImage: this.$store.getters.mergedConfig.preloadImage, loading: false, - img: fileTypeService.fileType(this.attachment.mimetype) === 'image' && document.createElement('img'), + img: + fileTypeService.fileType(this.attachment.mimetype) === 'image' && + document.createElement('img'), modalOpen: false, showHidden: false, flashLoaded: false, - showDescription: false + showDescription: false, } }, components: { Flash, StillImage, - VideoAttachment + VideoAttachment, }, computed: { - classNames () { + classNames() { return [ { '-loading': this.loading, '-nsfw-placeholder': this.hidden, '-editable': this.edit !== undefined, - '-compact': this.compact + '-compact': this.compact, }, '-type-' + this.type, this.size && '-size-' + this.size, - `-${this.useContainFit ? 'contain' : 'cover'}-fit` + `-${this.useContainFit ? 'contain' : 'cover'}-fit`, ] }, - usePlaceholder () { + usePlaceholder() { return this.size === 'hide' }, - useContainFit () { + useContainFit() { return this.$store.getters.mergedConfig.useContainFit }, - placeholderName () { + placeholderName() { if (this.attachment.description === '' || !this.attachment.description) { return this.type.toUpperCase() } return this.attachment.description }, - placeholderIconClass () { + placeholderIconClass() { if (this.type === 'image') return 'image' if (this.type === 'video') return 'video' if (this.type === 'audio') return 'music' return 'file' }, - referrerpolicy () { + referrerpolicy() { return this.$store.state.instance.mediaProxyAvailable ? '' : 'no-referrer' }, - type () { + type() { return fileTypeService.fileType(this.attachment.mimetype) }, - hidden () { + hidden() { return this.nsfw && this.hideNsfwLocal && !this.showHidden }, - isEmpty () { - return (this.type === 'html' && !this.attachment.oembed) + isEmpty() { + return this.type === 'html' && !this.attachment.oembed }, - useModal () { + useModal() { let modalTypes = [] switch (this.size) { case 'hide': @@ -126,26 +128,26 @@ const Attachment = { } return modalTypes.includes(this.type) }, - videoTag () { + videoTag() { return this.useModal ? 'button' : 'span' }, - ...mapGetters(['mergedConfig']) + ...mapGetters(['mergedConfig']), }, watch: { - 'attachment.description' (newVal) { + 'attachment.description'(newVal) { this.localDescription = newVal }, - localDescription (newVal) { + localDescription(newVal) { this.onEdit(newVal) - } + }, }, methods: { - linkClicked ({ target }) { + linkClicked({ target }) { if (target.tagName === 'A') { window.open(target.href, '_blank') } }, - openModal () { + openModal() { if (this.useModal) { this.$emit('setMedia') useMediaViewerStore().setCurrentMedia(this.attachment) @@ -153,34 +155,35 @@ const Attachment = { window.open(this.attachment.url) } }, - openModalForce () { + openModalForce() { this.$emit('setMedia') useMediaViewerStore().setCurrentMedia(this.attachment) }, - onEdit (event) { + onEdit(event) { this.edit && this.edit(this.attachment, event) }, - onRemove () { + onRemove() { this.remove && this.remove(this.attachment) }, - onShiftUp () { + onShiftUp() { this.shiftUp && this.shiftUp(this.attachment) }, - onShiftDn () { + onShiftDn() { this.shiftDn && this.shiftDn(this.attachment) }, - stopFlash () { + stopFlash() { this.$refs.flash.closePlayer() }, - setFlashLoaded (event) { + setFlashLoaded(event) { this.flashLoaded = event }, - toggleDescription () { + toggleDescription() { this.showDescription = !this.showDescription }, - toggleHidden (event) { + toggleHidden(event) { if ( - (this.mergedConfig.useOneClickNsfw && !this.showHidden) && + this.mergedConfig.useOneClickNsfw && + !this.showHidden && (this.type !== 'video' || this.mergedConfig.playVideosInModal) ) { this.openModal(event) @@ -201,12 +204,12 @@ const Attachment = { this.showHidden = !this.showHidden } }, - onImageLoad (image) { + onImageLoad(image) { const width = image.naturalWidth const height = image.naturalHeight this.$emit('naturalSizeLoad', { id: this.attachment.id, width, height }) - } - } + }, + }, } export default Attachment diff --git a/src/components/auth_form/auth_form.js b/src/components/auth_form/auth_form.js index 243cbf574..5ebf5c418 100644 --- a/src/components/auth_form/auth_form.js +++ b/src/components/auth_form/auth_form.js @@ -7,22 +7,26 @@ import { useAuthFlowStore } from 'src/stores/auth_flow' const AuthForm = { name: 'AuthForm', - render () { + render() { return h(resolveComponent(this.authForm)) }, computed: { - authForm () { - if (this.requiredTOTP) { return 'MFATOTPForm' } - if (this.requiredRecovery) { return 'MFARecoveryForm' } + authForm() { + if (this.requiredTOTP) { + return 'MFATOTPForm' + } + if (this.requiredRecovery) { + return 'MFARecoveryForm' + } return 'LoginForm' }, - ...mapState(useAuthFlowStore, ['requiredTOTP', 'requiredRecovery']) + ...mapState(useAuthFlowStore, ['requiredTOTP', 'requiredRecovery']), }, components: { MFARecoveryForm, MFATOTPForm, - LoginForm - } + LoginForm, + }, } export default AuthForm diff --git a/src/components/autosuggest/autosuggest.js b/src/components/autosuggest/autosuggest.js index f58f17bb6..37b7706db 100644 --- a/src/components/autosuggest/autosuggest.js +++ b/src/components/autosuggest/autosuggest.js @@ -2,51 +2,55 @@ const debounceMilliseconds = 500 export default { props: { - query: { // function to query results and return a promise + query: { + // function to query results and return a promise type: Function, - required: true + required: true, }, - filter: { // function to filter results in real time - type: Function + filter: { + // function to filter results in real time + type: Function, }, placeholder: { type: String, - default: 'Search...' - } + default: 'Search...', + }, }, - data () { + data() { return { term: '', timeout: null, results: [], - resultsVisible: false + resultsVisible: false, } }, computed: { - filtered () { + filtered() { return this.filter ? this.filter(this.results) : this.results - } + }, }, watch: { - term (val) { + term(val) { this.fetchResults(val) - } + }, }, methods: { - fetchResults (term) { + fetchResults(term) { clearTimeout(this.timeout) this.timeout = setTimeout(() => { this.results = [] if (term) { - this.query(term).then((results) => { this.results = results }) + this.query(term).then((results) => { + this.results = results + }) } }, debounceMilliseconds) }, - onInputClick () { + onInputClick() { this.resultsVisible = true }, - onClickOutside () { + onClickOutside() { this.resultsVisible = false - } - } + }, + }, } diff --git a/src/components/avatar_list/avatar_list.js b/src/components/avatar_list/avatar_list.js index 9b6301b22..7ef42cf2b 100644 --- a/src/components/avatar_list/avatar_list.js +++ b/src/components/avatar_list/avatar_list.js @@ -4,18 +4,22 @@ import generateProfileLink from 'src/services/user_profile_link_generator/user_p const AvatarList = { props: ['users'], computed: { - slicedUsers () { + slicedUsers() { return this.users ? this.users.slice(0, 15) : [] - } + }, }, components: { - UserAvatar + UserAvatar, }, methods: { - userProfileLink (user) { - return generateProfileLink(user.id, user.screen_name, this.$store.state.instance.restrictedNicknames) - } - } + userProfileLink(user) { + return generateProfileLink( + user.id, + user.screen_name, + this.$store.state.instance.restrictedNicknames, + ) + }, + }, } export default AvatarList diff --git a/src/components/badge.style.js b/src/components/badge.style.js index 0697cac6c..ab28429cc 100644 --- a/src/components/badge.style.js +++ b/src/components/badge.style.js @@ -1,30 +1,27 @@ export default { name: 'Badge', selector: '.badge', - validInnerComponents: [ - 'Text', - 'Icon' - ], + validInnerComponents: ['Text', 'Icon'], variants: { - notification: '.-notification' + notification: '.-notification', }, defaultRules: [ { component: 'Root', directives: { - '--badgeNotification': 'color | --cRed' - } + '--badgeNotification': 'color | --cRed', + }, }, { directives: { - background: '--cGreen' - } + background: '--cGreen', + }, }, { variant: 'notification', directives: { - background: '--cRed' - } - } - ] + background: '--cRed', + }, + }, + ], } diff --git a/src/components/basic_user_card/basic_user_card.js b/src/components/basic_user_card/basic_user_card.js index 31de2d75f..018c80065 100644 --- a/src/components/basic_user_card/basic_user_card.js +++ b/src/components/basic_user_card/basic_user_card.js @@ -5,20 +5,22 @@ import RichContent from 'src/components/rich_content/rich_content.jsx' import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' const BasicUserCard = { - props: [ - 'user' - ], + props: ['user'], components: { UserPopover, UserAvatar, RichContent, - UserLink + UserLink, }, methods: { - userProfileLink (user) { - return generateProfileLink(user.id, user.screen_name, this.$store.state.instance.restrictedNicknames) - } - } + userProfileLink(user) { + return generateProfileLink( + user.id, + user.screen_name, + this.$store.state.instance.restrictedNicknames, + ) + }, + }, } export default BasicUserCard diff --git a/src/components/block_card/block_card.js b/src/components/block_card/block_card.js index 9a618db3f..754c2fb12 100644 --- a/src/components/block_card/block_card.js +++ b/src/components/block_card/block_card.js @@ -5,42 +5,44 @@ import BasicUserCard from '../basic_user_card/basic_user_card.vue' const BlockCard = { props: ['userId'], computed: { - user () { + user() { return this.$store.getters.findUser(this.userId) }, - relationship () { + relationship() { return this.$store.getters.relationship(this.userId) }, - blocked () { + blocked() { return this.relationship.blocking }, - blockExpiryAvailable () { + blockExpiryAvailable() { return this.user.block_expires_at !== undefined }, - blockExpiry () { + blockExpiry() { return this.user.block_expires_at == null ? this.$t('user_card.block_expires_forever') - : this.$t('user_card.block_expires_at', [new Date(this.user.mute_expires_at).toLocaleString()]) + : this.$t('user_card.block_expires_at', [ + new Date(this.user.mute_expires_at).toLocaleString(), + ]) }, ...mapState({ - blockExpirationSupported: state => state.instance.blockExpiration, - }) + blockExpirationSupported: (state) => state.instance.blockExpiration, + }), }, components: { - BasicUserCard + BasicUserCard, }, methods: { - unblockUser () { + unblockUser() { this.$store.dispatch('unblockUser', this.user.id) }, - blockUser () { + blockUser() { if (this.blockExpirationSupported) { this.$refs.timedBlockDialog.optionallyPrompt() } else { this.$store.dispatch('blockUser', { id: this.user.id }) } - } - } + }, + }, } export default BlockCard diff --git a/src/components/bookmark_folder_card/bookmark_folder_card.js b/src/components/bookmark_folder_card/bookmark_folder_card.js index bf274d9d7..37b3f2e5e 100644 --- a/src/components/bookmark_folder_card/bookmark_folder_card.js +++ b/src/components/bookmark_folder_card/bookmark_folder_card.js @@ -1,22 +1,15 @@ import { library } from '@fortawesome/fontawesome-svg-core' -import { - faEllipsisH -} from '@fortawesome/free-solid-svg-icons' +import { faEllipsisH } from '@fortawesome/free-solid-svg-icons' -library.add( - faEllipsisH -) +library.add(faEllipsisH) const BookmarkFolderCard = { - props: [ - 'folder', - 'allBookmarks' - ], + props: ['folder', 'allBookmarks'], computed: { - firstLetter () { + firstLetter() { return this.folder ? this.folder.name[0] : null - } - } + }, + }, } export default BookmarkFolderCard diff --git a/src/components/bookmark_folder_edit/bookmark_folder_edit.js b/src/components/bookmark_folder_edit/bookmark_folder_edit.js index bb96585bf..822f47f3c 100644 --- a/src/components/bookmark_folder_edit/bookmark_folder_edit.js +++ b/src/components/bookmark_folder_edit/bookmark_folder_edit.js @@ -4,7 +4,7 @@ import { useInterfaceStore } from 'src/stores/interface' import { useBookmarkFoldersStore } from 'src/stores/bookmark_folders' const BookmarkFolderEdit = { - data () { + data() { return { name: '', nameDraft: '', @@ -13,54 +13,59 @@ const BookmarkFolderEdit = { emojiDraft: '', emojiUrlDraft: null, emojiPickerExpanded: false, - reallyDelete: false + reallyDelete: false, } }, components: { - EmojiPicker + EmojiPicker, }, - created () { + created() { if (!this.id) return const credentials = this.$store.state.users.currentUser.credentials - apiService.fetchBookmarkFolders({ credentials }) - .then((folders) => { - const folder = folders.find(folder => folder.id === this.id) - if (!folder) return + apiService.fetchBookmarkFolders({ credentials }).then((folders) => { + const folder = folders.find((folder) => folder.id === this.id) + if (!folder) return - this.nameDraft = this.name = folder.name - this.emojiDraft = this.emoji = folder.emoji - this.emojiUrlDraft = this.emojiUrl = folder.emoji_url - }) + this.nameDraft = this.name = folder.name + this.emojiDraft = this.emoji = folder.emoji + this.emojiUrlDraft = this.emojiUrl = folder.emoji_url + }) }, computed: { - id () { + id() { return this.$route.params.id - } + }, }, methods: { - selectEmoji (event) { + selectEmoji(event) { this.emojiDraft = event.insertion this.emojiUrlDraft = event.insertionUrl }, - showEmojiPicker () { + showEmojiPicker() { if (!this.emojiPickerExpanded) { this.$refs.picker.showPicker() } }, - onShowPicker () { + onShowPicker() { this.emojiPickerExpanded = true }, - onClosePicker () { + onClosePicker() { this.emojiPickerExpanded = false }, - updateFolder () { - useBookmarkFoldersStore().updateBookmarkFolder({ folderId: this.id, name: this.nameDraft, emoji: this.emojiDraft }) + updateFolder() { + useBookmarkFoldersStore() + .updateBookmarkFolder({ + folderId: this.id, + name: this.nameDraft, + emoji: this.emojiDraft, + }) .then(() => { this.$router.push({ name: 'bookmark-folders' }) }) }, - createFolder () { - useBookmarkFoldersStore().createBookmarkFolder({ name: this.nameDraft, emoji: this.emojiDraft }) + createFolder() { + useBookmarkFoldersStore() + .createBookmarkFolder({ name: this.nameDraft, emoji: this.emojiDraft }) .then(() => { this.$router.push({ name: 'bookmark-folders' }) }) @@ -68,15 +73,15 @@ const BookmarkFolderEdit = { useInterfaceStore().pushGlobalNotice({ messageKey: 'bookmark_folders.error', messageArgs: [e.message], - level: 'error' + level: 'error', }) }) }, - deleteFolder () { + deleteFolder() { useBookmarkFoldersStore().deleteBookmarkFolder({ folderId: this.id }) this.$router.push({ name: 'bookmark-folders' }) - } - } + }, + }, } export default BookmarkFolderEdit diff --git a/src/components/bookmark_folders/bookmark_folders.js b/src/components/bookmark_folders/bookmark_folders.js index 096f3769d..15d2c925c 100644 --- a/src/components/bookmark_folders/bookmark_folders.js +++ b/src/components/bookmark_folders/bookmark_folders.js @@ -2,27 +2,27 @@ import BookmarkFolderCard from '../bookmark_folder_card/bookmark_folder_card.vue import { useBookmarkFoldersStore } from 'src/stores/bookmark_folders' const BookmarkFolders = { - data () { + data() { return { - isNew: false + isNew: false, } }, components: { - BookmarkFolderCard + BookmarkFolderCard, }, computed: { - bookmarkFolders () { + bookmarkFolders() { return useBookmarkFoldersStore().allFolders - } + }, }, methods: { - cancelNewFolder () { + cancelNewFolder() { this.isNew = false }, - newFolder () { + newFolder() { this.isNew = true - } - } + }, + }, } export default BookmarkFolders diff --git a/src/components/bookmark_folders_menu/bookmark_folders_menu_content.js b/src/components/bookmark_folders_menu/bookmark_folders_menu_content.js index dc46b91b3..944484b74 100644 --- a/src/components/bookmark_folders_menu/bookmark_folders_menu_content.js +++ b/src/components/bookmark_folders_menu/bookmark_folders_menu_content.js @@ -4,17 +4,15 @@ import { getBookmarkFolderEntries } from 'src/components/navigation/filter.js' import { useBookmarkFoldersStore } from 'src/stores/bookmark_folders' export const BookmarkFoldersMenuContent = { - props: [ - 'showPin' - ], + props: ['showPin'], components: { - NavigationEntry + NavigationEntry, }, computed: { ...mapState(useBookmarkFoldersStore, { - folders: getBookmarkFolderEntries - }) - } + folders: getBookmarkFolderEntries, + }), + }, } export default BookmarkFoldersMenuContent diff --git a/src/components/bookmark_timeline/bookmark_timeline.js b/src/components/bookmark_timeline/bookmark_timeline.js index 9571d630f..a241b6ac7 100644 --- a/src/components/bookmark_timeline/bookmark_timeline.js +++ b/src/components/bookmark_timeline/bookmark_timeline.js @@ -1,32 +1,38 @@ import Timeline from '../timeline/timeline.vue' const Bookmarks = { - created () { + created() { this.$store.commit('clearTimeline', { timeline: 'bookmarks' }) - this.$store.dispatch('startFetchingTimeline', { timeline: 'bookmarks', bookmarkFolderId: this.folderId || null }) + this.$store.dispatch('startFetchingTimeline', { + timeline: 'bookmarks', + bookmarkFolderId: this.folderId || null, + }) }, components: { - Timeline + Timeline, }, computed: { - folderId () { + folderId() { return this.$route.params.id }, - timeline () { + timeline() { return this.$store.state.statuses.timelines.bookmarks - } + }, }, watch: { - folderId () { + folderId() { this.$store.commit('clearTimeline', { timeline: 'bookmarks' }) this.$store.dispatch('stopFetchingTimeline', 'bookmarks') - this.$store.dispatch('startFetchingTimeline', { timeline: 'bookmarks', bookmarkFolderId: this.folderId || null }) - } + this.$store.dispatch('startFetchingTimeline', { + timeline: 'bookmarks', + bookmarkFolderId: this.folderId || null, + }) + }, }, - unmounted () { + unmounted() { this.$store.commit('clearTimeline', { timeline: 'bookmarks' }) this.$store.dispatch('stopFetchingTimeline', 'bookmarks') - } + }, } export default Bookmarks diff --git a/src/components/border.style.js b/src/components/border.style.js index 7f2c30163..e7cc31c57 100644 --- a/src/components/border.style.js +++ b/src/components/border.style.js @@ -6,8 +6,8 @@ export default { { directives: { textColor: '$mod(--parent 10)', - textAuto: 'no-auto' - } - } - ] + textAuto: 'no-auto', + }, + }, + ], } diff --git a/src/components/bubble_timeline/bubble_timeline.js b/src/components/bubble_timeline/bubble_timeline.js index 6f73dd2b8..f4d3994db 100644 --- a/src/components/bubble_timeline/bubble_timeline.js +++ b/src/components/bubble_timeline/bubble_timeline.js @@ -1,18 +1,19 @@ import Timeline from '../timeline/timeline.vue' const BubbleTimeline = { components: { - Timeline + Timeline, }, computed: { - timeline () { return this.$store.state.statuses.timelines.bubble } + timeline() { + return this.$store.state.statuses.timelines.bubble + }, }, - created () { + created() { this.$store.dispatch('startFetchingTimeline', { timeline: 'bubble' }) }, - unmounted () { + unmounted() { this.$store.dispatch('stopFetchingTimeline', 'bubble') - } - + }, } export default BubbleTimeline diff --git a/src/components/button.style.js b/src/components/button.style.js index 5cffefd91..3fb308b89 100644 --- a/src/components/button.style.js +++ b/src/components/button.style.js @@ -12,25 +12,22 @@ export default { focused: ':focus-within', pressed: ':active', hover: ':is(:hover, :focus-visible):not(:disabled)', - disabled: ':disabled' + disabled: ':disabled', }, // Variants are mutually exclusive, each component implicitly has "normal" variant, and all other variants inherit from it. variants: { // Variants save on computation time since adding new variant just adds one more "set". // normal: '', // you can override normal variant, it will be appenended to the main class danger: '.-danger', - transparent: '.-transparent' + transparent: '.-transparent', // Overall the compuation difficulty is N*((1/6)M^3+M) where M is number of distinct states and N is number of variants. // This (currently) is further multipled by number of places where component can exist. }, editor: { - aspect: '2 / 1' + aspect: '2 / 1', }, // This lists all other components that can possibly exist within one. Recursion is currently not supported (and probably won't be supported ever). - validInnerComponents: [ - 'Text', - 'Icon' - ], + validInnerComponents: ['Text', 'Icon'], // Default rules, used as "default theme", essentially. defaultRules: [ { @@ -39,9 +36,11 @@ export default { '--buttonDefaultHoverGlow': 'shadow | 0 0 1 2 --text / 0.4', '--buttonDefaultFocusGlow': 'shadow | 0 0 1 2 --link / 0.5', '--buttonDefaultShadow': 'shadow | 0 0 2 #000000', - '--buttonDefaultBevel': 'shadow | $borderSide(#FFFFFF top 0.2 1), $borderSide(#000000 bottom 0.2 1)', - '--buttonPressedBevel': 'shadow | inset 0 0 4 #000000, $borderSide(#FFFFFF bottom 0.2 1), $borderSide(#000000 top 0.2 1)' - } + '--buttonDefaultBevel': + 'shadow | $borderSide(#FFFFFF top 0.2 1), $borderSide(#000000 bottom 0.2 1)', + '--buttonPressedBevel': + 'shadow | inset 0 0 4 #000000, $borderSide(#FFFFFF bottom 0.2 1), $borderSide(#000000 top 0.2 1)', + }, }, { // component: 'Button', // no need to specify components every time unless you're specifying how other component should look @@ -49,128 +48,128 @@ export default { directives: { background: '--fg', shadow: ['--buttonDefaultShadow', '--buttonDefaultBevel'], - roundness: 3 - } + roundness: 3, + }, }, { variant: 'danger', directives: { - background: '--cRed' - } + background: '--cRed', + }, }, { variant: 'transparent', directives: { - opacity: 0.5 - } + opacity: 0.5, + }, }, { component: 'Text', parent: { component: 'Button', - variant: 'transparent' + variant: 'transparent', }, directives: { - textColor: '--text' - } + textColor: '--text', + }, }, { component: 'Icon', parent: { component: 'Button', - variant: 'transparent' + variant: 'transparent', }, directives: { - textColor: '--text' - } + textColor: '--text', + }, }, { state: ['hover'], directives: { - shadow: ['--buttonDefaultHoverGlow', '--buttonDefaultBevel'] - } + shadow: ['--buttonDefaultHoverGlow', '--buttonDefaultBevel'], + }, }, { state: ['focused'], directives: { - shadow: ['--buttonDefaultFocusGlow', '--buttonDefaultBevel'] - } + shadow: ['--buttonDefaultFocusGlow', '--buttonDefaultBevel'], + }, }, { state: ['pressed'], directives: { - shadow: ['--buttonDefaultShadow', '--buttonPressedBevel'] - } + shadow: ['--buttonDefaultShadow', '--buttonPressedBevel'], + }, }, { state: ['pressed', 'hover'], directives: { - shadow: ['--buttonPressedBevel', '--buttonDefaultHoverGlow'] - } + shadow: ['--buttonPressedBevel', '--buttonDefaultHoverGlow'], + }, }, { state: ['toggled'], directives: { background: '--accent,-24.2', - shadow: ['--buttonDefaultShadow', '--buttonPressedBevel'] - } + shadow: ['--buttonDefaultShadow', '--buttonPressedBevel'], + }, }, { state: ['toggled', 'hover'], directives: { background: '--accent,-24.2', - shadow: ['--buttonDefaultHoverGlow', '--buttonPressedBevel'] - } + shadow: ['--buttonDefaultHoverGlow', '--buttonPressedBevel'], + }, }, { state: ['toggled', 'focused'], directives: { background: '--accent,-24.2', - shadow: ['--buttonDefaultHoverGlow', '--buttonPressedBevel'] - } + shadow: ['--buttonDefaultHoverGlow', '--buttonPressedBevel'], + }, }, { state: ['toggled', 'hover', 'focused'], directives: { background: '--accent,-24.2', - shadow: ['--buttonDefaultHoverGlow', '--buttonPressedBevel'] - } + shadow: ['--buttonDefaultHoverGlow', '--buttonPressedBevel'], + }, }, { state: ['toggled', 'disabled'], directives: { background: '$blend(--accent 0.25 --parent)', - shadow: ['--buttonPressedBevel'] - } + shadow: ['--buttonPressedBevel'], + }, }, { state: ['disabled'], directives: { background: '$blend(--inheritedBackground 0.25 --parent)', - shadow: ['--buttonDefaultBevel'] - } + shadow: ['--buttonDefaultBevel'], + }, }, { component: 'Text', parent: { component: 'Button', - state: ['disabled'] + state: ['disabled'], }, directives: { textOpacity: 0.25, - textOpacityMode: 'blend' - } + textOpacityMode: 'blend', + }, }, { component: 'Icon', parent: { component: 'Button', - state: ['disabled'] + state: ['disabled'], }, directives: { textOpacity: 0.25, - textOpacityMode: 'blend' - } - } - ] + textOpacityMode: 'blend', + }, + }, + ], } diff --git a/src/components/button_unstyled.style.js b/src/components/button_unstyled.style.js index 9e1a2ca90..c35fb8f69 100644 --- a/src/components/button_unstyled.style.js +++ b/src/components/button_unstyled.style.js @@ -7,91 +7,86 @@ export default { toggled: '.toggled', disabled: ':disabled', hover: ':is(:hover, :focus-visible):not(:disabled)', - focused: ':focus-within:not(:is(:focus-visible))' + focused: ':focus-within:not(:is(:focus-visible))', }, - validInnerComponents: [ - 'Text', - 'Link', - 'Icon', - 'Badge' - ], + validInnerComponents: ['Text', 'Link', 'Icon', 'Badge'], defaultRules: [ { directives: { - shadow: [] - } + shadow: [], + }, }, { component: 'Icon', parent: { component: 'ButtonUnstyled', - state: ['hover'] + state: ['hover'], }, directives: { - textColor: '--parent--text' - } + textColor: '--parent--text', + }, }, { component: 'Icon', parent: { component: 'ButtonUnstyled', - state: ['toggled'] + state: ['toggled'], }, directives: { - textColor: '--parent--text' - } + textColor: '--parent--text', + }, }, { component: 'Icon', parent: { component: 'ButtonUnstyled', - state: ['toggled', 'hover'] + state: ['toggled', 'hover'], }, directives: { - textColor: '--parent--text' - } + textColor: '--parent--text', + }, }, { component: 'Icon', parent: { component: 'ButtonUnstyled', - state: ['toggled', 'focused'] + state: ['toggled', 'focused'], }, directives: { - textColor: '--parent--text' - } + textColor: '--parent--text', + }, }, { component: 'Icon', parent: { component: 'ButtonUnstyled', - state: ['toggled', 'focused', 'hover'] + state: ['toggled', 'focused', 'hover'], }, directives: { - textColor: '--parent--text' - } + textColor: '--parent--text', + }, }, { component: 'Text', parent: { component: 'ButtonUnstyled', - state: ['disabled'] + state: ['disabled'], }, directives: { textOpacity: 0.25, - textOpacityMode: 'blend' - } + textOpacityMode: 'blend', + }, }, { component: 'Icon', parent: { component: 'ButtonUnstyled', - state: ['disabled'] + state: ['disabled'], }, directives: { textOpacity: 0.25, - textOpacityMode: 'blend' - } - } - ] + textOpacityMode: 'blend', + }, + }, + ], } diff --git a/src/components/chat/chat.js b/src/components/chat/chat.js index 56f389e60..77a80c8b9 100644 --- a/src/components/chat/chat.js +++ b/src/components/chat/chat.js @@ -7,19 +7,18 @@ import PostStatusForm from '../post_status_form/post_status_form.vue' import ChatTitle from '../chat_title/chat_title.vue' import chatService from '../../services/chat_service/chat_service.js' import { promiseInterval } from '../../services/promise_interval/promise_interval.js' -import { getScrollPosition, getNewTopPosition, isBottomedOut, isScrollable } from './chat_layout_utils.js' -import { library } from '@fortawesome/fontawesome-svg-core' import { - faChevronDown, - faChevronLeft -} from '@fortawesome/free-solid-svg-icons' + getScrollPosition, + getNewTopPosition, + isBottomedOut, + isScrollable, +} from './chat_layout_utils.js' +import { library } from '@fortawesome/fontawesome-svg-core' +import { faChevronDown, faChevronLeft } from '@fortawesome/free-solid-svg-icons' import { buildFakeMessage } from '../../services/chat_utils/chat_utils.js' import { useInterfaceStore } from 'src/stores/interface.js' -library.add( - faChevronDown, - faChevronLeft -) +library.add(faChevronDown, faChevronLeft) const BOTTOMED_OUT_OFFSET = 10 const JUMP_TO_BOTTOM_BUTTON_VISIBILITY_OFFSET = 10 @@ -31,78 +30,95 @@ const Chat = { components: { ChatMessage, ChatTitle, - PostStatusForm + PostStatusForm, }, - data () { + data() { return { jumpToBottomButtonVisible: false, hoveredMessageChainId: undefined, lastScrollPosition: {}, scrollableContainerHeight: '100%', errorLoadingChat: false, - messageRetriers: {} + messageRetriers: {}, } }, - created () { + created() { this.startFetching() window.addEventListener('resize', this.handleResize) }, - mounted () { + mounted() { window.addEventListener('scroll', this.handleScroll) if (typeof document.hidden !== 'undefined') { - document.addEventListener('visibilitychange', this.handleVisibilityChange, false) + document.addEventListener( + 'visibilitychange', + this.handleVisibilityChange, + false, + ) } this.$nextTick(() => { this.handleResize() }) }, - unmounted () { + unmounted() { window.removeEventListener('scroll', this.handleScroll) window.removeEventListener('resize', this.handleResize) - if (typeof document.hidden !== 'undefined') document.removeEventListener('visibilitychange', this.handleVisibilityChange, false) + if (typeof document.hidden !== 'undefined') + document.removeEventListener( + 'visibilitychange', + this.handleVisibilityChange, + false, + ) this.$store.dispatch('clearCurrentChat') }, computed: { - recipient () { + recipient() { return this.currentChat && this.currentChat.account }, - recipientId () { + recipientId() { return this.$route.params.recipient_id }, - formPlaceholder () { + formPlaceholder() { if (this.recipient) { - return this.$t('chats.message_user', { nickname: this.recipient.screen_name_ui }) + return this.$t('chats.message_user', { + nickname: this.recipient.screen_name_ui, + }) } else { return '' } }, - chatViewItems () { + chatViewItems() { return chatService.getView(this.currentChatMessageService) }, - newMessageCount () { - return this.currentChatMessageService && this.currentChatMessageService.newMessageCount + newMessageCount() { + return ( + this.currentChatMessageService && + this.currentChatMessageService.newMessageCount + ) }, - streamingEnabled () { - return this.mergedConfig.useStreamingApi && this.mastoUserSocketStatus === WSConnectionStatus.JOINED + streamingEnabled() { + return ( + this.mergedConfig.useStreamingApi && + this.mastoUserSocketStatus === WSConnectionStatus.JOINED + ) }, ...mapGetters([ 'currentChat', 'currentChatMessageService', 'findOpenedChatByRecipientId', - 'mergedConfig' + 'mergedConfig', ]), ...mapPiniaState(useInterfaceStore, { - mobileLayout: store => store.layoutType === 'mobile' + mobileLayout: (store) => store.layoutType === 'mobile', }), ...mapState({ - backendInteractor: state => state.api.backendInteractor, - mastoUserSocketStatus: state => state.api.mastoUserSocketStatus, - currentUser: state => state.users.currentUser - }) + backendInteractor: (state) => state.api.backendInteractor, + mastoUserSocketStatus: (state) => state.api.mastoUserSocketStatus, + currentUser: (state) => state.users.currentUser, + }), }, watch: { - chatViewItems () { + chatViewItems() { // We don't want to scroll to the bottom on a new message when the user is viewing older messages. // Therefore we need to know whether the scroll position was at the bottom before the DOM update. const bottomedOutBeforeUpdate = this.bottomedOut(BOTTOMED_OUT_OFFSET) @@ -115,23 +131,23 @@ const Chat = { $route: function () { this.startFetching() }, - mastoUserSocketStatus (newValue) { + mastoUserSocketStatus(newValue) { if (newValue === WSConnectionStatus.JOINED) { this.fetchChat({ isFirstFetch: true }) } - } + }, }, methods: { // Used to animate the avatar near the first message of the message chain when any message belonging to the chain is hovered - onMessageHover ({ isHovered, messageChainId }) { + onMessageHover({ isHovered, messageChainId }) { this.hoveredMessageChainId = isHovered ? messageChainId : undefined }, - onFilesDropped () { + onFilesDropped() { this.$nextTick(() => { this.handleResize() }) }, - handleVisibilityChange () { + handleVisibilityChange() { this.$nextTick(() => { if (!document.hidden && this.bottomedOut(BOTTOMED_OUT_OFFSET)) { this.scrollDown({ forceRead: true }) @@ -139,7 +155,7 @@ const Chat = { }) }, // "Sticks" scroll to bottom instead of top, helps with OSK resizing the viewport - handleResize (opts = {}) { + handleResize(opts = {}) { const { delayed = false } = opts if (delayed) { @@ -160,40 +176,56 @@ const Chat = { this.lastScrollPosition = getScrollPosition() }) }, - scrollDown (options = {}) { + scrollDown(options = {}) { const { behavior = 'auto', forceRead = false } = options this.$nextTick(() => { - window.scrollTo({ top: document.documentElement.scrollHeight, behavior }) + window.scrollTo({ + top: document.documentElement.scrollHeight, + behavior, + }) }) if (forceRead) { this.readChat() } }, - readChat () { - if (!(this.currentChatMessageService && this.currentChatMessageService.maxId)) { return } - if (document.hidden) { return } + readChat() { + if ( + !( + this.currentChatMessageService && this.currentChatMessageService.maxId + ) + ) { + return + } + if (document.hidden) { + return + } const lastReadId = this.currentChatMessageService.maxId this.$store.dispatch('readChat', { id: this.currentChat.id, - lastReadId + lastReadId, }) }, - bottomedOut (offset) { + bottomedOut(offset) { return isBottomedOut(offset) }, - reachedTop () { + reachedTop() { return window.scrollY <= 0 }, - cullOlderCheck () { + cullOlderCheck() { window.setTimeout(() => { if (this.bottomedOut(JUMP_TO_BOTTOM_BUTTON_VISIBILITY_OFFSET)) { - this.$store.dispatch('cullOlderMessages', this.currentChatMessageService.chatId) + this.$store.dispatch( + 'cullOlderMessages', + this.currentChatMessageService.chatId, + ) } }, 5000) }, handleScroll: _.throttle(function () { this.lastScrollPosition = getScrollPosition() - if (!this.currentChat) { return } + if (!this.currentChat) { + return + } if (this.reachedTop()) { this.fetchChat({ maxId: this.currentChatMessageService.minId }) @@ -213,22 +245,27 @@ const Chat = { this.jumpToBottomButtonVisible = true } }, 200), - handleScrollUp (positionBeforeLoading) { + handleScrollUp(positionBeforeLoading) { const positionAfterLoading = getScrollPosition() window.scrollTo({ - top: getNewTopPosition(positionBeforeLoading, positionAfterLoading) + top: getNewTopPosition(positionBeforeLoading, positionAfterLoading), }) }, - fetchChat ({ isFirstFetch = false, fetchLatest = false, maxId }) { + fetchChat({ isFirstFetch = false, fetchLatest = false, maxId }) { const chatMessageService = this.currentChatMessageService - if (!chatMessageService) { return } - if (fetchLatest && this.streamingEnabled) { return } + if (!chatMessageService) { + return + } + if (fetchLatest && this.streamingEnabled) { + return + } const chatId = chatMessageService.chatId const fetchOlderMessages = !!maxId const sinceId = fetchLatest && chatMessageService.maxId - return this.backendInteractor.chatMessages({ id: chatId, maxId, sinceId }) + return this.backendInteractor + .chatMessages({ id: chatId, maxId, sinceId }) .then((messages) => { // Clear the current chat in case we're recovering from a ws connection loss. if (isFirstFetch) { @@ -236,28 +273,34 @@ const Chat = { } const positionBeforeUpdate = getScrollPosition() - this.$store.dispatch('addChatMessages', { chatId, messages }).then(() => { - this.$nextTick(() => { - if (fetchOlderMessages) { - this.handleScrollUp(positionBeforeUpdate) - } + this.$store + .dispatch('addChatMessages', { chatId, messages }) + .then(() => { + this.$nextTick(() => { + if (fetchOlderMessages) { + this.handleScrollUp(positionBeforeUpdate) + } - // In vertical screens, the first batch of fetched messages may not always take the - // full height of the scrollable container. - // If this is the case, we want to fetch the messages until the scrollable container - // is fully populated so that the user has the ability to scroll up and load the history. - if (!isScrollable() && messages.length > 0) { - this.fetchChat({ maxId: this.currentChatMessageService.minId }) - } + // In vertical screens, the first batch of fetched messages may not always take the + // full height of the scrollable container. + // If this is the case, we want to fetch the messages until the scrollable container + // is fully populated so that the user has the ability to scroll up and load the history. + if (!isScrollable() && messages.length > 0) { + this.fetchChat({ + maxId: this.currentChatMessageService.minId, + }) + } + }) }) - }) }) }, - async startFetching () { + async startFetching() { let chat = this.findOpenedChatByRecipientId(this.recipientId) if (!chat) { try { - chat = await this.backendInteractor.getOrCreateChat({ accountId: this.recipientId }) + chat = await this.backendInteractor.getOrCreateChat({ + accountId: this.recipientId, + }) } catch (e) { console.error('Error creating or getting a chat', e) this.errorLoadingChat = true @@ -271,13 +314,14 @@ const Chat = { this.doStartFetching() } }, - doStartFetching () { + doStartFetching() { this.$store.dispatch('startFetchingCurrentChat', { - fetcher: () => promiseInterval(() => this.fetchChat({ fetchLatest: true }), 5000) + fetcher: () => + promiseInterval(() => this.fetchChat({ fetchLatest: true }), 5000), }) this.fetchChat({ isFirstFetch: true }) }, - handleAttachmentPosting () { + handleAttachmentPosting() { this.$nextTick(() => { this.handleResize() // When the posting form size changes because of a media attachment, we need an extra resize @@ -285,11 +329,11 @@ const Chat = { this.scrollDown({ forceRead: true }) }) }, - sendMessage ({ status, media, idempotencyKey }) { + sendMessage({ status, media, idempotencyKey }) { const params = { id: this.currentChat.id, content: status, - idempotencyKey + idempotencyKey, } if (media[0]) { @@ -301,52 +345,72 @@ const Chat = { chatId: this.currentChat.id, content: status, userId: this.currentUser.id, - idempotencyKey + idempotencyKey, }) - this.$store.dispatch('addChatMessages', { - chatId: this.currentChat.id, - messages: [fakeMessage] - }).then(() => { - this.handleAttachmentPosting() - }) + this.$store + .dispatch('addChatMessages', { + chatId: this.currentChat.id, + messages: [fakeMessage], + }) + .then(() => { + this.handleAttachmentPosting() + }) - return this.doSendMessage({ params, fakeMessage, retriesLeft: MAX_RETRIES }) + return this.doSendMessage({ + params, + fakeMessage, + retriesLeft: MAX_RETRIES, + }) }, - doSendMessage ({ params, fakeMessage, retriesLeft = MAX_RETRIES }) { + doSendMessage({ params, fakeMessage, retriesLeft = MAX_RETRIES }) { if (retriesLeft <= 0) return - this.backendInteractor.sendChatMessage(params) - .then(data => { + this.backendInteractor + .sendChatMessage(params) + .then((data) => { this.$store.dispatch('addChatMessages', { chatId: this.currentChat.id, updateMaxId: false, - messages: [{ ...data, fakeId: fakeMessage.id }] + messages: [{ ...data, fakeId: fakeMessage.id }], }) return data }) - .catch(error => { + .catch((error) => { console.error('Error sending message', error) this.$store.dispatch('handleMessageError', { chatId: this.currentChat.id, fakeId: fakeMessage.id, - isRetry: retriesLeft !== MAX_RETRIES + isRetry: retriesLeft !== MAX_RETRIES, }) - if ((error.statusCode >= 500 && error.statusCode < 600) || error.message === 'Failed to fetch') { - this.messageRetriers[fakeMessage.id] = setTimeout(() => { - this.doSendMessage({ params, fakeMessage, retriesLeft: retriesLeft - 1 }) - }, 1000 * (2 ** (MAX_RETRIES - retriesLeft))) + if ( + (error.statusCode >= 500 && error.statusCode < 600) || + error.message === 'Failed to fetch' + ) { + this.messageRetriers[fakeMessage.id] = setTimeout( + () => { + this.doSendMessage({ + params, + fakeMessage, + retriesLeft: retriesLeft - 1, + }) + }, + 1000 * 2 ** (MAX_RETRIES - retriesLeft), + ) } return {} }) return Promise.resolve(fakeMessage) }, - goBack () { - this.$router.push({ name: 'chats', params: { username: this.currentUser.screen_name } }) - } - } + goBack() { + this.$router.push({ + name: 'chats', + params: { username: this.currentUser.screen_name }, + }) + }, + }, } export default Chat diff --git a/src/components/chat/chat.style.js b/src/components/chat/chat.style.js index 9ae2b7d71..55cf657c2 100644 --- a/src/components/chat/chat.style.js +++ b/src/components/chat/chat.style.js @@ -1,19 +1,13 @@ export default { name: 'Chat', selector: '.chat-message-list', - validInnerComponents: [ - 'Text', - 'Link', - 'Icon', - 'Avatar', - 'ChatMessage' - ], + validInnerComponents: ['Text', 'Link', 'Icon', 'Avatar', 'ChatMessage'], defaultRules: [ { directives: { background: '--bg', - blur: '5px' - } - } - ] + blur: '5px', + }, + }, + ], } diff --git a/src/components/chat/chat_layout_utils.js b/src/components/chat/chat_layout_utils.js index c187892d9..10d0a5e45 100644 --- a/src/components/chat/chat_layout_utils.js +++ b/src/components/chat/chat_layout_utils.js @@ -3,14 +3,17 @@ export const getScrollPosition = () => { return { scrollTop: window.scrollY, scrollHeight: document.documentElement.scrollHeight, - offsetHeight: window.innerHeight + offsetHeight: window.innerHeight, } } // A helper function that is used to keep the scroll position fixed as the new elements are added to the top // Takes two scroll positions, before and after the update. export const getNewTopPosition = (previousPosition, newPosition) => { - return previousPosition.scrollTop + (newPosition.scrollHeight - previousPosition.scrollHeight) + return ( + previousPosition.scrollTop + + (newPosition.scrollHeight - previousPosition.scrollHeight) + ) } export const isBottomedOut = (offset = 0) => { diff --git a/src/components/chat_list/chat_list.js b/src/components/chat_list/chat_list.js index 95708d1dd..65e8b02d6 100644 --- a/src/components/chat_list/chat_list.js +++ b/src/components/chat_list/chat_list.js @@ -7,31 +7,31 @@ const ChatList = { components: { ChatListItem, List, - ChatNew + ChatNew, }, computed: { ...mapState({ - currentUser: state => state.users.currentUser + currentUser: (state) => state.users.currentUser, }), - ...mapGetters(['sortedChatList']) + ...mapGetters(['sortedChatList']), }, - data () { + data() { return { - isNew: false + isNew: false, } }, - created () { + created() { this.$store.dispatch('fetchChats', { latest: true }) }, methods: { - cancelNewChat () { + cancelNewChat() { this.isNew = false this.$store.dispatch('fetchChats', { latest: true }) }, - newChat () { + newChat() { this.isNew = true - } - } + }, + }, } export default ChatList diff --git a/src/components/chat_list_item/chat_list_item.js b/src/components/chat_list_item/chat_list_item.js index 8f8c491f5..2fbc9bae8 100644 --- a/src/components/chat_list_item/chat_list_item.js +++ b/src/components/chat_list_item/chat_list_item.js @@ -8,24 +8,26 @@ import ChatTitle from '../chat_title/chat_title.vue' const ChatListItem = { name: 'ChatListItem', - props: [ - 'chat' - ], + props: ['chat'], components: { UserAvatar, AvatarList, Timeago, ChatTitle, - StatusBody + StatusBody, }, computed: { ...mapState({ - currentUser: state => state.users.currentUser + currentUser: (state) => state.users.currentUser, }), - attachmentInfo () { - if (this.chat.lastMessage.attachments.length === 0) { return } + attachmentInfo() { + if (this.chat.lastMessage.attachments.length === 0) { + return + } - const types = this.chat.lastMessage.attachments.map(file => fileType.fileType(file.mimetype)) + const types = this.chat.lastMessage.attachments.map((file) => + fileType.fileType(file.mimetype), + ) if (types.includes('video')) { return this.$t('file_type.video') } else if (types.includes('audio')) { @@ -36,34 +38,36 @@ const ChatListItem = { return this.$t('file_type.file') } }, - messageForStatusContent () { + messageForStatusContent() { const message = this.chat.lastMessage const messageEmojis = message ? message.emojis : [] const isYou = message && message.account_id === this.currentUser.id - const content = message ? (this.attachmentInfo || message.content) : '' - const messagePreview = isYou ? `${this.$t('chats.you')} ${content}` : content + const content = message ? this.attachmentInfo || message.content : '' + const messagePreview = isYou + ? `${this.$t('chats.you')} ${content}` + : content return { summary: '', emojis: messageEmojis, raw_html: messagePreview, text: messagePreview, - attachments: [] + attachments: [], } - } + }, }, methods: { - openChat () { + openChat() { if (this.chat.id) { this.$router.push({ name: 'chat', params: { username: this.currentUser.screen_name, - recipient_id: this.chat.account.id - } + recipient_id: this.chat.account.id, + }, }) } - } - } + }, + }, } export default ChatListItem diff --git a/src/components/chat_message/chat_message.js b/src/components/chat_message/chat_message.js index 837f6d214..6b2315a82 100644 --- a/src/components/chat_message/chat_message.js +++ b/src/components/chat_message/chat_message.js @@ -9,16 +9,10 @@ import StatusContent from '../status_content/status_content.vue' import ChatMessageDate from '../chat_message_date/chat_message_date.vue' import { defineAsyncComponent } from 'vue' import { library } from '@fortawesome/fontawesome-svg-core' -import { - faTimes, - faEllipsisH -} from '@fortawesome/free-solid-svg-icons' +import { faTimes, faEllipsisH } from '@fortawesome/free-solid-svg-icons' import { useInterfaceStore } from 'src/stores/interface' -library.add( - faTimes, - faEllipsisH -) +library.add(faTimes, faEllipsisH) const ChatMessage = { name: 'ChatMessage', @@ -27,7 +21,7 @@ const ChatMessage = { 'edited', 'noHeading', 'chatViewItem', - 'hoveredMessageChain' + 'hoveredMessageChain', ], emits: ['hover'], components: { @@ -38,73 +32,82 @@ const ChatMessage = { Gallery, LinkPreview, ChatMessageDate, - UserPopover: defineAsyncComponent(() => import('../user_popover/user_popover.vue')) + UserPopover: defineAsyncComponent( + () => import('../user_popover/user_popover.vue'), + ), }, computed: { // Returns HH:MM (hours and minutes) in local time. - createdAt () { + createdAt() { const time = this.chatViewItem.data.created_at - return time.toLocaleTimeString('en', { hour: '2-digit', minute: '2-digit', hour12: false }) + return time.toLocaleTimeString('en', { + hour: '2-digit', + minute: '2-digit', + hour12: false, + }) }, - isCurrentUser () { + isCurrentUser() { return this.message.account_id === this.currentUser.id }, - message () { + message() { return this.chatViewItem.data }, - isMessage () { + isMessage() { return this.chatViewItem.type === 'message' }, - messageForStatusContent () { + messageForStatusContent() { return { summary: '', emojis: this.message.emojis, raw_html: this.message.content || '', text: this.message.content || '', - attachments: this.message.attachments + attachments: this.message.attachments, } }, - hasAttachment () { + hasAttachment() { return this.message.attachments.length > 0 }, ...mapPiniaState(useInterfaceStore, { - betterShadow: store => store.browserSupport.cssFilter + betterShadow: (store) => store.browserSupport.cssFilter, }), ...mapState({ - currentUser: state => state.users.currentUser, - restrictedNicknames: state => state.instance.restrictedNicknames + currentUser: (state) => state.users.currentUser, + restrictedNicknames: (state) => state.instance.restrictedNicknames, }), - popoverMarginStyle () { + popoverMarginStyle() { if (this.isCurrentUser) { return {} } else { return { left: 50 } } }, - ...mapGetters(['mergedConfig', 'findUser']) + ...mapGetters(['mergedConfig', 'findUser']), }, - data () { + data() { return { hovered: false, - menuOpened: false + menuOpened: false, } }, methods: { - onHover (bool) { - this.$emit('hover', { isHovered: bool, messageChainId: this.chatViewItem.messageChainId }) + onHover(bool) { + this.$emit('hover', { + isHovered: bool, + messageChainId: this.chatViewItem.messageChainId, + }) }, - async deleteMessage () { + async deleteMessage() { const confirmed = window.confirm(this.$t('chats.delete_confirm')) if (confirmed) { await this.$store.dispatch('deleteChatMessage', { messageId: this.chatViewItem.data.id, - chatId: this.chatViewItem.data.chat_id + chatId: this.chatViewItem.data.chat_id, }) } this.hovered = false this.menuOpened = false - } - } + }, + }, } export default ChatMessage diff --git a/src/components/chat_message/chat_message.style.js b/src/components/chat_message/chat_message.style.js index 76b565823..f7632bc6f 100644 --- a/src/components/chat_message/chat_message.style.js +++ b/src/components/chat_message/chat_message.style.js @@ -2,26 +2,21 @@ export default { name: 'ChatMessage', selector: '.chat-message', variants: { - outgoing: '.outgoing' + outgoing: '.outgoing', }, - validInnerComponents: [ - 'Text', - 'Icon', - 'Border', - 'PollGraph' - ], + validInnerComponents: ['Text', 'Icon', 'Border', 'PollGraph'], defaultRules: [ { directives: { background: '--bg, 2', - backgroundNoCssColor: 'yes' - } + backgroundNoCssColor: 'yes', + }, }, { variant: 'outgoing', directives: { - background: '--bg, 5' - } - } - ] + background: '--bg, 5', + }, + }, + ], } diff --git a/src/components/chat_message_date/chat_message_date.vue b/src/components/chat_message_date/chat_message_date.vue index 98349b753..f0cadb6e7 100644 --- a/src/components/chat_message_date/chat_message_date.vue +++ b/src/components/chat_message_date/chat_message_date.vue @@ -11,16 +11,19 @@ export default { name: 'Timeago', props: ['date'], computed: { - displayDate () { + displayDate() { const today = new Date() today.setHours(0, 0, 0, 0) if (this.date.getTime() === today.getTime()) { return this.$t('display_date.today') } else { - return this.date.toLocaleDateString(localeService.internalToBrowserLocale(this.$i18n.locale), { day: 'numeric', month: 'long' }) + return this.date.toLocaleDateString( + localeService.internalToBrowserLocale(this.$i18n.locale), + { day: 'numeric', month: 'long' }, + ) } - } - } + }, + }, } diff --git a/src/components/chat_new/chat_new.js b/src/components/chat_new/chat_new.js index 71585995a..8e2a8dacb 100644 --- a/src/components/chat_new/chat_new.js +++ b/src/components/chat_new/chat_new.js @@ -2,38 +2,32 @@ import { mapState, mapGetters } from 'vuex' import BasicUserCard from '../basic_user_card/basic_user_card.vue' import UserAvatar from '../user_avatar/user_avatar.vue' import { library } from '@fortawesome/fontawesome-svg-core' -import { - faSearch, - faChevronLeft -} from '@fortawesome/free-solid-svg-icons' +import { faSearch, faChevronLeft } from '@fortawesome/free-solid-svg-icons' -library.add( - faSearch, - faChevronLeft -) +library.add(faSearch, faChevronLeft) const chatNew = { components: { BasicUserCard, - UserAvatar + UserAvatar, }, - data () { + data() { return { suggestions: [], userIds: [], loading: false, - query: '' + query: '', } }, - async created () { + async created() { const { chats } = await this.backendInteractor.chats() - chats.forEach(chat => this.suggestions.push(chat.account)) + chats.forEach((chat) => this.suggestions.push(chat.account)) }, computed: { - users () { - return this.userIds.map(userId => this.findUser(userId)) + users() { + return this.userIds.map((userId) => this.findUser(userId)) }, - availableUsers () { + availableUsers() { if (this.query.length !== 0) { return this.users } else { @@ -41,29 +35,29 @@ const chatNew = { } }, ...mapState({ - currentUser: state => state.users.currentUser, - backendInteractor: state => state.api.backendInteractor + currentUser: (state) => state.users.currentUser, + backendInteractor: (state) => state.api.backendInteractor, }), - ...mapGetters(['findUser']) + ...mapGetters(['findUser']), }, methods: { - goBack () { + goBack() { this.$emit('cancel') }, - goToChat (user) { + goToChat(user) { this.$router.push({ name: 'chat', params: { recipient_id: user.id } }) }, - onInput () { + onInput() { this.search(this.query) }, - addUser (user) { + addUser(user) { this.selectedUserIds.push(user.id) this.query = '' }, - removeUser (userId) { - this.selectedUserIds = this.selectedUserIds.filter(id => id !== userId) + removeUser(userId) { + this.selectedUserIds = this.selectedUserIds.filter((id) => id !== userId) }, - search (query) { + search(query) { if (!query) { this.loading = false return @@ -71,13 +65,14 @@ const chatNew = { this.loading = true this.userIds = [] - this.$store.dispatch('search', { q: query, resolve: true, type: 'accounts' }) - .then(data => { + this.$store + .dispatch('search', { q: query, resolve: true, type: 'accounts' }) + .then((data) => { this.loading = false - this.userIds = data.accounts.map(a => a.id) + this.userIds = data.accounts.map((a) => a.id) }) - } - } + }, + }, } export default chatNew diff --git a/src/components/chat_title/chat_title.js b/src/components/chat_title/chat_title.js index b87211265..0d5ba93e0 100644 --- a/src/components/chat_title/chat_title.js +++ b/src/components/chat_title/chat_title.js @@ -7,17 +7,17 @@ export default { components: { UserAvatar, RichContent, - UserPopover: defineAsyncComponent(() => import('../user_popover/user_popover.vue')) + UserPopover: defineAsyncComponent( + () => import('../user_popover/user_popover.vue'), + ), }, - props: [ - 'user', 'withAvatar' - ], + props: ['user', 'withAvatar'], computed: { - title () { + title() { return this.user ? this.user.screen_name_ui : '' }, - htmlTitle () { + htmlTitle() { return this.user ? this.user.name_html : '' - } - } + }, + }, } diff --git a/src/components/checkbox/checkbox.vue b/src/components/checkbox/checkbox.vue index 1e0f5ad05..cbe3dd80f 100644 --- a/src/components/checkbox/checkbox.vue +++ b/src/components/checkbox/checkbox.vue @@ -36,30 +36,25 @@ diff --git a/src/components/color_input/color_input.vue b/src/components/color_input/color_input.vue index 58aee575c..8367908d9 100644 --- a/src/components/color_input/color_input.vue +++ b/src/components/color_input/color_input.vue @@ -69,90 +69,89 @@ import { hex2rgb } from '../../services/color_convert/color_convert.js' import { throttle } from 'lodash' import { library } from '@fortawesome/fontawesome-svg-core' -import { - faEyeDropper -} from '@fortawesome/free-solid-svg-icons' +import { faEyeDropper } from '@fortawesome/free-solid-svg-icons' -library.add( - faEyeDropper -) +library.add(faEyeDropper) export default { components: { - Checkbox + Checkbox, }, props: { // Name of color, used for identifying name: { required: true, - type: String + type: String, }, // Readable label label: { required: true, - type: String + type: String, }, // use unstyled, uh, style unstyled: { required: false, - type: Boolean + type: Boolean, }, // Color value, should be required but vue cannot tell the difference // between "property missing" and "property set to undefined" modelValue: { required: false, type: String, - default: undefined + default: undefined, }, // Color fallback to use when value is not defeind fallback: { required: false, type: String, - default: undefined + default: undefined, }, // Disable the control disabled: { required: false, type: Boolean, - default: false + default: false, }, // Show "optional" tickbox, for when value might become mandatory showOptionalCheckbox: { required: false, type: Boolean, - default: true + default: true, }, // Force "optional" tickbox to hide hideOptionalCheckbox: { required: false, type: Boolean, - default: false + default: false, }, compact: { required: false, - type: Boolean - } + type: Boolean, + }, }, emits: ['update:modelValue'], computed: { - present () { + present() { return typeof this.modelValue !== 'undefined' }, - validColor () { + validColor() { return hex2rgb(this.modelValue || this.fallback) }, - transparentColor () { + transparentColor() { return this.modelValue === 'transparent' }, - computedColor () { - return this.modelValue && (this.modelValue.startsWith('--') || this.modelValue.startsWith('$')) - } + computedColor() { + return ( + this.modelValue && + (this.modelValue.startsWith('--') || this.modelValue.startsWith('$')) + ) + }, }, methods: { updateValue: throttle(function (value) { this.$emit('update:modelValue', value) - }, 100) - } + }, 100), + }, } diff --git a/src/components/component_preview/component_preview.js b/src/components/component_preview/component_preview.js index 9f830cd72..6a19c3631 100644 --- a/src/components/component_preview/component_preview.js +++ b/src/components/component_preview/component_preview.js @@ -2,12 +2,15 @@ import Checkbox from 'src/components/checkbox/checkbox.vue' import ColorInput from 'src/components/color_input/color_input.vue' import genRandomSeed from 'src/services/random_seed/random_seed.service.js' -import { createStyleSheet, adoptStyleSheets } from 'src/services/style_setter/style_setter.js' +import { + createStyleSheet, + adoptStyleSheets, +} from 'src/services/style_setter/style_setter.js' export default { components: { Checkbox, - ColorInput + ColorInput, }, props: [ 'shadow', @@ -17,41 +20,41 @@ export default { 'previewCss', 'disabled', 'invalid', - 'noColorControl' + 'noColorControl', ], emits: ['update:shadow'], - data () { + data() { return { colorOverride: undefined, lightGrid: false, zoom: 100, - randomSeed: genRandomSeed() + randomSeed: genRandomSeed(), } }, - mounted () { + mounted() { this.update() }, computed: { - hideControls () { + hideControls() { return typeof this.shadow === 'string' - } + }, }, watch: { - previewCss () { + previewCss() { this.update() }, - previewStyle () { + previewStyle() { this.update() }, - zoom () { + zoom() { this.update() - } + }, }, methods: { - updateProperty (axis, value) { + updateProperty(axis, value) { this.$emit('update:shadow', { axis, value: Number(value) }) }, - update () { + update() { const sheet = createStyleSheet('style-component-preview', 90) sheet.clear() @@ -60,23 +63,25 @@ export default { if (this.colorOverride) result.push(`--background: ${this.colorOverride}`) const styleRule = [ - '#component-preview-', this.randomSeed, ' {\n', + '#component-preview-', + this.randomSeed, + ' {\n', '.preview-block {\n', `zoom: ${this.zoom / 100};`, this.previewStyle, '\n}', - '\n}' + '\n}', ].join('') sheet.addRule(styleRule) - sheet.addRule([ - '#component-preview-', this.randomSeed, ' {\n', - ...result, - '\n}' - ].join('')) + sheet.addRule( + ['#component-preview-', this.randomSeed, ' {\n', ...result, '\n}'].join( + '', + ), + ) sheet.ready = true adoptStyleSheets() - } - } + }, + }, } diff --git a/src/components/confirm_modal/confirm_modal.js b/src/components/confirm_modal/confirm_modal.js index 3e2bc2cb7..cd83194be 100644 --- a/src/components/confirm_modal/confirm_modal.js +++ b/src/components/confirm_modal/confirm_modal.js @@ -9,30 +9,29 @@ import DialogModal from '../dialog_modal/dialog_modal.vue' */ const ConfirmModal = { components: { - DialogModal + DialogModal, }, props: { title: { - type: String + type: String, }, cancelText: { - type: String + type: String, }, confirmText: { - type: String - } + type: String, + }, }, emits: ['cancelled', 'accepted'], - computed: { - }, + computed: {}, methods: { - onCancel () { + onCancel() { this.$emit('cancelled') }, - onAccept () { + onAccept() { this.$emit('accepted') - } - } + }, + }, } export default ConfirmModal diff --git a/src/components/confirm_modal/mute_confirm.js b/src/components/confirm_modal/mute_confirm.js index a279dc716..36c86b6ef 100644 --- a/src/components/confirm_modal/mute_confirm.js +++ b/src/components/confirm_modal/mute_confirm.js @@ -7,58 +7,61 @@ export default { props: ['type', 'user', 'status'], emits: ['hide', 'show', 'muted'], data: () => ({ - showing: false + showing: false, }), components: { ConfirmModal, - Select + Select, }, computed: { - domain () { + domain() { return this.user.fqn.split('@')[1] }, - keypath () { + keypath() { if (this.type === 'domain') { return 'status.mute_domain_confirm' } else if (this.type === 'conversation') { return 'status.mute_conversation_confirm' } }, - conversationIsMuted () { + conversationIsMuted() { return this.status.conversation_muted }, - domainIsMuted () { - return new Set(this.$store.state.users.currentUser.domainMutes).has(this.domain) + domainIsMuted() { + return new Set(this.$store.state.users.currentUser.domainMutes).has( + this.domain, + ) }, - shouldConfirm () { + shouldConfirm() { switch (this.type) { case 'domain': { return this.mergedConfig.modalOnMuteDomain } - default: { // conversation + default: { + // conversation return this.mergedConfig.modalOnMuteConversation } } }, - ...mapGetters(['mergedConfig']) + ...mapGetters(['mergedConfig']), }, methods: { - optionallyPrompt () { + optionallyPrompt() { if (this.shouldConfirm) { this.show() } else { this.doMute() } }, - show () { + show() { this.showing = true this.$emit('show') }, - hide () { + hide() { this.showing = false this.$emit('hide') }, - doMute () { + doMute() { switch (this.type) { case 'domain': { if (!this.domainIsMuted) { @@ -79,6 +82,6 @@ export default { } this.$emit('muted') this.hide() - } - } + }, + }, } diff --git a/src/components/contrast_ratio/contrast_ratio.vue b/src/components/contrast_ratio/contrast_ratio.vue index 93799e4c2..c413e2e8b 100644 --- a/src/components/contrast_ratio/contrast_ratio.vue +++ b/src/components/contrast_ratio/contrast_ratio.vue @@ -63,54 +63,66 @@ import { library } from '@fortawesome/fontawesome-svg-core' import { faAdjust, faExclamationTriangle, - faThumbsUp + faThumbsUp, } from '@fortawesome/free-solid-svg-icons' -library.add( - faAdjust, - faExclamationTriangle, - faThumbsUp -) +library.add(faAdjust, faExclamationTriangle, faThumbsUp) export default { components: { - Tooltip + Tooltip, }, props: { large: { required: false, type: Boolean, - default: false + default: false, }, // TODO: Make theme switcher compute theme initially so that contrast // component won't be called without contrast data contrast: { required: false, type: Object, - default: () => ({}) + default: () => ({}), }, showRatio: { required: false, type: Boolean, - default: false - } + default: false, + }, }, computed: { - hint () { - const levelVal = this.contrast.aaa ? 'aaa' : (this.contrast.aa ? 'aa' : 'bad') + hint() { + const levelVal = this.contrast.aaa + ? 'aaa' + : this.contrast.aa + ? 'aa' + : 'bad' const level = this.$t(`settings.style.common.contrast.level.${levelVal}`) const context = this.$t('settings.style.common.contrast.context.text') const ratio = this.contrast.text - return this.$t('settings.style.common.contrast.hint', { level, context, ratio }) + return this.$t('settings.style.common.contrast.hint', { + level, + context, + ratio, + }) }, - hint_18pt () { - const levelVal = this.contrast.laaa ? 'aaa' : (this.contrast.laa ? 'aa' : 'bad') + hint_18pt() { + const levelVal = this.contrast.laaa + ? 'aaa' + : this.contrast.laa + ? 'aa' + : 'bad' const level = this.$t(`settings.style.common.contrast.level.${levelVal}`) const context = this.$t('settings.style.common.contrast.context.18pt') const ratio = this.contrast.text - return this.$t('settings.style.common.contrast.hint', { level, context, ratio }) - } - } + return this.$t('settings.style.common.contrast.hint', { + level, + context, + ratio, + }) + }, + }, } diff --git a/src/components/conversation-page/conversation-page.js b/src/components/conversation-page/conversation-page.js index 8f996be12..d4705303e 100644 --- a/src/components/conversation-page/conversation-page.js +++ b/src/components/conversation-page/conversation-page.js @@ -2,13 +2,13 @@ import Conversation from '../conversation/conversation.vue' const conversationPage = { components: { - Conversation + Conversation, }, computed: { - statusId () { + statusId() { return this.$route.params.id - } - } + }, + }, } export default conversationPage diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js index 491a8543f..699b73cbb 100644 --- a/src/components/conversation/conversation.js +++ b/src/components/conversation/conversation.js @@ -12,14 +12,10 @@ import { library } from '@fortawesome/fontawesome-svg-core' import { faAngleDoubleDown, faAngleDoubleLeft, - faChevronLeft + faChevronLeft, } from '@fortawesome/free-solid-svg-icons' -library.add( - faAngleDoubleDown, - faAngleDoubleLeft, - faChevronLeft -) +library.add(faAngleDoubleDown, faAngleDoubleLeft, faChevronLeft) const sortById = (a, b) => { const idA = a.type === 'retweet' ? a.retweeted_status.id : a.id @@ -43,23 +39,25 @@ const sortAndFilterConversation = (conversation, statusoid) => { if (statusoid.type === 'retweet') { conversation = filter( conversation, - (status) => (status.type === 'retweet' || status.id !== statusoid.retweeted_status.id) + (status) => + status.type === 'retweet' || + status.id !== statusoid.retweeted_status.id, ) } else { conversation = filter(conversation, (status) => status.type !== 'retweet') } - return conversation.filter(_ => _).sort(sortById) + return conversation.filter((_) => _).sort(sortById) } const conversation = { - data () { + data() { return { highlight: null, expanded: false, threadDisplayStatusObject: {}, // id => 'showing' | 'hidden' statusContentPropertiesObject: {}, inlineDivePosition: null, - loadStatusError: null + loadStatusError: null, } }, props: [ @@ -69,76 +67,80 @@ const conversation = { 'pinnedStatusIdsObject', 'inProfile', 'profileUserId', - 'virtualHidden' + 'virtualHidden', ], - created () { + created() { if (this.isPage) { this.fetchConversation() } }, computed: { - maxDepthToShowByDefault () { + maxDepthToShowByDefault() { // maxDepthInThread = max number of depths that is *visible* // since our depth starts with 0 and "showing" means "showing children" // there is a -2 here const maxDepth = this.$store.getters.mergedConfig.maxDepthInThread - 2 return maxDepth >= 1 ? maxDepth : 1 }, - streamingEnabled () { - return this.mergedConfig.useStreamingApi && this.mastoUserSocketStatus === WSConnectionStatus.JOINED + streamingEnabled() { + return ( + this.mergedConfig.useStreamingApi && + this.mastoUserSocketStatus === WSConnectionStatus.JOINED + ) }, - displayStyle () { + displayStyle() { return this.$store.getters.mergedConfig.conversationDisplay }, - isTreeView () { + isTreeView() { return !this.isLinearView }, - treeViewIsSimple () { + treeViewIsSimple() { return !this.$store.getters.mergedConfig.conversationTreeAdvanced }, - isLinearView () { + isLinearView() { return this.displayStyle === 'linear' }, - shouldFadeAncestors () { + shouldFadeAncestors() { return this.$store.getters.mergedConfig.conversationTreeFadeAncestors }, - otherRepliesButtonPosition () { + otherRepliesButtonPosition() { return this.$store.getters.mergedConfig.conversationOtherRepliesButton }, - showOtherRepliesButtonBelowStatus () { + showOtherRepliesButtonBelowStatus() { return this.otherRepliesButtonPosition === 'below' }, - showOtherRepliesButtonInsideStatus () { + showOtherRepliesButtonInsideStatus() { return this.otherRepliesButtonPosition === 'inside' }, - suspendable () { + suspendable() { if (this.isTreeView) { - return Object.entries(this.statusContentProperties) - .every(([, prop]) => !prop.replying && prop.mediaPlaying.length === 0) + return Object.entries(this.statusContentProperties).every( + ([, prop]) => !prop.replying && prop.mediaPlaying.length === 0, + ) } if (this.$refs.statusComponent && this.$refs.statusComponent[0]) { - return this.$refs.statusComponent.every(s => s.suspendable) + return this.$refs.statusComponent.every((s) => s.suspendable) } else { return true } }, - hideStatus () { + hideStatus() { return this.virtualHidden && this.suspendable }, - status () { + status() { return this.$store.state.statuses.allStatusesObject[this.statusId] }, - originalStatusId () { + originalStatusId() { if (this.status.retweeted_status) { return this.status.retweeted_status.id } else { return this.statusId } }, - conversationId () { + conversationId() { return this.getConversationId(this.statusId) }, - conversation () { + conversation() { if (!this.status) { return [] } @@ -147,7 +149,9 @@ const conversation = { return [this.status] } - const conversation = clone(this.$store.state.statuses.conversationsObject[this.conversationId]) + const conversation = clone( + this.$store.state.statuses.conversationsObject[this.conversationId], + ) const statusIndex = findIndex(conversation, { id: this.originalStatusId }) if (statusIndex !== -1) { conversation[statusIndex] = this.status @@ -155,144 +159,188 @@ const conversation = { return sortAndFilterConversation(conversation, this.status) }, - statusMap () { + statusMap() { return this.conversation.reduce((res, s) => { res[s.id] = s return res }, {}) }, - threadTree () { - const reverseLookupTable = this.conversation.reduce((table, status, index) => { - table[status.id] = index - return table - }, {}) + threadTree() { + const reverseLookupTable = this.conversation.reduce( + (table, status, index) => { + table[status.id] = index + return table + }, + {}, + ) - const threads = this.conversation.reduce((a, cur) => { - const id = cur.id - a.forest[id] = this.getReplies(id) - .map(s => s.id) + const threads = this.conversation.reduce( + (a, cur) => { + const id = cur.id + a.forest[id] = this.getReplies(id).map((s) => s.id) - return a - }, { - forest: {} - }) + return a + }, + { + forest: {}, + }, + ) - const walk = (forest, topLevel, depth = 0, processed = {}) => topLevel.map(id => { - if (processed[id]) { - return [] - } + const walk = (forest, topLevel, depth = 0, processed = {}) => + topLevel + .map((id) => { + if (processed[id]) { + return [] + } - processed[id] = true - return [{ - status: this.conversation[reverseLookupTable[id]], - id, - depth - }, walk(forest, forest[id], depth + 1, processed)].reduce((a, b) => a.concat(b), []) - }).reduce((a, b) => a.concat(b), []) + processed[id] = true + return [ + { + status: this.conversation[reverseLookupTable[id]], + id, + depth, + }, + walk(forest, forest[id], depth + 1, processed), + ].reduce((a, b) => a.concat(b), []) + }) + .reduce((a, b) => a.concat(b), []) - const linearized = walk(threads.forest, this.topLevel.map(k => k.id)) + const linearized = walk( + threads.forest, + this.topLevel.map((k) => k.id), + ) return linearized }, - replyIds () { - return this.conversation.map(k => k.id) + replyIds() { + return this.conversation + .map((k) => k.id) .reduce((res, id) => { - res[id] = (this.replies[id] || []).map(k => k.id) + res[id] = (this.replies[id] || []).map((k) => k.id) return res }, {}) }, - totalReplyCount () { + totalReplyCount() { const sizes = {} const subTreeSizeFor = (id) => { if (sizes[id]) { return sizes[id] } - sizes[id] = 1 + this.replyIds[id].map(cid => subTreeSizeFor(cid)).reduce((a, b) => a + b, 0) + sizes[id] = + 1 + + this.replyIds[id] + .map((cid) => subTreeSizeFor(cid)) + .reduce((a, b) => a + b, 0) return sizes[id] } - this.conversation.map(k => k.id).map(subTreeSizeFor) + this.conversation.map((k) => k.id).map(subTreeSizeFor) return Object.keys(sizes).reduce((res, id) => { res[id] = sizes[id] - 1 // exclude itself return res }, {}) }, - totalReplyDepth () { + totalReplyDepth() { const depths = {} const subTreeDepthFor = (id) => { if (depths[id]) { return depths[id] } - depths[id] = 1 + this.replyIds[id].map(cid => subTreeDepthFor(cid)).reduce((a, b) => a > b ? a : b, 0) + depths[id] = + 1 + + this.replyIds[id] + .map((cid) => subTreeDepthFor(cid)) + .reduce((a, b) => (a > b ? a : b), 0) return depths[id] } - this.conversation.map(k => k.id).map(subTreeDepthFor) + this.conversation.map((k) => k.id).map(subTreeDepthFor) return Object.keys(depths).reduce((res, id) => { res[id] = depths[id] - 1 // exclude itself return res }, {}) }, - depths () { + depths() { return this.threadTree.reduce((a, k) => { a[k.id] = k.depth return a }, {}) }, - topLevel () { - const topLevel = this.conversation.reduce((tl, cur) => - tl.filter(k => this.getReplies(cur.id).map(v => v.id).indexOf(k.id) === -1), this.conversation) + topLevel() { + const topLevel = this.conversation.reduce( + (tl, cur) => + tl.filter( + (k) => + this.getReplies(cur.id) + .map((v) => v.id) + .indexOf(k.id) === -1, + ), + this.conversation, + ) return topLevel }, - otherTopLevelCount () { + otherTopLevelCount() { return this.topLevel.length - 1 }, - showingTopLevel () { + showingTopLevel() { if (this.canDive && this.diveRoot) { return [this.statusMap[this.diveRoot]] } return this.topLevel }, - diveRoot () { + diveRoot() { const statusId = this.inlineDivePosition || this.statusId const isTopLevel = !this.parentOf(statusId) return isTopLevel ? null : statusId }, - diveDepth () { + diveDepth() { return this.canDive && this.diveRoot ? this.depths[this.diveRoot] : 0 }, - diveMode () { + diveMode() { return this.canDive && !!this.diveRoot }, - shouldShowAllConversationButton () { + shouldShowAllConversationButton() { // The "show all conversation" button tells the user that there exist // other toplevel statuses, so do not show it if there is only a single root - return this.isTreeView && this.isExpanded && this.diveMode && this.topLevel.length > 1 + return ( + this.isTreeView && + this.isExpanded && + this.diveMode && + this.topLevel.length > 1 + ) }, - shouldShowAncestors () { - return this.isTreeView && this.isExpanded && this.ancestorsOf(this.diveRoot).length + shouldShowAncestors() { + return ( + this.isTreeView && + this.isExpanded && + this.ancestorsOf(this.diveRoot).length + ) }, - replies () { + replies() { let i = 1 - return reduce(this.conversation, (result, { id, in_reply_to_status_id: irid }) => { - if (irid) { - result[irid] = result[irid] || [] - result[irid].push({ - name: `#${i}`, - id - }) - } - i++ - return result - }, {}) + return reduce( + this.conversation, + (result, { id, in_reply_to_status_id: irid }) => { + if (irid) { + result[irid] = result[irid] || [] + result[irid].push({ + name: `#${i}`, + id, + }) + } + i++ + return result + }, + {}, + ) }, - isExpanded () { + isExpanded() { return !!(this.expanded || this.isPage) }, - hiddenStyle () { + hiddenStyle() { const height = (this.status && this.status.virtualHeight) || '120px' return this.virtualHidden ? { height } : {} }, - threadDisplayStatus () { + threadDisplayStatus() { return this.conversation.reduce((a, k) => { const id = k.id const depth = this.depths[id] @@ -300,7 +348,7 @@ const conversation = { if (this.threadDisplayStatusObject[id]) { return this.threadDisplayStatusObject[id] } - if ((depth - this.diveDepth) <= this.maxDepthToShowByDefault) { + if (depth - this.diveDepth <= this.maxDepthToShowByDefault) { return 'showing' } else { return 'hidden' @@ -311,7 +359,7 @@ const conversation = { return a }, {}) }, - statusContentProperties () { + statusContentProperties() { return this.conversation.reduce((a, k) => { const id = k.id const props = (() => { @@ -320,13 +368,13 @@ const conversation = { expandingSubject: false, showingLongSubject: false, isReplying: false, - mediaPlaying: [] + mediaPlaying: [], } if (this.statusContentPropertiesObject[id]) { return { ...def, - ...this.statusContentPropertiesObject[id] + ...this.statusContentPropertiesObject[id], } } return def @@ -336,54 +384,59 @@ const conversation = { return a }, {}) }, - canDive () { + canDive() { return this.isTreeView && this.isExpanded }, - maybeHighlight () { + maybeHighlight() { return this.isExpanded ? this.highlight : null }, ...mapGetters(['mergedConfig']), ...mapState({ - mastoUserSocketStatus: state => state.api.mastoUserSocketStatus + mastoUserSocketStatus: (state) => state.api.mastoUserSocketStatus, }), ...mapPiniaState(useInterfaceStore, { - mobileLayout: store => store.layoutType === 'mobile' - }) + mobileLayout: (store) => store.layoutType === 'mobile', + }), }, components: { Status, ThreadTree, QuickFilterSettings, - QuickViewSettings + QuickViewSettings, }, watch: { - statusId (newVal, oldVal) { + statusId(newVal, oldVal) { const newConversationId = this.getConversationId(newVal) const oldConversationId = this.getConversationId(oldVal) - if (newConversationId && oldConversationId && newConversationId === oldConversationId) { + if ( + newConversationId && + oldConversationId && + newConversationId === oldConversationId + ) { this.setHighlight(this.originalStatusId) } else { this.fetchConversation() } }, - expanded (value) { + expanded(value) { if (value) { this.fetchConversation() } else { this.resetDisplayState() } }, - virtualHidden () { - this.$store.dispatch( - 'setVirtualHeight', - { statusId: this.statusId, height: `${this.$el.clientHeight}px` } - ) - } + virtualHidden() { + this.$store.dispatch('setVirtualHeight', { + statusId: this.statusId, + height: `${this.$el.clientHeight}px`, + }) + }, }, methods: { - fetchConversation () { + fetchConversation() { if (this.status) { - this.$store.state.api.backendInteractor.fetchConversation({ id: this.statusId }) + this.$store.state.api.backendInteractor + .fetchConversation({ id: this.statusId }) .then(({ ancestors, descendants }) => { this.$store.dispatch('addNewStatuses', { statuses: ancestors }) this.$store.dispatch('addNewStatuses', { statuses: descendants }) @@ -391,7 +444,8 @@ const conversation = { }) } else { this.loadStatusError = null - this.$store.state.api.backendInteractor.fetchStatus({ id: this.statusId }) + this.$store.state.api.backendInteractor + .fetchStatus({ id: this.statusId }) .then((status) => { this.$store.dispatch('addNewStatuses', { statuses: [status] }) this.fetchConversation() @@ -401,16 +455,16 @@ const conversation = { }) } }, - isFocused (id) { - return (this.isExpanded) && id === this.highlight + isFocused(id) { + return this.isExpanded && id === this.highlight }, - getReplies (id) { + getReplies(id) { return this.replies[id] || [] }, - getHighlight () { + getHighlight() { return this.isExpanded ? this.highlight : null }, - setHighlight (id) { + setHighlight(id) { if (!id) return this.highlight = id @@ -421,44 +475,54 @@ const conversation = { this.$store.dispatch('fetchFavsAndRepeats', id) this.$store.dispatch('fetchEmojiReactionsBy', id) }, - toggleExpanded () { + toggleExpanded() { this.expanded = !this.expanded }, - getConversationId (statusId) { + getConversationId(statusId) { const status = this.$store.state.statuses.allStatusesObject[statusId] - return get(status, 'retweeted_status.statusnet_conversation_id', get(status, 'statusnet_conversation_id')) + return get( + status, + 'retweeted_status.statusnet_conversation_id', + get(status, 'statusnet_conversation_id'), + ) }, - setThreadDisplay (id, nextStatus) { + setThreadDisplay(id, nextStatus) { this.threadDisplayStatusObject = { ...this.threadDisplayStatusObject, - [id]: nextStatus + [id]: nextStatus, } }, - toggleThreadDisplay (id) { + toggleThreadDisplay(id) { const curStatus = this.threadDisplayStatus[id] const nextStatus = curStatus === 'showing' ? 'hidden' : 'showing' this.setThreadDisplay(id, nextStatus) }, - setThreadDisplayRecursively (id, nextStatus) { + setThreadDisplayRecursively(id, nextStatus) { this.setThreadDisplay(id, nextStatus) - this.getReplies(id).map(k => k.id).map(id => this.setThreadDisplayRecursively(id, nextStatus)) + this.getReplies(id) + .map((k) => k.id) + .map((id) => this.setThreadDisplayRecursively(id, nextStatus)) }, - showThreadRecursively (id) { + showThreadRecursively(id) { this.setThreadDisplayRecursively(id, 'showing') }, - setStatusContentProperty (id, name, value) { + setStatusContentProperty(id, name, value) { this.statusContentPropertiesObject = { ...this.statusContentPropertiesObject, [id]: { ...this.statusContentPropertiesObject[id], - [name]: value - } + [name]: value, + }, } }, - toggleStatusContentProperty (id, name) { - this.setStatusContentProperty(id, name, !this.statusContentProperties[id][name]) + toggleStatusContentProperty(id, name) { + this.setStatusContentProperty( + id, + name, + !this.statusContentProperties[id][name], + ) }, - leastVisibleAncestor (id) { + leastVisibleAncestor(id) { let cur = id let parent = this.parentOf(cur) while (cur) { @@ -472,18 +536,20 @@ const conversation = { // nothing found, fall back to toplevel return this.topLevel[0] ? this.topLevel[0].id : undefined }, - diveIntoStatus (id) { + diveIntoStatus(id) { this.tryScrollTo(id) }, - diveToTopLevel () { - this.tryScrollTo(this.topLevelAncestorOrSelfId(this.diveRoot) || this.topLevel[0].id) + diveToTopLevel() { + this.tryScrollTo( + this.topLevelAncestorOrSelfId(this.diveRoot) || this.topLevel[0].id, + ) }, // only used when we are not on a page - undive () { + undive() { this.inlineDivePosition = null this.setHighlight(this.statusId) }, - tryScrollTo (id) { + tryScrollTo(id) { if (!id) { return } @@ -512,13 +578,13 @@ const conversation = { this.setHighlight(id) }) }, - goToCurrent () { + goToCurrent() { this.tryScrollTo(this.diveRoot || this.topLevel[0].id) }, - statusById (id) { + statusById(id) { return this.statusMap[id] }, - parentOf (id) { + parentOf(id) { const status = this.statusById(id) if (!status) { return undefined @@ -529,11 +595,11 @@ const conversation = { } return parentId }, - parentOrSelf (id) { + parentOrSelf(id) { return this.parentOf(id) || id }, // Ancestors of some status, from top to bottom - ancestorsOf (id) { + ancestorsOf(id) { const ancestors = [] let cur = this.parentOf(id) while (cur) { @@ -542,7 +608,7 @@ const conversation = { } return ancestors }, - topLevelAncestorOrSelfId (id) { + topLevelAncestorOrSelfId(id) { let cur = id let parent = this.parentOf(id) while (parent) { @@ -551,11 +617,11 @@ const conversation = { } return cur }, - resetDisplayState () { + resetDisplayState() { this.undive() this.threadDisplayStatusObject = {} - } - } + }, + }, } export default conversation diff --git a/src/components/desktop_nav/desktop_nav.js b/src/components/desktop_nav/desktop_nav.js index 98d408a7e..a775ff090 100644 --- a/src/components/desktop_nav/desktop_nav.js +++ b/src/components/desktop_nav/desktop_nav.js @@ -12,7 +12,7 @@ import { faSearch, faTachometerAlt, faCog, - faInfoCircle + faInfoCircle, } from '@fortawesome/free-solid-svg-icons' import { useInterfaceStore } from 'src/stores/interface' @@ -27,91 +27,109 @@ library.add( faSearch, faTachometerAlt, faCog, - faInfoCircle + faInfoCircle, ) export default { components: { SearchBar, - ConfirmModal + ConfirmModal, }, data: () => ({ searchBarHidden: true, - supportsMask: window.CSS && window.CSS.supports && ( - window.CSS.supports('mask-size', 'contain') || + supportsMask: + window.CSS && + window.CSS.supports && + (window.CSS.supports('mask-size', 'contain') || window.CSS.supports('-webkit-mask-size', 'contain') || window.CSS.supports('-moz-mask-size', 'contain') || window.CSS.supports('-ms-mask-size', 'contain') || - window.CSS.supports('-o-mask-size', 'contain') - ), - showingConfirmLogout: false + window.CSS.supports('-o-mask-size', 'contain')), + showingConfirmLogout: false, }), computed: { - enableMask () { return this.supportsMask && this.$store.state.instance.logoMask }, - logoStyle () { + enableMask() { + return this.supportsMask && this.$store.state.instance.logoMask + }, + logoStyle() { return { - visibility: this.enableMask ? 'hidden' : 'visible' + visibility: this.enableMask ? 'hidden' : 'visible', } }, - logoMaskStyle () { + logoMaskStyle() { return this.enableMask ? { - 'mask-image': `url(${this.$store.state.instance.logo})` + 'mask-image': `url(${this.$store.state.instance.logo})`, } : { - 'background-color': this.enableMask ? '' : 'transparent' + 'background-color': this.enableMask ? '' : 'transparent', } }, - logoBgStyle () { - return Object.assign({ - margin: `${this.$store.state.instance.logoMargin} 0`, - opacity: this.searchBarHidden ? 1 : 0 - }, this.enableMask - ? {} - : { - 'background-color': this.enableMask ? '' : 'transparent' - }) + logoBgStyle() { + return Object.assign( + { + margin: `${this.$store.state.instance.logoMargin} 0`, + opacity: this.searchBarHidden ? 1 : 0, + }, + this.enableMask + ? {} + : { + 'background-color': this.enableMask ? '' : 'transparent', + }, + ) }, - logo () { return this.$store.state.instance.logo }, - sitename () { return this.$store.state.instance.name }, - hideSitename () { return this.$store.state.instance.hideSitename }, - logoLeft () { return this.$store.state.instance.logoLeft }, - currentUser () { return this.$store.state.users.currentUser }, - privateMode () { return this.$store.state.instance.private }, - shouldConfirmLogout () { + logo() { + return this.$store.state.instance.logo + }, + sitename() { + return this.$store.state.instance.name + }, + hideSitename() { + return this.$store.state.instance.hideSitename + }, + logoLeft() { + return this.$store.state.instance.logoLeft + }, + currentUser() { + return this.$store.state.users.currentUser + }, + privateMode() { + return this.$store.state.instance.private + }, + shouldConfirmLogout() { return this.$store.getters.mergedConfig.modalOnLogout - } + }, }, methods: { - scrollToTop () { + scrollToTop() { window.scrollTo(0, 0) }, - showConfirmLogout () { + showConfirmLogout() { this.showingConfirmLogout = true }, - hideConfirmLogout () { + hideConfirmLogout() { this.showingConfirmLogout = false }, - logout () { + logout() { if (!this.shouldConfirmLogout) { this.doLogout() } else { this.showConfirmLogout() } }, - doLogout () { + doLogout() { this.$router.replace('/main/public') this.$store.dispatch('logout') this.hideConfirmLogout() }, - onSearchBarToggled (hidden) { + onSearchBarToggled(hidden) { this.searchBarHidden = hidden }, - openSettingsModal () { + openSettingsModal() { useInterfaceStore().openSettingsModal('user') }, - openAdminModal () { + openAdminModal() { useInterfaceStore().openSettingsModal('admin') - } - } + }, + }, } diff --git a/src/components/dialog_modal/dialog_modal.js b/src/components/dialog_modal/dialog_modal.js index b39851fe7..fa7b6b364 100644 --- a/src/components/dialog_modal/dialog_modal.js +++ b/src/components/dialog_modal/dialog_modal.js @@ -2,18 +2,18 @@ const DialogModal = { props: { darkOverlay: { default: true, - type: Boolean + type: Boolean, }, onCancel: { default: () => {}, - type: Function - } + type: Function, + }, }, computed: { - mobileCenter () { + mobileCenter() { return this.$store.getters.mergedConfig.modalMobileCenter - } - } + }, + }, } export default DialogModal diff --git a/src/components/dm_timeline/dm_timeline.js b/src/components/dm_timeline/dm_timeline.js index 8b5393a98..c977efe30 100644 --- a/src/components/dm_timeline/dm_timeline.js +++ b/src/components/dm_timeline/dm_timeline.js @@ -2,13 +2,13 @@ import Timeline from '../timeline/timeline.vue' const DMs = { computed: { - timeline () { + timeline() { return this.$store.state.statuses.timelines.dms - } + }, }, components: { - Timeline - } + Timeline, + }, } export default DMs diff --git a/src/components/domain_mute_card/domain_mute_card.js b/src/components/domain_mute_card/domain_mute_card.js index f234dcb0f..71da2684c 100644 --- a/src/components/domain_mute_card/domain_mute_card.js +++ b/src/components/domain_mute_card/domain_mute_card.js @@ -3,24 +3,24 @@ import ProgressButton from '../progress_button/progress_button.vue' const DomainMuteCard = { props: ['domain'], components: { - ProgressButton + ProgressButton, }, computed: { - user () { + user() { return this.$store.state.users.currentUser }, - muted () { + muted() { return this.user.domainMutes.includes(this.domain) - } + }, }, methods: { - unmuteDomain () { + unmuteDomain() { return this.$store.dispatch('unmuteDomain', this.domain) }, - muteDomain () { + muteDomain() { return this.$store.dispatch('muteDomain', this.domain) - } - } + }, + }, } export default DomainMuteCard diff --git a/src/components/draft/draft.js b/src/components/draft/draft.js index 55ee11a15..1f1a871cc 100644 --- a/src/components/draft/draft.js +++ b/src/components/draft/draft.js @@ -6,13 +6,9 @@ import Gallery from 'src/components/gallery/gallery.vue' import { cloneDeep } from 'lodash' import { library } from '@fortawesome/fontawesome-svg-core' -import { - faPollH -} from '@fortawesome/free-solid-svg-icons' +import { faPollH } from '@fortawesome/free-solid-svg-icons' -library.add( - faPollH -) +library.add(faPollH) const Draft = { components: { @@ -20,23 +16,23 @@ const Draft = { EditStatusForm, ConfirmModal, StatusContent, - Gallery + Gallery, }, props: { draft: { type: Object, - required: true - } + required: true, + }, }, - data () { + data() { return { referenceDraft: cloneDeep(this.draft), editing: false, - showingConfirmDialog: false + showingConfirmDialog: false, } }, computed: { - relAttrs () { + relAttrs() { if (this.draft.type === 'edit') { return { statusId: this.draft.refId } } else if (this.draft.type === 'reply') { @@ -45,24 +41,24 @@ const Draft = { return {} } }, - safeToSave () { - return this.draft.status || - this.draft.files?.length || - this.draft.hasPoll + safeToSave() { + return this.draft.status || this.draft.files?.length || this.draft.hasPoll }, - postStatusFormProps () { + postStatusFormProps() { return { draftId: this.draft.id, - ...this.relAttrs + ...this.relAttrs, } }, - refStatus () { - return this.draft.refId ? this.$store.state.statuses.allStatusesObject[this.draft.refId] : undefined + refStatus() { + return this.draft.refId + ? this.$store.state.statuses.allStatusesObject[this.draft.refId] + : undefined }, - localCollapseSubjectDefault () { + localCollapseSubjectDefault() { return this.$store.getters.mergedConfig.collapseMessageWithSubject }, - nsfwClickthrough () { + nsfwClickthrough() { if (!this.draft.nsfw) { return false } @@ -70,35 +66,34 @@ const Draft = { return false } return true - } + }, }, watch: { - editing (newVal) { + editing(newVal) { if (newVal) return if (this.safeToSave) { this.$store.dispatch('addOrSaveDraft', { draft: this.draft }) } else { this.$store.dispatch('addOrSaveDraft', { draft: this.referenceDraft }) } - } + }, }, methods: { - toggleEditing () { + toggleEditing() { this.editing = !this.editing }, - abandon () { + abandon() { this.showingConfirmDialog = true }, - doAbandon () { - this.$store.dispatch('abandonDraft', { id: this.draft.id }) - .then(() => { - this.hideConfirmDialog() - }) + doAbandon() { + this.$store.dispatch('abandonDraft', { id: this.draft.id }).then(() => { + this.hideConfirmDialog() + }) }, - hideConfirmDialog () { + hideConfirmDialog() { this.showingConfirmDialog = false - } - } + }, + }, } export default Draft diff --git a/src/components/draft_closer/draft_closer.js b/src/components/draft_closer/draft_closer.js index e50ea05ab..d724ab4ac 100644 --- a/src/components/draft_closer/draft_closer.js +++ b/src/components/draft_closer/draft_closer.js @@ -1,32 +1,29 @@ import DialogModal from 'src/components/dialog_modal/dialog_modal.vue' const DraftCloser = { - data () { + data() { return { - showing: false + showing: false, } }, components: { - DialogModal + DialogModal, }, - emits: [ - 'save', - 'discard' - ], + emits: ['save', 'discard'], computed: { - action () { + action() { if (this.$store.getters.mergedConfig.autoSaveDraft) { return 'save' } else { return this.$store.getters.mergedConfig.unsavedPostAction } }, - shouldConfirm () { + shouldConfirm() { return this.action === 'confirm' - } + }, }, methods: { - requestClose () { + requestClose() { if (this.shouldConfirm) { this.showing = true } else if (this.action === 'save') { @@ -35,18 +32,18 @@ const DraftCloser = { this.discard() } }, - save () { + save() { this.$emit('save') this.showing = false }, - discard () { + discard() { this.$emit('discard') this.showing = false }, - cancel () { + cancel() { this.showing = false - } - } + }, + }, } export default DraftCloser diff --git a/src/components/drafts/drafts.js b/src/components/drafts/drafts.js index 201417f66..8acde3c17 100644 --- a/src/components/drafts/drafts.js +++ b/src/components/drafts/drafts.js @@ -4,13 +4,13 @@ import List from 'src/components/list/list.vue' const Drafts = { components: { Draft, - List + List, }, computed: { - drafts () { + drafts() { return this.$store.getters.draftsArray - } - } + }, + }, } export default Drafts diff --git a/src/components/edit_status_form/edit_status_form.js b/src/components/edit_status_form/edit_status_form.js index 323763370..a3c02c2a3 100644 --- a/src/components/edit_status_form/edit_status_form.js +++ b/src/components/edit_status_form/edit_status_form.js @@ -3,19 +3,19 @@ import statusPosterService from '../../services/status_poster/status_poster.serv const EditStatusForm = { components: { - PostStatusForm + PostStatusForm, }, props: { params: { type: Object, - required: true - } + required: true, + }, }, methods: { - requestClose () { + requestClose() { this.$refs.postStatusForm.requestClose() }, - doEditStatus ({ status, spoilerText, sensitive, media, contentType, poll }) { + doEditStatus({ status, spoilerText, sensitive, media, contentType, poll }) { const params = { store: this.$store, statusId: this.params.statusId, @@ -24,21 +24,22 @@ const EditStatusForm = { sensitive, poll, media, - contentType + contentType, } - return statusPosterService.editStatus(params) + return statusPosterService + .editStatus(params) .then((data) => { return data }) .catch((err) => { console.error('Error editing status', err) return { - error: err.message + error: err.message, } }) - } - } + }, + }, } export default EditStatusForm diff --git a/src/components/edit_status_modal/edit_status_modal.js b/src/components/edit_status_modal/edit_status_modal.js index 4c10c21a0..7daed7c89 100644 --- a/src/components/edit_status_modal/edit_status_modal.js +++ b/src/components/edit_status_modal/edit_status_modal.js @@ -6,29 +6,29 @@ import { useEditStatusStore } from 'src/stores/editStatus' const EditStatusModal = { components: { EditStatusForm, - Modal + Modal, }, - data () { + data() { return { - resettingForm: false + resettingForm: false, } }, computed: { - isLoggedIn () { + isLoggedIn() { return !!this.$store.state.users.currentUser }, - modalActivated () { + modalActivated() { return useEditStatusStore().modalActivated }, - isFormVisible () { + isFormVisible() { return this.isLoggedIn && !this.resettingForm && this.modalActivated }, - params () { + params() { return useEditStatusStore().params || {} - } + }, }, watch: { - params (newVal, oldVal) { + params(newVal, oldVal) { if (get(newVal, 'statusId') !== get(oldVal, 'statusId')) { this.resettingForm = true this.$nextTick(() => { @@ -36,20 +36,22 @@ const EditStatusModal = { }) } }, - isFormVisible (val) { + isFormVisible(val) { if (val) { - this.$nextTick(() => this.$el && this.$el.querySelector('textarea').focus()) + this.$nextTick( + () => this.$el && this.$el.querySelector('textarea').focus(), + ) } - } + }, }, methods: { - closeModal () { + closeModal() { this.$refs.editStatusForm.requestClose() }, - doCloseModal () { + doCloseModal() { useEditStatusStore().closeEditStatusModal() - } - } + }, + }, } export default EditStatusModal diff --git a/src/components/emoji_input/emoji_input.js b/src/components/emoji_input/emoji_input.js index f6ba6e245..8f681c25c 100644 --- a/src/components/emoji_input/emoji_input.js +++ b/src/components/emoji_input/emoji_input.js @@ -8,13 +8,9 @@ import { take } from 'lodash' import { findOffset } from '../../services/offset_finder/offset_finder.service.js' import { ensureFinalFallback } from '../../i18n/languages.js' import { library } from '@fortawesome/fontawesome-svg-core' -import { - faSmileBeam -} from '@fortawesome/free-regular-svg-icons' +import { faSmileBeam } from '@fortawesome/free-regular-svg-icons' -library.add( - faSmileBeam -) +library.add(faSmileBeam) /** * EmojiInput - augmented inputs for emoji and autocomplete support in inputs @@ -60,14 +56,14 @@ const EmojiInput = { * For commonly used suggestors (emoji, users, both) use suggestor.js */ required: true, - type: Function + type: Function, }, modelValue: { /** * Used for v-model */ required: true, - type: String + type: String, }, enableEmojiPicker: { /** @@ -75,7 +71,7 @@ const EmojiInput = { */ required: false, type: Boolean, - default: false + default: false, }, hideEmojiButton: { /** @@ -84,7 +80,7 @@ const EmojiInput = { */ required: false, type: Boolean, - default: false + default: false, }, enableStickerPicker: { /** @@ -92,7 +88,7 @@ const EmojiInput = { */ required: false, type: Boolean, - default: false + default: false, }, placement: { /** @@ -101,15 +97,15 @@ const EmojiInput = { */ required: false, type: String, // 'auto', 'top', 'bottom' - default: 'auto' + default: 'auto', }, newlineOnCtrlEnter: { required: false, type: Boolean, - default: false - } + default: false, + }, }, - data () { + data() { return { randomSeed: genRandomSeed(), input: undefined, @@ -122,58 +118,65 @@ const EmojiInput = { disableClickOutside: false, suggestions: [], overlayStyle: {}, - pickerShown: false + pickerShown: false, } }, components: { Popover, EmojiPicker, UnicodeDomainIndicator, - ScreenReaderNotice + ScreenReaderNotice, }, computed: { - padEmoji () { + padEmoji() { return this.$store.getters.mergedConfig.padEmoji }, - defaultCandidateIndex () { + defaultCandidateIndex() { return this.$store.getters.mergedConfig.autocompleteSelect ? 0 : -1 }, - preText () { + preText() { return this.modelValue.slice(0, this.caret) }, - postText () { + postText() { return this.modelValue.slice(this.caret) }, - showSuggestions () { - return this.focused && + showSuggestions() { + return ( + this.focused && this.suggestions && this.suggestions.length > 0 && !this.pickerShown && !this.temporarilyHideSuggestions + ) }, - textAtCaret () { + textAtCaret() { return this.wordAtCaret?.word }, - wordAtCaret () { + wordAtCaret() { if (this.modelValue && this.caret) { - const word = Completion.wordAtPosition(this.modelValue, this.caret - 1) || {} + const word = + Completion.wordAtPosition(this.modelValue, this.caret - 1) || {} return word } }, - languages () { - return ensureFinalFallback(this.$store.getters.mergedConfig.interfaceLanguage) + languages() { + return ensureFinalFallback( + this.$store.getters.mergedConfig.interfaceLanguage, + ) }, - maybeLocalizedEmojiNamesAndKeywords () { - return emoji => { + maybeLocalizedEmojiNamesAndKeywords() { + return (emoji) => { const names = [emoji.displayText] const keywords = [] if (emoji.displayTextI18n) { - names.push(this.$t(emoji.displayTextI18n.key, emoji.displayTextI18n.args)) + names.push( + this.$t(emoji.displayTextI18n.key, emoji.displayTextI18n.args), + ) } if (emoji.annotations) { - this.languages.forEach(lang => { + this.languages.forEach((lang) => { names.push(emoji.annotations[lang]?.name) keywords.push(...(emoji.annotations[lang]?.keywords || [])) @@ -181,13 +184,13 @@ const EmojiInput = { } return { - names: names.filter(k => k), - keywords: keywords.filter(k => k) + names: names.filter((k) => k), + keywords: keywords.filter((k) => k), } } }, - maybeLocalizedEmojiName () { - return emoji => { + maybeLocalizedEmojiName() { + return (emoji) => { if (!emoji.annotations) { return emoji.displayText } @@ -205,16 +208,18 @@ const EmojiInput = { return emoji.displayText } }, - suggestionListId () { + suggestionListId() { return `suggestions-${this.randomSeed}` }, - suggestionItemId () { + suggestionItemId() { return (index) => `suggestion-item-${index}-${this.randomSeed}` - } + }, }, - mounted () { + mounted() { const { root, hiddenOverlayCaret, suggestorPopover } = this.$refs - const input = root.querySelector('.emoji-input > input') || root.querySelector('.emoji-input > textarea') + const input = + root.querySelector('.emoji-input > input') || + root.querySelector('.emoji-input > textarea') if (!input) return this.input = input this.caretEl = hiddenOverlayCaret @@ -243,7 +248,7 @@ const EmojiInput = { input.addEventListener('input', this.onInput) input.addEventListener('scroll', this.onInputScroll) }, - unmounted () { + unmounted() { const { input } = this if (input) { input.removeEventListener('blur', this.onBlur) @@ -273,36 +278,40 @@ const EmojiInput = { this.suggestions = [] return } - const matchedSuggestions = await this.suggest(newWord, this.maybeLocalizedEmojiNamesAndKeywords) + const matchedSuggestions = await this.suggest( + newWord, + this.maybeLocalizedEmojiNamesAndKeywords, + ) // Async: cancel if textAtCaret has changed during wait if (this.textAtCaret !== newWord || matchedSuggestions.length <= 0) { this.suggestions = [] return } - this.suggestions = take(matchedSuggestions, 5) - .map(({ imageUrl, ...rest }) => ({ + this.suggestions = take(matchedSuggestions, 5).map( + ({ imageUrl, ...rest }) => ({ ...rest, - img: imageUrl || '' - })) + img: imageUrl || '', + }), + ) this.highlighted = this.defaultCandidateIndex this.$refs.screenReaderNotice.announce( this.$t( 'tool_tip.autocomplete_available', { number: this.suggestions.length }, - this.suggestions.length - ) + this.suggestions.length, + ), ) - } + }, }, methods: { - onInputScroll (e) { + onInputScroll(e) { this.$refs.hiddenOverlay.scrollTo({ top: this.input.scrollTop, - left: this.input.scrollLeft + left: this.input.scrollLeft, }) this.setCaret(e) }, - triggerShowPicker () { + triggerShowPicker() { this.$nextTick(() => { this.$refs.picker.showPicker() this.scrollIntoView() @@ -315,7 +324,7 @@ const EmojiInput = { this.disableClickOutside = false }, 0) }, - togglePicker () { + togglePicker() { this.input.focus() if (!this.pickerShown) { this.scrollIntoView() @@ -325,12 +334,16 @@ const EmojiInput = { this.$refs.picker.hidePicker() } }, - replace (replacement) { - const newValue = Completion.replaceWord(this.modelValue, this.wordAtCaret, replacement) + replace(replacement) { + const newValue = Completion.replaceWord( + this.modelValue, + this.wordAtCaret, + replacement, + ) this.$emit('update:modelValue', newValue) this.caret = 0 }, - insert ({ insertion, keepOpen, surroundingSpace = true }) { + insert({ insertion, keepOpen, surroundingSpace = true }) { const before = this.modelValue.substring(0, this.caret) || '' const after = this.modelValue.substring(this.caret) || '' @@ -349,18 +362,24 @@ const EmojiInput = { * them, masto seem to be rendering :emoji::emoji: correctly now so why not */ const isSpaceRegex = /\s/ - const spaceBefore = (surroundingSpace && !isSpaceRegex.exec(before.slice(-1)) && before.length && this.padEmoji > 0) ? ' ' : '' - const spaceAfter = (surroundingSpace && !isSpaceRegex.exec(after[0]) && this.padEmoji) ? ' ' : '' + const spaceBefore = + surroundingSpace && + !isSpaceRegex.exec(before.slice(-1)) && + before.length && + this.padEmoji > 0 + ? ' ' + : '' + const spaceAfter = + surroundingSpace && !isSpaceRegex.exec(after[0]) && this.padEmoji + ? ' ' + : '' - const newValue = [ - before, - spaceBefore, - insertion, - spaceAfter, - after - ].join('') + const newValue = [before, spaceBefore, insertion, spaceAfter, after].join( + '', + ) this.$emit('update:modelValue', newValue) - const position = this.caret + (insertion + spaceAfter + spaceBefore).length + const position = + this.caret + (insertion + spaceAfter + spaceBefore).length if (!keepOpen) { this.input.focus() } @@ -372,13 +391,20 @@ const EmojiInput = { this.caret = position }) }, - replaceText (e, suggestion) { + replaceText(e, suggestion) { const len = this.suggestions.length || 0 - if (this.textAtCaret.length === 1) { return } + if (this.textAtCaret.length === 1) { + return + } if (len > 0 || suggestion) { - const chosenSuggestion = suggestion || this.suggestions[this.highlighted] + const chosenSuggestion = + suggestion || this.suggestions[this.highlighted] const replacement = chosenSuggestion.replacement - const newValue = Completion.replaceWord(this.modelValue, this.wordAtCaret, replacement) + const newValue = Completion.replaceWord( + this.modelValue, + this.wordAtCaret, + replacement, + ) this.$emit('update:modelValue', newValue) this.highlighted = 0 const position = this.wordAtCaret.start + replacement.length @@ -393,7 +419,7 @@ const EmojiInput = { e.preventDefault() } }, - cycleBackward (e) { + cycleBackward(e) { const len = this.suggestions.length || 0 this.highlighted -= 1 @@ -406,7 +432,7 @@ const EmojiInput = { e.preventDefault() } }, - cycleForward (e) { + cycleForward(e) { const len = this.suggestions.length || 0 this.highlighted += 1 @@ -418,26 +444,28 @@ const EmojiInput = { e.preventDefault() } }, - scrollIntoView () { + scrollIntoView() { const rootRef = this.$refs.picker.$el /* Scroller is either `window` (replies in TL), sidebar (main post form, * replies in notifs) or mobile post form. Note that getting and setting * scroll is different for `Window` and `Element`s */ - const scrollerRef = this.$el.closest('.sidebar-scroller') || - this.$el.closest('.post-form-modal-view') || - window - const currentScroll = scrollerRef === window - ? scrollerRef.scrollY - : scrollerRef.scrollTop - const scrollerHeight = scrollerRef === window - ? scrollerRef.innerHeight - : scrollerRef.offsetHeight + const scrollerRef = + this.$el.closest('.sidebar-scroller') || + this.$el.closest('.post-form-modal-view') || + window + const currentScroll = + scrollerRef === window ? scrollerRef.scrollY : scrollerRef.scrollTop + const scrollerHeight = + scrollerRef === window + ? scrollerRef.innerHeight + : scrollerRef.offsetHeight const scrollerBottomBorder = currentScroll + scrollerHeight // We check where the bottom border of root element is, this uses findOffset // to find offset relative to scrollable container (scroller) - const rootBottomBorder = rootRef.offsetHeight + findOffset(rootRef, scrollerRef).top + const rootBottomBorder = + rootRef.offsetHeight + findOffset(rootRef, scrollerRef).top const bottomDelta = Math.max(0, rootBottomBorder - scrollerBottomBorder) // could also check top delta but there's no case for it @@ -459,13 +487,13 @@ const EmojiInput = { } }) }, - onPickerShown () { + onPickerShown() { this.pickerShown = true }, - onPickerClosed () { + onPickerClosed() { this.pickerShown = false }, - onBlur (e) { + onBlur(e) { // Clicking on any suggestion removes focus from autocomplete, // preventing click handler ever executing. this.blurTimeout = setTimeout(() => { @@ -473,10 +501,10 @@ const EmojiInput = { this.setCaret(e) }, 200) }, - onClick (e, suggestion) { + onClick(e, suggestion) { this.replaceText(e, suggestion) }, - onFocus (e) { + onFocus(e) { if (this.blurTimeout) { clearTimeout(this.blurTimeout) this.blurTimeout = null @@ -486,7 +514,7 @@ const EmojiInput = { this.setCaret(e) this.temporarilyHideSuggestions = false }, - onKeyUp (e) { + onKeyUp(e) { const { key } = e this.setCaret(e) @@ -498,10 +526,10 @@ const EmojiInput = { this.temporarilyHideSuggestions = false } }, - onPaste (e) { + onPaste(e) { this.setCaret(e) }, - onKeyDown (e) { + onKeyDown(e) { const { ctrlKey, shiftKey, key } = e if (this.newlineOnCtrlEnter && ctrlKey && key === 'Enter') { this.insert({ insertion: '\n', surroundingSpace: false }) @@ -545,30 +573,30 @@ const EmojiInput = { } } }, - onInput (e) { + onInput(e) { this.setCaret(e) this.$emit('update:modelValue', e.target.value) }, - onStickerUploaded (e) { + onStickerUploaded(e) { this.$emit('sticker-uploaded', e) }, - onStickerUploadFailed (e) { + onStickerUploadFailed(e) { this.$emit('sticker-upload-Failed', e) }, - setCaret ({ target: { selectionStart } }) { + setCaret({ target: { selectionStart } }) { this.caret = selectionStart this.$nextTick(() => { this.$refs.suggestorPopover.updateStyles() }) }, - autoCompleteItemLabel (suggestion) { + autoCompleteItemLabel(suggestion) { if (suggestion.user) { return suggestion.displayText + ' ' + suggestion.detailText } else { return this.maybeLocalizedEmojiName(suggestion) } - } - } + }, + }, } export default EmojiInput diff --git a/src/components/emoji_input/suggestor.js b/src/components/emoji_input/suggestor.js index f2daf2f46..79d97cff7 100644 --- a/src/components/emoji_input/suggestor.js +++ b/src/components/emoji_input/suggestor.js @@ -10,7 +10,7 @@ * doesn't support user linking you can just provide only emoji. */ -export default data => { +export default (data) => { const emojiCurry = suggestEmoji(data.emoji) const usersCurry = data.store && suggestUsers(data.store) return (input, nameKeywordLocalizer) => { @@ -25,22 +25,35 @@ export default data => { } } -export const suggestEmoji = emojis => (input, nameKeywordLocalizer) => { +export const suggestEmoji = (emojis) => (input, nameKeywordLocalizer) => { const noPrefix = input.toLowerCase().substr(1) return emojis - .map(emoji => ({ ...emoji, ...nameKeywordLocalizer(emoji) })) - .filter((emoji) => (emoji.names.concat(emoji.keywords)).filter(kw => kw.toLowerCase().match(noPrefix)).length) - .map(k => { + .map((emoji) => ({ ...emoji, ...nameKeywordLocalizer(emoji) })) + .filter( + (emoji) => + emoji.names + .concat(emoji.keywords) + .filter((kw) => kw.toLowerCase().match(noPrefix)).length, + ) + .map((k) => { let score = 0 // An exact match always wins - score += Math.max(...k.names.map(name => name.toLowerCase() === noPrefix ? 200 : 0), 0) + score += Math.max( + ...k.names.map((name) => (name.toLowerCase() === noPrefix ? 200 : 0)), + 0, + ) // Prioritize custom emoji a lot score += k.imageUrl ? 100 : 0 // Prioritize prefix matches somewhat - score += Math.max(...k.names.map(kw => kw.toLowerCase().startsWith(noPrefix) ? 10 : 0), 0) + score += Math.max( + ...k.names.map((kw) => + kw.toLowerCase().startsWith(noPrefix) ? 10 : 0, + ), + 0, + ) // Sort by length score -= k.displayText.length @@ -78,7 +91,7 @@ export const suggestUsers = ({ dispatch, state }) => { }) } - return async input => { + return async (input) => { const noPrefix = input.toLowerCase().substr(1) if (previousQuery === noPrefix) return suggestions @@ -92,37 +105,42 @@ export const suggestUsers = ({ dispatch, state }) => { await debounceUserSearch(noPrefix) } - const newSuggestions = state.users.users.filter( - user => - user.screen_name && user.name && ( - user.screen_name.toLowerCase().startsWith(noPrefix) || - user.name.toLowerCase().startsWith(noPrefix)) - ).slice(0, 20).sort((a, b) => { - let aScore = 0 - let bScore = 0 + const newSuggestions = state.users.users + .filter( + (user) => + user.screen_name && + user.name && + (user.screen_name.toLowerCase().startsWith(noPrefix) || + user.name.toLowerCase().startsWith(noPrefix)), + ) + .slice(0, 20) + .sort((a, b) => { + let aScore = 0 + let bScore = 0 - // Matches on screen name (i.e. user@instance) makes a priority - aScore += a.screen_name.toLowerCase().startsWith(noPrefix) ? 2 : 0 - bScore += b.screen_name.toLowerCase().startsWith(noPrefix) ? 2 : 0 + // Matches on screen name (i.e. user@instance) makes a priority + aScore += a.screen_name.toLowerCase().startsWith(noPrefix) ? 2 : 0 + bScore += b.screen_name.toLowerCase().startsWith(noPrefix) ? 2 : 0 - // Matches on name takes second priority - aScore += a.name.toLowerCase().startsWith(noPrefix) ? 1 : 0 - bScore += b.name.toLowerCase().startsWith(noPrefix) ? 1 : 0 + // Matches on name takes second priority + aScore += a.name.toLowerCase().startsWith(noPrefix) ? 1 : 0 + bScore += b.name.toLowerCase().startsWith(noPrefix) ? 1 : 0 - const diff = (bScore - aScore) * 10 + const diff = (bScore - aScore) * 10 - // Then sort alphabetically - const nameAlphabetically = a.name > b.name ? 1 : -1 - const screenNameAlphabetically = a.screen_name > b.screen_name ? 1 : -1 + // Then sort alphabetically + const nameAlphabetically = a.name > b.name ? 1 : -1 + const screenNameAlphabetically = a.screen_name > b.screen_name ? 1 : -1 - return diff + nameAlphabetically + screenNameAlphabetically - }).map((user) => ({ - user, - displayText: user.screen_name_ui, - detailText: user.name, - imageUrl: user.profile_image_url_original, - replacement: '@' + user.screen_name + ' ' - })) + return diff + nameAlphabetically + screenNameAlphabetically + }) + .map((user) => ({ + user, + displayText: user.screen_name_ui, + detailText: user.name, + imageUrl: user.profile_image_url_original, + replacement: '@' + user.screen_name + ' ', + })) suggestions = newSuggestions || [] return suggestions diff --git a/src/components/emoji_picker/emoji_picker.js b/src/components/emoji_picker/emoji_picker.js index 8e572d1d2..821187ef6 100644 --- a/src/components/emoji_picker/emoji_picker.js +++ b/src/components/emoji_picker/emoji_picker.js @@ -16,7 +16,7 @@ import { faBasketballBall, faLightbulb, faCode, - faFlag + faFlag, } from '@fortawesome/free-solid-svg-icons' import { debounce, trim, chunk } from 'lodash' @@ -32,7 +32,7 @@ library.add( faBasketballBall, faLightbulb, faCode, - faFlag + faFlag, ) const UNICODE_EMOJI_GROUP_ICON = { @@ -44,16 +44,16 @@ const UNICODE_EMOJI_GROUP_ICON = { activities: 'basketball-ball', objects: 'lightbulb', symbols: 'code', - flags: 'flag' + flags: 'flag', } const maybeLocalizedKeywords = (emoji, languages, nameLocalizer) => { const res = [emoji.displayText, nameLocalizer(emoji)] if (emoji.annotations) { - languages.forEach(lang => { + languages.forEach((lang) => { const keywords = emoji.annotations[lang]?.keywords || [] const name = emoji.annotations[lang]?.name - res.push(...(keywords.concat([name]).filter(k => k))) + res.push(...keywords.concat([name]).filter((k) => k)) }) } return res @@ -66,8 +66,8 @@ const filterByKeyword = (list, keyword = '', languages, nameLocalizer) => { const orderedEmojiList = [] for (const emoji of list) { const indices = maybeLocalizedKeywords(emoji, languages, nameLocalizer) - .map(k => k.toLowerCase().indexOf(keywordLowercase)) - .filter(k => k > -1) + .map((k) => k.toLowerCase().indexOf(keywordLowercase)) + .filter((k) => k > -1) const indexOfKeyword = indices.length ? Math.min(...indices) : -1 @@ -84,11 +84,13 @@ const filterByKeyword = (list, keyword = '', languages, nameLocalizer) => { const getOffset = (elem) => { const style = elem.style.transform const res = /translateY\((\d+)px\)/.exec(style) - if (!res) { return 0 } + if (!res) { + return 0 + } return res[1] } -const toHeaderId = id => { +const toHeaderId = (id) => { return id.replace(/^row-\d+-/, '') } @@ -97,20 +99,20 @@ const EmojiPicker = { enableStickerPicker: { required: false, type: Boolean, - default: true + default: true, }, hideCustomEmoji: { required: false, type: Boolean, - default: false - } + default: false, + }, }, inject: { popoversZLayer: { - default: '' - } + default: '', + }, }, - data () { + data() { return { keyword: '', activeGroup: 'custom', @@ -125,20 +127,22 @@ const EmojiPicker = { emojiRefs: {}, filteredEmojiGroups: [], emojiSize: 0, - width: 0 + width: 0, } }, components: { - StickerPicker: defineAsyncComponent(() => import('../sticker_picker/sticker_picker.vue')), + StickerPicker: defineAsyncComponent( + () => import('../sticker_picker/sticker_picker.vue'), + ), Checkbox, StillImage, - Popover + Popover, }, methods: { - groupScroll (e) { + groupScroll(e) { e.currentTarget.scrollLeft += e.deltaY + e.deltaX }, - updateEmojiSize () { + updateEmojiSize() { const css = window.getComputedStyle(this.$refs.popover.$el) const fontSize = css.getPropertyValue('font-size') || '1rem' const emojiSize = css.getPropertyValue('--emojiSize') || '2.2rem' @@ -163,56 +167,68 @@ const EmojiPicker = { emojiSizeReal = emojiSizeValue } - const fullEmojiSize = emojiSizeReal + (2 * 0.2 * fontSizeMultiplier * 14) + const fullEmojiSize = emojiSizeReal + 2 * 0.2 * fontSizeMultiplier * 14 this.emojiSize = fullEmojiSize }, - showPicker () { + showPicker() { this.$refs.popover.showPopover() this.$nextTick(() => { this.onShowing() }) }, - hidePicker () { + hidePicker() { this.$refs.popover.hidePopover() }, - setAnchorEl (el) { + setAnchorEl(el) { this.$refs.popover.setAnchorEl(el) }, - setGroupRef (name) { - return el => { this.groupRefs[name] = el } + setGroupRef(name) { + return (el) => { + this.groupRefs[name] = el + } }, - onPopoverShown () { + onPopoverShown() { this.$emit('show') }, - onPopoverClosed () { + onPopoverClosed() { this.$emit('close') }, - onStickerUploaded (e) { + onStickerUploaded(e) { this.$emit('sticker-uploaded', e) }, - onStickerUploadFailed (e) { + onStickerUploadFailed(e) { this.$emit('sticker-upload-failed', e) }, - onEmoji (emoji) { - const value = emoji.imageUrl ? `:${emoji.displayText}:` : emoji.replacement + onEmoji(emoji) { + const value = emoji.imageUrl + ? `:${emoji.displayText}:` + : emoji.replacement if (!this.keepOpen) { this.$refs.popover.hidePopover() } - this.$emit('emoji', { insertion: value, insertionUrl: emoji.imageUrl, keepOpen: this.keepOpen }) + this.$emit('emoji', { + insertion: value, + insertionUrl: emoji.imageUrl, + keepOpen: this.keepOpen, + }) }, - onScroll (startIndex, endIndex, visibleStartIndex, visibleEndIndex) { + onScroll(startIndex, endIndex, visibleStartIndex, visibleEndIndex) { const target = this.$refs['emoji-groups'].$el this.scrolledGroup(target, visibleStartIndex, visibleEndIndex) }, - scrolledGroup (target, start, end) { + scrolledGroup(target, start, end) { const top = target.scrollTop + 5 this.$nextTick(() => { - this.emojiItems.slice(start, end + 1).forEach(group => { + this.emojiItems.slice(start, end + 1).forEach((group) => { const headerId = toHeaderId(group.id) const ref = this.groupRefs['group-' + group.id] - if (!ref) { return } + if (!ref) { + return + } const elem = ref.$el.parentElement - if (!elem) { return } + if (!elem) { + return + } if (elem && getOffset(elem) <= top) { this.activeGroup = headerId } @@ -220,7 +236,7 @@ const EmojiPicker = { this.scrollHeader() }) }, - scrollHeader () { + scrollHeader() { // Scroll the active tab's header into view const headerRef = this.groupRefs['group-header-' + this.activeGroup] const left = headerRef.offsetLeft @@ -228,7 +244,9 @@ const EmojiPicker = { const headerCont = this.$refs.header const currentScroll = headerCont.scrollLeft const currentScrollRight = currentScroll + headerCont.clientWidth - const setScroll = s => { headerCont.scrollLeft = s } + const setScroll = (s) => { + headerCont.scrollLeft = s + } const margin = 7 // .emoji-tabs-item: padding if (left - margin < currentScroll) { @@ -237,12 +255,12 @@ const EmojiPicker = { setScroll(right + margin - headerCont.clientWidth) } }, - highlight (groupId) { + highlight(groupId) { this.setShowStickers(false) - const indexInList = this.emojiItems.findIndex(k => k.id === groupId) + const indexInList = this.emojiItems.findIndex((k) => k.id === groupId) this.$refs['emoji-groups'].scrollToItem(indexInList) }, - updateScrolledClass (target) { + updateScrolledClass(target) { if (target.scrollTop <= 5) { this.groupsScrolledClass = 'scrolled-top' } else if (target.scrollTop >= target.scrollTopMax - 5) { @@ -251,16 +269,21 @@ const EmojiPicker = { this.groupsScrolledClass = 'scrolled-middle' } }, - toggleStickers () { + toggleStickers() { this.showingStickers = !this.showingStickers }, - setShowStickers (value) { + setShowStickers(value) { this.showingStickers = value }, - filterByKeyword (list, keyword) { - return filterByKeyword(list, keyword, this.languages, this.maybeLocalizedEmojiName) + filterByKeyword(list, keyword) { + return filterByKeyword( + list, + keyword, + this.languages, + this.maybeLocalizedEmojiName, + ) }, - onShowing () { + onShowing() { const oldContentLoaded = this.contentLoaded this.updateEmojiSize() this.recalculateItemPerRow() @@ -277,59 +300,59 @@ const EmojiPicker = { }) } }, - getFilteredEmojiGroups () { + getFilteredEmojiGroups() { return this.allEmojiGroups - .map(group => ({ + .map((group) => ({ ...group, - emojis: this.filterByKeyword(group.emojis, trim(this.keyword)) + emojis: this.filterByKeyword(group.emojis, trim(this.keyword)), })) - .filter(group => group.emojis.length > 0) + .filter((group) => group.emojis.length > 0) }, - recalculateItemPerRow () { + recalculateItemPerRow() { this.$nextTick(() => { if (!this.$refs['emoji-groups']) { return } this.width = this.$refs['emoji-groups'].$el.clientWidth }) - } + }, }, watch: { - keyword () { + keyword() { this.onScroll() this.debouncedHandleKeywordChange() }, - allCustomGroups () { + allCustomGroups() { this.filteredEmojiGroups = this.getFilteredEmojiGroups() - } + }, }, computed: { - minItemSize () { + minItemSize() { return this.emojiSize }, // used to watch it - fontSize () { + fontSize() { this.$nextTick(() => { this.updateEmojiSize() }) return this.$store.getters.mergedConfig.fontSize }, - emojiHeight () { + emojiHeight() { return this.emojiSize }, - itemPerRow () { + itemPerRow() { return this.width ? Math.floor(this.width / this.emojiSize) : 6 }, - activeGroupView () { + activeGroupView() { return this.showingStickers ? '' : this.activeGroup }, - stickersAvailable () { + stickersAvailable() { if (this.$store.state.instance.stickers) { return this.$store.state.instance.stickers.length > 0 } return 0 }, - allCustomGroups () { + allCustomGroups() { if (this.hideCustomEmoji || this.hideCustomEmojiInPicker) { return {} } @@ -339,46 +362,49 @@ const EmojiPicker = { } return emojis }, - defaultGroup () { + defaultGroup() { return Object.keys(this.allCustomGroups)[0] }, - unicodeEmojiGroups () { - return this.$store.getters.standardEmojiGroupList.map(group => ({ + unicodeEmojiGroups() { + return this.$store.getters.standardEmojiGroupList.map((group) => ({ id: `standard-${group.id}`, text: this.$t(`emoji.unicode_groups.${group.id}`), icon: UNICODE_EMOJI_GROUP_ICON[group.id], - emojis: group.emojis + emojis: group.emojis, })) }, - allEmojiGroups () { + allEmojiGroups() { return Object.entries(this.allCustomGroups) .map(([, v]) => v) .concat(this.unicodeEmojiGroups) }, - stickerPickerEnabled () { + stickerPickerEnabled() { return (this.$store.state.instance.stickers || []).length !== 0 }, - debouncedHandleKeywordChange () { + debouncedHandleKeywordChange() { return debounce(() => { this.filteredEmojiGroups = this.getFilteredEmojiGroups() }, 500) }, - emojiItems () { - return this.filteredEmojiGroups.map(group => - chunk(group.emojis, this.itemPerRow) - .map((items, index) => ({ + emojiItems() { + return this.filteredEmojiGroups + .map((group) => + chunk(group.emojis, this.itemPerRow).map((items, index) => ({ ...group, id: index === 0 ? group.id : `row-${index}-${group.id}`, emojis: items, - isFirstRow: index === 0 - }))) + isFirstRow: index === 0, + })), + ) .reduce((a, c) => a.concat(c), []) }, - languages () { - return ensureFinalFallback(this.$store.getters.mergedConfig.interfaceLanguage) + languages() { + return ensureFinalFallback( + this.$store.getters.mergedConfig.interfaceLanguage, + ) }, - maybeLocalizedEmojiName () { - return emoji => { + maybeLocalizedEmojiName() { + return (emoji) => { if (!emoji.annotations) { return emoji.displayText } @@ -396,10 +422,10 @@ const EmojiPicker = { return emoji.displayText } }, - isInModal () { + isInModal() { return this.popoversZLayer === 'modals' - } - } + }, + }, } export default EmojiPicker diff --git a/src/components/emoji_reactions/emoji_reactions.js b/src/components/emoji_reactions/emoji_reactions.js index f5e1b68f6..d15a13489 100644 --- a/src/components/emoji_reactions/emoji_reactions.js +++ b/src/components/emoji_reactions/emoji_reactions.js @@ -2,17 +2,9 @@ import UserAvatar from '../user_avatar/user_avatar.vue' import UserListPopover from '../user_list_popover/user_list_popover.vue' import StillImage from 'src/components/still-image/still-image.vue' import { library } from '@fortawesome/fontawesome-svg-core' -import { - faPlus, - faMinus, - faCheck -} from '@fortawesome/free-solid-svg-icons' +import { faPlus, faMinus, faCheck } from '@fortawesome/free-solid-svg-icons' -library.add( - faPlus, - faMinus, - faCheck -) +library.add(faPlus, faMinus, faCheck) const EMOJI_REACTION_COUNT_CUTOFF = 12 @@ -21,57 +13,62 @@ const EmojiReactions = { components: { UserAvatar, UserListPopover, - StillImage + StillImage, }, props: ['status'], data: () => ({ - showAll: false + showAll: false, }), computed: { - tooManyReactions () { + tooManyReactions() { return this.status.emoji_reactions.length > EMOJI_REACTION_COUNT_CUTOFF }, - emojiReactions () { + emojiReactions() { return this.showAll ? this.status.emoji_reactions : this.status.emoji_reactions.slice(0, EMOJI_REACTION_COUNT_CUTOFF) }, - showMoreString () { + showMoreString() { return `+${this.status.emoji_reactions.length - EMOJI_REACTION_COUNT_CUTOFF}` }, - accountsForEmoji () { + accountsForEmoji() { return this.status.emoji_reactions.reduce((acc, reaction) => { acc[reaction.name] = reaction.accounts || [] return acc }, {}) }, - loggedIn () { + loggedIn() { return !!this.$store.state.users.currentUser }, - remoteInteractionLink () { - return this.$store.getters.remoteInteractionLink({ statusId: this.status.id }) - } + remoteInteractionLink() { + return this.$store.getters.remoteInteractionLink({ + statusId: this.status.id, + }) + }, }, methods: { - toggleShowAll () { + toggleShowAll() { this.showAll = !this.showAll }, - reactedWith (emoji) { - return this.status.emoji_reactions.find(r => r.name === emoji).me + reactedWith(emoji) { + return this.status.emoji_reactions.find((r) => r.name === emoji).me }, - async fetchEmojiReactionsByIfMissing () { - const hasNoAccounts = this.status.emoji_reactions.find(r => !r.accounts) + async fetchEmojiReactionsByIfMissing() { + const hasNoAccounts = this.status.emoji_reactions.find((r) => !r.accounts) if (hasNoAccounts) { - return await this.$store.dispatch('fetchEmojiReactionsBy', this.status.id) + return await this.$store.dispatch( + 'fetchEmojiReactionsBy', + this.status.id, + ) } }, - reactWith (emoji) { + reactWith(emoji) { this.$store.dispatch('reactWithEmoji', { id: this.status.id, emoji }) }, - unreact (emoji) { + unreact(emoji) { this.$store.dispatch('unreactWithEmoji', { id: this.status.id, emoji }) }, - async emojiOnClick (emoji) { + async emojiOnClick(emoji) { if (!this.loggedIn) return await this.fetchEmojiReactionsByIfMissing() @@ -81,19 +78,23 @@ const EmojiReactions = { this.reactWith(emoji) } }, - counterTriggerAttrs (reaction) { + counterTriggerAttrs(reaction) { return { class: [ 'emoji-reaction-count-button', { '-picked-reaction': this.reactedWith(reaction.name), - toggled: this.reactedWith(reaction.name) - } + toggled: this.reactedWith(reaction.name), + }, ], - 'aria-label': this.$t('status.reaction_count_label', { num: reaction.count }, reaction.count) + 'aria-label': this.$t( + 'status.reaction_count_label', + { num: reaction.count }, + reaction.count, + ), } - } - } + }, + }, } export default EmojiReactions diff --git a/src/components/exporter/exporter.js b/src/components/exporter/exporter.js index fc75372e3..12213d9e3 100644 --- a/src/components/exporter/exporter.js +++ b/src/components/exporter/exporter.js @@ -1,45 +1,47 @@ import { library } from '@fortawesome/fontawesome-svg-core' import { faCircleNotch } from '@fortawesome/free-solid-svg-icons' -library.add( - faCircleNotch -) +library.add(faCircleNotch) const Exporter = { props: { getContent: { type: Function, - required: true + required: true, }, filename: { type: String, - default: 'export.csv' + default: 'export.csv', }, exportButtonLabel: { type: String }, - processingMessage: { type: String } + processingMessage: { type: String }, }, - data () { + data() { return { - processing: false + processing: false, } }, methods: { - process () { + process() { this.processing = true - this.getContent() - .then((content) => { - const fileToDownload = document.createElement('a') - fileToDownload.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(content)) - fileToDownload.setAttribute('download', this.filename) - fileToDownload.style.display = 'none' - document.body.appendChild(fileToDownload) - fileToDownload.click() - document.body.removeChild(fileToDownload) - // Add delay before hiding processing state since browser takes some time to handle file download - setTimeout(() => { this.processing = false }, 2000) - }) - } - } + this.getContent().then((content) => { + const fileToDownload = document.createElement('a') + fileToDownload.setAttribute( + 'href', + 'data:text/plain;charset=utf-8,' + encodeURIComponent(content), + ) + fileToDownload.setAttribute('download', this.filename) + fileToDownload.style.display = 'none' + document.body.appendChild(fileToDownload) + fileToDownload.click() + document.body.removeChild(fileToDownload) + // Add delay before hiding processing state since browser takes some time to handle file download + setTimeout(() => { + this.processing = false + }, 2000) + }) + }, + }, } export default Exporter diff --git a/src/components/extra_notifications/extra_notifications.js b/src/components/extra_notifications/extra_notifications.js index 673b28662..b8ead5fe8 100644 --- a/src/components/extra_notifications/extra_notifications.js +++ b/src/components/extra_notifications/extra_notifications.js @@ -6,50 +6,67 @@ import { library } from '@fortawesome/fontawesome-svg-core' import { faUserPlus, faComments, - faBullhorn + faBullhorn, } from '@fortawesome/free-solid-svg-icons' import { useInterfaceStore } from 'src/stores/interface' -library.add( - faUserPlus, - faComments, - faBullhorn -) +library.add(faUserPlus, faComments, faBullhorn) const ExtraNotifications = { computed: { - shouldShowChats () { - return this.mergedConfig.showExtraNotifications && this.mergedConfig.showChatsInExtraNotifications && this.unreadChatCount + shouldShowChats() { + return ( + this.mergedConfig.showExtraNotifications && + this.mergedConfig.showChatsInExtraNotifications && + this.unreadChatCount + ) }, - shouldShowAnnouncements () { - return this.mergedConfig.showExtraNotifications && this.mergedConfig.showAnnouncementsInExtraNotifications && this.unreadAnnouncementCount + shouldShowAnnouncements() { + return ( + this.mergedConfig.showExtraNotifications && + this.mergedConfig.showAnnouncementsInExtraNotifications && + this.unreadAnnouncementCount + ) }, - shouldShowFollowRequests () { - return this.mergedConfig.showExtraNotifications && this.mergedConfig.showFollowRequestsInExtraNotifications && this.followRequestCount + shouldShowFollowRequests() { + return ( + this.mergedConfig.showExtraNotifications && + this.mergedConfig.showFollowRequestsInExtraNotifications && + this.followRequestCount + ) }, - hasAnythingToShow () { - return this.shouldShowChats || this.shouldShowAnnouncements || this.shouldShowFollowRequests + hasAnythingToShow() { + return ( + this.shouldShowChats || + this.shouldShowAnnouncements || + this.shouldShowFollowRequests + ) }, - shouldShowCustomizationTip () { - return this.mergedConfig.showExtraNotificationsTip && this.hasAnythingToShow + shouldShowCustomizationTip() { + return ( + this.mergedConfig.showExtraNotificationsTip && this.hasAnythingToShow + ) }, - currentUser () { + currentUser() { return this.$store.state.users.currentUser }, ...mapGetters(['unreadChatCount', 'followRequestCount', 'mergedConfig']), ...mapPiniaState(useAnnouncementsStore, { - unreadAnnouncementCount: 'unreadAnnouncementCount' - }) + unreadAnnouncementCount: 'unreadAnnouncementCount', + }), }, methods: { - openNotificationSettings () { + openNotificationSettings() { return useInterfaceStore().openSettingsModalTab('notifications') }, - dismissConfigurationTip () { - return this.$store.dispatch('setOption', { name: 'showExtraNotificationsTip', value: false }) - } - } + dismissConfigurationTip() { + return this.$store.dispatch('setOption', { + name: 'showExtraNotificationsTip', + value: false, + }) + }, + }, } export default ExtraNotifications diff --git a/src/components/features_panel/features_panel.js b/src/components/features_panel/features_panel.js index d177efebe..e92cb975e 100644 --- a/src/components/features_panel/features_panel.js +++ b/src/components/features_panel/features_panel.js @@ -2,15 +2,33 @@ import fileSizeFormatService from '../../services/file_size_format/file_size_for const FeaturesPanel = { computed: { - shout: function () { return this.$store.state.instance.shoutAvailable }, - pleromaChatMessages: function () { return this.$store.state.instance.pleromaChatMessagesAvailable }, - gopher: function () { return this.$store.state.instance.gopherAvailable }, - whoToFollow: function () { return this.$store.state.instance.suggestionsEnabled }, - mediaProxy: function () { return this.$store.state.instance.mediaProxyAvailable }, - minimalScopesMode: function () { return this.$store.state.instance.minimalScopesMode }, - textlimit: function () { return this.$store.state.instance.textlimit }, - uploadlimit: function () { return fileSizeFormatService.fileSizeFormat(this.$store.state.instance.uploadlimit) } - } + shout: function () { + return this.$store.state.instance.shoutAvailable + }, + pleromaChatMessages: function () { + return this.$store.state.instance.pleromaChatMessagesAvailable + }, + gopher: function () { + return this.$store.state.instance.gopherAvailable + }, + whoToFollow: function () { + return this.$store.state.instance.suggestionsEnabled + }, + mediaProxy: function () { + return this.$store.state.instance.mediaProxyAvailable + }, + minimalScopesMode: function () { + return this.$store.state.instance.minimalScopesMode + }, + textlimit: function () { + return this.$store.state.instance.textlimit + }, + uploadlimit: function () { + return fileSizeFormatService.fileSizeFormat( + this.$store.state.instance.uploadlimit, + ) + }, + }, } export default FeaturesPanel diff --git a/src/components/flash/flash.js b/src/components/flash/flash.js index 87c1d650c..fea63c79a 100644 --- a/src/components/flash/flash.js +++ b/src/components/flash/flash.js @@ -2,52 +2,52 @@ import RuffleService from '../../services/ruffle_service/ruffle_service.js' import { library } from '@fortawesome/fontawesome-svg-core' import { faStop, - faExclamationTriangle + faExclamationTriangle, } from '@fortawesome/free-solid-svg-icons' -library.add( - faStop, - faExclamationTriangle -) +library.add(faStop, faExclamationTriangle) const Flash = { props: ['src'], - data () { + data() { return { player: false, // can be true, "hidden", false. hidden = element exists loaded: false, - ruffleInstance: null + ruffleInstance: null, } }, methods: { - openPlayer () { + openPlayer() { if (this.player) return // prevent double-loading, or re-loading on failure this.player = 'hidden' RuffleService.getRuffle().then((ruffle) => { const player = ruffle.newest().createPlayer() player.config = { - letterbox: 'on' + letterbox: 'on', } const container = this.$refs.container container.appendChild(player) player.style.width = '100%' player.style.height = '100%' - player.load(this.src).then(() => { - this.player = true - }).catch((e) => { - console.error('Error loading ruffle', e) - this.player = 'error' - }) + player + .load(this.src) + .then(() => { + this.player = true + }) + .catch((e) => { + console.error('Error loading ruffle', e) + this.player = 'error' + }) this.ruffleInstance = player this.$emit('playerOpened') }) }, - closePlayer () { + closePlayer() { this.ruffleInstance && this.ruffleInstance.remove() this.player = false this.$emit('playerClosed') - } - } + }, + }, } export default Flash diff --git a/src/components/follow_button/follow_button.js b/src/components/follow_button/follow_button.js index 443aa9bcc..78158ddd3 100644 --- a/src/components/follow_button/follow_button.js +++ b/src/components/follow_button/follow_button.js @@ -1,24 +1,27 @@ import ConfirmModal from '../confirm_modal/confirm_modal.vue' -import { requestFollow, requestUnfollow } from '../../services/follow_manipulate/follow_manipulate' +import { + requestFollow, + requestUnfollow, +} from '../../services/follow_manipulate/follow_manipulate' export default { props: ['relationship', 'user', 'labelFollowing', 'buttonClass'], components: { - ConfirmModal + ConfirmModal, }, - data () { + data() { return { inProgress: false, - showingConfirmUnfollow: false + showingConfirmUnfollow: false, } }, computed: { - shouldConfirmUnfollow () { + shouldConfirmUnfollow() { return this.$store.getters.mergedConfig.modalOnUnfollow }, - isPressed () { + isPressed() { return this.inProgress || this.relationship.following }, - title () { + title() { if (this.inProgress || this.relationship.following) { return this.$t('user_card.follow_unfollow') } else if (this.relationship.requested) { @@ -27,7 +30,7 @@ export default { return this.$t('user_card.follow') } }, - label () { + label() { if (this.inProgress) { return this.$t('user_card.follow_progress') } else if (this.relationship.following) { @@ -38,42 +41,47 @@ export default { return this.$t('user_card.follow') } }, - disabled () { + disabled() { return this.inProgress || this.user.deactivated - } + }, }, methods: { - showConfirmUnfollow () { + showConfirmUnfollow() { this.showingConfirmUnfollow = true }, - hideConfirmUnfollow () { + hideConfirmUnfollow() { this.showingConfirmUnfollow = false }, - onClick () { - this.relationship.following || this.relationship.requested ? this.unfollow() : this.follow() + onClick() { + this.relationship.following || this.relationship.requested + ? this.unfollow() + : this.follow() }, - follow () { + follow() { this.inProgress = true requestFollow(this.relationship.id, this.$store).then(() => { this.inProgress = false }) }, - unfollow () { + unfollow() { if (this.shouldConfirmUnfollow) { this.showConfirmUnfollow() } else { this.doUnfollow() } }, - doUnfollow () { + doUnfollow() { const store = this.$store this.inProgress = true requestUnfollow(this.relationship.id, store).then(() => { this.inProgress = false - store.commit('removeStatus', { timeline: 'friends', userId: this.relationship.id }) + store.commit('removeStatus', { + timeline: 'friends', + userId: this.relationship.id, + }) }) this.hideConfirmUnfollow() - } - } + }, + }, } diff --git a/src/components/follow_card/follow_card.js b/src/components/follow_card/follow_card.js index b26b27a74..0b54e0806 100644 --- a/src/components/follow_card/follow_card.js +++ b/src/components/follow_card/follow_card.js @@ -4,27 +4,24 @@ import FollowButton from '../follow_button/follow_button.vue' import RemoveFollowerButton from '../remove_follower_button/remove_follower_button.vue' const FollowCard = { - props: [ - 'user', - 'noFollowsYou' - ], + props: ['user', 'noFollowsYou'], components: { BasicUserCard, RemoteFollow, FollowButton, - RemoveFollowerButton + RemoveFollowerButton, }, computed: { - isMe () { + isMe() { return this.$store.state.users.currentUser.id === this.user.id }, - loggedIn () { + loggedIn() { return this.$store.state.users.currentUser }, - relationship () { + relationship() { return this.$store.getters.relationship(this.user.id) - } - } + }, + }, } export default FollowCard diff --git a/src/components/follow_request_card/follow_request_card.js b/src/components/follow_request_card/follow_request_card.js index b0873bb10..ee55e509f 100644 --- a/src/components/follow_request_card/follow_request_card.js +++ b/src/components/follow_request_card/follow_request_card.js @@ -6,41 +6,43 @@ const FollowRequestCard = { props: ['user'], components: { BasicUserCard, - ConfirmModal + ConfirmModal, }, - data () { + data() { return { showingApproveConfirmDialog: false, - showingDenyConfirmDialog: false + showingDenyConfirmDialog: false, } }, methods: { - findFollowRequestNotificationId () { + findFollowRequestNotificationId() { const notif = notificationsFromStore(this.$store).find( - (notif) => notif.from_profile.id === this.user.id && notif.type === 'follow_request' + (notif) => + notif.from_profile.id === this.user.id && + notif.type === 'follow_request', ) return notif && notif.id }, - showApproveConfirmDialog () { + showApproveConfirmDialog() { this.showingApproveConfirmDialog = true }, - hideApproveConfirmDialog () { + hideApproveConfirmDialog() { this.showingApproveConfirmDialog = false }, - showDenyConfirmDialog () { + showDenyConfirmDialog() { this.showingDenyConfirmDialog = true }, - hideDenyConfirmDialog () { + hideDenyConfirmDialog() { this.showingDenyConfirmDialog = false }, - approveUser () { + approveUser() { if (this.shouldConfirmApprove) { this.showApproveConfirmDialog() } else { this.doApprove() } }, - doApprove () { + doApprove() { this.$store.state.api.backendInteractor.approveUser({ id: this.user.id }) this.$store.dispatch('removeFollowRequest', this.user) @@ -48,40 +50,41 @@ const FollowRequestCard = { this.$store.dispatch('markSingleNotificationAsSeen', { id: notifId }) this.$store.dispatch('updateNotification', { id: notifId, - updater: notification => { + updater: (notification) => { notification.type = 'follow' - } + }, }) this.hideApproveConfirmDialog() }, - denyUser () { + denyUser() { if (this.shouldConfirmDeny) { this.showDenyConfirmDialog() } else { this.doDeny() } }, - doDeny () { + doDeny() { const notifId = this.findFollowRequestNotificationId() - this.$store.state.api.backendInteractor.denyUser({ id: this.user.id }) + this.$store.state.api.backendInteractor + .denyUser({ id: this.user.id }) .then(() => { this.$store.dispatch('dismissNotificationLocal', { id: notifId }) this.$store.dispatch('removeFollowRequest', this.user) }) this.hideDenyConfirmDialog() - } + }, }, computed: { - mergedConfig () { + mergedConfig() { return this.$store.getters.mergedConfig }, - shouldConfirmApprove () { + shouldConfirmApprove() { return this.mergedConfig.modalOnApproveFollow }, - shouldConfirmDeny () { + shouldConfirmDeny() { return this.mergedConfig.modalOnDenyFollow - } - } + }, + }, } export default FollowRequestCard diff --git a/src/components/follow_requests/follow_requests.js b/src/components/follow_requests/follow_requests.js index 704a76c66..7dbecee53 100644 --- a/src/components/follow_requests/follow_requests.js +++ b/src/components/follow_requests/follow_requests.js @@ -2,13 +2,13 @@ import FollowRequestCard from '../follow_request_card/follow_request_card.vue' const FollowRequests = { components: { - FollowRequestCard + FollowRequestCard, }, computed: { - requests () { + requests() { return this.$store.state.api.followRequests - } - } + }, + }, } export default FollowRequests diff --git a/src/components/font_control/font_control.js b/src/components/font_control/font_control.js index ffc866788..1db23ad43 100644 --- a/src/components/font_control/font_control.js +++ b/src/components/font_control/font_control.js @@ -7,29 +7,23 @@ import { library } from '@fortawesome/fontawesome-svg-core' import { faExclamationTriangle, faKeyboard, - faFont + faFont, } from '@fortawesome/free-solid-svg-icons' -library.add( - faExclamationTriangle, - faKeyboard, - faFont -) +library.add(faExclamationTriangle, faKeyboard, faFont) export default { components: { Select, Checkbox, - Popover + Popover, }, - props: [ - 'name', 'label', 'modelValue', 'fallback', 'options', 'no-inherit' - ], - mounted () { + props: ['name', 'label', 'modelValue', 'fallback', 'options', 'no-inherit'], + mounted() { useInterfaceStore().queryLocalFonts() }, emits: ['update:modelValue'], - data () { + data() { return { manualEntry: false, availableOptions: [ @@ -37,24 +31,24 @@ export default { 'serif', 'sans-serif', 'monospace', - ...(this.options || []) - ].filter(_ => _) + ...(this.options || []), + ].filter((_) => _), } }, methods: { - toggleManualEntry () { + toggleManualEntry() { this.manualEntry = !this.manualEntry - } + }, }, computed: { - present () { + present() { return typeof this.modelValue !== 'undefined' }, - localFontsList () { + localFontsList() { return useInterfaceStore().localFonts }, - localFontsSize () { + localFontsSize() { return useInterfaceStore().localFonts?.length - } - } + }, + }, } diff --git a/src/components/friends_timeline/friends_timeline.js b/src/components/friends_timeline/friends_timeline.js index 948b23a49..e990ff406 100644 --- a/src/components/friends_timeline/friends_timeline.js +++ b/src/components/friends_timeline/friends_timeline.js @@ -1,11 +1,13 @@ import Timeline from '../timeline/timeline.vue' const FriendsTimeline = { components: { - Timeline + Timeline, }, computed: { - timeline () { return this.$store.state.statuses.timelines.friends } - } + timeline() { + return this.$store.state.statuses.timelines.friends + }, + }, } export default FriendsTimeline diff --git a/src/components/fun_text.style.js b/src/components/fun_text.style.js index 2d3ac1549..eff88feb7 100644 --- a/src/components/fun_text.style.js +++ b/src/components/fun_text.style.js @@ -4,37 +4,37 @@ export default { virtual: true, variants: { greentext: '.greentext', - cyantext: '.cyantext' + cyantext: '.cyantext', }, states: { - faint: '.faint' + faint: '.faint', }, defaultRules: [ { directives: { textColor: '--text', - textAuto: 'preserve' - } + textAuto: 'preserve', + }, }, { state: ['faint'], directives: { - textOpacity: 0.5 - } + textOpacity: 0.5, + }, }, { variant: 'greentext', directives: { textColor: '--cGreen', - textAuto: 'preserve' - } + textAuto: 'preserve', + }, }, { variant: 'cyantext', directives: { textColor: '--cBlue', - textAuto: 'preserve' - } - } - ] + textAuto: 'preserve', + }, + }, + ], } diff --git a/src/components/gallery/gallery.js b/src/components/gallery/gallery.js index 676c830b0..40e57fc0c 100644 --- a/src/components/gallery/gallery.js +++ b/src/components/gallery/gallery.js @@ -17,52 +17,71 @@ const Gallery = { 'shiftUpAttachment', 'shiftDnAttachment', 'editAttachment', - 'grid' + 'grid', ], - data () { + data() { return { sizes: {}, - hidingLong: true + hidingLong: true, } }, components: { Attachment }, computed: { - rows () { + rows() { if (!this.attachments) { return [] } - const attachments = this.limit > 0 - ? this.attachments.slice(0, this.limit) - : this.attachments + const attachments = + this.limit > 0 + ? this.attachments.slice(0, this.limit) + : this.attachments if (this.size === 'hide') { - return attachments.map(item => ({ minimal: true, items: [item] })) + return attachments.map((item) => ({ minimal: true, items: [item] })) } const rows = this.grid ? [{ grid: true, items: attachments }] - : attachments.reduce((acc, attachment, i) => { - if (attachment.mimetype.includes('audio')) { - return [...acc, { audio: true, items: [attachment] }, { items: [] }] - } - if (!( - attachment.mimetype.includes('image') || - attachment.mimetype.includes('video') || - attachment.mimetype.includes('flash') - )) { - return [...acc, { minimal: true, items: [attachment] }, { items: [] }] - } - const maxPerRow = 3 - const attachmentsRemaining = this.attachments.length - i + 1 - const currentRow = acc[acc.length - 1].items - currentRow.push(attachment) - if (currentRow.length >= maxPerRow && attachmentsRemaining > maxPerRow) { - return [...acc, { items: [] }] - } else { - return acc - } - }, [{ items: [] }]).filter(_ => _.items.length > 0) + : attachments + .reduce( + (acc, attachment, i) => { + if (attachment.mimetype.includes('audio')) { + return [ + ...acc, + { audio: true, items: [attachment] }, + { items: [] }, + ] + } + if ( + !( + attachment.mimetype.includes('image') || + attachment.mimetype.includes('video') || + attachment.mimetype.includes('flash') + ) + ) { + return [ + ...acc, + { minimal: true, items: [attachment] }, + { items: [] }, + ] + } + const maxPerRow = 3 + const attachmentsRemaining = this.attachments.length - i + 1 + const currentRow = acc[acc.length - 1].items + currentRow.push(attachment) + if ( + currentRow.length >= maxPerRow && + attachmentsRemaining > maxPerRow + ) { + return [...acc, { items: [] }] + } else { + return acc + } + }, + [{ items: [] }], + ) + .filter((_) => _.items.length > 0) return rows }, - attachmentsDimensionalScore () { + attachmentsDimensionalScore() { return this.rows.reduce((acc, row) => { let size = 0 if (row.minimal) { @@ -75,7 +94,7 @@ const Gallery = { return acc + size }, 0) }, - tooManyAttachments () { + tooManyAttachments() { if (this.editable || this.size === 'small') { return false } else if (this.size === 'hide') { @@ -83,38 +102,38 @@ const Gallery = { } else { return this.attachmentsDimensionalScore > 1 } - } + }, }, methods: { - onNaturalSizeLoad ({ id, width, height }) { + onNaturalSizeLoad({ id, width, height }) { set(this.sizes, id, { width, height }) }, - rowStyle (row) { + rowStyle(row) { if (row.audio) { return { 'padding-bottom': '25%' } // fixed reduced height for audio } else if (!row.minimal && !row.grid) { - return { 'padding-bottom': `${(100 / (row.items.length + 0.6))}%` } + return { 'padding-bottom': `${100 / (row.items.length + 0.6)}%` } } }, - itemStyle (id, row) { - const total = sumBy(row, item => this.getAspectRatio(item.id)) + itemStyle(id, row) { + const total = sumBy(row, (item) => this.getAspectRatio(item.id)) return { flex: `${this.getAspectRatio(id) / total} 1 0%` } }, - getAspectRatio (id) { + getAspectRatio(id) { const size = this.sizes[id] return size ? size.width / size.height : 1 }, - toggleHidingLong (event) { + toggleHidingLong(event) { this.hidingLong = event }, - openGallery () { + openGallery() { useMediaViewerStore().setMedia(this.attachments) useMediaViewerStore().setCurrentMedia(this.attachments[0]) }, - onMedia () { + onMedia() { useMediaViewerStore().setMedia(this.attachments) - } - } + }, + }, } export default Gallery diff --git a/src/components/global_notice_list/global_notice_list.js b/src/components/global_notice_list/global_notice_list.js index cf14d1a07..7135d5084 100644 --- a/src/components/global_notice_list/global_notice_list.js +++ b/src/components/global_notice_list/global_notice_list.js @@ -1,24 +1,20 @@ import { library } from '@fortawesome/fontawesome-svg-core' -import { - faTimes -} from '@fortawesome/free-solid-svg-icons' +import { faTimes } from '@fortawesome/free-solid-svg-icons' import { useInterfaceStore } from 'src/stores/interface' -library.add( - faTimes -) +library.add(faTimes) const GlobalNoticeList = { computed: { - notices () { + notices() { return useInterfaceStore().globalNotices - } + }, }, methods: { - closeNotice (notice) { + closeNotice(notice) { useInterfaceStore().removeGlobalNotice(notice) - } - } + }, + }, } export default GlobalNoticeList diff --git a/src/components/hashtag_link/hashtag_link.js b/src/components/hashtag_link/hashtag_link.js index a2433c2ab..09cfaf199 100644 --- a/src/components/hashtag_link/hashtag_link.js +++ b/src/components/hashtag_link/hashtag_link.js @@ -5,20 +5,20 @@ const HashtagLink = { props: { url: { required: true, - type: String + type: String, }, content: { required: true, - type: String + type: String, }, tag: { required: false, type: String, - default: '' - } + default: '', + }, }, methods: { - onClick () { + onClick() { const tag = this.tag || extractTagFromUrl(this.url) if (tag) { const link = this.generateTagLink(tag) @@ -27,10 +27,10 @@ const HashtagLink = { window.open(this.url, '_blank') } }, - generateTagLink (tag) { + generateTagLink(tag) { return `/tag/${tag}` - } - } + }, + }, } export default HashtagLink diff --git a/src/components/icon.style.js b/src/components/icon.style.js index 4d30f389b..b13485c1b 100644 --- a/src/components/icon.style.js +++ b/src/components/icon.style.js @@ -7,8 +7,8 @@ export default { component: 'Icon', directives: { textColor: '$blend(--stack 0.5 --parent--text)', - textAuto: 'no-auto' - } - } - ] + textAuto: 'no-auto', + }, + }, + ], } diff --git a/src/components/image_cropper/image_cropper.js b/src/components/image_cropper/image_cropper.js index afca328e8..158fcdefa 100644 --- a/src/components/image_cropper/image_cropper.js +++ b/src/components/image_cropper/image_cropper.js @@ -1,29 +1,25 @@ import 'cropperjs' // This adds all of the cropperjs's components into DOM import { library } from '@fortawesome/fontawesome-svg-core' -import { - faCircleNotch -} from '@fortawesome/free-solid-svg-icons' +import { faCircleNotch } from '@fortawesome/free-solid-svg-icons' -library.add( - faCircleNotch -) +library.add(faCircleNotch) const ImageCropper = { props: { // Mime-types to accept, i.e. which filetypes to accept (.gif, .png, etc.) mimes: { type: String, - default: 'image/png, image/gif, image/jpeg, image/bmp, image/x-icon' + default: 'image/png, image/gif, image/jpeg, image/bmp, image/x-icon', }, // Fixed aspect-ratio for selection box aspectRatio: { - type: Number - } + type: Number, + }, }, - data () { + data() { return { dataUrl: undefined, - filename: undefined + filename: undefined, } }, emits: [ @@ -31,12 +27,12 @@ const ImageCropper = { 'close', // cropper is closed ], methods: { - destroy () { + destroy() { this.$refs.input.value = '' this.dataUrl = undefined this.$emit('close') }, - submit (cropping = true) { + submit(cropping = true) { let cropperPromise if (cropping) { cropperPromise = this.$refs.cropperSelection.$toCanvas() @@ -44,14 +40,14 @@ const ImageCropper = { cropperPromise = Promise.resolve() } - cropperPromise.then(canvas => { + cropperPromise.then((canvas) => { this.$emit('submit', { canvas, file: this.file }) }) }, - pickImage () { + pickImage() { this.$refs.input.click() }, - readFile () { + readFile() { const fileInput = this.$refs.input if (fileInput.files != null && fileInput.files[0] != null) { this.file = fileInput.files[0] @@ -66,10 +62,10 @@ const ImageCropper = { }, inSelection(selection, maxSelection) { return ( - selection.x >= maxSelection.x - && selection.y >= maxSelection.y - && (selection.x + selection.width) <= (maxSelection.x + maxSelection.width) - && (selection.y + selection.height) <= (maxSelection.y + maxSelection.height) + selection.x >= maxSelection.x && + selection.y >= maxSelection.y && + selection.x + selection.width <= maxSelection.x + maxSelection.width && + selection.y + selection.height <= maxSelection.y + maxSelection.height ) }, onCropperSelectionChange(event) { @@ -84,11 +80,11 @@ const ImageCropper = { } if (!this.inSelection(selection, maxSelection)) { - event.preventDefault(); + event.preventDefault() } - } + }, }, - mounted () { + mounted() { // listen for input file changes const fileInput = this.$refs.input fileInput.addEventListener('change', this.readFile) @@ -96,7 +92,7 @@ const ImageCropper = { beforeUnmount: function () { const fileInput = this.$refs.input fileInput.removeEventListener('change', this.readFile) - } + }, } export default ImageCropper diff --git a/src/components/importer/importer.js b/src/components/importer/importer.js index da86a223c..0403cc944 100644 --- a/src/components/importer/importer.js +++ b/src/components/importer/importer.js @@ -1,49 +1,49 @@ import { library } from '@fortawesome/fontawesome-svg-core' -import { - faCircleNotch, - faTimes -} from '@fortawesome/free-solid-svg-icons' +import { faCircleNotch, faTimes } from '@fortawesome/free-solid-svg-icons' -library.add( - faCircleNotch, - faTimes -) +library.add(faCircleNotch, faTimes) const Importer = { props: { submitHandler: { type: Function, - required: true + required: true, }, submitButtonLabel: { type: String }, successMessage: { type: String }, - errorMessage: { type: String } + errorMessage: { type: String }, }, - data () { + data() { return { file: null, error: false, success: false, - submitting: false + submitting: false, } }, methods: { - change () { + change() { this.file = this.$refs.input.files[0] }, - submit () { + submit() { this.dismiss() this.submitting = true this.submitHandler(this.file) - .then(() => { this.success = true }) - .catch(() => { this.error = true }) - .finally(() => { this.submitting = false }) + .then(() => { + this.success = true + }) + .catch(() => { + this.error = true + }) + .finally(() => { + this.submitting = false + }) }, - dismiss () { + dismiss() { this.success = false this.error = false - } - } + }, + }, } export default Importer diff --git a/src/components/input.style.js b/src/components/input.style.js index 00f51cbf6..2663392fb 100644 --- a/src/components/input.style.js +++ b/src/components/input.style.js @@ -4,91 +4,96 @@ export default { states: { hover: ':is(:hover, :focus-visible):not(.disabled)', focused: ':focus-within', - disabled: '.disabled' + disabled: '.disabled', }, variants: { checkbox: '.-checkbox', - radio: '.-radio' + radio: '.-radio', }, - validInnerComponents: [ - 'Text', - 'Icon' - ], + validInnerComponents: ['Text', 'Icon'], defaultRules: [ { component: 'Root', directives: { - '--defaultInputBevel': 'shadow | $borderSide(#FFFFFF bottom 0.2), $borderSide(#000000 top 0.2), inset 0 0 2 #000000 / 0.15, 1 0 1 1 --text / 0.15, -1 0 1 1 --text / 0.15', + '--defaultInputBevel': + 'shadow | $borderSide(#FFFFFF bottom 0.2), $borderSide(#000000 top 0.2), inset 0 0 2 #000000 / 0.15, 1 0 1 1 --text / 0.15, -1 0 1 1 --text / 0.15', '--defaultInputHoverGlow': 'shadow | 0 0 4 --text / 0.5', - '--defaultInputFocusGlow': 'shadow | 0 0 4 4 --link / 0.5' - } + '--defaultInputFocusGlow': 'shadow | 0 0 4 4 --link / 0.5', + }, }, { variant: 'checkbox', directives: { - roundness: 1 - } + roundness: 1, + }, }, { directives: { '--font': 'generic | inherit', background: '--fg, -5', roundness: 3, - shadow: [{ - x: 0, - y: 0, - blur: 2, - spread: 0, - color: '#000000', - alpha: 1 - }, '--defaultInputBevel'] - } + shadow: [ + { + x: 0, + y: 0, + blur: 2, + spread: 0, + color: '#000000', + alpha: 1, + }, + '--defaultInputBevel', + ], + }, }, { state: ['hover'], directives: { - shadow: ['--defaultInputHoverGlow', '--defaultInputBevel'] - } + shadow: ['--defaultInputHoverGlow', '--defaultInputBevel'], + }, }, { state: ['focused'], directives: { - shadow: ['--defaultInputFocusGlow', '--defaultInputBevel'] - } + shadow: ['--defaultInputFocusGlow', '--defaultInputBevel'], + }, }, { state: ['focused', 'hover'], directives: { - shadow: ['--defaultInputFocusGlow', '--defaultInputHoverGlow', '--defaultInputBevel'] - } + shadow: [ + '--defaultInputFocusGlow', + '--defaultInputHoverGlow', + '--defaultInputBevel', + ], + }, }, { state: ['disabled'], directives: { - background: '--parent' - } + background: '--parent', + }, }, { component: 'Text', parent: { component: 'Input', - state: ['disabled'] + state: ['disabled'], }, directives: { textOpacity: 0.25, - textOpacityMode: 'blend' - } + textOpacityMode: 'blend', + }, }, { component: 'Icon', parent: { component: 'Input', - state: ['disabled'] + state: ['disabled'], }, directives: { textOpacity: 0.25, - textOpacityMode: 'blend' - } - } - ] + textOpacityMode: 'blend', + }, + }, + ], } diff --git a/src/components/instance_specific_panel/instance_specific_panel.js b/src/components/instance_specific_panel/instance_specific_panel.js index 09e3d0557..eead52f40 100644 --- a/src/components/instance_specific_panel/instance_specific_panel.js +++ b/src/components/instance_specific_panel/instance_specific_panel.js @@ -1,9 +1,9 @@ const InstanceSpecificPanel = { computed: { - instanceSpecificPanelContent () { + instanceSpecificPanelContent() { return this.$store.state.instance.instanceSpecificPanelContent - } - } + }, + }, } export default InstanceSpecificPanel diff --git a/src/components/interactions/interactions.js b/src/components/interactions/interactions.js index fc441b908..a5f3095d9 100644 --- a/src/components/interactions/interactions.js +++ b/src/components/interactions/interactions.js @@ -8,26 +8,29 @@ const tabModeDict = { follows: ['follow'], reactions: ['pleroma:emoji_reaction'], reports: ['pleroma:report'], - moves: ['move'] + moves: ['move'], } const Interactions = { - data () { + data() { return { - allowFollowingMove: this.$store.state.users.currentUser.allow_following_move, + allowFollowingMove: + this.$store.state.users.currentUser.allow_following_move, filterMode: tabModeDict.mentions, - canSeeReports: this.$store.state.users.currentUser.privileges.includes('reports_manage_reports') + canSeeReports: this.$store.state.users.currentUser.privileges.includes( + 'reports_manage_reports', + ), } }, methods: { - onModeSwitch (key) { + onModeSwitch(key) { this.filterMode = tabModeDict[key] - } + }, }, components: { Notifications, - TabSwitcher - } + TabSwitcher, + }, } export default Interactions diff --git a/src/components/interface_language_switcher/interface_language_switcher.js b/src/components/interface_language_switcher/interface_language_switcher.js index 721c19887..2599901c6 100644 --- a/src/components/interface_language_switcher/interface_language_switcher.js +++ b/src/components/interface_language_switcher/interface_language_switcher.js @@ -3,60 +3,62 @@ import localeService from '../../services/locale/locale.service.js' import Select from '../select/select.vue' import ProfileSettingIndicator from 'src/components/settings_modal/helpers/profile_setting_indicator.vue' -import { v4 as uuidv4 } from 'uuid'; +import { v4 as uuidv4 } from 'uuid' export default { components: { Select, - ProfileSettingIndicator + ProfileSettingIndicator, }, props: { // List of languages (or just one language) modelValue: { type: [Array, String], - required: true + required: true, }, // Is this setting stored in user profile (true) or elsewhere (false) // Doesn't affect storage, just shows an icon if true profile: { type: Boolean, - default: false - } + default: false, + }, }, emits: ['update:modelValue'], computed: { - languages () { + languages() { return localeService.languages }, - uniqueId () { - return uuidv4() + uniqueId() { + return uuidv4() }, controlledLanguage: { get: function () { - return Array.isArray(this.modelValue) ? this.modelValue : [this.modelValue] + return Array.isArray(this.modelValue) + ? this.modelValue + : [this.modelValue] }, set: function (val) { this.$emit('update:modelValue', val) - } - } + }, + }, }, methods: { - getLanguageName (code) { + getLanguageName(code) { return localeService.getLanguageName(code) }, - addLanguage () { + addLanguage() { this.controlledLanguage = [...this.controlledLanguage, ''] }, - setLanguageAt (index, val) { + setLanguageAt(index, val) { const lang = [...this.controlledLanguage] lang[index] = val this.controlledLanguage = lang }, - removeLanguageAt (index) { + removeLanguageAt(index) { const lang = [...this.controlledLanguage] lang.splice(index, 1) this.controlledLanguage = lang - } - } + }, + }, } diff --git a/src/components/link-preview/link-preview.js b/src/components/link-preview/link-preview.js index add7c5631..ac91f916d 100644 --- a/src/components/link-preview/link-preview.js +++ b/src/components/link-preview/link-preview.js @@ -2,37 +2,31 @@ import { mapGetters } from 'vuex' const LinkPreview = { name: 'LinkPreview', - props: [ - 'card', - 'size', - 'nsfw' - ], - data () { + props: ['card', 'size', 'nsfw'], + data() { return { - imageLoaded: false + imageLoaded: false, } }, computed: { - useImage () { + useImage() { // Currently BE shoudn't give cards if tagged NSFW, this is a bit paranoid // as it makes sure to hide the image if somehow NSFW tagged preview can // exist. return this.card.image && !this.censored && this.size !== 'hide' }, - censored () { + censored() { return this.nsfw && this.hideNsfwConfig }, - useDescription () { + useDescription() { return this.card.description && /\S/.test(this.card.description) }, - hideNsfwConfig () { + hideNsfwConfig() { return this.mergedConfig.hideNsfw }, - ...mapGetters([ - 'mergedConfig' - ]) + ...mapGetters(['mergedConfig']), }, - created () { + created() { if (this.useImage) { const newImg = new Image() newImg.onload = () => { @@ -40,7 +34,7 @@ const LinkPreview = { } newImg.src = this.card.image } - } + }, } export default LinkPreview diff --git a/src/components/link.style.js b/src/components/link.style.js index d13cef338..141a1f023 100644 --- a/src/components/link.style.js +++ b/src/components/link.style.js @@ -3,22 +3,22 @@ export default { selector: 'a', virtual: true, states: { - faint: '.faint' + faint: '.faint', }, defaultRules: [ { component: 'Link', directives: { - textColor: '--link' - } + textColor: '--link', + }, }, { component: 'Link', state: ['faint'], directives: { textOpacity: 0.5, - textOpacityMode: 'fake' - } - } - ] + textOpacityMode: 'fake', + }, + }, + ], } diff --git a/src/components/list/list.vue b/src/components/list/list.vue index 5d2c49b3c..f34a5f073 100644 --- a/src/components/list/list.vue +++ b/src/components/list/list.vue @@ -29,20 +29,20 @@ export default { props: { items: { type: Array, - default: () => [] + default: () => [], }, getKey: { type: Function, - default: item => item.id + default: (item) => item.id, }, getClass: { type: Function, - default: () => '' + default: () => '', }, nonInteractive: { type: Boolean, - default: false - } - } + default: false, + }, + }, } diff --git a/src/components/lists/lists.js b/src/components/lists/lists.js index 8dcb48b52..446178245 100644 --- a/src/components/lists/lists.js +++ b/src/components/lists/lists.js @@ -2,27 +2,27 @@ import { useListsStore } from 'src/stores/lists' import ListsCard from '../lists_card/lists_card.vue' const Lists = { - data () { + data() { return { - isNew: false + isNew: false, } }, components: { - ListsCard + ListsCard, }, computed: { - lists () { + lists() { return useListsStore().allLists - } + }, }, methods: { - cancelNewList () { + cancelNewList() { this.isNew = false }, - newList () { + newList() { this.isNew = true - } - } + }, + }, } export default Lists diff --git a/src/components/lists_card/lists_card.js b/src/components/lists_card/lists_card.js index b503caec4..81b811534 100644 --- a/src/components/lists_card/lists_card.js +++ b/src/components/lists_card/lists_card.js @@ -1,16 +1,10 @@ import { library } from '@fortawesome/fontawesome-svg-core' -import { - faEllipsisH -} from '@fortawesome/free-solid-svg-icons' +import { faEllipsisH } from '@fortawesome/free-solid-svg-icons' -library.add( - faEllipsisH -) +library.add(faEllipsisH) const ListsCard = { - props: [ - 'list' - ] + props: ['list'], } export default ListsCard diff --git a/src/components/lists_edit/lists_edit.js b/src/components/lists_edit/lists_edit.js index c62bf9769..64badb2d4 100644 --- a/src/components/lists_edit/lists_edit.js +++ b/src/components/lists_edit/lists_edit.js @@ -6,17 +6,11 @@ import PanelLoading from 'src/components/panel_loading/panel_loading.vue' import UserAvatar from '../user_avatar/user_avatar.vue' import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx' import { library } from '@fortawesome/fontawesome-svg-core' -import { - faSearch, - faChevronLeft -} from '@fortawesome/free-solid-svg-icons' +import { faSearch, faChevronLeft } from '@fortawesome/free-solid-svg-icons' import { useInterfaceStore } from 'src/stores/interface' import { useListsStore } from 'src/stores/lists' -library.add( - faSearch, - faChevronLeft -) +library.add(faSearch, faChevronLeft) const ListsNew = { components: { @@ -24,9 +18,9 @@ const ListsNew = { UserAvatar, ListsUserSearch, TabSwitcher, - PanelLoading + PanelLoading, }, - data () { + data() { return { title: '', titleDraft: '', @@ -35,46 +29,51 @@ const ListsNew = { searchUserIds: [], addedUserIds: new Set([]), // users we added from search, to undo searchLoading: false, - reallyDelete: false + reallyDelete: false, } }, - created () { + created() { if (!this.id) return - useListsStore().fetchList({ listId: this.id }) + useListsStore() + .fetchList({ listId: this.id }) .then(() => { this.title = this.findListTitle(this.id) this.titleDraft = this.title }) - useListsStore().fetchListAccounts({ listId: this.id }) + useListsStore() + .fetchListAccounts({ listId: this.id }) .then(() => { this.membersUserIds = this.findListAccounts(this.id) - this.membersUserIds.forEach(userId => { + this.membersUserIds.forEach((userId) => { this.$store.dispatch('fetchUserIfMissing', userId) }) }) }, computed: { - id () { + id() { return this.$route.params.id }, - membersUsers () { + membersUsers() { return [...this.membersUserIds, ...this.addedUserIds] - .map(userId => this.findUser(userId)).filter(user => user) + .map((userId) => this.findUser(userId)) + .filter((user) => user) }, - searchUsers () { - return this.searchUserIds.map(userId => this.findUser(userId)).filter(user => user) + searchUsers() { + return this.searchUserIds + .map((userId) => this.findUser(userId)) + .filter((user) => user) }, ...mapState({ - currentUser: state => state.users.currentUser + currentUser: (state) => state.users.currentUser, }), ...mapPiniaState(useListsStore, ['findListTitle', 'findListAccounts']), - ...mapGetters(['findUser']) + ...mapGetters(['findUser']), }, methods: { - onInput () { + onInput() { this.search(this.query) }, - toggleRemoveMember (user) { + toggleRemoveMember(user) { if (this.removedUserIds.has(user.id)) { this.id && this.addUser(user) this.removedUserIds.delete(user.id) @@ -83,7 +82,7 @@ const ListsNew = { this.removedUserIds.add(user.id) } }, - toggleAddFromSearch (user) { + toggleAddFromSearch(user) { if (this.addedUserIds.has(user.id)) { this.id && this.removeUser(user.id) this.addedUserIds.delete(user.id) @@ -92,37 +91,41 @@ const ListsNew = { this.addedUserIds.add(user.id) } }, - isRemoved (user) { + isRemoved(user) { return this.removedUserIds.has(user.id) }, - isAdded (user) { + isAdded(user) { return this.addedUserIds.has(user.id) }, - addUser (user) { + addUser(user) { useListsStore().addListAccount({ accountId: user.id, listId: this.id }) }, - removeUser (userId) { + removeUser(userId) { useListsStore().removeListAccount({ accountId: userId, listId: this.id }) }, - onSearchLoading () { + onSearchLoading() { this.searchLoading = true }, - onSearchLoadingDone () { + onSearchLoadingDone() { this.searchLoading = false }, - onSearchResults (results) { + onSearchResults(results) { this.searchLoading = false this.searchUserIds = results }, - updateListTitle () { + updateListTitle() { useListsStore().setList({ listId: this.id, title: this.titleDraft }) this.title = this.findListTitle(this.id) }, - createList () { - useListsStore().createList({ title: this.titleDraft }) + createList() { + useListsStore() + .createList({ title: this.titleDraft }) .then((list) => { return useListsStore() - .setListAccounts({ listId: list.id, accountIds: [...this.addedUserIds] }) + .setListAccounts({ + listId: list.id, + accountIds: [...this.addedUserIds], + }) .then(() => list.id) }) .then((listId) => { @@ -132,15 +135,15 @@ const ListsNew = { useInterfaceStore().pushGlobalNotice({ messageKey: 'lists.error', messageArgs: [e.message], - level: 'error' + level: 'error', }) }) }, - deleteList () { + deleteList() { useListsStore().deleteList({ listId: this.id }) this.$router.push({ name: 'lists' }) - } - } + }, + }, } export default ListsNew diff --git a/src/components/lists_menu/lists_menu_content.js b/src/components/lists_menu/lists_menu_content.js index 726fd5fc4..bf7385403 100644 --- a/src/components/lists_menu/lists_menu_content.js +++ b/src/components/lists_menu/lists_menu_content.js @@ -5,22 +5,20 @@ import { getListEntries } from 'src/components/navigation/filter.js' import { useListsStore } from 'src/stores/lists' export const ListsMenuContent = { - props: [ - 'showPin' - ], + props: ['showPin'], components: { - NavigationEntry + NavigationEntry, }, computed: { ...mapPiniaState(useListsStore, { - lists: getListEntries + lists: getListEntries, }), ...mapState({ - currentUser: state => state.users.currentUser, - privateMode: state => state.instance.private, - federating: state => state.instance.federating - }) - } + currentUser: (state) => state.users.currentUser, + privateMode: (state) => state.instance.private, + federating: (state) => state.instance.federating, + }), + }, } export default ListsMenuContent diff --git a/src/components/lists_timeline/lists_timeline.js b/src/components/lists_timeline/lists_timeline.js index eae82a867..429e21867 100644 --- a/src/components/lists_timeline/lists_timeline.js +++ b/src/components/lists_timeline/lists_timeline.js @@ -1,16 +1,18 @@ import { useListsStore } from 'src/stores/lists' import Timeline from '../timeline/timeline.vue' const ListsTimeline = { - data () { + data() { return { - listId: null + listId: null, } }, components: { - Timeline + Timeline, }, computed: { - timeline () { return this.$store.state.statuses.timelines.list } + timeline() { + return this.$store.state.statuses.timelines.list + }, }, watch: { $route: function (route) { @@ -19,19 +21,25 @@ const ListsTimeline = { this.$store.dispatch('stopFetchingTimeline', 'list') this.$store.commit('clearTimeline', { timeline: 'list' }) useListsStore().fetchList({ listId: this.listId }) - this.$store.dispatch('startFetchingTimeline', { timeline: 'list', listId: this.listId }) + this.$store.dispatch('startFetchingTimeline', { + timeline: 'list', + listId: this.listId, + }) } - } + }, }, - created () { + created() { this.listId = this.$route.params.id useListsStore().fetchList({ listId: this.listId }) - this.$store.dispatch('startFetchingTimeline', { timeline: 'list', listId: this.listId }) + this.$store.dispatch('startFetchingTimeline', { + timeline: 'list', + listId: this.listId, + }) }, - unmounted () { + unmounted() { this.$store.dispatch('stopFetchingTimeline', 'list') this.$store.commit('clearTimeline', { timeline: 'list' }) - } + }, } export default ListsTimeline diff --git a/src/components/lists_user_search/lists_user_search.js b/src/components/lists_user_search/lists_user_search.js index c92ec0eee..68c730bc2 100644 --- a/src/components/lists_user_search/lists_user_search.js +++ b/src/components/lists_user_search/lists_user_search.js @@ -1,33 +1,27 @@ import { library } from '@fortawesome/fontawesome-svg-core' -import { - faSearch, - faChevronLeft -} from '@fortawesome/free-solid-svg-icons' +import { faSearch, faChevronLeft } from '@fortawesome/free-solid-svg-icons' import { debounce } from 'lodash' import Checkbox from '../checkbox/checkbox.vue' -library.add( - faSearch, - faChevronLeft -) +library.add(faSearch, faChevronLeft) const ListsUserSearch = { components: { - Checkbox + Checkbox, }, emits: ['loading', 'loadingDone', 'results'], - data () { + data() { return { loading: false, query: '', - followingOnly: true + followingOnly: true, } }, methods: { onInput: debounce(function () { this.search(this.query) }, 2000), - search (query) { + search(query) { if (!query) { this.loading = false return @@ -36,16 +30,25 @@ const ListsUserSearch = { this.loading = true this.$emit('loading') this.userIds = [] - this.$store.dispatch('search', { q: query, resolve: true, type: 'accounts', following: this.followingOnly }) - .then(data => { - this.$emit('results', data.accounts.map(a => a.id)) + this.$store + .dispatch('search', { + q: query, + resolve: true, + type: 'accounts', + following: this.followingOnly, + }) + .then((data) => { + this.$emit( + 'results', + data.accounts.map((a) => a.id), + ) }) .finally(() => { this.loading = false this.$emit('loadingDone') }) - } - } + }, + }, } export default ListsUserSearch diff --git a/src/components/login_form/login_form.js b/src/components/login_form/login_form.js index 9566aa903..694aebe43 100644 --- a/src/components/login_form/login_form.js +++ b/src/components/login_form/login_form.js @@ -4,53 +4,56 @@ import oauthApi from '../../services/new_api/oauth.js' import { useOAuthStore } from 'src/stores/oauth.js' import { useAuthFlowStore } from 'src/stores/auth_flow.js' import { library } from '@fortawesome/fontawesome-svg-core' -import { - faTimes -} from '@fortawesome/free-solid-svg-icons' +import { faTimes } from '@fortawesome/free-solid-svg-icons' -library.add( - faTimes -) +library.add(faTimes) const LoginForm = { data: () => ({ user: {}, - error: false + error: false, }), computed: { - isPasswordAuth () { return this.requiredPassword }, - isTokenAuth () { return this.requiredToken }, + isPasswordAuth() { + return this.requiredPassword + }, + isTokenAuth() { + return this.requiredToken + }, ...mapStores(useOAuthStore), ...mapState({ - registrationOpen: state => state.instance.registrationOpen, - instance: state => state.instance, - loggingIn: state => state.users.loggingIn, + registrationOpen: (state) => state.instance.registrationOpen, + instance: (state) => state.instance, + loggingIn: (state) => state.users.loggingIn, }), - ...mapPiniaState(useAuthFlowStore, ['requiredPassword', 'requiredToken', 'requiredMFA']) + ...mapPiniaState(useAuthFlowStore, [ + 'requiredPassword', + 'requiredToken', + 'requiredMFA', + ]), }, methods: { ...mapActions(useAuthFlowStore, ['requireMFA', 'login']), - submit () { + submit() { this.isTokenAuth ? this.submitToken() : this.submitPassword() }, - submitToken () { + submitToken() { const data = { instance: this.instance.server, - commit: this.$store.commit + commit: this.$store.commit, } // NOTE: we do not really need the app token, but obtaining a token and // calling verify_credentials is the only way to ensure the app still works. - this.oauthStore.ensureAppToken() - .then(() => { - const app = { - clientId: this.oauthStore.clientId, - clientSecret: this.oauthStore.clientSecret, - } - oauthApi.login({ ...app, ...data }) - }) + this.oauthStore.ensureAppToken().then(() => { + const app = { + clientId: this.oauthStore.clientId, + clientSecret: this.oauthStore.clientSecret, + } + oauthApi.login({ ...app, ...data }) + }) }, - submitPassword () { + submitPassword() { this.error = false // NOTE: we do not really need the app token, but obtaining a token and @@ -61,38 +64,43 @@ const LoginForm = { clientSecret: this.oauthStore.clientSecret, } - oauthApi.getTokenWithCredentials( - { + oauthApi + .getTokenWithCredentials({ ...app, instance: this.instance.server, username: this.user.username, - password: this.user.password - } - ).then((result) => { - if (result.error) { - if (result.error === 'mfa_required') { - this.requireMFA({ settings: result }) - } else if (result.identifier === 'password_reset_required') { - this.$router.push({ name: 'password-reset', params: { passwordResetRequested: true } }) - } else { - this.error = result.error - this.focusOnPasswordInput() - } - return - } - this.login(result).then(() => { - this.$router.push({ name: 'friends' }) + password: this.user.password, + }) + .then((result) => { + if (result.error) { + if (result.error === 'mfa_required') { + this.requireMFA({ settings: result }) + } else if (result.identifier === 'password_reset_required') { + this.$router.push({ + name: 'password-reset', + params: { passwordResetRequested: true }, + }) + } else { + this.error = result.error + this.focusOnPasswordInput() + } + return + } + this.login(result).then(() => { + this.$router.push({ name: 'friends' }) + }) }) - }) }) }, - clearError () { this.error = false }, - focusOnPasswordInput () { + clearError() { + this.error = false + }, + focusOnPasswordInput() { const passwordInput = this.$refs.passwordInput passwordInput.focus() passwordInput.setSelectionRange(0, passwordInput.value.length) - } - } + }, + }, } export default LoginForm diff --git a/src/components/media_modal/media_modal.js b/src/components/media_modal/media_modal.js index 9a57f8250..5fc9f7296 100644 --- a/src/components/media_modal/media_modal.js +++ b/src/components/media_modal/media_modal.js @@ -11,16 +11,11 @@ import { faChevronLeft, faChevronRight, faCircleNotch, - faTimes + faTimes, } from '@fortawesome/free-solid-svg-icons' import { useMediaViewerStore } from 'src/stores/media_viewer' -library.add( - faChevronLeft, - faChevronRight, - faCircleNotch, - faTimes -) +library.add(faChevronLeft, faChevronRight, faCircleNotch, faTimes) const MediaModal = { components: { @@ -29,9 +24,9 @@ const MediaModal = { PinchZoom, SwipeClick, Modal, - Flash + Flash, }, - data () { + data() { return { loading: false, swipeDirection: GestureService.DIRECTION_LEFT, @@ -40,42 +35,42 @@ const MediaModal = { return window.innerWidth * considerableMoveRatio }, pinchZoomMinScale: 1, - pinchZoomScaleResetLimit: 1.2 + pinchZoomScaleResetLimit: 1.2, } }, computed: { - showing () { + showing() { return useMediaViewerStore().activated }, - media () { + media() { return useMediaViewerStore().media }, - description () { + description() { return this.currentMedia.description }, - currentIndex () { + currentIndex() { return useMediaViewerStore().currentIndex }, - currentMedia () { + currentMedia() { return this.media[this.currentIndex] }, - canNavigate () { + canNavigate() { return this.media.length > 1 }, - type () { + type() { return this.currentMedia ? this.getType(this.currentMedia) : null }, - swipeDisableClickThreshold () { + swipeDisableClickThreshold() { // If there is only one media, allow more mouse movements to close the modal // because there is less chance that the user wants to switch to another image - return () => this.canNavigate ? 1 : 30 - } + return () => (this.canNavigate ? 1 : 30) + }, }, methods: { - getType (media) { + getType(media) { return fileTypeService.fileType(media.mimetype) }, - hide () { + hide() { // HACK: Closing immediately via a touch will cause the click // to be processed on the content below the overlay const transitionTime = 100 // ms @@ -83,7 +78,7 @@ const MediaModal = { useMediaViewerStore().closeMediaViewer() }, transitionTime) }, - hideIfNotSwiped (event) { + hideIfNotSwiped(event) { // If we have swiped over SwipeClick, do not trigger hide const comp = this.$refs.swipeClick if (!comp) { @@ -92,9 +87,12 @@ const MediaModal = { comp.$gesture.click(event) } }, - goPrev () { + goPrev() { if (this.canNavigate) { - const prevIndex = this.currentIndex === 0 ? this.media.length - 1 : (this.currentIndex - 1) + const prevIndex = + this.currentIndex === 0 + ? this.media.length - 1 + : this.currentIndex - 1 const newMedia = this.media[prevIndex] if (this.getType(newMedia) === 'image') { this.loading = true @@ -102,9 +100,12 @@ const MediaModal = { useMediaViewerStore().setCurrentMedia(newMedia) } }, - goNext () { + goNext() { if (this.canNavigate) { - const nextIndex = this.currentIndex === this.media.length - 1 ? 0 : (this.currentIndex + 1) + const nextIndex = + this.currentIndex === this.media.length - 1 + ? 0 + : this.currentIndex + 1 const newMedia = this.media[nextIndex] if (this.getType(newMedia) === 'image') { this.loading = true @@ -112,13 +113,13 @@ const MediaModal = { useMediaViewerStore().setCurrentMedia(newMedia) } }, - onImageLoaded () { + onImageLoaded() { this.loading = false }, - handleSwipePreview (offsets) { + handleSwipePreview(offsets) { this.$refs.pinchZoom.setTransform({ scale: 1, x: offsets[0], y: 0 }) }, - handleSwipeEnd (sign) { + handleSwipeEnd(sign) { this.$refs.pinchZoom.setTransform({ scale: 1, x: 0, y: 0 }) if (sign > 0) { this.goNext() @@ -126,33 +127,36 @@ const MediaModal = { this.goPrev() } }, - handleKeyupEvent (e) { - if (this.showing && e.keyCode === 27) { // escape + handleKeyupEvent(e) { + if (this.showing && e.keyCode === 27) { + // escape this.hide() } }, - handleKeydownEvent (e) { + handleKeydownEvent(e) { if (!this.showing) { return } - if (e.keyCode === 39) { // arrow right + if (e.keyCode === 39) { + // arrow right this.goNext() - } else if (e.keyCode === 37) { // arrow left + } else if (e.keyCode === 37) { + // arrow left this.goPrev() } - } + }, }, - mounted () { + mounted() { window.addEventListener('popstate', this.hide) document.addEventListener('keyup', this.handleKeyupEvent) document.addEventListener('keydown', this.handleKeydownEvent) }, - unmounted () { + unmounted() { window.removeEventListener('popstate', this.hide) document.removeEventListener('keyup', this.handleKeyupEvent) document.removeEventListener('keydown', this.handleKeydownEvent) - } + }, } export default MediaModal diff --git a/src/components/media_upload/media_upload.js b/src/components/media_upload/media_upload.js index f2cbfc405..37d1a76cd 100644 --- a/src/components/media_upload/media_upload.js +++ b/src/components/media_upload/media_upload.js @@ -5,30 +5,27 @@ import fileSizeFormatService from '../../services/file_size_format/file_size_for import { library } from '@fortawesome/fontawesome-svg-core' import { faUpload, faCircleNotch } from '@fortawesome/free-solid-svg-icons' -library.add( - faUpload, - faCircleNotch -) +library.add(faUpload, faCircleNotch) const mediaUpload = { - data () { + data() { return { uploadCount: 0, - uploadReady: true + uploadReady: true, } }, computed: { - uploading () { + uploading() { return this.uploadCount > 0 - } + }, }, methods: { - onClick () { + onClick() { if (this.uploadReady) { this.$refs.input.click() } }, - async resizeImage (file) { + async resizeImage(file) { // Skip if not an image or if it's a GIF if (!file.type.startsWith('image/') || file.type === 'image/gif') { return file @@ -74,46 +71,67 @@ const mediaUpload = { // Check WebP support by trying to create a WebP canvas const testCanvas = document.createElement('canvas') - const supportsWebP = testCanvas.toDataURL('image/webp').startsWith('data:image/webp') + const supportsWebP = testCanvas + .toDataURL('image/webp') + .startsWith('data:image/webp') // Convert to WebP if supported and alwaysUseJpeg is false, otherwise JPEG - const type = (!this.$store.getters.mergedConfig.alwaysUseJpeg && supportsWebP) ? 'image/webp' : 'image/jpeg' + const type = + !this.$store.getters.mergedConfig.alwaysUseJpeg && supportsWebP + ? 'image/webp' + : 'image/jpeg' const extension = type === 'image/webp' ? '.webp' : '.jpg' // Remove the original extension and add new one const newFileName = file.name.replace(/\.[^/.]+$/, '') + extension - canvas.toBlob((blob) => { - resolve(new File([blob], newFileName, { - type, - lastModified: Date.now() - })) - }, type, 0.85) + canvas.toBlob( + (blob) => { + resolve( + new File([blob], newFileName, { + type, + lastModified: Date.now(), + }), + ) + }, + type, + 0.85, + ) } img.src = URL.createObjectURL(file) }) }, - async isAnimatedPng (file) { + async isAnimatedPng(file) { const buffer = await file.arrayBuffer() const view = new Uint8Array(buffer) // Look for animated PNG chunks (acTL) for (let i = 0; i < view.length - 8; i++) { - if (view[i] === 0x61 && // a - view[i + 1] === 0x63 && // c - view[i + 2] === 0x54 && // T - view[i + 3] === 0x4C) { // L + if ( + view[i] === 0x61 && // a + view[i + 1] === 0x63 && // c + view[i + 2] === 0x54 && // T + view[i + 3] === 0x4c + ) { + // L return true } } return false }, - async uploadFile (file) { + async uploadFile(file) { const self = this const store = this.$store if (file.size > store.state.instance.uploadlimit) { const filesize = fileSizeFormatService.fileSizeFormat(file.size) - const allowedsize = fileSizeFormatService.fileSizeFormat(store.state.instance.uploadlimit) - self.$emit('upload-failed', 'file_too_big', { filesize: filesize.num, filesizeunit: filesize.unit, allowedsize: allowedsize.num, allowedsizeunit: allowedsize.unit }) + const allowedsize = fileSizeFormatService.fileSizeFormat( + store.state.instance.uploadlimit, + ) + self.$emit('upload-failed', 'file_too_big', { + filesize: filesize.num, + filesizeunit: filesize.unit, + allowedsize: allowedsize.num, + allowedsizeunit: allowedsize.unit, + }) return } @@ -125,36 +143,38 @@ const mediaUpload = { self.$emit('uploading') self.uploadCount++ - statusPosterService.uploadMedia({ store, formData }) - .then((fileData) => { + statusPosterService.uploadMedia({ store, formData }).then( + (fileData) => { self.$emit('uploaded', fileData) self.decreaseUploadCount() - }, (error) => { + }, + (error) => { console.error('Error uploading file', error) self.$emit('upload-failed', 'default') self.decreaseUploadCount() - }) + }, + ) }, - decreaseUploadCount () { + decreaseUploadCount() { this.uploadCount-- if (this.uploadCount === 0) { this.$emit('all-uploaded') } }, - clearFile () { + clearFile() { this.uploadReady = false this.$nextTick(() => { this.uploadReady = true }) }, - multiUpload (files) { + multiUpload(files) { for (const file of files) { this.uploadFile(file) } }, - change ({ target }) { + change({ target }) { this.multiUpload(target.files) - } + }, }, props: { dropFiles: Object, @@ -162,16 +182,16 @@ const mediaUpload = { normalButton: Boolean, acceptTypes: { type: String, - default: '*/*' - } + default: '*/*', + }, }, watch: { dropFiles: function (fileInfos) { if (!this.uploading) { this.multiUpload(fileInfos) } - } - } + }, + }, } export default mediaUpload diff --git a/src/components/mention_link/mention_link.js b/src/components/mention_link/mention_link.js index ca5c24618..d2cf0f401 100644 --- a/src/components/mention_link/mention_link.js +++ b/src/components/mention_link/mention_link.js @@ -1,100 +1,109 @@ import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' import { mapGetters, mapState } from 'vuex' -import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js' +import { + highlightClass, + highlightStyle, +} from '../../services/user_highlighter/user_highlighter.js' import UserAvatar from '../user_avatar/user_avatar.vue' import UnicodeDomainIndicator from '../unicode_domain_indicator/unicode_domain_indicator.vue' import { defineAsyncComponent } from 'vue' import { library } from '@fortawesome/fontawesome-svg-core' -import { - faAt -} from '@fortawesome/free-solid-svg-icons' +import { faAt } from '@fortawesome/free-solid-svg-icons' -library.add( - faAt -) +library.add(faAt) const MentionLink = { name: 'MentionLink', components: { UserAvatar, UnicodeDomainIndicator, - UserPopover: defineAsyncComponent(() => import('../user_popover/user_popover.vue')) + UserPopover: defineAsyncComponent( + () => import('../user_popover/user_popover.vue'), + ), }, props: { url: { required: true, - type: String + type: String, }, content: { required: true, - type: String + type: String, }, userId: { required: false, - type: String + type: String, }, userScreenName: { required: false, - type: String - } + type: String, + }, }, - data () { + data() { return { - hasSelection: false + hasSelection: false, } }, methods: { - onClick () { + onClick() { if (this.shouldShowTooltip) return const link = generateProfileLink( this.userId || this.user.id, - this.userScreenName || this.user.screen_name + this.userScreenName || this.user.screen_name, ) this.$router.push(link) }, - handleSelection () { + handleSelection() { if (this.$refs.full) { - this.hasSelection = document.getSelection().containsNode(this.$refs.full, true) + this.hasSelection = document + .getSelection() + .containsNode(this.$refs.full, true) } - } + }, }, - mounted () { + mounted() { document.addEventListener('selectionchange', this.handleSelection) }, - unmounted () { + unmounted() { document.removeEventListener('selectionchange', this.handleSelection) }, computed: { - user () { - return this.url && this.$store && this.$store.getters.findUserByUrl(this.url) + user() { + return ( + this.url && this.$store && this.$store.getters.findUserByUrl(this.url) + ) }, - isYou () { + isYou() { // FIXME why user !== currentUser??? return this.user && this.user.id === this.currentUser.id }, - userName () { + userName() { return this.user && this.userNameFullUi.split('@')[0] }, - serverName () { + serverName() { // XXX assumed that domain does not contain @ - return this.user && (this.userNameFullUi.split('@')[1] || this.$store.getters.instanceDomain) + return ( + this.user && + (this.userNameFullUi.split('@')[1] || + this.$store.getters.instanceDomain) + ) }, - userNameFull () { + userNameFull() { return this.user && this.user.screen_name }, - userNameFullUi () { + userNameFullUi() { return this.user && this.user.screen_name_ui }, - highlight () { + highlight() { return this.user && this.mergedConfig.highlight[this.user.screen_name] }, - highlightType () { - return this.highlight && ('-' + this.highlight.type) + highlightType() { + return this.highlight && '-' + this.highlight.type }, - highlightClass () { + highlightClass() { if (this.highlight) return highlightClass(this.user) }, - style () { + style() { if (this.highlight) { /* eslint-disable no-unused-vars */ const { @@ -107,49 +116,50 @@ const MentionLink = { return rest } }, - classnames () { + classnames() { return [ { '-you': this.isYou && this.shouldBoldenYou, '-highlighted': this.highlight, - '-has-selection': this.hasSelection + '-has-selection': this.hasSelection, }, - this.highlightType + this.highlightType, ] }, - isRemote () { + isRemote() { return this.userName !== this.userNameFull }, - shouldShowFullUserName () { + shouldShowFullUserName() { const conf = this.mergedConfig.mentionLinkDisplay if (conf === 'short') { return false } else if (conf === 'full') { return true - } else { // full_for_remote + } else { + // full_for_remote return this.isRemote } }, - shouldShowTooltip () { + shouldShowTooltip() { return this.mergedConfig.mentionLinkShowTooltip }, - shouldShowAvatar () { + shouldShowAvatar() { return this.mergedConfig.mentionLinkShowAvatar }, - shouldShowYous () { + shouldShowYous() { return this.mergedConfig.mentionLinkShowYous }, - shouldBoldenYou () { + shouldBoldenYou() { return this.mergedConfig.mentionLinkBoldenYou }, - shouldFadeDomain () { + shouldFadeDomain() { return this.mergedConfig.mentionLinkFadeDomain }, ...mapGetters(['mergedConfig']), ...mapState({ - currentUser: state => state.users.currentUser - }) - } + currentUser: (state) => state.users.currentUser, + }), + }, } export default MentionLink diff --git a/src/components/mentions/mentions.js b/src/components/mentions/mentions.js index 841d5aa48..10167ac77 100644 --- a/src/components/mentions/mentions.js +++ b/src/components/mentions/mentions.js @@ -2,13 +2,13 @@ import Timeline from '../timeline/timeline.vue' const Mentions = { computed: { - timeline () { + timeline() { return this.$store.state.statuses.timelines.mentions - } + }, }, components: { - Timeline - } + Timeline, + }, } export default Mentions diff --git a/src/components/mentions_line/mentions_line.js b/src/components/mentions_line/mentions_line.js index a4a0c7246..8d546a3a7 100644 --- a/src/components/mentions_line/mentions_line.js +++ b/src/components/mentions_line/mentions_line.js @@ -8,30 +8,30 @@ const MentionsLine = { props: { mentions: { required: true, - type: Array - } + type: Array, + }, }, data: () => ({ expanded: false }), components: { - MentionLink + MentionLink, }, computed: { - mentionsComputed () { + mentionsComputed() { return this.mentions.slice(0, MENTIONS_LIMIT) }, - extraMentions () { + extraMentions() { return this.mentions.slice(MENTIONS_LIMIT) }, - manyMentions () { + manyMentions() { return this.extraMentions.length > 0 }, - ...mapGetters(['mergedConfig']) + ...mapGetters(['mergedConfig']), }, methods: { - toggleShowMore () { + toggleShowMore() { this.expanded = !this.expanded - } - } + }, + }, } export default MentionsLine diff --git a/src/components/menu_item.style.js b/src/components/menu_item.style.js index 859932062..a5567b49b 100644 --- a/src/components/menu_item.style.js +++ b/src/components/menu_item.style.js @@ -1,109 +1,105 @@ export default { name: 'MenuItem', selector: '.menu-item', - validInnerComponents: [ - 'Text', - 'Icon', - 'Border' - ], + validInnerComponents: ['Text', 'Icon', 'Border'], states: { hover: ':is(:hover, :focus-visible, :has(:focus-visible)):not(.disabled)', active: '.-active', - disabled: '.disabled' + disabled: '.disabled', }, defaultRules: [ { directives: { background: '--bg', - opacity: 0 - } + opacity: 0, + }, }, { state: ['hover'], directives: { background: '$mod(--bg 5)', - opacity: 1 - } + opacity: 1, + }, }, { state: ['active'], directives: { background: '$mod(--bg 10)', - opacity: 1 - } + opacity: 1, + }, }, { state: ['active', 'hover'], directives: { background: '$mod(--bg 15)', - opacity: 1 - } + opacity: 1, + }, }, { component: 'Text', parent: { component: 'MenuItem', - state: ['hover'] + state: ['hover'], }, directives: { textColor: '--link', - textAuto: 'no-preserve' - } + textAuto: 'no-preserve', + }, }, { component: 'Text', parent: { component: 'MenuItem', - state: ['active'] + state: ['active'], }, directives: { textColor: '--link', - textAuto: 'no-preserve' - } + textAuto: 'no-preserve', + }, }, { component: 'Icon', parent: { component: 'MenuItem', - state: ['active'] + state: ['active'], }, directives: { textColor: '--link', - textAuto: 'no-preserve' - } + textAuto: 'no-preserve', + }, }, { component: 'Icon', parent: { component: 'MenuItem', - state: ['hover'] + state: ['hover'], }, directives: { textColor: '--link', - textAuto: 'no-preserve' - } + textAuto: 'no-preserve', + }, }, { component: 'Text', parent: { component: 'MenuItem', - state: ['disabled'] + state: ['disabled'], }, directives: { textOpacity: 0.25, - textOpacityMode: 'blend' - } + textOpacityMode: 'blend', + }, }, { component: 'Icon', parent: { component: 'MenuItem', - state: ['disabled'] + state: ['disabled'], }, directives: { textOpacity: 0.25, - textOpacityMode: 'blend' - } - } - ] + textOpacityMode: 'blend', + }, + }, + ], } diff --git a/src/components/mfa_form/recovery_form.js b/src/components/mfa_form/recovery_form.js index 84479b1ec..179eb3add 100644 --- a/src/components/mfa_form/recovery_form.js +++ b/src/components/mfa_form/recovery_form.js @@ -4,39 +4,37 @@ import { mapStores, mapActions, mapState as mapPiniaState } from 'pinia' import { useOAuthStore } from 'src/stores/oauth.js' import { useAuthFlowStore } from 'src/stores/auth_flow.js' import { library } from '@fortawesome/fontawesome-svg-core' -import { - faTimes -} from '@fortawesome/free-solid-svg-icons' +import { faTimes } from '@fortawesome/free-solid-svg-icons' -library.add( - faTimes -) +library.add(faTimes) export default { data: () => ({ code: null, - error: false + error: false, }), computed: { ...mapPiniaState(useAuthFlowStore, { - authSettings: store => store.settings + authSettings: (store) => store.settings, }), ...mapStores(useOAuthStore), ...mapState({ instance: 'instance', - }) + }), }, methods: { ...mapActions(useAuthFlowStore, ['requireTOTP', 'abortMFA', 'login']), - clearError () { this.error = false }, + clearError() { + this.error = false + }, - focusOnCodeInput () { + focusOnCodeInput() { const codeInput = this.$refs.codeInput codeInput.focus() codeInput.setSelectionRange(0, codeInput.value.length) }, - submit () { + submit() { const { clientId, clientSecret } = this.oauthStore const data = { @@ -44,7 +42,7 @@ export default { clientSecret, instance: this.instance.server, mfaToken: this.authSettings.mfa_token, - code: this.code + code: this.code, } mfaApi.verifyRecoveryCode(data).then((result) => { @@ -59,6 +57,6 @@ export default { this.$router.push({ name: 'friends' }) }) }) - } - } + }, + }, } diff --git a/src/components/mfa_form/totp_form.js b/src/components/mfa_form/totp_form.js index e369d8a5d..21ef7ca67 100644 --- a/src/components/mfa_form/totp_form.js +++ b/src/components/mfa_form/totp_form.js @@ -4,39 +4,37 @@ import { mapStores, mapActions, mapState as mapPiniaState } from 'pinia' import { useOAuthStore } from 'src/stores/oauth.js' import { useAuthFlowStore } from 'src/stores/auth_flow.js' import { library } from '@fortawesome/fontawesome-svg-core' -import { - faTimes -} from '@fortawesome/free-solid-svg-icons' +import { faTimes } from '@fortawesome/free-solid-svg-icons' -library.add( - faTimes -) +library.add(faTimes) export default { data: () => ({ code: null, - error: false + error: false, }), computed: { ...mapPiniaState(useAuthFlowStore, { - authSettings: store => store.settings + authSettings: (store) => store.settings, }), ...mapStores(useOAuthStore), ...mapState({ instance: 'instance', - }) + }), }, methods: { ...mapActions(useAuthFlowStore, ['requireRecovery', 'abortMFA', 'login']), - clearError () { this.error = false }, + clearError() { + this.error = false + }, - focusOnCodeInput () { + focusOnCodeInput() { const codeInput = this.$refs.codeInput codeInput.focus() codeInput.setSelectionRange(0, codeInput.value.length) }, - submit () { + submit() { const { clientId, clientSecret } = this.oauthStore const data = { @@ -44,7 +42,7 @@ export default { clientSecret, instance: this.instance.server, mfaToken: this.authSettings.mfa_token, - code: this.code + code: this.code, } mfaApi.verifyOTPCode(data).then((result) => { @@ -59,6 +57,6 @@ export default { this.$router.push({ name: 'friends' }) }) }) - } - } + }, + }, } diff --git a/src/components/mobile_drawer.style.js b/src/components/mobile_drawer.style.js index 0f2cf4e59..306bd5c17 100644 --- a/src/components/mobile_drawer.style.js +++ b/src/components/mobile_drawer.style.js @@ -1,15 +1,13 @@ export default { name: 'MobileDrawer', selector: '.mobile-drawer', - validInnerComponents: [ - 'MenuItem' - ], + validInnerComponents: ['MenuItem'], defaultRules: [ { directives: { background: '--bg', - backgroundNoCssColor: 'yes' - } - } - ] + backgroundNoCssColor: 'yes', + }, + }, + ], } diff --git a/src/components/mobile_nav/mobile_nav.js b/src/components/mobile_nav/mobile_nav.js index 2085d24e3..1915f74a4 100644 --- a/src/components/mobile_nav/mobile_nav.js +++ b/src/components/mobile_nav/mobile_nav.js @@ -6,7 +6,7 @@ import NavigationPins from 'src/components/navigation/navigation_pins.vue' import { unseenNotificationsFromStore, - countExtraNotifications + countExtraNotifications, } from '../../services/notification_utils/notification_utils' import { mapGetters } from 'vuex' @@ -21,79 +21,79 @@ import { faBars, faArrowUp, faMinus, - faCheckDouble + faCheckDouble, } from '@fortawesome/free-solid-svg-icons' -library.add( - faTimes, - faBell, - faBars, - faArrowUp, - faMinus, - faCheckDouble -) +library.add(faTimes, faBell, faBars, faArrowUp, faMinus, faCheckDouble) const MobileNav = { components: { SideDrawer, Notifications, NavigationPins, - ConfirmModal + ConfirmModal, }, data: () => ({ notificationsCloseGesture: undefined, notificationsOpen: false, notificationsAtTop: true, - showingConfirmLogout: false + showingConfirmLogout: false, }), - created () { + created() { this.notificationsCloseGesture = GestureService.swipeGesture( GestureService.DIRECTION_RIGHT, () => this.closeMobileNotifications(true), - 50 + 50, ) }, computed: { - currentUser () { + currentUser() { return this.$store.state.users.currentUser }, - unseenNotifications () { + unseenNotifications() { return unseenNotificationsFromStore(this.$store) }, - unseenNotificationsCount () { - return this.unseenNotifications.length + countExtraNotifications(this.$store) + unseenNotificationsCount() { + return ( + this.unseenNotifications.length + countExtraNotifications(this.$store) + ) }, - unseenCount () { + unseenCount() { return this.unseenNotifications.length }, - unseenCountBadgeText () { + unseenCountBadgeText() { return `${this.unseenCount ? this.unseenCount : ''}` }, - hideSitename () { return this.$store.state.instance.hideSitename }, - sitename () { return this.$store.state.instance.name }, - isChat () { + hideSitename() { + return this.$store.state.instance.hideSitename + }, + sitename() { + return this.$store.state.instance.name + }, + isChat() { return this.$route.name === 'chat' }, ...mapState(useAnnouncementsStore, ['unreadAnnouncementCount']), ...mapState(useServerSideStorageStore, { - pinnedItems: store => new Set(store.prefsStorage.collections.pinnedNavItems).has('chats') + pinnedItems: (store) => + new Set(store.prefsStorage.collections.pinnedNavItems).has('chats'), }), - shouldConfirmLogout () { + shouldConfirmLogout() { return this.$store.getters.mergedConfig.modalOnLogout }, - closingDrawerMarksAsSeen () { + closingDrawerMarksAsSeen() { return this.$store.getters.mergedConfig.closingDrawerMarksAsSeen }, - ...mapGetters(['unreadChatCount']) + ...mapGetters(['unreadChatCount']), }, methods: { - toggleMobileSidebar () { + toggleMobileSidebar() { this.$refs.sideDrawer.toggleDrawer() }, - openMobileNotifications () { + openMobileNotifications() { this.notificationsOpen = true }, - closeMobileNotifications (markRead) { + closeMobileNotifications(markRead) { if (this.notificationsOpen) { // make sure to mark notifs seen only when the notifs were open and not // from close-calls. @@ -103,53 +103,53 @@ const MobileNav = { } } }, - notificationsTouchStart (e) { + notificationsTouchStart(e) { GestureService.beginSwipe(e, this.notificationsCloseGesture) }, - notificationsTouchMove (e) { + notificationsTouchMove(e) { GestureService.updateSwipe(e, this.notificationsCloseGesture) }, - scrollToTop () { + scrollToTop() { window.scrollTo(0, 0) }, - scrollMobileNotificationsToTop () { + scrollMobileNotificationsToTop() { this.$refs.mobileNotifications.scrollTo(0, 0) }, - showConfirmLogout () { + showConfirmLogout() { this.showingConfirmLogout = true }, - hideConfirmLogout () { + hideConfirmLogout() { this.showingConfirmLogout = false }, - logout () { + logout() { if (!this.shouldConfirmLogout) { this.doLogout() } else { this.showConfirmLogout() } }, - doLogout () { + doLogout() { this.$router.replace('/main/public') this.$store.dispatch('logout') this.hideConfirmLogout() }, - markNotificationsAsSeen () { + markNotificationsAsSeen() { this.$store.dispatch('markNotificationsAsSeen') }, - onScroll ({ target: { scrollTop, clientHeight, scrollHeight } }) { + onScroll({ target: { scrollTop, clientHeight, scrollHeight } }) { this.notificationsAtTop = scrollTop > 0 if (scrollTop + clientHeight >= scrollHeight) { this.$refs.notifications.fetchOlderNotifications() } - } + }, }, watch: { - $route () { + $route() { // handles closing notificaitons when you press any router-link on the // notifications. this.closeMobileNotifications() - } - } + }, + }, } export default MobileNav diff --git a/src/components/mobile_post_status_button/mobile_post_status_button.js b/src/components/mobile_post_status_button/mobile_post_status_button.js index 031013559..4dce38e2b 100644 --- a/src/components/mobile_post_status_button/mobile_post_status_button.js +++ b/src/components/mobile_post_status_button/mobile_post_status_button.js @@ -1,57 +1,53 @@ import { debounce } from 'lodash' import { library } from '@fortawesome/fontawesome-svg-core' -import { - faPen -} from '@fortawesome/free-solid-svg-icons' +import { faPen } from '@fortawesome/free-solid-svg-icons' import { usePostStatusStore } from 'src/stores/post_status' -library.add( - faPen -) +library.add(faPen) -const HIDDEN_FOR_PAGES = new Set([ - 'chats', - 'chat', - 'lists-edit' -]) +const HIDDEN_FOR_PAGES = new Set(['chats', 'chat', 'lists-edit']) const MobilePostStatusButton = { - data () { + data() { return { hidden: false, scrollingDown: false, inputActive: false, oldScrollPos: 0, - amountScrolled: 0 + amountScrolled: 0, } }, - created () { + created() { if (this.autohideFloatingPostButton) { this.activateFloatingPostButtonAutohide() } window.addEventListener('resize', this.handleOSK) }, - unmounted () { + unmounted() { if (this.autohideFloatingPostButton) { this.deactivateFloatingPostButtonAutohide() } window.removeEventListener('resize', this.handleOSK) }, computed: { - isLoggedIn () { + isLoggedIn() { return !!this.$store.state.users.currentUser }, - isHidden () { - if (HIDDEN_FOR_PAGES.has(this.$route.name)) { return true } + isHidden() { + if (HIDDEN_FOR_PAGES.has(this.$route.name)) { + return true + } - return this.autohideFloatingPostButton && (this.hidden || this.inputActive) + return ( + this.autohideFloatingPostButton && (this.hidden || this.inputActive) + ) }, - isPersistent () { + isPersistent() { return !!this.$store.getters.mergedConfig.alwaysShowNewPostButton }, - autohideFloatingPostButton () { + autohideFloatingPostButton() { return !!this.$store.getters.mergedConfig.autohideFloatingPostButton - } + }, }, watch: { autohideFloatingPostButton: function (isEnabled) { @@ -60,21 +56,21 @@ const MobilePostStatusButton = { } else { this.deactivateFloatingPostButtonAutohide() } - } + }, }, methods: { - activateFloatingPostButtonAutohide () { + activateFloatingPostButtonAutohide() { window.addEventListener('scroll', this.handleScrollStart) window.addEventListener('scroll', this.handleScrollEnd) }, - deactivateFloatingPostButtonAutohide () { + deactivateFloatingPostButtonAutohide() { window.removeEventListener('scroll', this.handleScrollStart) window.removeEventListener('scroll', this.handleScrollEnd) }, - openPostForm () { + openPostForm() { usePostStatusStore().openPostStatusModal() }, - handleOSK () { + handleOSK() { // This is a big hack: we're guessing from changed window sizes if the // on-screen keyboard is active or not. This is only really important // for phones in portrait mode and it's more important to show the button @@ -94,20 +90,28 @@ const MobilePostStatusButton = { this.inputActive = false } }, - handleScrollStart: debounce(function () { - if (window.scrollY > this.oldScrollPos) { - this.hidden = true - } else { - this.hidden = false - } - this.oldScrollPos = window.scrollY - }, 100, { leading: true, trailing: false }), + handleScrollStart: debounce( + function () { + if (window.scrollY > this.oldScrollPos) { + this.hidden = true + } else { + this.hidden = false + } + this.oldScrollPos = window.scrollY + }, + 100, + { leading: true, trailing: false }, + ), - handleScrollEnd: debounce(function () { - this.hidden = false - this.oldScrollPos = window.scrollY - }, 100, { leading: false, trailing: true }) - } + handleScrollEnd: debounce( + function () { + this.hidden = false + this.oldScrollPos = window.scrollY + }, + 100, + { leading: false, trailing: true }, + ), + }, } export default MobilePostStatusButton diff --git a/src/components/modal/modal.vue b/src/components/modal/modal.vue index 032e7dbeb..a022f7427 100644 --- a/src/components/modal/modal.vue +++ b/src/components/modal/modal.vue @@ -13,27 +13,27 @@ diff --git a/src/components/modal/modals.style.js b/src/components/modal/modals.style.js index 1cb4a34a7..9c8c279f7 100644 --- a/src/components/modal/modals.style.js +++ b/src/components/modal/modals.style.js @@ -3,8 +3,6 @@ export default { selector: ['.modal-view', '#modal', '.shout-panel'], lazy: true, notEditable: true, - validInnerComponents: [ - 'Panel' - ], - defaultRules: [] + validInnerComponents: ['Panel'], + defaultRules: [], } diff --git a/src/components/moderation_tools/moderation_tools.js b/src/components/moderation_tools/moderation_tools.js index bd57a353a..3b8055ba6 100644 --- a/src/components/moderation_tools/moderation_tools.js +++ b/src/components/moderation_tools/moderation_tools.js @@ -15,10 +15,8 @@ const SANDBOX = 'mrf_tag:sandbox' const QUARANTINE = 'mrf_tag:quarantine' const ModerationTools = { - props: [ - 'user' - ], - data () { + props: ['user'], + data() { return { tags: { FORCE_NSFW, @@ -27,92 +25,124 @@ const ModerationTools = { DISABLE_REMOTE_SUBSCRIPTION, DISABLE_ANY_SUBSCRIPTION, SANDBOX, - QUARANTINE + QUARANTINE, }, showDeleteUserDialog: false, - toggled: false + toggled: false, } }, components: { DialogModal, - Popover + Popover, }, computed: { - tagsSet () { + tagsSet() { return new Set(this.user.tags) }, - canGrantRole () { - return this.user.is_local && !this.user.deactivated && this.$store.state.users.currentUser.role === 'admin' + canGrantRole() { + return ( + this.user.is_local && + !this.user.deactivated && + this.$store.state.users.currentUser.role === 'admin' + ) }, - canChangeActivationState () { + canChangeActivationState() { return this.privileged('users_manage_activation_state') }, - canDeleteAccount () { + canDeleteAccount() { return this.privileged('users_delete') }, - canUseTagPolicy () { - return this.$store.state.instance.tagPolicyAvailable && this.privileged('users_manage_tags') - } + canUseTagPolicy() { + return ( + this.$store.state.instance.tagPolicyAvailable && + this.privileged('users_manage_tags') + ) + }, }, methods: { - hasTag (tagName) { + hasTag(tagName) { return this.tagsSet.has(tagName) }, - privileged (privilege) { + privileged(privilege) { return this.$store.state.users.currentUser.privileges.includes(privilege) }, - toggleTag (tag) { + toggleTag(tag) { const store = this.$store if (this.tagsSet.has(tag)) { - store.state.api.backendInteractor.untagUser({ user: this.user, tag }).then(response => { - if (!response.ok) { return } - store.commit('untagUser', { user: this.user, tag }) - }) + store.state.api.backendInteractor + .untagUser({ user: this.user, tag }) + .then((response) => { + if (!response.ok) { + return + } + store.commit('untagUser', { user: this.user, tag }) + }) } else { - store.state.api.backendInteractor.tagUser({ user: this.user, tag }).then(response => { - if (!response.ok) { return } - store.commit('tagUser', { user: this.user, tag }) - }) + store.state.api.backendInteractor + .tagUser({ user: this.user, tag }) + .then((response) => { + if (!response.ok) { + return + } + store.commit('tagUser', { user: this.user, tag }) + }) } }, - toggleRight (right) { + toggleRight(right) { const store = this.$store if (this.user.rights[right]) { - store.state.api.backendInteractor.deleteRight({ user: this.user, right }).then(response => { - if (!response.ok) { return } - store.commit('updateRight', { user: this.user, right, value: false }) - }) + store.state.api.backendInteractor + .deleteRight({ user: this.user, right }) + .then((response) => { + if (!response.ok) { + return + } + store.commit('updateRight', { + user: this.user, + right, + value: false, + }) + }) } else { - store.state.api.backendInteractor.addRight({ user: this.user, right }).then(response => { - if (!response.ok) { return } - store.commit('updateRight', { user: this.user, right, value: true }) - }) + store.state.api.backendInteractor + .addRight({ user: this.user, right }) + .then((response) => { + if (!response.ok) { + return + } + store.commit('updateRight', { user: this.user, right, value: true }) + }) } }, - toggleActivationStatus () { + toggleActivationStatus() { this.$store.dispatch('toggleActivationStatus', { user: this.user }) }, - deleteUserDialog (show) { + deleteUserDialog(show) { this.showDeleteUserDialog = show }, - deleteUser () { + deleteUser() { const store = this.$store const user = this.user const { id, name } = user - store.state.api.backendInteractor.deleteUser({ user }) - .then(() => { - this.$store.dispatch('markStatusesAsDeleted', status => user.id === status.user.id) - const isProfile = this.$route.name === 'external-user-profile' || this.$route.name === 'user-profile' - const isTargetUser = this.$route.params.name === name || this.$route.params.id === id - if (isProfile && isTargetUser) { - window.history.back() - } - }) + store.state.api.backendInteractor.deleteUser({ user }).then(() => { + this.$store.dispatch( + 'markStatusesAsDeleted', + (status) => user.id === status.user.id, + ) + const isProfile = + this.$route.name === 'external-user-profile' || + this.$route.name === 'user-profile' + const isTargetUser = + this.$route.params.name === name || this.$route.params.id === id + if (isProfile && isTargetUser) { + window.history.back() + } + }) }, - setToggled (value) { + setToggled(value) { this.toggled = value - } - } + }, + }, } export default ModerationTools diff --git a/src/components/mrf_transparency_panel/mrf_transparency_panel.js b/src/components/mrf_transparency_panel/mrf_transparency_panel.js index 13cfb52ee..98d63ef4a 100644 --- a/src/components/mrf_transparency_panel/mrf_transparency_panel.js +++ b/src/components/mrf_transparency_panel/mrf_transparency_panel.js @@ -8,7 +8,7 @@ import { get } from 'lodash' * to add an extra "info" key. */ const toInstanceReasonObject = (instances, info, key) => { - return instances.map(instance => { + return instances.map((instance) => { if (info[key] && info[key][instance] && info[key][instance].reason) { return { instance, reason: info[key][instance].reason } } @@ -19,56 +19,82 @@ const toInstanceReasonObject = (instances, info, key) => { const MRFTransparencyPanel = { computed: { ...mapState({ - federationPolicy: state => get(state, 'instance.federationPolicy'), - mrfPolicies: state => get(state, 'instance.federationPolicy.mrf_policies', []), - quarantineInstances: state => toInstanceReasonObject( - get(state, 'instance.federationPolicy.quarantined_instances', []), - get(state, 'instance.federationPolicy.quarantined_instances_info', []), - 'quarantined_instances' - ), - acceptInstances: state => toInstanceReasonObject( - get(state, 'instance.federationPolicy.mrf_simple.accept', []), - get(state, 'instance.federationPolicy.mrf_simple_info', []), - 'accept' - ), - rejectInstances: state => toInstanceReasonObject( - get(state, 'instance.federationPolicy.mrf_simple.reject', []), - get(state, 'instance.federationPolicy.mrf_simple_info', []), - 'reject' - ), - ftlRemovalInstances: state => toInstanceReasonObject( - get(state, 'instance.federationPolicy.mrf_simple.federated_timeline_removal', []), - get(state, 'instance.federationPolicy.mrf_simple_info', []), - 'federated_timeline_removal' - ), - mediaNsfwInstances: state => toInstanceReasonObject( - get(state, 'instance.federationPolicy.mrf_simple.media_nsfw', []), - get(state, 'instance.federationPolicy.mrf_simple_info', []), - 'media_nsfw' - ), - mediaRemovalInstances: state => toInstanceReasonObject( - get(state, 'instance.federationPolicy.mrf_simple.media_removal', []), - get(state, 'instance.federationPolicy.mrf_simple_info', []), - 'media_removal' - ), - keywordsFtlRemoval: state => get(state, 'instance.federationPolicy.mrf_keyword.federated_timeline_removal', []), - keywordsReject: state => get(state, 'instance.federationPolicy.mrf_keyword.reject', []), - keywordsReplace: state => get(state, 'instance.federationPolicy.mrf_keyword.replace', []) + federationPolicy: (state) => get(state, 'instance.federationPolicy'), + mrfPolicies: (state) => + get(state, 'instance.federationPolicy.mrf_policies', []), + quarantineInstances: (state) => + toInstanceReasonObject( + get(state, 'instance.federationPolicy.quarantined_instances', []), + get( + state, + 'instance.federationPolicy.quarantined_instances_info', + [], + ), + 'quarantined_instances', + ), + acceptInstances: (state) => + toInstanceReasonObject( + get(state, 'instance.federationPolicy.mrf_simple.accept', []), + get(state, 'instance.federationPolicy.mrf_simple_info', []), + 'accept', + ), + rejectInstances: (state) => + toInstanceReasonObject( + get(state, 'instance.federationPolicy.mrf_simple.reject', []), + get(state, 'instance.federationPolicy.mrf_simple_info', []), + 'reject', + ), + ftlRemovalInstances: (state) => + toInstanceReasonObject( + get( + state, + 'instance.federationPolicy.mrf_simple.federated_timeline_removal', + [], + ), + get(state, 'instance.federationPolicy.mrf_simple_info', []), + 'federated_timeline_removal', + ), + mediaNsfwInstances: (state) => + toInstanceReasonObject( + get(state, 'instance.federationPolicy.mrf_simple.media_nsfw', []), + get(state, 'instance.federationPolicy.mrf_simple_info', []), + 'media_nsfw', + ), + mediaRemovalInstances: (state) => + toInstanceReasonObject( + get(state, 'instance.federationPolicy.mrf_simple.media_removal', []), + get(state, 'instance.federationPolicy.mrf_simple_info', []), + 'media_removal', + ), + keywordsFtlRemoval: (state) => + get( + state, + 'instance.federationPolicy.mrf_keyword.federated_timeline_removal', + [], + ), + keywordsReject: (state) => + get(state, 'instance.federationPolicy.mrf_keyword.reject', []), + keywordsReplace: (state) => + get(state, 'instance.federationPolicy.mrf_keyword.replace', []), }), - hasInstanceSpecificPolicies () { - return this.quarantineInstances.length || + hasInstanceSpecificPolicies() { + return ( + this.quarantineInstances.length || this.acceptInstances.length || this.rejectInstances.length || this.ftlRemovalInstances.length || this.mediaNsfwInstances.length || this.mediaRemovalInstances.length + ) }, - hasKeywordPolicies () { - return this.keywordsFtlRemoval.length || + hasKeywordPolicies() { + return ( + this.keywordsFtlRemoval.length || this.keywordsReject.length || this.keywordsReplace.length - } - } + ) + }, + }, } export default MRFTransparencyPanel diff --git a/src/components/mute_card/mute_card.js b/src/components/mute_card/mute_card.js index 895586888..ce3bab6d7 100644 --- a/src/components/mute_card/mute_card.js +++ b/src/components/mute_card/mute_card.js @@ -4,36 +4,38 @@ import UserTimedFilterModal from 'src/components/user_timed_filter_modal/user_ti const MuteCard = { props: ['userId'], computed: { - user () { + user() { return this.$store.getters.findUser(this.userId) }, - relationship () { + relationship() { return this.$store.getters.relationship(this.userId) }, - muted () { + muted() { return this.relationship.muting }, - muteExpiryAvailable () { + muteExpiryAvailable() { return this.user.mute_expires_at !== undefined }, - muteExpiry () { + muteExpiry() { return this.user.mute_expires_at == null ? this.$t('user_card.mute_expires_forever') - : this.$t('user_card.mute_expires_at', [new Date(this.user.mute_expires_at).toLocaleString()]) - } + : this.$t('user_card.mute_expires_at', [ + new Date(this.user.mute_expires_at).toLocaleString(), + ]) + }, }, components: { BasicUserCard, - UserTimedFilterModal + UserTimedFilterModal, }, methods: { - unmuteUser () { + unmuteUser() { this.$store.dispatch('unmuteUser', this.userId) }, - muteUser () { + muteUser() { this.$refs.timedMuteDialog.optionallyPrompt() - } - } + }, + }, } export default MuteCard diff --git a/src/components/nav_panel/nav_panel.js b/src/components/nav_panel/nav_panel.js index a155abe0c..4eff8a513 100644 --- a/src/components/nav_panel/nav_panel.js +++ b/src/components/nav_panel/nav_panel.js @@ -26,7 +26,7 @@ import { faStream, faList, faBullhorn, - faFilePen + faFilePen, } from '@fortawesome/free-solid-svg-icons' library.add( @@ -43,80 +43,93 @@ library.add( faStream, faList, faBullhorn, - faFilePen + faFilePen, ) const NavPanel = { props: ['forceExpand', 'forceEditMode'], - created () { - }, + created() {}, components: { BookmarkFoldersMenuContent, ListsMenuContent, NavigationEntry, NavigationPins, - Checkbox + Checkbox, }, - data () { + data() { return { editMode: false, showTimelines: false, showLists: false, showBookmarkFolders: false, - timelinesList: Object.entries(TIMELINES).map(([k, v]) => ({ ...v, name: k })), - rootList: Object.entries(ROOT_ITEMS).map(([k, v]) => ({ ...v, name: k })) + timelinesList: Object.entries(TIMELINES).map(([k, v]) => ({ + ...v, + name: k, + })), + rootList: Object.entries(ROOT_ITEMS).map(([k, v]) => ({ ...v, name: k })), } }, methods: { - toggleTimelines () { + toggleTimelines() { this.showTimelines = !this.showTimelines }, - toggleLists () { + toggleLists() { this.showLists = !this.showLists }, - toggleBookmarkFolders () { + toggleBookmarkFolders() { this.showBookmarkFolders = !this.showBookmarkFolders }, - toggleEditMode () { + toggleEditMode() { this.editMode = !this.editMode }, - toggleCollapse () { - useServerSideStorageStore().setPreference({ path: 'simple.collapseNav', value: !this.collapsed }) + toggleCollapse() { + useServerSideStorageStore().setPreference({ + path: 'simple.collapseNav', + value: !this.collapsed, + }) useServerSideStorageStore().pushServerSideStorage() }, - isPinned (item) { + isPinned(item) { return this.pinnedItems.has(item) }, - togglePin (item) { + togglePin(item) { if (this.isPinned(item)) { - useServerSideStorageStore().removeCollectionPreference({ path: 'collections.pinnedNavItems', value: item }) + useServerSideStorageStore().removeCollectionPreference({ + path: 'collections.pinnedNavItems', + value: item, + }) } else { - useServerSideStorageStore().addCollectionPreference({ path: 'collections.pinnedNavItems', value: item }) + useServerSideStorageStore().addCollectionPreference({ + path: 'collections.pinnedNavItems', + value: item, + }) } useServerSideStorageStore().pushServerSideStorage() - } + }, }, computed: { ...mapPiniaState(useAnnouncementsStore, { unreadAnnouncementCount: 'unreadAnnouncementCount', - supportsAnnouncements: store => store.supportsAnnouncements + supportsAnnouncements: (store) => store.supportsAnnouncements, }), ...mapPiniaState(useServerSideStorageStore, { - collapsed: store => store.prefsStorage.simple.collapseNav, - pinnedItems: store => new Set(store.prefsStorage.collections.pinnedNavItems) + collapsed: (store) => store.prefsStorage.simple.collapseNav, + pinnedItems: (store) => + new Set(store.prefsStorage.collections.pinnedNavItems), }), ...mapState({ - currentUser: state => state.users.currentUser, - followRequestCount: state => state.api.followRequests.length, - privateMode: state => state.instance.private, - federating: state => state.instance.federating, - pleromaChatMessagesAvailable: state => state.instance.pleromaChatMessagesAvailable, - bookmarkFolders: state => state.instance.pleromaBookmarkFoldersAvailable, - bubbleTimeline: state => state.instance.localBubbleInstances.length > 0 + currentUser: (state) => state.users.currentUser, + followRequestCount: (state) => state.api.followRequests.length, + privateMode: (state) => state.instance.private, + federating: (state) => state.instance.federating, + pleromaChatMessagesAvailable: (state) => + state.instance.pleromaChatMessagesAvailable, + bookmarkFolders: (state) => + state.instance.pleromaBookmarkFoldersAvailable, + bubbleTimeline: (state) => state.instance.localBubbleInstances.length > 0, }), - timelinesItems () { + timelinesItems() { return filterNavigation( - Object - .entries({ ...TIMELINES }) + Object.entries({ ...TIMELINES }) // do not show in timeliens list since it's in a better place now .filter(([key]) => key !== 'bookmarks') .map(([k, v]) => ({ ...v, name: k })), @@ -127,15 +140,13 @@ const NavPanel = { isPrivate: this.privateMode, currentUser: this.currentUser, supportsBubbleTimeline: this.bubbleTimeline, - supportsBookmarkFolders: this.bookmarkFolders - } + supportsBookmarkFolders: this.bookmarkFolders, + }, ) }, - rootItems () { + rootItems() { return filterNavigation( - Object - .entries({ ...ROOT_ITEMS }) - .map(([k, v]) => ({ ...v, name: k })), + Object.entries({ ...ROOT_ITEMS }).map(([k, v]) => ({ ...v, name: k })), { hasChats: this.pleromaChatMessagesAvailable, hasAnnouncements: this.supportsAnnouncements, @@ -143,12 +154,12 @@ const NavPanel = { isPrivate: this.privateMode, currentUser: this.currentUser, supportsBubbleTimeline: this.bubbleTimeline, - supportsBookmarkFolders: this.bookmarkFolders - } + supportsBookmarkFolders: this.bookmarkFolders, + }, ) }, - ...mapGetters(['unreadChatCount']) - } + ...mapGetters(['unreadChatCount']), + }, } export default NavPanel diff --git a/src/components/navigation/filter.js b/src/components/navigation/filter.js index 54abb67b4..0255db6aa 100644 --- a/src/components/navigation/filter.js +++ b/src/components/navigation/filter.js @@ -1,39 +1,50 @@ -export const filterNavigation = (list = [], { - hasChats, - hasAnnouncements, - isFederating, - isPrivate, - currentUser, - supportsBookmarkFolders, - supportsBubbleTimeline -}) => { +export const filterNavigation = ( + list = [], + { + hasChats, + hasAnnouncements, + isFederating, + isPrivate, + currentUser, + supportsBookmarkFolders, + supportsBubbleTimeline, + }, +) => { return list.filter(({ criteria, anon, anonRoute }) => { const set = new Set(criteria || []) 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')) return false + if ((!currentUser || !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')) return false - if (!supportsBookmarkFolders && set.has('supportsBookmarkFolders')) return false - if (supportsBookmarkFolders && set.has('!supportsBookmarkFolders')) return false + if (!supportsBubbleTimeline && set.has('supportsBubbleTimeline')) + return false + if (!supportsBookmarkFolders && set.has('supportsBookmarkFolders')) + return false + if (supportsBookmarkFolders && set.has('!supportsBookmarkFolders')) + return false return true }) } -export const getListEntries = store => store.allLists.map(list => ({ - name: 'list-' + list.id, - routeObject: { name: 'lists-timeline', params: { id: list.id } }, - labelRaw: list.title, - iconLetter: list.title[0] -})) +export const getListEntries = (store) => + store.allLists.map((list) => ({ + name: 'list-' + list.id, + routeObject: { name: 'lists-timeline', params: { id: list.id } }, + labelRaw: list.title, + iconLetter: list.title[0], + })) -export const getBookmarkFolderEntries = store => store.allFolders ? store.allFolders.map(folder => ({ - name: 'bookmark-folder-' + folder.id, - routeObject: { name: 'bookmark-folder', params: { id: folder.id } }, - labelRaw: folder.name, - iconEmoji: folder.emoji, - iconEmojiUrl: folder.emoji_url, - iconLetter: folder.name[0] -})) : [] +export const getBookmarkFolderEntries = (store) => + store.allFolders + ? store.allFolders.map((folder) => ({ + name: 'bookmark-folder-' + folder.id, + routeObject: { name: 'bookmark-folder', params: { id: folder.id } }, + labelRaw: folder.name, + iconEmoji: folder.emoji, + iconEmojiUrl: folder.emoji_url, + iconLetter: folder.name[0], + })) + : [] diff --git a/src/components/navigation/navigation.js b/src/components/navigation/navigation.js index d1c2b6763..66fb0d347 100644 --- a/src/components/navigation/navigation.js +++ b/src/components/navigation/navigation.js @@ -4,42 +4,39 @@ export const USERNAME_ROUTES = new Set([ 'interactions', 'notifications', 'chat', - 'chats' + 'chats', ]) // routes that take :name property -export const NAME_ROUTES = new Set([ - 'user-profile', - 'legacy-user-profile' -]) +export const NAME_ROUTES = new Set(['user-profile', 'legacy-user-profile']) export const TIMELINES = { home: { route: 'friends', icon: 'home', label: 'nav.home_timeline', - criteria: ['!private'] + criteria: ['!private'], }, public: { route: 'public-timeline', anon: true, icon: 'users', label: 'nav.public_tl', - criteria: ['!private'] + criteria: ['!private'], }, bubble: { route: 'bubble', anon: true, icon: 'city', label: 'nav.bubble', - criteria: ['!private', 'federating', 'supportsBubbleTimeline'] + criteria: ['!private', 'federating', 'supportsBubbleTimeline'], }, twkn: { route: 'public-external-timeline', anon: true, icon: 'globe', label: 'nav.twkn', - criteria: ['!private', 'federating'] + criteria: ['!private', 'federating'], }, // bookmarks are still technically a timeline so we should show it in the dropdown bookmarks: { @@ -50,13 +47,13 @@ export const TIMELINES = { favorites: { routeObject: { name: 'user-profile', query: { tab: 'favorites' } }, icon: 'star', - label: 'user_card.favorites' + label: 'user_card.favorites', }, dms: { route: 'dms', icon: 'envelope', - label: 'nav.dms' - } + label: 'nav.dms', + }, } export const ROOT_ITEMS = { @@ -67,12 +64,12 @@ export const ROOT_ITEMS = { // shows bookmarks entry in a better suited location // hides it when bookmark folders are supported since // we show custom component instead of it - criteria: ['!supportsBookmarkFolders'] + criteria: ['!supportsBookmarkFolders'], }, interactions: { route: 'interactions', icon: 'bell', - label: 'nav.interactions' + label: 'nav.interactions', }, chats: { route: 'chats', @@ -80,7 +77,7 @@ export const ROOT_ITEMS = { label: 'nav.chats', badgeStyle: 'notification', badgeGetter: 'unreadChatCount', - criteria: ['chats'] + criteria: ['chats'], }, friendRequests: { route: 'friend-requests', @@ -88,13 +85,13 @@ export const ROOT_ITEMS = { label: 'nav.friend_requests', badgeStyle: 'notification', criteria: ['lockedUser'], - badgeGetter: 'followRequestCount' + badgeGetter: 'followRequestCount', }, about: { route: 'about', anon: true, icon: 'info-circle', - label: 'nav.about' + label: 'nav.about', }, announcements: { route: 'announcements', @@ -103,18 +100,18 @@ export const ROOT_ITEMS = { store: 'announcements', badgeStyle: 'notification', badgeGetter: 'unreadAnnouncementCount', - criteria: ['announcements'] + criteria: ['announcements'], }, drafts: { route: 'drafts', icon: 'file-pen', label: 'nav.drafts', badgeStyle: 'neutral', - badgeGetter: 'draftCount' - } + badgeGetter: 'draftCount', + }, } -export function routeTo (item, currentUser) { +export function routeTo(item, currentUser) { if (!item.route && !item.routeObject) return null let route @@ -122,7 +119,7 @@ export function routeTo (item, currentUser) { if (item.routeObject) { route = item.routeObject } else { - route = { name: (item.anon || currentUser) ? item.route : item.anonRoute } + route = { name: item.anon || currentUser ? item.route : item.anonRoute } } if (USERNAME_ROUTES.has(route.name)) { diff --git a/src/components/navigation/navigation_entry.js b/src/components/navigation/navigation_entry.js index 11db1c9e3..745163261 100644 --- a/src/components/navigation/navigation_entry.js +++ b/src/components/navigation/navigation_entry.js @@ -13,36 +13,43 @@ library.add(faThumbtack) const NavigationEntry = { props: ['item', 'showPin'], components: { - OptionalRouterLink + OptionalRouterLink, }, methods: { - isPinned (value) { + isPinned(value) { return this.pinnedItems.has(value) }, - togglePin (value) { + togglePin(value) { if (this.isPinned(value)) { - useServerSideStorageStore().removeCollectionPreference({ path: 'collections.pinnedNavItems', value }) + useServerSideStorageStore().removeCollectionPreference({ + path: 'collections.pinnedNavItems', + value, + }) } else { - useServerSideStorageStore().addCollectionPreference({ path: 'collections.pinnedNavItems', value }) + useServerSideStorageStore().addCollectionPreference({ + path: 'collections.pinnedNavItems', + value, + }) } useServerSideStorageStore().pushServerSideStorage() - } + }, }, computed: { - routeTo () { + routeTo() { return routeTo(this.item, this.currentUser) }, - getters () { + getters() { return this.$store.getters }, ...mapStores(useAnnouncementsStore), ...mapState({ - currentUser: state => state.users.currentUser + currentUser: (state) => state.users.currentUser, }), ...mapPiniaState(useServerSideStorageStore, { - pinnedItems: store => new Set(store.prefsStorage.collections.pinnedNavItems) + pinnedItems: (store) => + new Set(store.prefsStorage.collections.pinnedNavItems), }), - } + }, } export default NavigationEntry diff --git a/src/components/navigation/navigation_pins.js b/src/components/navigation/navigation_pins.js index f9a900fc6..b40ea5563 100644 --- a/src/components/navigation/navigation_pins.js +++ b/src/components/navigation/navigation_pins.js @@ -1,7 +1,15 @@ import { mapState } from 'vuex' import { mapState as mapPiniaState } from 'pinia' -import { TIMELINES, ROOT_ITEMS, routeTo } from 'src/components/navigation/navigation.js' -import { getBookmarkFolderEntries, getListEntries, filterNavigation } from 'src/components/navigation/filter.js' +import { + TIMELINES, + ROOT_ITEMS, + routeTo, +} from 'src/components/navigation/navigation.js' +import { + getBookmarkFolderEntries, + getListEntries, + filterNavigation, +} from 'src/components/navigation/filter.js' import StillImage from 'src/components/still-image/still-image.vue' @@ -16,7 +24,7 @@ import { faBell, faInfoCircle, faStream, - faList + faList, } from '@fortawesome/free-solid-svg-icons' import { useListsStore } from 'src/stores/lists' import { useAnnouncementsStore } from 'src/stores/announcements' @@ -33,72 +41,74 @@ library.add( faBell, faInfoCircle, faStream, - faList + faList, ) const NavPanel = { props: ['limit'], methods: { - getRouteTo (item) { + getRouteTo(item) { return routeTo(item, this.currentUser) - } + }, }, components: { - StillImage + StillImage, }, computed: { - getters () { + getters() { return this.$store.getters }, ...mapPiniaState(useListsStore, { - lists: getListEntries + lists: getListEntries, }), ...mapPiniaState(useAnnouncementsStore, { - supportsAnnouncements: store => store.supportsAnnouncements + supportsAnnouncements: (store) => store.supportsAnnouncements, }), ...mapPiniaState(useBookmarkFoldersStore, { bookmarks: getBookmarkFolderEntries, }), ...mapPiniaState(useServerSideStorageStore, { - pinnedItems: store => new Set(store.prefsStorage.collections.pinnedNavItems) + pinnedItems: (store) => + new Set(store.prefsStorage.collections.pinnedNavItems), }), ...mapState({ - currentUser: state => state.users.currentUser, - followRequestCount: state => state.api.followRequests.length, - privateMode: state => state.instance.private, - federating: state => state.instance.federating, - pleromaChatMessagesAvailable: state => state.instance.pleromaChatMessagesAvailable, - bubbleTimeline: state => state.instance.localBubbleInstances.length > 0 + currentUser: (state) => state.users.currentUser, + followRequestCount: (state) => state.api.followRequests.length, + privateMode: (state) => state.instance.private, + federating: (state) => state.instance.federating, + pleromaChatMessagesAvailable: (state) => + state.instance.pleromaChatMessagesAvailable, + bubbleTimeline: (state) => state.instance.localBubbleInstances.length > 0, }), - pinnedList () { + pinnedList() { if (!this.currentUser) { - return filterNavigation([ - { ...TIMELINES.public, name: 'public' }, - { ...TIMELINES.twkn, name: 'twkn' }, - { ...ROOT_ITEMS.about, name: 'about' } - ], - { - hasChats: this.pleromaChatMessagesAvailable, - hasAnnouncements: this.supportsAnnouncements, - isFederating: this.federating, - isPrivate: this.privateMode, - currentUser: this.currentUser, - supportsBubbleTimeline: this.bubbleTimeline, - supportsBookmarkFolders: this.bookmarks - }) + return filterNavigation( + [ + { ...TIMELINES.public, name: 'public' }, + { ...TIMELINES.twkn, name: 'twkn' }, + { ...ROOT_ITEMS.about, name: 'about' }, + ], + { + hasChats: this.pleromaChatMessagesAvailable, + hasAnnouncements: this.supportsAnnouncements, + isFederating: this.federating, + isPrivate: this.privateMode, + currentUser: this.currentUser, + supportsBubbleTimeline: this.bubbleTimeline, + supportsBookmarkFolders: this.bookmarks, + }, + ) } return filterNavigation( [ - ...Object - .entries({ ...TIMELINES }) + ...Object.entries({ ...TIMELINES }) .filter(([k]) => this.pinnedItems.has(k)) .map(([k, v]) => ({ ...v, name: k })), ...this.lists.filter((k) => this.pinnedItems.has(k.name)), ...this.bookmarks.filter((k) => this.pinnedItems.has(k.name)), - ...Object - .entries({ ...ROOT_ITEMS }) + ...Object.entries({ ...ROOT_ITEMS }) .filter(([k]) => this.pinnedItems.has(k)) - .map(([k, v]) => ({ ...v, name: k })) + .map(([k, v]) => ({ ...v, name: k })), ], { hasChats: this.pleromaChatMessagesAvailable, @@ -107,11 +117,11 @@ const NavPanel = { supportsBookmarkFolders: this.bookmarks, isFederating: this.federating, isPrivate: this.privateMode, - currentUser: this.currentUser - } + currentUser: this.currentUser, + }, ).slice(0, this.limit) - } - } + }, + }, } export default NavPanel diff --git a/src/components/notification/notification.js b/src/components/notification/notification.js index 8052e3a73..1feef0cf2 100644 --- a/src/components/notification/notification.js +++ b/src/components/notification/notification.js @@ -10,7 +10,10 @@ import RichContent from 'src/components/rich_content/rich_content.jsx' import UserPopover from '../user_popover/user_popover.vue' import ConfirmModal from '../confirm_modal/confirm_modal.vue' import { isStatusNotification } from '../../services/notification_utils/notification_utils.js' -import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js' +import { + highlightClass, + highlightStyle, +} from '../../services/user_highlighter/user_highlighter.js' import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' import { library } from '@fortawesome/fontawesome-svg-core' import { @@ -23,7 +26,7 @@ import { faUser, faSuitcaseRolling, faExpandAlt, - faCompressAlt + faCompressAlt, } from '@fortawesome/free-solid-svg-icons' library.add( @@ -36,17 +39,17 @@ library.add( faEyeSlash, faSuitcaseRolling, faExpandAlt, - faCompressAlt + faCompressAlt, ) const Notification = { - data () { + data() { return { selecting: false, statusExpanded: false, unmuted: false, showingApproveConfirmDialog: false, - showingDenyConfirmDialog: false + showingDenyConfirmDialog: false, } }, props: ['notification'], @@ -61,141 +64,158 @@ const Notification = { RichContent, UserPopover, UserLink, - ConfirmModal + ConfirmModal, }, - mounted () { + mounted() { document.addEventListener('selectionchange', this.onContentSelect) }, - unmounted () { + unmounted() { document.removeEventListener('selectionchange', this.onContentSelect) }, methods: { - toggleStatusExpanded () { + toggleStatusExpanded() { if (!this.expandable) return this.statusExpanded = !this.statusExpanded }, - onContentSelect () { + onContentSelect() { const { isCollapsed, anchorNode, offsetNode } = document.getSelection() if (isCollapsed) { this.selecting = false return } - const within = this.$refs.root.contains(anchorNode) || this.$refs.root.contains(offsetNode) + const within = + this.$refs.root.contains(anchorNode) || + this.$refs.root.contains(offsetNode) if (within) { this.selecting = true } else { this.selecting = false } }, - onContentClick (e) { - if (!this.selecting && !e.target.closest('a') && !e.target.closest('button')) { + onContentClick(e) { + if ( + !this.selecting && + !e.target.closest('a') && + !e.target.closest('button') + ) { this.toggleStatusExpanded() } }, - generateUserProfileLink (user) { - return generateProfileLink(user.id, user.screen_name, this.$store.state.instance.restrictedNicknames) + generateUserProfileLink(user) { + return generateProfileLink( + user.id, + user.screen_name, + this.$store.state.instance.restrictedNicknames, + ) }, - getUser (notification) { + getUser(notification) { return this.$store.state.users.usersObject[notification.from_profile.id] }, - interacted () { + interacted() { this.$emit('interacted') }, - toggleMute () { + toggleMute() { this.unmuted = !this.unmuted }, - showApproveConfirmDialog () { + showApproveConfirmDialog() { this.showingApproveConfirmDialog = true }, - hideApproveConfirmDialog () { + hideApproveConfirmDialog() { this.showingApproveConfirmDialog = false }, - showDenyConfirmDialog () { + showDenyConfirmDialog() { this.showingDenyConfirmDialog = true }, - hideDenyConfirmDialog () { + hideDenyConfirmDialog() { this.showingDenyConfirmDialog = false }, - approveUser () { + approveUser() { if (this.shouldConfirmApprove) { this.showApproveConfirmDialog() } else { this.doApprove() } }, - doApprove () { + doApprove() { this.$emit('interacted') this.$store.state.api.backendInteractor.approveUser({ id: this.user.id }) this.$store.dispatch('removeFollowRequest', this.user) - this.$store.dispatch('markSingleNotificationAsSeen', { id: this.notification.id }) + this.$store.dispatch('markSingleNotificationAsSeen', { + id: this.notification.id, + }) this.$store.dispatch('updateNotification', { id: this.notification.id, - updater: notification => { + updater: (notification) => { notification.type = 'follow' - } + }, }) this.hideApproveConfirmDialog() }, - denyUser () { + denyUser() { if (this.shouldConfirmDeny) { this.showDenyConfirmDialog() } else { this.doDeny() } }, - doDeny () { + doDeny() { this.$emit('interacted') - this.$store.state.api.backendInteractor.denyUser({ id: this.user.id }) + this.$store.state.api.backendInteractor + .denyUser({ id: this.user.id }) .then(() => { - this.$store.dispatch('dismissNotificationLocal', { id: this.notification.id }) + this.$store.dispatch('dismissNotificationLocal', { + id: this.notification.id, + }) this.$store.dispatch('removeFollowRequest', this.user) }) this.hideDenyConfirmDialog() - } + }, }, computed: { - userClass () { + userClass() { return highlightClass(this.notification.from_profile) }, - userStyle () { + userStyle() { const highlight = this.$store.getters.mergedConfig.highlight const user = this.notification.from_profile return highlightStyle(highlight[user.screen_name]) }, - expandable () { - return (new Set(['like', 'pleroma:emoji_reaction', 'repeat', 'poll'])).has(this.notification.type) + expandable() { + return new Set(['like', 'pleroma:emoji_reaction', 'repeat', 'poll']).has( + this.notification.type, + ) }, - user () { + user() { return this.$store.getters.findUser(this.notification.from_profile.id) }, - userProfileLink () { + userProfileLink() { return this.generateUserProfileLink(this.user) }, - targetUser () { + targetUser() { return this.$store.getters.findUser(this.notification.target.id) }, - targetUserProfileLink () { + targetUserProfileLink() { return this.generateUserProfileLink(this.targetUser) }, - needMute () { + needMute() { return this.$store.getters.relationship(this.user.id).muting }, - isStatusNotification () { + isStatusNotification() { return isStatusNotification(this.notification.type) }, - mergedConfig () { + mergedConfig() { return this.$store.getters.mergedConfig }, - shouldConfirmApprove () { + shouldConfirmApprove() { return this.mergedConfig.modalOnApproveFollow }, - shouldConfirmDeny () { + shouldConfirmDeny() { return this.mergedConfig.modalOnDenyFollow }, ...mapState({ - currentUser: state => state.users.currentUser - }) - } + currentUser: (state) => state.users.currentUser, + }), + }, } export default Notification diff --git a/src/components/notification/notification.style.js b/src/components/notification/notification.style.js index 05c1f6f23..49e28cf2e 100644 --- a/src/components/notification/notification.style.js +++ b/src/components/notification/notification.style.js @@ -7,7 +7,7 @@ export default { 'Icon', 'Border', 'Avatar', - 'PollGraph' + 'PollGraph', ], - defaultRules: [] + defaultRules: [], } diff --git a/src/components/notifications/notification_filters.vue b/src/components/notifications/notification_filters.vue index 0b740ea0a..af4f412cd 100644 --- a/src/components/notifications/notification_filters.vue +++ b/src/components/notifications/notification_filters.vue @@ -110,27 +110,25 @@ import Popover from '../popover/popover.vue' import { library } from '@fortawesome/fontawesome-svg-core' import { faFilter } from '@fortawesome/free-solid-svg-icons' -library.add( - faFilter -) +library.add(faFilter) export default { components: { Popover }, computed: { - filters () { + filters() { return this.$store.getters.mergedConfig.notificationVisibility - } + }, }, methods: { - toggleNotificationFilter (type) { + toggleNotificationFilter(type) { this.$store.dispatch('setOption', { name: 'notificationVisibility', value: { ...this.filters, - [type]: !this.filters[type] - } + [type]: !this.filters[type], + }, }) - } - } + }, + }, } diff --git a/src/components/notifications/notifications.js b/src/components/notifications/notifications.js index ced97d57f..1decc6646 100644 --- a/src/components/notifications/notifications.js +++ b/src/components/notifications/notifications.js @@ -10,19 +10,19 @@ import { filteredNotificationsFromStore, unseenNotificationsFromStore, countExtraNotifications, - ACTIONABLE_NOTIFICATION_TYPES + ACTIONABLE_NOTIFICATION_TYPES, } from '../../services/notification_utils/notification_utils.js' import FaviconService from '../../services/favicon_service/favicon_service.js' import { library } from '@fortawesome/fontawesome-svg-core' -import { faCircleNotch, faArrowUp, faMinus } from '@fortawesome/free-solid-svg-icons' +import { + faCircleNotch, + faArrowUp, + faMinus, +} from '@fortawesome/free-solid-svg-icons' import { useInterfaceStore } from 'src/stores/interface' import { useAnnouncementsStore } from 'src/stores/announcements' -library.add( - faCircleNotch, - faArrowUp, - faMinus -) +library.add(faCircleNotch, faArrowUp, faMinus) const DEFAULT_SEEN_TO_DISPLAY_COUNT = 30 @@ -30,7 +30,7 @@ const Notifications = { components: { Notification, NotificationFilters, - ExtraNotifications + ExtraNotifications, }, props: { // Disables panel styles, unread mark, potentially other notification-related actions @@ -41,93 +41,110 @@ const Notifications = { // Do not show extra notifications noExtra: { type: Boolean, - default: false + default: false, }, // Disable teleporting (i.e. for /users/user/notifications) - disableTeleport: Boolean + disableTeleport: Boolean, }, - data () { + data() { return { showScrollTop: false, bottomedOut: false, // How many seen notifications to display in the list. The more there are, // the heavier the page becomes. This count is increased when loading // older notifications, and cut back to default whenever hitting "Read!". - seenToDisplayCount: DEFAULT_SEEN_TO_DISPLAY_COUNT + seenToDisplayCount: DEFAULT_SEEN_TO_DISPLAY_COUNT, } }, - provide () { + provide() { return { - popoversZLayer: computed(() => this.popoversZLayer) + popoversZLayer: computed(() => this.popoversZLayer), } }, computed: { - mainClass () { + mainClass() { return this.minimalMode ? '' : 'panel panel-default' }, - notifications () { + notifications() { return notificationsFromStore(this.$store) }, - error () { + error() { return this.$store.state.notifications.error }, - unseenNotifications () { + unseenNotifications() { return unseenNotificationsFromStore(this.$store) }, - filteredNotifications () { + filteredNotifications() { if (this.unseenAtTop) { return [ - ...filteredNotificationsFromStore(this.$store).filter(n => this.shouldShowUnseen(n)), - ...filteredNotificationsFromStore(this.$store).filter(n => !this.shouldShowUnseen(n)) + ...filteredNotificationsFromStore(this.$store).filter((n) => + this.shouldShowUnseen(n), + ), + ...filteredNotificationsFromStore(this.$store).filter( + (n) => !this.shouldShowUnseen(n), + ), ] } else { return filteredNotificationsFromStore(this.$store, this.filterMode) } }, - unseenCountBadgeText () { + unseenCountBadgeText() { return `${this.unseenCount ? this.unseenCount : ''}${this.extraNotificationsCount ? '*' : ''}` }, - unseenCount () { + unseenCount() { return this.unseenNotifications.length }, - ignoreInactionableSeen () { return this.$store.getters.mergedConfig.ignoreInactionableSeen }, - extraNotificationsCount () { + ignoreInactionableSeen() { + return this.$store.getters.mergedConfig.ignoreInactionableSeen + }, + extraNotificationsCount() { return countExtraNotifications(this.$store) }, - unseenCountTitle () { - return this.unseenNotifications.length + (this.unreadChatCount) + this.unreadAnnouncementCount + unseenCountTitle() { + return ( + this.unseenNotifications.length + + this.unreadChatCount + + this.unreadAnnouncementCount + ) }, - loading () { + loading() { return this.$store.state.notifications.loading }, - noHeading () { + noHeading() { const { layoutType } = useInterfaceStore() return this.minimalMode || layoutType === 'mobile' }, - teleportTarget () { + teleportTarget() { const { layoutType } = useInterfaceStore() const map = { wide: '#notifs-column', - mobile: '#mobile-notifications' + mobile: '#mobile-notifications', } return map[layoutType] || '#notifs-sidebar' }, - popoversZLayer () { + popoversZLayer() { const { layoutType } = useInterfaceStore() return layoutType === 'mobile' ? 'navbar' : null }, - notificationsToDisplay () { - return this.filteredNotifications.slice(0, this.unseenCount + this.seenToDisplayCount) + notificationsToDisplay() { + return this.filteredNotifications.slice( + 0, + this.unseenCount + this.seenToDisplayCount, + ) }, - noSticky () { return this.$store.getters.mergedConfig.disableStickyHeaders }, - unseenAtTop () { return this.$store.getters.mergedConfig.unseenAtTop }, - showExtraNotifications () { + noSticky() { + return this.$store.getters.mergedConfig.disableStickyHeaders + }, + unseenAtTop() { + return this.$store.getters.mergedConfig.unseenAtTop + }, + showExtraNotifications() { return !this.noExtra }, ...mapState(useAnnouncementsStore, ['unreadAnnouncementCount']), - ...mapGetters(['unreadChatCount']) + ...mapGetters(['unreadChatCount']), }, - mounted () { + mounted() { this.scrollerRef = this.$refs.root.closest('.column.-scrollable') if (!this.scrollerRef) { this.scrollerRef = this.$refs.root.closest('.mobile-notifications') @@ -137,12 +154,12 @@ const Notifications = { } this.scrollerRef.addEventListener('scroll', this.updateScrollPosition) }, - unmounted () { + unmounted() { if (!this.scrollerRef) return this.scrollerRef.removeEventListener('scroll', this.updateScrollPosition) }, watch: { - unseenCountTitle (count) { + unseenCountTitle(count) { if (count > 0) { FaviconService.drawFaviconBadge() useInterfaceStore().setPageTitle(`(${count})`) @@ -151,10 +168,13 @@ const Notifications = { useInterfaceStore().setPageTitle('') } }, - teleportTarget () { + teleportTarget() { // handle scroller change this.$nextTick(() => { - this.scrollerRef.removeEventListener('scroll', this.updateScrollPosition) + this.scrollerRef.removeEventListener( + 'scroll', + this.updateScrollPosition, + ) this.scrollerRef = this.$refs.root.closest('.column.-scrollable') if (!this.scrollerRef) { this.scrollerRef = this.$refs.root.closest('.mobile-notifications') @@ -162,17 +182,18 @@ const Notifications = { this.scrollerRef.addEventListener('scroll', this.updateScrollPosition) this.updateScrollPosition() }) - } + }, }, methods: { - scrollToTop () { + scrollToTop() { const scrollable = this.scrollerRef scrollable.scrollTo({ top: this.$refs.root.offsetTop }) }, - updateScrollPosition () { - this.showScrollTop = this.$refs.root.offsetTop < this.scrollerRef.scrollTop + updateScrollPosition() { + this.showScrollTop = + this.$refs.root.offsetTop < this.scrollerRef.scrollTop }, - shouldShowUnseen (notification) { + shouldShowUnseen(notification) { if (notification.seen) return false const actionable = ACTIONABLE_NOTIFICATION_TYPES.has(notification.type) @@ -182,26 +203,29 @@ const Notifications = { * everything else (likes/repeats/reacts) cannot be acted and therefore we just clear * the "seen" status upon any clicks on them */ - notificationClicked (notification) { + notificationClicked(notification) { const { id } = notification this.$store.dispatch('notificationClicked', { id }) }, - notificationInteracted (notification) { + notificationInteracted(notification) { const { id } = notification this.$store.dispatch('markSingleNotificationAsSeen', { id }) }, - markAsSeen () { + markAsSeen() { this.$store.dispatch('markNotificationsAsSeen') this.seenToDisplayCount = DEFAULT_SEEN_TO_DISPLAY_COUNT }, - fetchOlderNotifications () { + fetchOlderNotifications() { if (this.loading) { return } const seenCount = this.filteredNotifications.length - this.unseenCount if (this.seenToDisplayCount < seenCount) { - this.seenToDisplayCount = Math.min(this.seenToDisplayCount + 20, seenCount) + this.seenToDisplayCount = Math.min( + this.seenToDisplayCount + 20, + seenCount, + ) return } else if (this.seenToDisplayCount > seenCount) { this.seenToDisplayCount = seenCount @@ -210,19 +234,21 @@ const Notifications = { const store = this.$store const credentials = store.state.users.currentUser.credentials store.commit('setNotificationsLoading', { value: true }) - notificationsFetcher.fetchAndUpdate({ - store, - credentials, - older: true - }).then(notifs => { - store.commit('setNotificationsLoading', { value: false }) - if (notifs.length === 0) { - this.bottomedOut = true - } - this.seenToDisplayCount += notifs.length - }) - } - } + notificationsFetcher + .fetchAndUpdate({ + store, + credentials, + older: true, + }) + .then((notifs) => { + store.commit('setNotificationsLoading', { value: false }) + if (notifs.length === 0) { + this.bottomedOut = true + } + this.seenToDisplayCount += notifs.length + }) + }, + }, } export default Notifications diff --git a/src/components/oauth_callback/oauth_callback.js b/src/components/oauth_callback/oauth_callback.js index 02e4c9ffc..b28276ee0 100644 --- a/src/components/oauth_callback/oauth_callback.js +++ b/src/components/oauth_callback/oauth_callback.js @@ -3,23 +3,25 @@ import { useOAuthStore } from 'src/stores/oauth.js' const oac = { props: ['code'], - mounted () { + mounted() { if (this.code) { const oauthStore = useOAuthStore() const { clientId, clientSecret } = oauthStore - oauth.getToken({ - clientId, - clientSecret, - instance: this.$store.state.instance.server, - code: this.code - }).then((result) => { - oauthStore.setToken(result.access_token) - this.$store.dispatch('loginUser', result.access_token) - this.$router.push({ name: 'friends' }) - }) + oauth + .getToken({ + clientId, + clientSecret, + instance: this.$store.state.instance.server, + code: this.code, + }) + .then((result) => { + oauthStore.setToken(result.access_token) + this.$store.dispatch('loginUser', result.access_token) + this.$router.push({ name: 'friends' }) + }) } - } + }, } export default oac diff --git a/src/components/opacity_input/opacity_input.vue b/src/components/opacity_input/opacity_input.vue index 19de233d7..b8012741f 100644 --- a/src/components/opacity_input/opacity_input.vue +++ b/src/components/opacity_input/opacity_input.vue @@ -36,16 +36,14 @@ import Checkbox from '../checkbox/checkbox.vue' export default { components: { - Checkbox + Checkbox, }, - props: [ - 'name', 'label', 'modelValue', 'fallback', 'disabled' - ], + props: ['name', 'label', 'modelValue', 'fallback', 'disabled'], emits: ['update:modelValue'], computed: { - present () { + present() { return typeof this.modelValue !== 'undefined' - } - } + }, + }, } diff --git a/src/components/optional_router_link/optional_router_link.vue b/src/components/optional_router_link/optional_router_link.vue index d56ad268a..b0d553801 100644 --- a/src/components/optional_router_link/optional_router_link.vue +++ b/src/components/optional_router_link/optional_router_link.vue @@ -18,6 +18,6 @@ diff --git a/src/components/palette_editor/palette_editor.vue b/src/components/palette_editor/palette_editor.vue index 3040696d0..66914cc82 100644 --- a/src/components/palette_editor/palette_editor.vue +++ b/src/components/palette_editor/palette_editor.vue @@ -50,21 +50,15 @@ import { computed } from 'vue' import ColorInput from 'src/components/color_input/color_input.vue' import { newImporter, - newExporter + newExporter, } from 'src/services/export_import/export_import.js' import { library } from '@fortawesome/fontawesome-svg-core' -import { - faFileImport, - faFileExport -} from '@fortawesome/free-solid-svg-icons' +import { faFileImport, faFileExport } from '@fortawesome/free-solid-svg-icons' import { useInterfaceStore } from 'src/stores/interface' -library.add( - faFileImport, - faFileExport -) +library.add(faFileImport, faFileExport) const paletteKeys = [ 'bg', @@ -76,30 +70,31 @@ const paletteKeys = [ 'cBlue', 'cGreen', 'cOrange', - 'wallpaper' + 'wallpaper', ] const props = defineProps(['modelValue', 'compact', 'apply', 'disabled']) const emit = defineEmits(['update:modelValue', 'applyPalette']) -const getExportedObject = () => paletteKeys.reduce((acc, key) => { - const value = props.modelValue[key] - if (value == null) { - return acc - } else { - return { ...acc, [key]: props.modelValue[key] } - } -}, {}) +const getExportedObject = () => + paletteKeys.reduce((acc, key) => { + const value = props.modelValue[key] + if (value == null) { + return acc + } else { + return { ...acc, [key]: props.modelValue[key] } + } + }, {}) const paletteExporter = newExporter({ filename: 'pleroma_palette', extension: 'json', - getExportedObject + getExportedObject, }) const paletteImporter = newImporter({ accept: '.json', - onImport (parsed) { + onImport(parsed) { emit('update:modelValue', parsed) - } + }, }) const exportPalette = () => { @@ -136,7 +131,7 @@ const fallback = (key) => { const updatePalette = (paletteKey, value) => { emit('update:modelValue', { ...props.modelValue, - [paletteKey]: value + [paletteKey]: value, }) } diff --git a/src/components/panel.style.js b/src/components/panel.style.js index 2c8956ae1..ff8c2bf3b 100644 --- a/src/components/panel.style.js +++ b/src/components/panel.style.js @@ -9,15 +9,9 @@ export default { 'PanelHeader', 'Post', 'Notification', - 'MenuItem' - ], - validInnerComponentsLite: [ - 'Text', - 'Link', - 'Icon', - 'Border', - 'PanelHeader' + 'MenuItem', ], + validInnerComponentsLite: ['Text', 'Link', 'Icon', 'Border', 'PanelHeader'], defaultRules: [ { directives: { @@ -25,23 +19,25 @@ export default { background: '--bg', roundness: 3, blur: '5px', - shadow: [{ - x: 0, - y: 0, - blur: 3, - spread: 0, - color: '#000000', - alpha: 0.5 - }, - { - x: 0, - y: 4, - blur: 6, - spread: 3, - color: '#000000', - alpha: 0.3 - }] - } - } - ] + shadow: [ + { + x: 0, + y: 0, + blur: 3, + spread: 0, + color: '#000000', + alpha: 0.5, + }, + { + x: 0, + y: 4, + blur: 6, + spread: 3, + color: '#000000', + alpha: 0.3, + }, + ], + }, + }, + ], } diff --git a/src/components/panel_header.style.js b/src/components/panel_header.style.js index 4e5f97760..67673e1bf 100644 --- a/src/components/panel_header.style.js +++ b/src/components/panel_header.style.js @@ -7,7 +7,7 @@ export default { 'Icon', 'Button', 'ButtonUnstyled', - 'Alert' + 'Alert', ], defaultRules: [ { @@ -15,24 +15,26 @@ export default { directives: { backgroundNoCssColor: 'yes', background: '--fg', - shadow: [{ - x: 0, - y: 1, - blur: 3, - spread: 0, - color: '#000000', - alpha: 0.4 - }, - { - x: 0, - y: 1, - blur: 0, - spread: 0, - color: '#ffffff', - alpha: 0.2, - inset: true - }] - } - } - ] + shadow: [ + { + x: 0, + y: 1, + blur: 3, + spread: 0, + color: '#000000', + alpha: 0.4, + }, + { + x: 0, + y: 1, + blur: 0, + spread: 0, + color: '#ffffff', + alpha: 0.2, + inset: true, + }, + ], + }, + }, + ], } diff --git a/src/components/panel_loading/panel_loading.vue b/src/components/panel_loading/panel_loading.vue index 7a8321220..df29e5df0 100644 --- a/src/components/panel_loading/panel_loading.vue +++ b/src/components/panel_loading/panel_loading.vue @@ -15,9 +15,7 @@ import { library } from '@fortawesome/fontawesome-svg-core' import { faCircleNotch } from '@fortawesome/free-solid-svg-icons' -library.add( - faCircleNotch -) +library.add(faCircleNotch) export default {} diff --git a/src/components/password_reset/password_reset.js b/src/components/password_reset/password_reset.js index 3d94f5e72..62b8fe521 100644 --- a/src/components/password_reset/password_reset.js +++ b/src/components/password_reset/password_reset.js @@ -1,34 +1,30 @@ import { mapState } from 'vuex' import passwordResetApi from '../../services/new_api/password_reset.js' import { library } from '@fortawesome/fontawesome-svg-core' -import { - faTimes -} from '@fortawesome/free-solid-svg-icons' +import { faTimes } from '@fortawesome/free-solid-svg-icons' -library.add( - faTimes -) +library.add(faTimes) const passwordReset = { data: () => ({ user: { - email: '' + email: '', }, isPending: false, success: false, throttled: false, - error: null + error: null, }), computed: { ...mapState({ signedIn: (state) => !!state.users.currentUser, - instance: state => state.instance + instance: (state) => state.instance, }), - mailerEnabled () { + mailerEnabled() { return this.instance.mailerEnabled - } + }, }, - created () { + created() { if (this.signedIn) { this.$router.push({ name: 'root' }) } @@ -36,36 +32,38 @@ const passwordReset = { props: { passwordResetRequested: { default: false, - type: Boolean - } + type: Boolean, + }, }, methods: { - dismissError () { + dismissError() { this.error = null }, - submit () { + submit() { this.isPending = true const email = this.user.email const instance = this.instance.server - passwordResetApi({ instance, email }).then(({ status }) => { - this.isPending = false - this.user.email = '' + passwordResetApi({ instance, email }) + .then(({ status }) => { + this.isPending = false + this.user.email = '' - if (status === 204) { - this.success = true - this.error = null - } else if (status === 429) { - this.throttled = true - this.error = this.$t('password_reset.too_many_requests') - } - }).catch(() => { - this.isPending = false - this.user.email = '' - this.error = this.$t('general.generic_error') - }) - } - } + if (status === 204) { + this.success = true + this.error = null + } else if (status === 429) { + this.throttled = true + this.error = this.$t('password_reset.too_many_requests') + } + }) + .catch(() => { + this.isPending = false + this.user.email = '' + this.error = this.$t('general.generic_error') + }) + }, + }, } export default passwordReset diff --git a/src/components/pinch_zoom/pinch_zoom.js b/src/components/pinch_zoom/pinch_zoom.js index 82670ddfa..448f90e4b 100644 --- a/src/components/pinch_zoom/pinch_zoom.js +++ b/src/components/pinch_zoom/pinch_zoom.js @@ -2,12 +2,12 @@ import PinchZoom from '@kazvmoe-infra/pinch-zoom-element' export default { methods: { - setTransform ({ scale, x, y }) { + setTransform({ scale, x, y }) { this.$el.setTransform({ scale, x, y }) - } + }, }, - created () { + created() { // Make lint happy - (() => PinchZoom)() - } + ;(() => PinchZoom)() + }, } diff --git a/src/components/poll/poll.js b/src/components/poll/poll.js index a1b7808f2..50d135268 100644 --- a/src/components/poll/poll.js +++ b/src/components/poll/poll.js @@ -10,83 +10,85 @@ export default { components: { Timeago, RichContent, - Checkbox + Checkbox, }, - data () { + data() { return { loading: false, choices: [], - randomSeed: genRandomSeed() + randomSeed: genRandomSeed(), } }, - created () { + created() { if (!usePollsStore().pollsObject[this.pollId]) { usePollsStore().mergeOrAddPoll(this.basePoll) } usePollsStore().trackPoll(this.pollId) }, - unmounted () { + unmounted() { usePollsStore().untrackPoll(this.pollId) }, computed: { - pollId () { + pollId() { return this.basePoll.id }, - poll () { + poll() { const storePoll = usePollsStore().pollsObject[this.pollId] return storePoll || {} }, - options () { + options() { return (this.poll && this.poll.options) || [] }, - expiresAt () { + expiresAt() { return (this.poll && this.poll.expires_at) || null }, - expired () { + expired() { return (this.poll && this.poll.expired) || false }, - expirationLabel () { + expirationLabel() { if (this.$store.getters.mergedConfig.useAbsoluteTimeFormat) { return this.expired ? 'polls.expired_at' : 'polls.expires_at' } else { return this.expired ? 'polls.expired' : 'polls.expires_in' } }, - loggedIn () { + loggedIn() { return this.$store.state.users.currentUser }, - showResults () { + showResults() { return this.poll.voted || this.expired || !this.loggedIn }, - totalVotesCount () { + totalVotesCount() { return this.poll.votes_count }, - containerClass () { + containerClass() { return { - loading: this.loading + loading: this.loading, } }, - choiceIndices () { + choiceIndices() { // Convert array of booleans into an array of indices of the // items that were 'true', so [true, false, false, true] becomes // [0, 3]. return this.choices .map((entry, index) => entry && index) - .filter(value => typeof value === 'number') + .filter((value) => typeof value === 'number') }, - isDisabled () { + isDisabled() { const noChoice = this.choiceIndices.length === 0 return this.loading || noChoice - } + }, }, methods: { - percentageForOption (count) { - return this.totalVotesCount === 0 ? 0 : Math.round(count / this.totalVotesCount * 100) + percentageForOption(count) { + return this.totalVotesCount === 0 + ? 0 + : Math.round((count / this.totalVotesCount) * 100) }, - resultTitle (option) { + resultTitle(option) { return `${option.votes_count}/${this.totalVotesCount} ${this.$t('polls.votes')}` }, - activateOption (index, value) { + activateOption(index, value) { let result if (this.poll.multiple) { result = this.choices || this.options.map(() => false) @@ -96,17 +98,21 @@ export default { result[index] = value this.choices = result }, - optionId (index) { + optionId(index) { return `poll${this.poll.id}-${index}` }, - vote () { + vote() { if (this.choiceIndices.length === 0) return this.loading = true - usePollsStore().votePoll( - { id: this.statusId, pollId: this.poll.id, choices: this.choiceIndices } - ).then(() => { - this.loading = false - }) - } - } + usePollsStore() + .votePoll({ + id: this.statusId, + pollId: this.poll.id, + choices: this.choiceIndices, + }) + .then(() => { + this.loading = false + }) + }, + }, } diff --git a/src/components/poll/poll_form.js b/src/components/poll/poll_form.js index 7660db0c2..55a7442c2 100644 --- a/src/components/poll/poll_form.js +++ b/src/components/poll/poll_form.js @@ -2,34 +2,32 @@ import * as DateUtils from 'src/services/date_utils/date_utils.js' import { pollFallback } from 'src/services/poll/poll.service.js' import { library } from '@fortawesome/fontawesome-svg-core' import Select from '../select/select.vue' -import { - faTimes, - faPlus -} from '@fortawesome/free-solid-svg-icons' +import { faTimes, faPlus } from '@fortawesome/free-solid-svg-icons' -library.add( - faTimes, - faPlus -) +library.add(faTimes, faPlus) export default { components: { - Select + Select, }, name: 'PollForm', props: { visible: {}, params: { type: Object, - required: true - } + required: true, + }, }, computed: { pollType: { - get () { return pollFallback(this.params, 'pollType') }, - set (newVal) { this.params.pollType = newVal } + get() { + return pollFallback(this.params, 'pollType') + }, + set(newVal) { + this.params.pollType = newVal + }, }, - options () { + options() { const hasOptions = !!this.params.options if (!hasOptions) { this.params.options = pollFallback(this.params, 'options') @@ -37,54 +35,62 @@ export default { return this.params.options }, expiryAmount: { - get () { return pollFallback(this.params, 'expiryAmount') }, - set (newVal) { this.params.expiryAmount = newVal } + get() { + return pollFallback(this.params, 'expiryAmount') + }, + set(newVal) { + this.params.expiryAmount = newVal + }, }, expiryUnit: { - get () { return pollFallback(this.params, 'expiryUnit') }, - set (newVal) { this.params.expiryUnit = newVal } + get() { + return pollFallback(this.params, 'expiryUnit') + }, + set(newVal) { + this.params.expiryUnit = newVal + }, }, - pollLimits () { + pollLimits() { return this.$store.state.instance.pollLimits }, - maxOptions () { + maxOptions() { return this.pollLimits.max_options }, - maxLength () { + maxLength() { return this.pollLimits.max_option_chars }, - expiryUnits () { + expiryUnits() { const allUnits = ['minutes', 'hours', 'days'] const expiry = this.convertExpiryFromUnit return allUnits.filter( - unit => this.pollLimits.max_expiration >= expiry(unit, 1) + (unit) => this.pollLimits.max_expiration >= expiry(unit, 1), ) }, - minExpirationInCurrentUnit () { + minExpirationInCurrentUnit() { return Math.ceil( this.convertExpiryToUnit( this.expiryUnit, - this.pollLimits.min_expiration - ) + this.pollLimits.min_expiration, + ), ) }, - maxExpirationInCurrentUnit () { + maxExpirationInCurrentUnit() { return Math.floor( this.convertExpiryToUnit( this.expiryUnit, - this.pollLimits.max_expiration - ) + this.pollLimits.max_expiration, + ), ) - } + }, }, methods: { - clear () { + clear() { this.pollType = 'single' this.options = ['', ''] this.expiryAmount = 10 this.expiryUnit = 'minutes' }, - nextOption (index) { + nextOption(index) { const element = this.$el.querySelector(`#poll-${index + 1}`) if (element) { element.focus() @@ -98,30 +104,34 @@ export default { } } }, - addOption () { + addOption() { if (this.options.length < this.maxOptions) { this.options.push('') return true } return false }, - deleteOption (index) { + deleteOption(index) { if (this.options.length > 2) { this.options.splice(index, 1) } }, - convertExpiryToUnit (unit, amount) { + convertExpiryToUnit(unit, amount) { // Note: we want seconds and not milliseconds return DateUtils.secondsToUnit(unit, amount) }, - convertExpiryFromUnit (unit, amount) { + convertExpiryFromUnit(unit, amount) { return DateUtils.unitToSeconds(unit, amount) }, - expiryAmountChange () { - this.expiryAmount = - Math.max(this.minExpirationInCurrentUnit, this.expiryAmount) - this.expiryAmount = - Math.min(this.maxExpirationInCurrentUnit, this.expiryAmount) - } - } + expiryAmountChange() { + this.expiryAmount = Math.max( + this.minExpirationInCurrentUnit, + this.expiryAmount, + ) + this.expiryAmount = Math.min( + this.maxExpirationInCurrentUnit, + this.expiryAmount, + ) + }, + }, } diff --git a/src/components/poll/poll_graph.style.js b/src/components/poll/poll_graph.style.js index 247a266a2..69e2fd3ad 100644 --- a/src/components/poll/poll_graph.style.js +++ b/src/components/poll/poll_graph.style.js @@ -5,8 +5,8 @@ export default { { directives: { background: '--accent', - opacity: 0.5 - } - } - ] + opacity: 0.5, + }, + }, + ], } diff --git a/src/components/popover.style.js b/src/components/popover.style.js index 455b5f5e0..22c3e43d2 100644 --- a/src/components/popover.style.js +++ b/src/components/popover.style.js @@ -3,25 +3,25 @@ export default { selector: '.popover', lazy: true, variants: { - modal: '.modal' + modal: '.modal', }, - validInnerComponents: [ - 'MenuItem' - ], + validInnerComponents: ['MenuItem'], defaultRules: [ { directives: { background: '--bg', blur: '10px', - shadow: [{ - x: 2, - y: 2, - blur: 3, - spread: 0, - color: '#000000', - alpha: 0.5 - }] - } - } - ] + shadow: [ + { + x: 2, + y: 2, + blur: 3, + spread: 0, + color: '#000000', + alpha: 0.5, + }, + ], + }, + }, + ], } diff --git a/src/components/popover/popover.js b/src/components/popover/popover.js index e8e857918..3b83bd58f 100644 --- a/src/components/popover/popover.js +++ b/src/components/popover/popover.js @@ -57,15 +57,16 @@ const Popover = { triggerAttrs: { type: Object, - default: {} - } + default: {}, + }, }, - inject: { // override popover z layer + inject: { + // override popover z layer popoversZLayer: { - default: '' - } + default: '', + }, }, - data () { + data() { return { // lockReEntry is a flag that is set when mouse cursor is leaving the popover's content // so that if mouse goes back into popover it won't be re-shown again to prevent annoyance @@ -83,14 +84,16 @@ const Popover = { graceTimeout: null, parentPopover: null, disableClickOutside: false, - childrenShown: new Set() + childrenShown: new Set(), } }, computed: { - allTriggerAttrs () { + allTriggerAttrs() { if (process.env.NODE_ENV === 'development') { if ('aria-hidden' in this.triggerAttrs) { - throw new Error('Do not use aria-hidden in triggerAttrs. Instead set hideTrigger to true') + throw new Error( + 'Do not use aria-hidden in triggerAttrs. Instead set hideTrigger to true', + ) } } @@ -104,18 +107,20 @@ const Popover = { } return attrs - } + }, }, methods: { - setAnchorEl (el) { + setAnchorEl(el) { this.anchorEl = el this.updateStyles() }, - containerBoundingClientRect () { - const container = this.boundToSelector ? this.$el.closest(this.boundToSelector) : this.$el.offsetParent + containerBoundingClientRect() { + const container = this.boundToSelector + ? this.$el.closest(this.boundToSelector) + : this.$el.offsetParent return container.getBoundingClientRect() }, - updateStyles () { + updateStyles() { if (this.hidden) { this.styles = {} return @@ -123,7 +128,10 @@ 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 && this.$refs.trigger.children[0]) || this.$el + const anchorEl = + this.anchorEl || + (this.$refs.trigger && 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 @@ -138,7 +146,7 @@ const Popover = { // Screen position of the origin point for popover = center of the anchor const origin = { x: anchorScreenBox.left + anchorWidth * 0.5, - y: anchorScreenBox.top + anchorHeight * 0.5 + y: anchorScreenBox.top + anchorHeight * 0.5, } const content = this.$refs.content const overlayCenter = this.overlayCenters @@ -146,7 +154,8 @@ const Popover = { : null // Minor optimization, don't call a slow reflow call if we don't have to - const parentScreenBox = this.boundTo && + const parentScreenBox = + this.boundTo && (this.boundTo.x === 'container' || this.boundTo.y === 'container') && this.containerBoundingClientRect() @@ -154,25 +163,27 @@ 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' - ? { - min: parentScreenBox.left + (margin.left || 0), - max: parentScreenBox.right - (margin.right || 0) - } - : { - min: 0 + (margin.left || 10), - max: window.innerWidth - (margin.right || 10) - } + const xBounds = + this.boundTo && this.boundTo.x === 'container' + ? { + min: parentScreenBox.left + (margin.left || 0), + max: parentScreenBox.right - (margin.right || 0), + } + : { + min: 0 + (margin.left || 10), + max: window.innerWidth - (margin.right || 10), + } - const yBounds = this.boundTo && this.boundTo.y === 'container' - ? { - min: parentScreenBox.top + (margin.top || 0), - max: parentScreenBox.bottom - (margin.bottom || 0) - } - : { - min: 0 + (margin.top || 50), - max: window.innerHeight - (margin.bottom || 5) - } + const yBounds = + this.boundTo && this.boundTo.y === 'container' + ? { + min: parentScreenBox.top + (margin.top || 0), + max: parentScreenBox.bottom - (margin.bottom || 0), + } + : { + min: 0 + (margin.top || 50), + max: window.innerHeight - (margin.bottom || 5), + } let horizOffset = 0 let vertOffset = 0 @@ -227,8 +238,12 @@ const Popover = { // Handle special cases, first force to displaying on top if there's no space on bottom, // regardless of what placement value was. Then check if there's no space on top, and // force to bottom, again regardless of what placement value was. - const topBoundary = origin.y - anchorHeight * 0.5 + (this.removePadding ? topPadding : 0) - const bottomBoundary = origin.y + anchorHeight * 0.5 - (this.removePadding ? bottomPadding : 0) + const topBoundary = + origin.y - anchorHeight * 0.5 + (this.removePadding ? topPadding : 0) + const bottomBoundary = + origin.y + + anchorHeight * 0.5 - + (this.removePadding ? bottomPadding : 0) if (bottomBoundary + content.offsetHeight > yBounds.max) usingTop = true if (topBoundary - content.offsetHeight < yBounds.min) usingTop = false @@ -246,8 +261,10 @@ const Popover = { // Handle special cases, first force to displaying on left if there's no space on right, // regardless of what placement value was. Then check if there's no space on right, and // force to left, again regardless of what placement value was. - const leftBoundary = origin.x - anchorWidth * 0.5 + (this.removePadding ? leftPadding : 0) - const rightBoundary = origin.x + anchorWidth * 0.5 - (this.removePadding ? rightPadding : 0) + const leftBoundary = + origin.x - anchorWidth * 0.5 + (this.removePadding ? leftPadding : 0) + const rightBoundary = + origin.x + anchorWidth * 0.5 - (this.removePadding ? rightPadding : 0) if (rightBoundary + content.offsetWidth > xBounds.max) usingLeft = true if (leftBoundary - content.offsetWidth < xBounds.min) usingLeft = false @@ -262,17 +279,18 @@ const Popover = { this.styles = { left: `${Math.round(translateX)}px`, - top: `${Math.round(translateY)}px` + top: `${Math.round(translateY)}px`, } if (this.popoversZLayer) { - this.styles['--ZI_popover_override'] = `var(--ZI_${this.popoversZLayer}_popovers)` + this.styles['--ZI_popover_override'] = + `var(--ZI_${this.popoversZLayer}_popovers)` } if (parentScreenBox) { this.styles.maxWidth = `${Math.round(parentScreenBox.width)}px` } }, - showPopover () { + showPopover() { if (this.disabled) return this.disableClickOutside = true setTimeout(() => { @@ -291,7 +309,7 @@ const Popover = { this.updateStyles() }) }, - hidePopover () { + hidePopover() { if (this.disabled) return if (!this.hidden) this.$emit('close') this.hidden = true @@ -302,12 +320,12 @@ const Popover = { this.scrollable?.removeEventListener('scroll', this.onScroll) this.scrollable?.removeEventListener('resize', this.onResize) }, - resizePopover () { + resizePopover() { setTimeout(() => { this.updateStyles() }, 1) }, - onMouseenter () { + onMouseenter() { if (this.trigger === 'hover') { this.lockReEntry = false clearTimeout(this.graceTimeout) @@ -315,12 +333,12 @@ const Popover = { this.showPopover() } }, - onMouseleave () { + onMouseleave() { if (this.trigger === 'hover' && this.childrenShown.size === 0) { this.graceTimeout = setTimeout(() => this.hidePopover(), 1) } }, - onMouseenterContent () { + onMouseenterContent() { if (this.trigger === 'hover' && !this.lockReEntry) { this.lockReEntry = true clearTimeout(this.graceTimeout) @@ -328,12 +346,12 @@ const Popover = { this.showPopover() } }, - onMouseleaveContent () { + onMouseleaveContent() { if (this.trigger === 'hover' && this.childrenShown.size === 0) { this.graceTimeout = setTimeout(() => this.hidePopover(), 1) } }, - onClick () { + onClick() { if (this.trigger === 'click') { if (this.hidden) { this.showPopover() @@ -342,7 +360,7 @@ const Popover = { } } }, - onClickOutside (e) { + onClickOutside(e) { if (this.disableClickOutside) return if (this.hidden) return if (this.$refs.content && this.$refs.content.contains(e.target)) return @@ -351,35 +369,42 @@ const Popover = { this.hidePopover() if (this.parentPopover) this.parentPopover.onClickOutside(e) }, - onScroll () { + onScroll() { this.updateStyles() }, - onResize () { + onResize() { const content = this.$refs.content if (!content) return - if (this.oldSize.width !== content.offsetWidth || this.oldSize.height !== content.offsetHeight) { + if ( + this.oldSize.width !== content.offsetWidth || + this.oldSize.height !== content.offsetHeight + ) { this.updateStyles() - this.oldSize = { width: content.offsetWidth, height: content.offsetHeight } + this.oldSize = { + width: content.offsetWidth, + height: content.offsetHeight, + } } }, - onChildPopoverState (childRef, state) { + onChildPopoverState(childRef, state) { if (state) { this.childrenShown.add(childRef) } else { this.childrenShown.delete(childRef) } - } + }, }, - updated () { + updated() { // Monitor changes to content size, update styles only when content sizes have changed, // that should be the only time we need to move the popover box if we don't care about scroll // or resize this.onResize() }, - mounted () { + mounted() { this.teleport = true - let scrollable = this.$refs.trigger.closest('.column.-scrollable') || - this.$refs.trigger.closest('.mobile-notifications') + let scrollable = + this.$refs.trigger.closest('.column.-scrollable') || + this.$refs.trigger.closest('.mobile-notifications') if (!scrollable) scrollable = window this.scrollable = scrollable let parent = this.$parent @@ -388,9 +413,9 @@ const Popover = { } this.parentPopover = parent }, - beforeUnmount () { + beforeUnmount() { this.hidePopover() - } + }, } export default Popover diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index 238be88df..4fbcfce66 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -30,7 +30,7 @@ import { faCircleNotch, faChevronDown, faChevronLeft, - faChevronRight + faChevronRight, } from '@fortawesome/free-solid-svg-icons' import { useInterfaceStore } from 'src/stores/interface.js' @@ -45,7 +45,7 @@ library.add( faCircleNotch, faChevronDown, faChevronLeft, - faChevronRight + faChevronRight, ) const buildMentionsString = ({ user, attentions = [] }, currentUser) => { @@ -117,7 +117,7 @@ const PostStatusForm = { 'emojiPickerPlacement', 'optimisticPosting', 'profileMention', - 'draftId' + 'draftId', ], emits: [ 'posted', @@ -126,7 +126,7 @@ const PostStatusForm = { 'mediaplay', 'mediapause', 'can-close', - 'update' + 'update', ], components: { MediaUpload, @@ -139,9 +139,9 @@ const PostStatusForm = { StatusContent, Gallery, DraftCloser, - Popover + Popover, }, - mounted () { + mounted() { this.updateIdempotencyKey() this.resize(this.$refs.textarea) @@ -154,28 +154,41 @@ const PostStatusForm = { this.$refs.textarea.focus() } }, - data () { + data() { const preset = this.$route.query.message let statusText = preset || '' const { scopeCopy } = this.$store.getters.mergedConfig - const [statusType, refId] = typeAndRefId({ replyTo: this.replyTo, profileMention: this.profileMention && this.repliedUser?.id, statusId: this.statusId }) + const [statusType, refId] = typeAndRefId({ + replyTo: this.replyTo, + profileMention: this.profileMention && this.repliedUser?.id, + statusId: this.statusId, + }) // If we are starting a new post, do not associate it with old drafts - let statusParams = !this.disableDraft && (this.draftId || statusType !== 'new') ? this.getDraft(statusType, refId) : null + let statusParams = + !this.disableDraft && (this.draftId || statusType !== 'new') + ? this.getDraft(statusType, refId) + : null if (!statusParams) { if (statusType === 'reply' || statusType === 'mention') { const currentUser = this.$store.state.users.currentUser - statusText = buildMentionsString({ user: this.repliedUser, attentions: this.attentions }, currentUser) + statusText = buildMentionsString( + { user: this.repliedUser, attentions: this.attentions }, + currentUser, + ) } - const scope = ((this.copyMessageScope && scopeCopy) || this.copyMessageScope === 'direct') - ? this.copyMessageScope - : this.$store.state.users.currentUser.default_scope + const scope = + (this.copyMessageScope && scopeCopy) || + this.copyMessageScope === 'direct' + ? this.copyMessageScope + : this.$store.state.users.currentUser.default_scope - const { postContentType: contentType, sensitiveByDefault } = this.$store.getters.mergedConfig + const { postContentType: contentType, sensitiveByDefault } = + this.$store.getters.mergedConfig statusParams = { type: statusType, @@ -189,7 +202,7 @@ const PostStatusForm = { mediaDescriptions: {}, visibility: scope, contentType, - quoting: false + quoting: false, } if (statusType === 'edit') { @@ -205,7 +218,7 @@ const PostStatusForm = { hasPoll: false, mediaDescriptions: this.statusMediaDescriptions || {}, visibility: this.statusScope || scope, - contentType: statusContentType + contentType: statusContentType, } } } @@ -226,101 +239,109 @@ const PostStatusForm = { emojiInputShown: false, idempotencyKey: '', saveInhibited: true, - saveable: false + saveable: false, } }, computed: { - users () { + users() { return this.$store.state.users.users }, - userDefaultScope () { + userDefaultScope() { return this.$store.state.users.currentUser.default_scope }, - showAllScopes () { + showAllScopes() { return !this.mergedConfig.minimalScopesMode }, - hideExtraActions () { + hideExtraActions() { return this.disableDraft || this.hideDraft }, - emojiUserSuggestor () { + emojiUserSuggestor() { return suggestor({ emoji: [ ...this.$store.getters.standardEmojiList, - ...this.$store.state.instance.customEmoji + ...this.$store.state.instance.customEmoji, ], - store: this.$store + store: this.$store, }) }, - emojiSuggestor () { + emojiSuggestor() { return suggestor({ emoji: [ ...this.$store.getters.standardEmojiList, - ...this.$store.state.instance.customEmoji - ] + ...this.$store.state.instance.customEmoji, + ], }) }, - emoji () { + emoji() { return this.$store.getters.standardEmojiList || [] }, - customEmoji () { + customEmoji() { return this.$store.state.instance.customEmoji || [] }, - statusLength () { + statusLength() { return this.newStatus.status.length }, - spoilerTextLength () { + spoilerTextLength() { return this.newStatus.spoilerText.length }, - statusLengthLimit () { + statusLengthLimit() { return this.$store.state.instance.textlimit }, - hasStatusLengthLimit () { + hasStatusLengthLimit() { return this.statusLengthLimit > 0 }, - charactersLeft () { - return this.statusLengthLimit - (this.statusLength + this.spoilerTextLength) + charactersLeft() { + return ( + this.statusLengthLimit - (this.statusLength + this.spoilerTextLength) + ) }, - isOverLengthLimit () { - return this.hasStatusLengthLimit && (this.charactersLeft < 0) + isOverLengthLimit() { + return this.hasStatusLengthLimit && this.charactersLeft < 0 }, - minimalScopesMode () { + minimalScopesMode() { return this.$store.state.instance.minimalScopesMode }, - alwaysShowSubject () { + alwaysShowSubject() { return this.mergedConfig.alwaysShowSubjectInput }, - postFormats () { + postFormats() { return this.$store.state.instance.postFormats || [] }, - safeDMEnabled () { + safeDMEnabled() { return this.$store.state.instance.safeDM }, - pollsAvailable () { - return this.$store.state.instance.pollsAvailable && + pollsAvailable() { + return ( + this.$store.state.instance.pollsAvailable && this.$store.state.instance.pollLimits.max_options >= 2 && this.disablePolls !== true + ) }, - hideScopeNotice () { - return this.disableNotice || this.$store.getters.mergedConfig.hideScopeNotice + hideScopeNotice() { + return ( + this.disableNotice || this.$store.getters.mergedConfig.hideScopeNotice + ) }, - pollContentError () { - return this.pollFormVisible && - this.newStatus.poll && - this.newStatus.poll.error + pollContentError() { + return ( + this.pollFormVisible && this.newStatus.poll && this.newStatus.poll.error + ) }, - showPreview () { + showPreview() { return !this.disablePreview && (!!this.preview || this.previewLoading) }, - emptyStatus () { - return this.newStatus.status.trim() === '' && this.newStatus.files.length === 0 + emptyStatus() { + return ( + this.newStatus.status.trim() === '' && this.newStatus.files.length === 0 + ) }, - uploadFileLimitReached () { + uploadFileLimitReached() { return this.newStatus.files.length >= this.fileLimit }, - isEdit () { + isEdit() { return typeof this.statusId !== 'undefined' && this.statusId.trim() !== '' }, - quotable () { + quotable() { if (!this.$store.state.instance.quotingAvailable) { return false } @@ -329,14 +350,17 @@ const PostStatusForm = { return false } - const repliedStatus = this.$store.state.statuses.allStatusesObject[this.replyTo] + const repliedStatus = + this.$store.state.statuses.allStatusesObject[this.replyTo] if (!repliedStatus) { return false } - if (repliedStatus.visibility === 'public' || - repliedStatus.visibility === 'unlisted' || - repliedStatus.visibility === 'local') { + if ( + repliedStatus.visibility === 'public' || + repliedStatus.visibility === 'unlisted' || + repliedStatus.visibility === 'local' + ) { return true } else if (repliedStatus.visibility === 'private') { return repliedStatus.user.id === this.$store.state.users.currentUser.id @@ -344,16 +368,16 @@ const PostStatusForm = { return false }, - debouncedMaybeAutoSaveDraft () { + debouncedMaybeAutoSaveDraft() { return debounce(this.maybeAutoSaveDraft, 3000) }, - pollFormVisible () { + pollFormVisible() { return this.newStatus.hasPoll }, - shouldAutoSaveDraft () { + shouldAutoSaveDraft() { return this.$store.getters.mergedConfig.autoSaveDraft }, - autoSaveState () { + autoSaveState() { if (this.saveable) { return this.$t('post_status.auto_save_saving') } else if (this.newStatus.id) { @@ -362,35 +386,39 @@ const PostStatusForm = { return this.$t('post_status.auto_save_nothing_new') } }, - safeToSaveDraft () { + safeToSaveDraft() { return ( - this.newStatus.status || - this.newStatus.spoilerText || - this.newStatus.files?.length || - this.newStatus.hasPoll - ) && this.saveable + (this.newStatus.status || + this.newStatus.spoilerText || + this.newStatus.files?.length || + this.newStatus.hasPoll) && + this.saveable + ) }, - hasEmptyDraft () { - return this.newStatus.id && !( - this.newStatus.status || + hasEmptyDraft() { + return ( + this.newStatus.id && + !( + this.newStatus.status || this.newStatus.spoilerText || this.newStatus.files?.length || this.newStatus.hasPoll + ) ) }, ...mapGetters(['mergedConfig']), ...mapState(useInterfaceStore, { - mobileLayout: store => store.mobileLayout - }) + mobileLayout: (store) => store.mobileLayout, + }), }, watch: { newStatus: { deep: true, - handler () { + handler() { this.statusChanged() - } + }, }, - saveable (val) { + saveable(val) { // https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event#usage_notes // MDN says we'd better add the beforeunload event listener only when needed, and remove it when it's no longer needed if (val) { @@ -398,22 +426,22 @@ const PostStatusForm = { } else { this.removeBeforeUnloadListener() } - } + }, }, - beforeUnmount () { + beforeUnmount() { this.maybeAutoSaveDraft() this.removeBeforeUnloadListener() }, methods: { ...mapActions(useMediaViewerStore, ['increment']), - statusChanged () { + statusChanged() { this.autoPreview() this.updateIdempotencyKey() this.debouncedMaybeAutoSaveDraft() this.saveable = true this.saveInhibited = false }, - clearStatus () { + clearStatus() { const newStatus = this.newStatus this.saveInhibited = true this.newStatus = { @@ -425,7 +453,7 @@ const PostStatusForm = { poll: {}, hasPoll: false, mediaDescriptions: {}, - quoting: false + quoting: false, } this.$refs.mediaUpload && this.$refs.mediaUpload.clearFile() this.clearPollForm() @@ -441,23 +469,36 @@ const PostStatusForm = { if (this.preview) this.previewStatus() this.saveable = false }, - async postStatus (event, newStatus) { - if (this.posting && !this.optimisticPosting) { return } - if (this.disableSubmit) { return } - if (this.emojiInputShown) { return } + async postStatus(event, newStatus) { + if (this.posting && !this.optimisticPosting) { + return + } + if (this.disableSubmit) { + return + } + if (this.emojiInputShown) { + return + } if (this.submitOnEnter) { event.stopPropagation() event.preventDefault() } - if (this.optimisticPosting && (this.emptyStatus || this.isOverLengthLimit)) { return } + if ( + this.optimisticPosting && + (this.emptyStatus || this.isOverLengthLimit) + ) { + return + } if (this.emptyStatus) { this.error = this.$t('post_status.empty_status_error') return } - const poll = this.newStatus.hasPoll ? pollFormToMasto(this.newStatus.poll) : {} + const poll = this.newStatus.hasPoll + ? pollFormToMasto(this.newStatus.poll) + : {} if (this.pollContentError) { this.error = this.pollContentError return @@ -473,7 +514,9 @@ const PostStatusForm = { return } - const replyOrQuoteAttr = newStatus.quoting ? 'quoteId' : 'inReplyToStatusId' + const replyOrQuoteAttr = newStatus.quoting + ? 'quoteId' + : 'inReplyToStatusId' const postingOptions = { status: newStatus.status, @@ -485,10 +528,12 @@ const PostStatusForm = { [replyOrQuoteAttr]: this.replyTo, contentType: newStatus.contentType, poll, - idempotencyKey: this.idempotencyKey + idempotencyKey: this.idempotencyKey, } - const postHandler = this.postHandler ? this.postHandler : statusPoster.postStatus + const postHandler = this.postHandler + ? this.postHandler + : statusPoster.postStatus postHandler(postingOptions).then((data) => { if (!data.error) { @@ -501,7 +546,7 @@ const PostStatusForm = { this.posting = false }) }, - previewStatus () { + previewStatus() { if (this.emptyStatus && this.newStatus.spoilerText.trim() === '') { this.preview = { error: this.$t('post_status.preview_empty') } this.previewLoading = false @@ -509,89 +554,100 @@ const PostStatusForm = { } const newStatus = this.newStatus this.previewLoading = true - const replyOrQuoteAttr = newStatus.quoting ? 'quoteId' : 'inReplyToStatusId' - statusPoster.postStatus({ - status: newStatus.status, - spoilerText: newStatus.spoilerText || null, - visibility: newStatus.visibility, - sensitive: newStatus.nsfw, - media: [], - store: this.$store, - [replyOrQuoteAttr]: this.replyTo, - contentType: newStatus.contentType, - poll: {}, - preview: true - }).then((data) => { - // Don't apply preview if not loading, because it means - // user has closed the preview manually. - if (!this.previewLoading) return - if (!data.error) { - this.preview = data - } else { - this.preview = { error: data.error } - } - }).catch((error) => { - this.preview = { error } - }).finally(() => { - this.previewLoading = false - }) + const replyOrQuoteAttr = newStatus.quoting + ? 'quoteId' + : 'inReplyToStatusId' + statusPoster + .postStatus({ + status: newStatus.status, + spoilerText: newStatus.spoilerText || null, + visibility: newStatus.visibility, + sensitive: newStatus.nsfw, + media: [], + store: this.$store, + [replyOrQuoteAttr]: this.replyTo, + contentType: newStatus.contentType, + poll: {}, + preview: true, + }) + .then((data) => { + // Don't apply preview if not loading, because it means + // user has closed the preview manually. + if (!this.previewLoading) return + if (!data.error) { + this.preview = data + } else { + this.preview = { error: data.error } + } + }) + .catch((error) => { + this.preview = { error } + }) + .finally(() => { + this.previewLoading = false + }) }, - debouncePreviewStatus: debounce(function () { this.previewStatus() }, 500), - autoPreview () { + debouncePreviewStatus: debounce(function () { + this.previewStatus() + }, 500), + autoPreview() { if (!this.preview) return this.previewLoading = true this.debouncePreviewStatus() }, - closePreview () { + closePreview() { this.preview = null this.previewLoading = false }, - togglePreview () { + togglePreview() { if (this.showPreview) { this.closePreview() } else { this.previewStatus() } }, - addMediaFile (fileInfo) { + addMediaFile(fileInfo) { this.newStatus.files.push(fileInfo) this.$emit('resize', { delayed: true }) }, - removeMediaFile (fileInfo) { + removeMediaFile(fileInfo) { const index = this.newStatus.files.indexOf(fileInfo) this.newStatus.files.splice(index, 1) this.$emit('resize') }, - editAttachment (fileInfo, newText) { + editAttachment(fileInfo, newText) { this.newStatus.mediaDescriptions[fileInfo.id] = newText }, - shiftUpMediaFile (fileInfo) { + shiftUpMediaFile(fileInfo) { const { files } = this.newStatus const index = this.newStatus.files.indexOf(fileInfo) files.splice(index, 1) files.splice(index - 1, 0, fileInfo) }, - shiftDnMediaFile (fileInfo) { + shiftDnMediaFile(fileInfo) { const { files } = this.newStatus const index = this.newStatus.files.indexOf(fileInfo) files.splice(index, 1) files.splice(index + 1, 0, fileInfo) }, - uploadFailed (errString, templateArgs) { + uploadFailed(errString, templateArgs) { templateArgs = templateArgs || {} - this.error = this.$t('upload.error.base') + ' ' + this.$t('upload.error.' + errString, templateArgs) + this.error = + this.$t('upload.error.base') + + ' ' + + this.$t('upload.error.' + errString, templateArgs) }, - startedUploadingFiles () { + startedUploadingFiles() { this.uploadingFiles = true }, - finishedUploadingFiles () { + finishedUploadingFiles() { this.$emit('resize') this.uploadingFiles = false }, - type (fileInfo) { + type(fileInfo) { return fileTypeService.fileType(fileInfo.mimetype) }, - paste (e) { + paste(e) { this.autoPreview() this.resize(e) if (e.clipboardData.files.length > 0) { @@ -603,7 +659,7 @@ const PostStatusForm = { this.dropFiles = [e.clipboardData.files[0]] } }, - fileDrop (e) { + fileDrop(e) { if (e.dataTransfer && e.dataTransfer.types.includes('Files')) { e.preventDefault() // allow dropping text like before this.dropFiles = e.dataTransfer.files @@ -611,7 +667,7 @@ const PostStatusForm = { this.showDropIcon = 'hide' } }, - fileDragStop () { + fileDragStop() { // The false-setting is done with delay because just using leave-events // directly caused unwanted flickering, this is not perfect either but // much less noticable. @@ -619,21 +675,23 @@ const PostStatusForm = { this.showDropIcon = 'fade' this.dropStopTimeout = setTimeout(() => (this.showDropIcon = 'hide'), 500) }, - fileDrag (e) { + fileDrag(e) { e.dataTransfer.dropEffect = this.uploadFileLimitReached ? 'none' : 'copy' if (e.dataTransfer && e.dataTransfer.types.includes('Files')) { clearTimeout(this.dropStopTimeout) this.showDropIcon = 'show' } }, - onEmojiInputInput () { + onEmojiInputInput() { this.$nextTick(() => { this.resize(this.$refs.textarea) }) }, - resize (e) { + resize(e) { const target = e.target || e - if (!(target instanceof window.Element)) { return } + if (!(target instanceof window.Element)) { + return + } // Reset to default height for empty form, nothing else to do here. if (target.value === '') { @@ -648,12 +706,14 @@ const PostStatusForm = { * replies in notifs) or mobile post form. Note that getting and setting * scroll is different for `Window` and `Element`s */ - const bottomBottomPaddingStr = window.getComputedStyle(bottomRef)['padding-bottom'] + const bottomBottomPaddingStr = + window.getComputedStyle(bottomRef)['padding-bottom'] const bottomBottomPadding = pxStringToNumber(bottomBottomPaddingStr) - const scrollerRef = this.$el.closest('.column.-scrollable') || - this.$el.closest('.post-form-modal-view') || - window + const scrollerRef = + this.$el.closest('.column.-scrollable') || + this.$el.closest('.post-form-modal-view') || + window // Getting info about padding we have to account for, removing 'px' part const topPaddingStr = window.getComputedStyle(target)['padding-top'] @@ -682,18 +742,20 @@ const PostStatusForm = { */ // this part has to be BEFORE the content size update - const currentScroll = scrollerRef === window - ? scrollerRef.scrollY - : scrollerRef.scrollTop - const scrollerHeight = scrollerRef === window - ? scrollerRef.innerHeight - : scrollerRef.offsetHeight + const currentScroll = + scrollerRef === window ? scrollerRef.scrollY : scrollerRef.scrollTop + const scrollerHeight = + scrollerRef === window + ? scrollerRef.innerHeight + : scrollerRef.offsetHeight const scrollerBottomBorder = currentScroll + scrollerHeight // BEGIN content size update target.style.height = 'auto' const heightWithoutPadding = Math.floor(target.scrollHeight - vertPadding) - let newHeight = this.maxHeight ? Math.min(heightWithoutPadding, this.maxHeight) : heightWithoutPadding + let newHeight = this.maxHeight + ? Math.min(heightWithoutPadding, this.maxHeight) + : heightWithoutPadding // This is a bit of a hack to combat target.scrollHeight being different on every other input // on some browsers for whatever reason. Don't change the height if difference is 1px or less. if (Math.abs(newHeight - oldHeight) <= 1) { @@ -705,7 +767,10 @@ const PostStatusForm = { // We check where the bottom border of form-bottom element is, this uses findOffset // to find offset relative to scrollable container (scroller) - const bottomBottomBorder = bottomRef.offsetHeight + findOffset(bottomRef, scrollerRef).top + bottomBottomPadding + const bottomBottomBorder = + bottomRef.offsetHeight + + findOffset(bottomRef, scrollerRef).top + + bottomBottomPadding const isBottomObstructed = scrollerBottomBorder < bottomBottomBorder const isFormBiggerThanScroller = scrollerHeight < formRef.offsetHeight @@ -714,9 +779,13 @@ const PostStatusForm = { // Keep form-bottom always visible so that submit button is in view EXCEPT // if form element bigger than scroller and caret isn't at the end, so that // if you scroll up and edit middle of text you won't get scrolled back to bottom - const shouldScrollToBottom = isBottomObstructed && - !(isFormBiggerThanScroller && - this.$refs.textarea.selectionStart !== this.$refs.textarea.value.length) + const shouldScrollToBottom = + isBottomObstructed && + !( + isFormBiggerThanScroller && + this.$refs.textarea.selectionStart !== + this.$refs.textarea.value.length + ) const totalDelta = shouldScrollToBottom ? bottomChangeDelta : 0 const targetScroll = Math.round(currentScroll + totalDelta) @@ -726,53 +795,60 @@ const PostStatusForm = { scrollerRef.scrollTop = targetScroll } }, - clearError () { + clearError() { this.error = null }, - changeVis (visibility) { + changeVis(visibility) { this.newStatus.visibility = visibility }, - togglePollForm () { + togglePollForm() { this.newStatus.hasPoll = !this.newStatus.hasPoll }, - setPoll (poll) { + setPoll(poll) { this.newStatus.poll = poll }, - clearPollForm () { + clearPollForm() { if (this.$refs.pollForm) { this.$refs.pollForm.clear() } }, - dismissScopeNotice () { - this.$store.dispatch('setOption', { name: 'hideScopeNotice', value: true }) + dismissScopeNotice() { + this.$store.dispatch('setOption', { + name: 'hideScopeNotice', + value: true, + }) }, - setMediaDescription (id) { + setMediaDescription(id) { const description = this.newStatus.mediaDescriptions[id] if (!description || description.trim() === '') return - return statusPoster.setMediaDescription({ store: this.$store, id, description }) + return statusPoster.setMediaDescription({ + store: this.$store, + id, + description, + }) }, - setAllMediaDescriptions () { - const ids = this.newStatus.files.map(file => file.id) - return Promise.all(ids.map(id => this.setMediaDescription(id))) + setAllMediaDescriptions() { + const ids = this.newStatus.files.map((file) => file.id) + return Promise.all(ids.map((id) => this.setMediaDescription(id))) }, - handleEmojiInputShow (value) { + handleEmojiInputShow(value) { this.emojiInputShown = value }, - updateIdempotencyKey () { + updateIdempotencyKey() { this.idempotencyKey = Date.now().toString() }, - openProfileTab () { + openProfileTab() { useInterfaceStore().openSettingsModalTab('profile') }, - propsToNative (props) { + propsToNative(props) { return propsToNative(props) }, - saveDraft () { - if (!this.disableDraft && - !this.saveInhibited) { + saveDraft() { + if (!this.disableDraft && !this.saveInhibited) { if (this.safeToSaveDraft) { - return this.$store.dispatch('addOrSaveDraft', { draft: this.newStatus }) - .then(id => { + return this.$store + .dispatch('addOrSaveDraft', { draft: this.newStatus }) + .then((id) => { if (this.newStatus.id !== id) { this.newStatus.id = id } @@ -784,32 +860,34 @@ const PostStatusForm = { }) } else if (this.hasEmptyDraft) { // There is a draft, but there is nothing in it, clear it - return this.abandonDraft() - .then(() => { - this.saveable = false - if (!this.shouldAutoSaveDraft) { - this.clearStatus() - this.$emit('draft-done') - } - }) + return this.abandonDraft().then(() => { + this.saveable = false + if (!this.shouldAutoSaveDraft) { + this.clearStatus() + this.$emit('draft-done') + } + }) } } return Promise.resolve() }, - maybeAutoSaveDraft () { + maybeAutoSaveDraft() { if (this.shouldAutoSaveDraft) { this.saveDraft(false) } }, - abandonDraft () { + abandonDraft() { return this.$store.dispatch('abandonDraft', { id: this.newStatus.id }) }, - getDraft (statusType, refId) { + getDraft(statusType, refId) { const maybeDraft = this.$store.state.drafts.drafts[this.draftId] if (this.draftId && maybeDraft) { return maybeDraft } else { - const existingDrafts = this.$store.getters.draftsByTypeAndRefId(statusType, refId) + const existingDrafts = this.$store.getters.draftsByTypeAndRefId( + statusType, + refId, + ) if (existingDrafts.length) { return existingDrafts[0] @@ -817,35 +895,35 @@ const PostStatusForm = { } // No draft available, fall back }, - requestClose () { + requestClose() { if (!this.saveable) { this.$emit('can-close') } else { this.$refs.draftCloser.requestClose() } }, - saveAndCloseDraft () { + saveAndCloseDraft() { this.saveDraft().then(() => { this.$emit('can-close') }) }, - discardAndCloseDraft () { + discardAndCloseDraft() { this.abandonDraft().then(() => { this.$emit('can-close') }) }, - addBeforeUnloadListener () { + addBeforeUnloadListener() { this._beforeUnloadListener ||= () => { this.saveDraft() } window.addEventListener('beforeunload', this._beforeUnloadListener) }, - removeBeforeUnloadListener () { + removeBeforeUnloadListener() { if (this._beforeUnloadListener) { window.removeEventListener('beforeunload', this._beforeUnloadListener) } - } - } + }, + }, } export default PostStatusForm diff --git a/src/components/post_status_modal/post_status_modal.js b/src/components/post_status_modal/post_status_modal.js index ee6cd6444..0087bd292 100644 --- a/src/components/post_status_modal/post_status_modal.js +++ b/src/components/post_status_modal/post_status_modal.js @@ -6,29 +6,29 @@ import { usePostStatusStore } from 'src/stores/post_status' const PostStatusModal = { components: { PostStatusForm, - Modal + Modal, }, - data () { + data() { return { - resettingForm: false + resettingForm: false, } }, computed: { - isLoggedIn () { + isLoggedIn() { return !!this.$store.state.users.currentUser }, - modalActivated () { + modalActivated() { return usePostStatusStore().modalActivated }, - isFormVisible () { + isFormVisible() { return this.isLoggedIn && !this.resettingForm && this.modalActivated }, - params () { + params() { return usePostStatusStore().params || {} - } + }, }, watch: { - params (newVal, oldVal) { + params(newVal, oldVal) { if (get(newVal, 'repliedUser.id') !== get(oldVal, 'repliedUser.id')) { this.resettingForm = true this.$nextTick(() => { @@ -36,21 +36,23 @@ const PostStatusModal = { }) } }, - isFormVisible (val) { + isFormVisible(val) { if (val) { - this.$nextTick(() => this.$el && this.$el.querySelector('textarea').focus()) + this.$nextTick( + () => this.$el && this.$el.querySelector('textarea').focus(), + ) } - } + }, }, methods: { - closeModal () { + closeModal() { usePostStatusStore().closePostStatusModal() }, - resetAndClose () { + resetAndClose() { usePostStatusStore().resetPostStatusModal() usePostStatusStore().closePostStatusModal() - } - } + }, + }, } export default PostStatusModal diff --git a/src/components/progress_button/progress_button.vue b/src/components/progress_button/progress_button.vue index 283a51af5..318260409 100644 --- a/src/components/progress_button/progress_button.vue +++ b/src/components/progress_button/progress_button.vue @@ -16,23 +16,26 @@ export default { props: { disabled: { - type: Boolean + type: Boolean, }, - click: { // click event handler. Must return a promise + click: { + // click event handler. Must return a promise type: Function, - default: () => Promise.resolve() - } + default: () => Promise.resolve(), + }, }, - data () { + data() { return { - progress: false + progress: false, } }, methods: { - onClick () { + onClick() { this.progress = true - this.click().then(() => { this.progress = false }) - } - } + this.click().then(() => { + this.progress = false + }) + }, + }, } diff --git a/src/components/public_and_external_timeline/public_and_external_timeline.js b/src/components/public_and_external_timeline/public_and_external_timeline.js index bfcce6ae6..a4e1f0be7 100644 --- a/src/components/public_and_external_timeline/public_and_external_timeline.js +++ b/src/components/public_and_external_timeline/public_and_external_timeline.js @@ -1,17 +1,21 @@ import Timeline from '../timeline/timeline.vue' const PublicAndExternalTimeline = { components: { - Timeline + Timeline, }, computed: { - timeline () { return this.$store.state.statuses.timelines.publicAndExternal } + timeline() { + return this.$store.state.statuses.timelines.publicAndExternal + }, }, - created () { - this.$store.dispatch('startFetchingTimeline', { timeline: 'publicAndExternal' }) + created() { + this.$store.dispatch('startFetchingTimeline', { + timeline: 'publicAndExternal', + }) }, - unmounted () { + unmounted() { this.$store.dispatch('stopFetchingTimeline', 'publicAndExternal') - } + }, } export default PublicAndExternalTimeline diff --git a/src/components/public_timeline/public_timeline.js b/src/components/public_timeline/public_timeline.js index 306935445..88b3b4f91 100644 --- a/src/components/public_timeline/public_timeline.js +++ b/src/components/public_timeline/public_timeline.js @@ -1,18 +1,19 @@ import Timeline from '../timeline/timeline.vue' const PublicTimeline = { components: { - Timeline + Timeline, }, computed: { - timeline () { return this.$store.state.statuses.timelines.public } + timeline() { + return this.$store.state.statuses.timelines.public + }, }, - created () { + created() { this.$store.dispatch('startFetchingTimeline', { timeline: 'public' }) }, - unmounted () { + unmounted() { this.$store.dispatch('stopFetchingTimeline', 'public') - } - + }, } export default PublicTimeline diff --git a/src/components/quick_filter_settings/quick_filter_settings.js b/src/components/quick_filter_settings/quick_filter_settings.js index f5e4721d0..cf59a8b7a 100644 --- a/src/components/quick_filter_settings/quick_filter_settings.js +++ b/src/components/quick_filter_settings/quick_filter_settings.js @@ -5,95 +5,126 @@ import { library } from '@fortawesome/fontawesome-svg-core' import { faFilter, faFont, faWrench } from '@fortawesome/free-solid-svg-icons' import { useInterfaceStore } from 'src/stores/interface' -library.add( - faFilter, - faFont, - faWrench -) +library.add(faFilter, faFont, faWrench) const QuickFilterSettings = { props: { conversation: Boolean, - nested: Boolean + nested: Boolean, }, components: { - Popover + Popover, }, methods: { - setReplyVisibility (visibility) { - this.$store.dispatch('setOption', { name: 'replyVisibility', value: visibility }) + setReplyVisibility(visibility) { + this.$store.dispatch('setOption', { + name: 'replyVisibility', + value: visibility, + }) this.$store.dispatch('queueFlushAll') }, - openTab (tab) { + openTab(tab) { useInterfaceStore().openSettingsModalTab(tab) - } + }, }, computed: { ...mapGetters(['mergedConfig']), ...mapState(useInterfaceStore, { - mobileLayout: state => state.layoutType === 'mobile' + mobileLayout: (state) => state.layoutType === 'mobile', }), - triggerAttrs () { + triggerAttrs() { if (this.mobileLayout) { return {} } else { return { - title: this.$t('timeline.quick_filter_settings') + title: this.$t('timeline.quick_filter_settings'), } } }, - mainClass () { + mainClass() { if (this.mobileLayout) { return 'main-button' } else { return 'dropdown-item' } }, - loggedIn () { + loggedIn() { return !!this.$store.state.users.currentUser }, replyVisibilitySelf: { - get () { return this.mergedConfig.replyVisibility === 'self' }, - set () { this.setReplyVisibility('self') } + get() { + return this.mergedConfig.replyVisibility === 'self' + }, + set() { + this.setReplyVisibility('self') + }, }, replyVisibilityFollowing: { - get () { return this.mergedConfig.replyVisibility === 'following' }, - set () { this.setReplyVisibility('following') } + get() { + return this.mergedConfig.replyVisibility === 'following' + }, + set() { + this.setReplyVisibility('following') + }, }, replyVisibilityAll: { - get () { return this.mergedConfig.replyVisibility === 'all' }, - set () { this.setReplyVisibility('all') } + get() { + return this.mergedConfig.replyVisibility === 'all' + }, + set() { + this.setReplyVisibility('all') + }, }, hideMedia: { - get () { return this.mergedConfig.hideAttachments || this.mergedConfig.hideAttachmentsInConv }, - set () { + get() { + return ( + this.mergedConfig.hideAttachments || + this.mergedConfig.hideAttachmentsInConv + ) + }, + set() { const value = !this.hideMedia this.$store.dispatch('setOption', { name: 'hideAttachments', value }) - this.$store.dispatch('setOption', { name: 'hideAttachmentsInConv', value }) - } + this.$store.dispatch('setOption', { + name: 'hideAttachmentsInConv', + value, + }) + }, }, hideMutedPosts: { - get () { return this.mergedConfig.hideFilteredStatuses }, - set () { + get() { + return this.mergedConfig.hideFilteredStatuses + }, + set() { const value = !this.hideMutedPosts - this.$store.dispatch('setOption', { name: 'hideFilteredStatuses', value }) - } + this.$store.dispatch('setOption', { + name: 'hideFilteredStatuses', + value, + }) + }, }, muteBotStatuses: { - get () { return this.mergedConfig.muteBotStatuses }, - set () { + get() { + return this.mergedConfig.muteBotStatuses + }, + set() { const value = !this.muteBotStatuses this.$store.dispatch('setOption', { name: 'muteBotStatuses', value }) - } + }, }, muteSensitiveStatuses: { - get () { return this.mergedConfig.muteSensitiveStatuses }, - set () { + get() { + return this.mergedConfig.muteSensitiveStatuses + }, + set() { const value = !this.muteSensitiveStatuses - this.$store.dispatch('setOption', { name: 'muteSensitiveStatuses', value }) - } - } - } + this.$store.dispatch('setOption', { + name: 'muteSensitiveStatuses', + value, + }) + }, + }, + }, } export default QuickFilterSettings diff --git a/src/components/quick_view_settings/quick_view_settings.js b/src/components/quick_view_settings/quick_view_settings.js index 5c98e0f88..084ad4387 100644 --- a/src/components/quick_view_settings/quick_view_settings.js +++ b/src/components/quick_view_settings/quick_view_settings.js @@ -3,80 +3,106 @@ import QuickFilterSettings from 'src/components/quick_filter_settings/quick_filt import { mapGetters } from 'vuex' import { mapState } from 'pinia' import { library } from '@fortawesome/fontawesome-svg-core' -import { faList, faFolderTree, faBars, faWrench } from '@fortawesome/free-solid-svg-icons' -import { useInterfaceStore } from 'src/stores/interface' - -library.add( +import { faList, faFolderTree, faBars, - faWrench -) + faWrench, +} from '@fortawesome/free-solid-svg-icons' +import { useInterfaceStore } from 'src/stores/interface' + +library.add(faList, faFolderTree, faBars, faWrench) const QuickViewSettings = { props: { - conversation: Boolean + conversation: Boolean, }, components: { Popover, - QuickFilterSettings + QuickFilterSettings, }, methods: { - setConversationDisplay (visibility) { - this.$store.dispatch('setOption', { name: 'conversationDisplay', value: visibility }) + setConversationDisplay(visibility) { + this.$store.dispatch('setOption', { + name: 'conversationDisplay', + value: visibility, + }) }, - openTab (tab) { + openTab(tab) { useInterfaceStore().openSettingsModalTab(tab) - } + }, }, computed: { ...mapGetters(['mergedConfig']), ...mapState(useInterfaceStore, { - mobileLayout: state => state.layoutType === 'mobile' + mobileLayout: (state) => state.layoutType === 'mobile', }), - loggedIn () { + loggedIn() { return !!this.$store.state.users.currentUser }, conversationDisplay: { - get () { return this.mergedConfig.conversationDisplay }, - set (newVal) { this.setConversationDisplay(newVal) } + get() { + return this.mergedConfig.conversationDisplay + }, + set(newVal) { + this.setConversationDisplay(newVal) + }, }, autoUpdate: { - get () { return this.mergedConfig.streaming }, - set () { + get() { + return this.mergedConfig.streaming + }, + set() { const value = !this.autoUpdate this.$store.dispatch('setOption', { name: 'streaming', value }) - } + }, }, collapseWithSubjects: { - get () { return this.mergedConfig.collapseMessageWithSubject }, - set () { + get() { + return this.mergedConfig.collapseMessageWithSubject + }, + set() { const value = !this.collapseWithSubjects - this.$store.dispatch('setOption', { name: 'collapseMessageWithSubject', value }) - } + this.$store.dispatch('setOption', { + name: 'collapseMessageWithSubject', + value, + }) + }, }, showUserAvatars: { - get () { return this.mergedConfig.mentionLinkShowAvatar }, - set () { + get() { + return this.mergedConfig.mentionLinkShowAvatar + }, + set() { const value = !this.showUserAvatars - this.$store.dispatch('setOption', { name: 'mentionLinkShowAvatar', value }) - } + this.$store.dispatch('setOption', { + name: 'mentionLinkShowAvatar', + value, + }) + }, }, muteBotStatuses: { - get () { return this.mergedConfig.muteBotStatuses }, - set () { + get() { + return this.mergedConfig.muteBotStatuses + }, + set() { const value = !this.muteBotStatuses this.$store.dispatch('setOption', { name: 'muteBotStatuses', value }) - } + }, }, muteSensitiveStatuses: { - get () { return this.mergedConfig.muteSensitiveStatuses }, - set () { + get() { + return this.mergedConfig.muteSensitiveStatuses + }, + set() { const value = !this.muteSensitiveStatuses - this.$store.dispatch('setOption', { name: 'muteSensitiveStatuses', value }) - } - } - } + this.$store.dispatch('setOption', { + name: 'muteSensitiveStatuses', + value, + }) + }, + }, + }, } export default QuickViewSettings diff --git a/src/components/quotes_timeline/quotes_timeline.js b/src/components/quotes_timeline/quotes_timeline.js index a5f42da56..b9b3e8f10 100644 --- a/src/components/quotes_timeline/quotes_timeline.js +++ b/src/components/quotes_timeline/quotes_timeline.js @@ -1,26 +1,36 @@ import Timeline from '../timeline/timeline.vue' const QuotesTimeline = { - created () { + created() { this.$store.commit('clearTimeline', { timeline: 'quotes' }) - this.$store.dispatch('startFetchingTimeline', { timeline: 'quotes', statusId: this.statusId }) + this.$store.dispatch('startFetchingTimeline', { + timeline: 'quotes', + statusId: this.statusId, + }) }, components: { - Timeline + Timeline, }, computed: { - statusId () { return this.$route.params.id }, - timeline () { return this.$store.state.statuses.timelines.quotes } + statusId() { + return this.$route.params.id + }, + timeline() { + return this.$store.state.statuses.timelines.quotes + }, }, watch: { - statusId () { + statusId() { this.$store.commit('clearTimeline', { timeline: 'quotes' }) - this.$store.dispatch('startFetchingTimeline', { timeline: 'quotes', statusId: this.statusId }) - } + this.$store.dispatch('startFetchingTimeline', { + timeline: 'quotes', + statusId: this.statusId, + }) + }, }, - unmounted () { + unmounted() { this.$store.dispatch('stopFetchingTimeline', 'quotes') - } + }, } export default QuotesTimeline diff --git a/src/components/range_input/range_input.vue b/src/components/range_input/range_input.vue index 2f8645c0b..91d3dcc3b 100644 --- a/src/components/range_input/range_input.vue +++ b/src/components/range_input/range_input.vue @@ -54,13 +54,22 @@ diff --git a/src/components/registration/registration.js b/src/components/registration/registration.js index 53a678680..430ef597b 100644 --- a/src/components/registration/registration.js +++ b/src/components/registration/registration.js @@ -7,7 +7,9 @@ import { DAY } from 'src/services/date_utils/date_utils.js' import TermsOfServicePanel from '../terms_of_service_panel/terms_of_service_panel.vue' const registration = { - setup () { return { v$: useVuelidate() } }, + setup() { + return { v$: useVuelidate() } + }, data: () => ({ user: { email: '', @@ -17,15 +19,15 @@ const registration = { confirm: '', birthday: '', reason: '', - language: [''] + language: [''], }, - captcha: {} + captcha: {}, }), components: { InterfaceLanguageSwitcher, - TermsOfServicePanel + TermsOfServicePanel, }, - validations () { + validations() { return { user: { email: { required: requiredIf(() => this.accountActivationRequired) }, @@ -34,20 +36,23 @@ const registration = { password: { required }, confirm: { required, - sameAs: sameAs(this.user.password) + sameAs: sameAs(this.user.password), }, birthday: { required: requiredIf(() => this.birthdayRequired), - maxValue: value => { - return !this.birthdayRequired || new Date(value).getTime() <= this.birthdayMin.getTime() - } + maxValue: (value) => { + return ( + !this.birthdayRequired || + new Date(value).getTime() <= this.birthdayMin.getTime() + ) + }, }, reason: { required: requiredIf(() => this.accountApprovalRequired) }, - language: {} - } + language: {}, + }, } }, - created () { + created() { if ((!this.registrationOpen && !this.token) || this.signedIn) { this.$router.push({ name: 'root' }) } @@ -55,14 +60,16 @@ const registration = { this.setCaptcha() }, computed: { - token () { return this.$route.params.token }, - bioPlaceholder () { + token() { + return this.$route.params.token + }, + bioPlaceholder() { return this.replaceNewlines(this.$t('registration.bio_placeholder')) }, - reasonPlaceholder () { + reasonPlaceholder() { return this.replaceNewlines(this.$t('registration.reason_placeholder')) }, - birthdayMin () { + birthdayMin() { const minAge = this.birthdayMinAge const today = new Date() today.setUTCMilliseconds(0) @@ -73,12 +80,20 @@ const registration = { minDate.setTime(today.getTime() - minAge * DAY) return minDate }, - birthdayMinAttr () { + birthdayMinAttr() { return this.birthdayMin.toJSON().replace(/T.+$/, '') }, - birthdayMinFormatted () { - const browserLocale = localeService.internalToBrowserLocale(this.$i18n.locale) - return this.user.birthday && new Date(Date.parse(this.birthdayMin)).toLocaleDateString(browserLocale, { timeZone: 'UTC', day: 'numeric', month: 'long', year: 'numeric' }) + birthdayMinFormatted() { + const browserLocale = localeService.internalToBrowserLocale( + this.$i18n.locale, + ) + return ( + this.user.birthday && + new Date(Date.parse(this.birthdayMin)).toLocaleDateString( + browserLocale, + { timeZone: 'UTC', day: 'numeric', month: 'long', year: 'numeric' }, + ) + ) }, ...mapState({ registrationOpen: (state) => state.instance.registrationOpen, @@ -89,15 +104,17 @@ const registration = { hasSignUpNotice: (state) => !!state.users.signUpNotice.message, termsOfService: (state) => state.instance.tos, embeddedToS: (state) => state.instance.embeddedToS, - accountActivationRequired: (state) => state.instance.accountActivationRequired, - accountApprovalRequired: (state) => state.instance.accountApprovalRequired, + accountActivationRequired: (state) => + state.instance.accountActivationRequired, + accountApprovalRequired: (state) => + state.instance.accountApprovalRequired, birthdayRequired: (state) => state.instance.birthdayRequired, - birthdayMinAge: (state) => state.instance.birthdayMinAge - }) + birthdayMinAge: (state) => state.instance.birthdayMinAge, + }), }, methods: { ...mapActions(['signUp', 'getCaptcha']), - async submit () { + async submit() { this.user.nickname = this.user.username this.user.token = this.token @@ -105,7 +122,9 @@ const registration = { this.user.captcha_token = this.captcha.token this.user.captcha_answer_data = this.captcha.answer_data if (this.user.language) { - this.user.language = localeService.internalToBackendLocaleMulti(this.user.language.filter(k => k)) + this.user.language = localeService.internalToBackendLocaleMulti( + this.user.language.filter((k) => k), + ) } this.v$.$touch() @@ -124,13 +143,15 @@ const registration = { } } }, - setCaptcha () { - this.getCaptcha().then(cpt => { this.captcha = cpt }) + setCaptcha() { + this.getCaptcha().then((cpt) => { + this.captcha = cpt + }) }, - replaceNewlines (str) { + replaceNewlines(str) { return str.replace(/\s*\n\s*/g, ' \n') - } - } + }, + }, } export default registration diff --git a/src/components/remote_follow/remote_follow.js b/src/components/remote_follow/remote_follow.js index 951b59419..a583b6f09 100644 --- a/src/components/remote_follow/remote_follow.js +++ b/src/components/remote_follow/remote_follow.js @@ -1,9 +1,9 @@ export default { props: ['user'], computed: { - subscribeUrl () { + subscribeUrl() { const serverUrl = new URL(this.user.statusnet_profile_url) return `${serverUrl.protocol}//${serverUrl.host}/main/ostatus` - } - } + }, + }, } diff --git a/src/components/remote_user_resolver/remote_user_resolver.js b/src/components/remote_user_resolver/remote_user_resolver.js index 9b5e511e0..430f56c84 100644 --- a/src/components/remote_user_resolver/remote_user_resolver.js +++ b/src/components/remote_user_resolver/remote_user_resolver.js @@ -1,14 +1,16 @@ const RemoteUserResolver = { data: () => ({ - error: false + error: false, }), - mounted () { + mounted() { this.redirect() }, methods: { - redirect () { - const acct = this.$route.params.username + '@' + this.$route.params.hostname - this.$store.state.api.backendInteractor.fetchUser({ id: acct }) + redirect() { + const acct = + this.$route.params.username + '@' + this.$route.params.hostname + this.$store.state.api.backendInteractor + .fetchUser({ id: acct }) .then((externalUser) => { if (externalUser.error) { this.error = true @@ -17,15 +19,15 @@ const RemoteUserResolver = { const id = externalUser.id this.$router.replace({ name: 'external-user-profile', - params: { id } + params: { id }, }) } }) .catch(() => { this.error = true }) - } - } + }, + }, } export default RemoteUserResolver diff --git a/src/components/remove_follower_button/remove_follower_button.js b/src/components/remove_follower_button/remove_follower_button.js index 052a519ff..b8f195815 100644 --- a/src/components/remove_follower_button/remove_follower_button.js +++ b/src/components/remove_follower_button/remove_follower_button.js @@ -2,47 +2,49 @@ import ConfirmModal from '../confirm_modal/confirm_modal.vue' export default { props: ['user', 'relationship'], - data () { + data() { return { inProgress: false, - showingConfirmRemoveFollower: false + showingConfirmRemoveFollower: false, } }, components: { - ConfirmModal + ConfirmModal, }, computed: { - label () { + label() { if (this.inProgress) { return this.$t('user_card.follow_progress') } else { return this.$t('user_card.remove_follower') } }, - shouldConfirmRemoveUserFromFollowers () { + shouldConfirmRemoveUserFromFollowers() { return this.$store.getters.mergedConfig.modalOnRemoveUserFromFollowers - } + }, }, methods: { - showConfirmRemoveUserFromFollowers () { + showConfirmRemoveUserFromFollowers() { this.showingConfirmRemoveFollower = true }, - hideConfirmRemoveUserFromFollowers () { + hideConfirmRemoveUserFromFollowers() { this.showingConfirmRemoveFollower = false }, - onClick () { + onClick() { if (!this.shouldConfirmRemoveUserFromFollowers) { this.doRemoveUserFromFollowers() } else { this.showConfirmRemoveUserFromFollowers() } }, - doRemoveUserFromFollowers () { + doRemoveUserFromFollowers() { this.inProgress = true - this.$store.dispatch('removeUserFromFollowers', this.relationship.id).then(() => { - this.inProgress = false - }) + this.$store + .dispatch('removeUserFromFollowers', this.relationship.id) + .then(() => { + this.inProgress = false + }) this.hideConfirmRemoveUserFromFollowers() - } - } + }, + }, } diff --git a/src/components/report/report.js b/src/components/report/report.js index 6e9f21eea..2b31ad116 100644 --- a/src/components/report/report.js +++ b/src/components/report/report.js @@ -6,32 +6,38 @@ import RichContent from 'src/components/rich_content/rich_content.jsx' import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' const Report = { - props: [ - 'reportId' - ], + props: ['reportId'], components: { Select, StatusContent, Timeago, - RichContent + RichContent, }, computed: { - report () { + report() { return useReportsStore().reports[this.reportId] || {} }, state: { - get: function () { return this.report.state }, - set: function (val) { this.setReportState(val) } - } + get: function () { + return this.report.state + }, + set: function (val) { + this.setReportState(val) + }, + }, }, methods: { - generateUserProfileLink (user) { - return generateProfileLink(user.id, user.screen_name, this.$store.state.instance.restrictedNicknames) + generateUserProfileLink(user) { + return generateProfileLink( + user.id, + user.screen_name, + this.$store.state.instance.restrictedNicknames, + ) }, - setReportState (state) { + setReportState(state) { return useReportsStore().setReportState({ id: this.report.id, state }) - } - } + }, + }, } export default Report diff --git a/src/components/rich_content/rich_content.jsx b/src/components/rich_content/rich_content.jsx index 5adc2443a..778a30192 100644 --- a/src/components/rich_content/rich_content.jsx +++ b/src/components/rich_content/rich_content.jsx @@ -1,5 +1,9 @@ import { unescape, flattenDeep } from 'lodash' -import { getTagName, processTextForEmoji, getAttrs } from 'src/services/html_converter/utility.service.js' +import { + getTagName, + processTextForEmoji, + getAttrs, +} from 'src/services/html_converter/utility.service.js' import { convertHtmlToTree } from 'src/services/html_converter/html_tree_converter.service.js' import { convertHtmlToLines } from 'src/services/html_converter/html_line_converter.service.js' import StillImageEmojiPopover from 'src/components/still-image/still-image-emoji-popover.vue' @@ -27,7 +31,7 @@ const MAYBE_LINE_BREAKING_ELEMENTS = [ 'h2', 'h3', 'h4', - 'h5' + 'h5', ] /** @@ -52,46 +56,46 @@ export default { name: 'RichContent', components: { MentionsLine, - HashtagLink + HashtagLink, }, props: { // Original html content html: { required: true, - type: String + type: String, }, attentions: { required: false, - default: () => [] + default: () => [], }, // Emoji object, as in status.emojis, note the "s" at the end... emoji: { required: true, - type: Array + type: Array, }, // Whether to handle links or not (posts: yes, everything else: no) handleLinks: { required: false, type: Boolean, - default: false + default: false, }, // Meme arrows greentext: { required: false, type: Boolean, - default: false + default: false, }, // Faint style (for notifs) faint: { required: false, type: Boolean, - default: false + default: false, }, // Collapse newlines collapse: { required: false, type: Boolean, - default: false + default: false, }, /* Content comes from current instance * @@ -103,11 +107,11 @@ export default { isLocal: { required: false, type: Boolean, - default: true - } + default: true, + }, }, // NEVER EVER TOUCH DATA INSIDE RENDER - render () { + render() { // Pre-process HTML const { newHtml: html } = preProcessPerLine(this.html, this.greentext) let currentMentions = null // Current chain of mentions, we group all mentions together @@ -124,10 +128,7 @@ export default { let tagsIndex = 0 const renderImage = (tag) => { - return + return } const renderHashtag = (attrs, children, encounteredTextReverse) => { @@ -137,12 +138,14 @@ export default { lastTags.push(linkData) } const { url, tag, content } = linkData - return + return } const renderMention = (attrs, children) => { const linkData = getLinkData(attrs, children, mentionIndex++) - linkData.notifying = this.attentions.some(a => a.statusnet_profile_url === linkData.url) + linkData.notifying = this.attentions.some( + (a) => a.statusnet_profile_url === linkData.url, + ) writtenMentions.push(linkData) if (currentMentions === null) { currentMentions = [] @@ -152,7 +155,7 @@ export default { invisibleMentions.push(linkData) } if (currentMentions.length === 1) { - return + return } else { return '' } @@ -171,26 +174,28 @@ export default { // in MentionsLine lastSpacing = item // Don't remove last space in a container (fixes poast mentions) - return (index !== array.length - 1) && (currentMentions !== null) ? item.trim() : item + return index !== array.length - 1 && currentMentions !== null + ? item.trim() + : item } currentMentions = null if (item.includes(':')) { - item = ['', processTextForEmoji( - item, - this.emoji, - ({ shortcode, url }) => { - return - } - )] + item = [ + '', + processTextForEmoji(item, this.emoji, ({ shortcode, url }) => { + return ( + + ) + }), + ] } return item } @@ -209,18 +214,24 @@ export default { * we have a tag right next to mentions */ const mentionsLinePadding = - // Padding is only needed if we just finished parsing mentions - previouslyMentions && - // Don't add padding if content is string and has padding already - !(children && typeof children[0] === 'string' && children[0].match(/^\s/)) - ? lastSpacing - : '' + // Padding is only needed if we just finished parsing mentions + previouslyMentions && + // Don't add padding if content is string and has padding already + !( + children && + typeof children[0] === 'string' && + children[0].match(/^\s/) + ) + ? lastSpacing + : '' if (MAYBE_LINE_BREAKING_ELEMENTS.includes(Tag)) { // all the elements that can cause a line change currentMentions = null - } else if (Tag === 'img') { // replace images with StillImage + } else if (Tag === 'img') { + // replace images with StillImage return ['', [mentionsLinePadding, renderImage(opener)], ''] - } else if (Tag === 'a' && this.handleLinks) { // replace mentions with MentionLink + } else if (Tag === 'a' && this.handleLinks) { + // replace mentions with MentionLink if (fullAttrs.class && fullAttrs.class.includes('mention')) { // Handling mentions here return renderMention(attrs, children) @@ -228,7 +239,11 @@ export default { currentMentions = null } } else if (Tag === 'span') { - if (this.handleLinks && fullAttrs.class && fullAttrs.class.includes('h-card')) { + if ( + this.handleLinks && + fullAttrs.class && + fullAttrs.class.includes('h-card') + ) { return ['', children.map(processItem), ''] } } @@ -236,11 +251,8 @@ export default { if (children !== undefined) { return [ '', - [ - mentionsLinePadding, - [opener, children.map(processItem), closer] - ], - '' + [mentionsLinePadding, [opener, children.map(processItem), closer]], + '', ] } else { return ['', [mentionsLinePadding, item], ''] @@ -262,23 +274,25 @@ export default { const [opener, children] = item const Tag = opener === '' ? '' : getTagName(opener) switch (Tag) { - case 'a': { // replace mentions with MentionLink + case 'a': { + // replace mentions with MentionLink if (!this.handleLinks) break const fullAttrs = getAttrs(opener, () => true) const attrs = getAttrs(opener, () => true) // should only be this if ( (fullAttrs.class && fullAttrs.class.includes('hashtag')) || // Pleroma style - (fullAttrs.rel === 'tag') // Mastodon style + fullAttrs.rel === 'tag' // Mastodon style ) { return renderHashtag(attrs, children, encounteredTextReverse) } else { attrs.target = '_blank' - const newChildren = [...children].reverse().map(processItemReverse).reverse() + const newChildren = [...children] + .reverse() + .map(processItemReverse) + .reverse() - return - { newChildren } - + return {newChildren} } } case '': @@ -290,11 +304,9 @@ export default { const newChildren = Array.isArray(children) ? [...children].reverse().map(processItemReverse).reverse() : children - return - { newChildren } - + return {newChildren} } else { - return + return } } return item @@ -306,30 +318,29 @@ export default { // DO NOT USE SLOTS they cause a re-render feedback loop here. // slots updated -> rerender -> emit -> update up the tree -> rerender -> ... // at least until vue3? - const result = - - { - this.collapse - ? pass2.map(x => { - if (!Array.isArray(x)) return x.replace(/\n/g, ' ') - return x.map(y => y.type === 'br' ? ' ' : y) - }) - : pass2 - } - + const result = ( + + {this.collapse + ? pass2.map((x) => { + if (!Array.isArray(x)) return x.replace(/\n/g, ' ') + return x.map((y) => (y.type === 'br' ? ' ' : y)) + }) + : pass2} + + ) const event = { lastTags, writtenMentions, writtenTags, - invisibleMentions + invisibleMentions, } // DO NOT MOVE TO UPDATE. BAD IDEA. this.$emit('parseReady', event) return result - } + }, } const getLinkData = (attrs, children, index) => { @@ -346,7 +357,7 @@ const getLinkData = (attrs, children, index) => { url: attrs.href, tag: attrs['data-tag'], content: flattenDeep(children).join(''), - textContent + textContent, } } @@ -362,31 +373,36 @@ export const preProcessPerLine = (html, greentext) => { const greentextHandle = new Set(['p', 'div']) const lines = convertHtmlToLines(html) - const newHtml = lines.reverse().map((item, index, array) => { - if (!item.text) return item - const string = item.text + const newHtml = lines + .reverse() + .map((item, index, array) => { + if (!item.text) return item + const string = item.text - // Greentext stuff - if ( - // Only if greentext is engaged - greentext && + // Greentext stuff + if ( + // Only if greentext is engaged + greentext && // Only handle p's and divs. Don't want to affect blockquotes, code etc - item.level.every(l => greentextHandle.has(l)) && + item.level.every((l) => greentextHandle.has(l)) && // Only if line begins with '>' or '<' (string.includes('>') || string.includes('<')) - ) { - const cleanedString = string.replace(/<[^>]+?>/gi, '') // remove all tags - .replace(/@\w+/gi, '') // remove mentions (even failed ones) - .trim() - if (cleanedString.startsWith('>')) { - return `${string}` - } else if (cleanedString.startsWith('<')) { - return `${string}` + ) { + const cleanedString = string + .replace(/<[^>]+?>/gi, '') // remove all tags + .replace(/@\w+/gi, '') // remove mentions (even failed ones) + .trim() + if (cleanedString.startsWith('>')) { + return `${string}` + } else if (cleanedString.startsWith('<')) { + return `${string}` + } } - } - return string - }).reverse().join('') + return string + }) + .reverse() + .join('') return { newHtml } } diff --git a/src/components/root.style.js b/src/components/root.style.js index 5075e33c8..54c4c6095 100644 --- a/src/components/root.style.js +++ b/src/components/root.style.js @@ -27,8 +27,8 @@ export default { // Selection colors '--selectionBackground': 'color | --accent', - '--selectionText': 'color | $textColor(--accent --text no-preserve)' - } - } - ] + '--selectionText': 'color | $textColor(--accent --text no-preserve)', + }, + }, + ], } diff --git a/src/components/roundness_input/roundness_input.vue b/src/components/roundness_input/roundness_input.vue index 1da71ca79..04b623e86 100644 --- a/src/components/roundness_input/roundness_input.vue +++ b/src/components/roundness_input/roundness_input.vue @@ -36,16 +36,14 @@ import Checkbox from '../checkbox/checkbox.vue' export default { components: { - Checkbox + Checkbox, }, - props: [ - 'name', 'label', 'modelValue', 'fallback', 'disabled' - ], + props: ['name', 'label', 'modelValue', 'fallback', 'disabled'], emits: ['update:modelValue'], computed: { - present () { + present() { return typeof this.modelValue !== 'undefined' - } - } + }, + }, } diff --git a/src/components/scope_selector/scope_selector.js b/src/components/scope_selector/scope_selector.js index 462892cb6..bc644d743 100644 --- a/src/components/scope_selector/scope_selector.js +++ b/src/components/scope_selector/scope_selector.js @@ -3,88 +3,90 @@ import { faEnvelope, faLock, faLockOpen, - faGlobe + faGlobe, } from '@fortawesome/free-solid-svg-icons' -library.add( - faEnvelope, - faGlobe, - faLock, - faLockOpen -) +library.add(faEnvelope, faGlobe, faLock, faLockOpen) const ScopeSelector = { props: { showAll: { required: true, - type: Boolean + type: Boolean, }, userDefault: { required: true, - type: String + type: String, }, originalScope: { required: false, - type: String + type: String, }, initialScope: { required: false, - type: String + type: String, }, onScopeChange: { required: true, - type: Function + type: Function, }, unstyled: { required: false, type: Boolean, - default: true - } + default: true, + }, }, - data () { + data() { return { - currentScope: this.initialScope + currentScope: this.initialScope, } }, computed: { - showNothing () { - return !this.showPublic && !this.showUnlisted && !this.showPrivate && !this.showDirect + showNothing() { + return ( + !this.showPublic && + !this.showUnlisted && + !this.showPrivate && + !this.showDirect + ) }, - showPublic () { + showPublic() { return this.originalScope !== 'direct' && this.shouldShow('public') }, - showUnlisted () { + showUnlisted() { return this.originalScope !== 'direct' && this.shouldShow('unlisted') }, - showPrivate () { + showPrivate() { return this.originalScope !== 'direct' && this.shouldShow('private') }, - showDirect () { + showDirect() { return this.shouldShow('direct') }, - css () { + css() { const style = this.unstyled ? 'button-unstyled' : 'button-default' return { public: [style, { toggled: this.currentScope === 'public' }], unlisted: [style, { toggled: this.currentScope === 'unlisted' }], private: [style, { toggled: this.currentScope === 'private' }], - direct: [style, { toggled: this.currentScope === 'direct' }] + direct: [style, { toggled: this.currentScope === 'direct' }], } - } + }, }, methods: { - shouldShow (scope) { - return this.showAll || + shouldShow(scope) { + return ( + this.showAll || this.currentScope === scope || this.originalScope === scope || this.userDefault === scope || scope === 'direct' + ) }, - changeVis (scope) { + changeVis(scope) { this.currentScope = scope this.onScopeChange && this.onScopeChange(scope) - } - } + }, + }, } export default ScopeSelector diff --git a/src/components/screen_reader_notice/screen_reader_notice.js b/src/components/screen_reader_notice/screen_reader_notice.js index 794b855ac..b834be531 100644 --- a/src/components/screen_reader_notice/screen_reader_notice.js +++ b/src/components/screen_reader_notice/screen_reader_notice.js @@ -2,20 +2,22 @@ const ScreenReaderNotice = { props: { ariaLive: { type: String, - default: 'assertive' - } + default: 'assertive', + }, }, - data () { + data() { return { - currentText: '' + currentText: '', } }, methods: { - announce (text) { + announce(text) { this.currentText = text - setTimeout(() => { this.currentText = '' }, 1000) - } - } + setTimeout(() => { + this.currentText = '' + }, 1000) + }, + }, } export default ScreenReaderNotice diff --git a/src/components/scroll_top_button/scroll_top_button.js b/src/components/scroll_top_button/scroll_top_button.js index bdc45b9b4..e4b908f0b 100644 --- a/src/components/scroll_top_button/scroll_top_button.js +++ b/src/components/scroll_top_button/scroll_top_button.js @@ -3,16 +3,16 @@ const ScrollTopButton = { fast: { type: Boolean, required: false, - default: false - } + default: false, + }, }, methods: { scrollToTop() { - const speed = this.fast ? 'instant' : 'smooth'; + const speed = this.fast ? 'instant' : 'smooth' window.scrollTo({ top: 0, behavior: speed }) - } - } + }, + }, } export default ScrollTopButton diff --git a/src/components/scrollbar.style.js b/src/components/scrollbar.style.js index 1168f67d8..fbe952076 100644 --- a/src/components/scrollbar.style.js +++ b/src/components/scrollbar.style.js @@ -1,12 +1,16 @@ export default { name: 'Scrollbar', - selector: ['::-webkit-scrollbar-button', '::-webkit-scrollbar-thumb', '::-webkit-resizer'], + selector: [ + '::-webkit-scrollbar-button', + '::-webkit-scrollbar-thumb', + '::-webkit-resizer', + ], notEditable: true, // for now defaultRules: [ { directives: { - background: '--wallpaper' - } - } - ] + background: '--wallpaper', + }, + }, + ], } diff --git a/src/components/scrollbar_element.style.js b/src/components/scrollbar_element.style.js index ef1ea8136..6f238e1f8 100644 --- a/src/components/scrollbar_element.style.js +++ b/src/components/scrollbar_element.style.js @@ -5,7 +5,7 @@ const border = (top, shadow) => ({ spread: 0, color: shadow ? '#000000' : '#FFFFFF', alpha: 0.2, - inset: true + inset: true, }) const buttonInsetFakeBorders = [border(true, false), border(false, true)] @@ -16,7 +16,7 @@ const buttonOuterShadow = { blur: 2, spread: 0, color: '#000000', - alpha: 1 + alpha: 1, } const hoverGlow = { @@ -25,7 +25,7 @@ const hoverGlow = { blur: 4, spread: 0, color: '--text', - alpha: 1 + alpha: 1, } export default { @@ -35,68 +35,66 @@ export default { states: { pressed: ':active', hover: ':is(:hover, :focus-visible, :has(:focus-visible)):not(:disabled)', - disabled: ':disabled' + disabled: ':disabled', }, - validInnerComponents: [ - 'Text' - ], + validInnerComponents: ['Text'], defaultRules: [ { directives: { background: '--fg', shadow: [buttonOuterShadow, ...buttonInsetFakeBorders], - roundness: 3 - } + roundness: 3, + }, }, { state: ['hover'], directives: { - shadow: [hoverGlow, ...buttonInsetFakeBorders] - } + shadow: [hoverGlow, ...buttonInsetFakeBorders], + }, }, { state: ['pressed'], directives: { - shadow: [buttonOuterShadow, ...inputInsetFakeBorders] - } + shadow: [buttonOuterShadow, ...inputInsetFakeBorders], + }, }, { state: ['hover', 'pressed'], directives: { - shadow: [hoverGlow, ...inputInsetFakeBorders] - } + shadow: [hoverGlow, ...inputInsetFakeBorders], + }, }, { state: ['toggled'], directives: { background: '--accent,-24.2', - shadow: [buttonOuterShadow, ...inputInsetFakeBorders] - } + shadow: [buttonOuterShadow, ...inputInsetFakeBorders], + }, }, { state: ['toggled', 'hover'], directives: { background: '--accent,-24.2', - shadow: [hoverGlow, ...inputInsetFakeBorders] - } + shadow: [hoverGlow, ...inputInsetFakeBorders], + }, }, { state: ['disabled'], directives: { background: '$blend(--inheritedBackground 0.25 --parent)', - shadow: [...buttonInsetFakeBorders] - } + shadow: [...buttonInsetFakeBorders], + }, }, { component: 'Text', parent: { component: 'Button', - state: ['disabled'] + state: ['disabled'], }, directives: { textOpacity: 0.25, - textOpacityMode: 'blend' - } - } - ] + textOpacityMode: 'blend', + }, + }, + ], } diff --git a/src/components/search/search.js b/src/components/search/search.js index 877d6f300..275672a15 100644 --- a/src/components/search/search.js +++ b/src/components/search/search.js @@ -4,28 +4,20 @@ import Status from '../status/status.vue' import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx' import map from 'lodash/map' import { library } from '@fortawesome/fontawesome-svg-core' -import { - faCircleNotch, - faSearch -} from '@fortawesome/free-solid-svg-icons' +import { faCircleNotch, faSearch } from '@fortawesome/free-solid-svg-icons' import { uniqBy } from 'lodash' -library.add( - faCircleNotch, - faSearch -) +library.add(faCircleNotch, faSearch) const Search = { components: { FollowCard, Conversation, Status, - TabSwitcher + TabSwitcher, }, - props: [ - 'query' - ], - data () { + props: ['query'], + data() { return { loaded: false, loading: false, @@ -37,36 +29,37 @@ const Search = { statusesOffset: 0, lastStatusFetchCount: 0, - lastQuery: '' + lastQuery: '', } }, computed: { - users () { - return this.userIds.map(userId => this.$store.getters.findUser(userId)) + users() { + return this.userIds.map((userId) => this.$store.getters.findUser(userId)) }, - visibleStatuses () { + visibleStatuses() { const allStatusesObject = this.$store.state.statuses.allStatusesObject - return this.statuses.filter(status => - allStatusesObject[status.id] && !allStatusesObject[status.id].deleted + return this.statuses.filter( + (status) => + allStatusesObject[status.id] && !allStatusesObject[status.id].deleted, ) - } + }, }, - mounted () { + mounted() { this.search(this.query) }, watch: { - query (newValue) { + query(newValue) { this.searchTerm = newValue this.search(newValue) - } + }, }, methods: { - newQuery (query) { + newQuery(query) { this.$router.push({ name: 'search', query: { query } }) this.$refs.searchInput.focus() }, - search (query, searchType = null) { + search(query, searchType = null) { if (!query) { this.loading = false return @@ -83,8 +76,14 @@ const Search = { this.lastStatusFetchCount = 0 } - this.$store.dispatch('search', { q: query, resolve: true, offset: this.statusesOffset, type: searchType }) - .then(data => { + this.$store + .dispatch('search', { + q: query, + resolve: true, + offset: this.statusesOffset, + type: searchType, + }) + .then((data) => { this.loading = false const oldLength = this.statuses.length @@ -104,14 +103,14 @@ const Search = { this.lastQuery = query }) }, - resultCount (tabName) { + resultCount(tabName) { const length = this[tabName].length return length === 0 ? '' : ` (${length})` }, - onResultTabSwitch (key) { + onResultTabSwitch(key) { this.currenResultTab = key }, - getActiveTab () { + getActiveTab() { if (this.visibleStatuses.length > 0) { return 'statuses' } else if (this.users.length > 0) { @@ -122,10 +121,10 @@ const Search = { return 'statuses' }, - lastHistoryRecord (hashtag) { + lastHistoryRecord(hashtag) { return hashtag.history && hashtag.history[0] - } - } + }, + }, } export default Search diff --git a/src/components/search_bar/search_bar.js b/src/components/search_bar/search_bar.js index 3b297f098..b270077e8 100644 --- a/src/components/search_bar/search_bar.js +++ b/src/components/search_bar/search_bar.js @@ -1,33 +1,27 @@ import { library } from '@fortawesome/fontawesome-svg-core' -import { - faTimes, - faSearch -} from '@fortawesome/free-solid-svg-icons' +import { faTimes, faSearch } from '@fortawesome/free-solid-svg-icons' -library.add( - faTimes, - faSearch -) +library.add(faTimes, faSearch) const SearchBar = { data: () => ({ searchTerm: undefined, hidden: true, - error: false + error: false, }), watch: { $route: function (route) { if (route.name === 'search') { this.searchTerm = route.query.query } - } + }, }, methods: { - find (searchTerm) { + find(searchTerm) { this.$router.push({ name: 'search', query: { query: searchTerm } }) this.$refs.searchInput.focus() }, - toggleHidden () { + toggleHidden() { this.hidden = !this.hidden this.$emit('toggled', this.hidden) this.$nextTick(() => { @@ -35,8 +29,8 @@ const SearchBar = { this.$refs.searchInput.focus() } }) - } - } + }, + }, } export default SearchBar diff --git a/src/components/select/select.js b/src/components/select/select.js index 34d64fd22..9bf9f7e23 100644 --- a/src/components/select/select.js +++ b/src/components/select/select.js @@ -1,19 +1,9 @@ import { library } from '@fortawesome/fontawesome-svg-core' -import { - faChevronDown -} from '@fortawesome/free-solid-svg-icons' +import { faChevronDown } from '@fortawesome/free-solid-svg-icons' -library.add( - faChevronDown -) +library.add(faChevronDown) export default { emits: ['update:modelValue'], - props: [ - 'modelValue', - 'disabled', - 'unstyled', - 'kind', - 'attrs' - ] + props: ['modelValue', 'disabled', 'unstyled', 'kind', 'attrs'], } diff --git a/src/components/select/select_motion.vue b/src/components/select/select_motion.vue index 45e278fc6..21262c256 100644 --- a/src/components/select/select_motion.vue +++ b/src/components/select/select_motion.vue @@ -54,20 +54,20 @@ import { computed, defineEmits, defineProps, nextTick } from 'vue' const props = defineProps({ modelValue: { type: Array, - required: true + required: true, }, selectedId: { type: Number, - required: true + required: true, }, disabled: { type: Boolean, - default: false + default: false, }, getAddValue: { type: Function, - required: true - } + required: true, + }, }) const emit = defineEmits(['update:modelValue', 'update:selectedId']) @@ -116,7 +116,10 @@ const del = async () => { emit('update:modelValue', newModel) await nextTick() - emit('update:selectedId', newModel.length === 0 ? undefined : Math.max(props.selectedId - 1, 0)) + emit( + 'update:selectedId', + newModel.length === 0 ? undefined : Math.max(props.selectedId - 1, 0), + ) } diff --git a/src/components/selectable_list/selectable_list.js b/src/components/selectable_list/selectable_list.js index 10980d46a..a8a7dd9e0 100644 --- a/src/components/selectable_list/selectable_list.js +++ b/src/components/selectable_list/selectable_list.js @@ -4,45 +4,45 @@ import Checkbox from '../checkbox/checkbox.vue' const SelectableList = { components: { List, - Checkbox + Checkbox, }, props: { items: { type: Array, - default: () => [] + default: () => [], }, getKey: { type: Function, - default: item => item.id - } + default: (item) => item.id, + }, }, - data () { + data() { return { - selected: [] + selected: [], } }, computed: { - allKeys () { + allKeys() { return this.items.map(this.getKey) }, - filteredSelected () { - return this.allKeys.filter(key => this.selected.indexOf(key) !== -1) + filteredSelected() { + return this.allKeys.filter((key) => this.selected.indexOf(key) !== -1) }, - allSelected () { + allSelected() { return this.filteredSelected.length === this.items.length }, - noneSelected () { + noneSelected() { return this.filteredSelected.length === 0 }, - someSelected () { + someSelected() { return !this.allSelected && !this.noneSelected - } + }, }, methods: { - isSelected (item) { + isSelected(item) { return this.filteredSelected.indexOf(this.getKey(item)) !== -1 }, - toggle (checked, item) { + toggle(checked, item) { const key = this.getKey(item) const oldChecked = this.isSelected(key) if (checked !== oldChecked) { @@ -53,14 +53,14 @@ const SelectableList = { } } }, - toggleAll (value) { + toggleAll(value) { if (value) { this.selected = this.allKeys.slice(0) } else { this.selected = [] } - } - } + }, + }, } export default SelectableList diff --git a/src/components/settings_modal/admin_tabs/auth_tab.js b/src/components/settings_modal/admin_tabs/auth_tab.js index a078291e6..d287257d5 100644 --- a/src/components/settings_modal/admin_tabs/auth_tab.js +++ b/src/components/settings_modal/admin_tabs/auth_tab.js @@ -11,10 +11,10 @@ import MapSetting from '../helpers/map_setting.vue' import SharedComputedObject from '../helpers/shared_computed_object.js' const AuthTab = { - provide () { + provide() { return { defaultDraftMode: true, - defaultSource: 'admin' + defaultSource: 'admin', } }, components: { @@ -26,14 +26,16 @@ const AuthTab = { AttachmentSetting, GroupSetting, ListSetting, - MapSetting + MapSetting, }, computed: { ...SharedComputedObject(), - LDAPEnabled () { - return this.$store.state.adminSettings.draft[':pleroma'][':ldap'][':enabled'] + LDAPEnabled() { + return this.$store.state.adminSettings.draft[':pleroma'][':ldap'][ + ':enabled' + ] }, - } + }, } export default AuthTab diff --git a/src/components/settings_modal/admin_tabs/emoji_tab.js b/src/components/settings_modal/admin_tabs/emoji_tab.js index 7c137e35f..950e5d2ac 100644 --- a/src/components/settings_modal/admin_tabs/emoji_tab.js +++ b/src/components/settings_modal/admin_tabs/emoji_tab.js @@ -16,15 +16,10 @@ import { faArrowsRotate, faFolderOpen, faServer, - faDownload + faDownload, } from '@fortawesome/free-solid-svg-icons' -library.add( - faArrowsRotate, - faFolderOpen, - faDownload, - faServer -) +library.add(faArrowsRotate, faFolderOpen, faDownload, faServer) const EmojiTab = { components: { @@ -36,14 +31,14 @@ const EmojiTab = { Popover, ConfirmModal, ModifiedIndicator, - EmojiEditingPopover + EmojiEditingPopover, }, - data () { + data() { return { - knownLocalPacks: { }, - knownRemotePacks: { }, - editedMetadata: { }, + knownLocalPacks: {}, + knownRemotePacks: {}, + editedMetadata: {}, packName: '', newPackName: '', deleteModalVisible: false, @@ -51,20 +46,20 @@ const EmojiTab = { remotePackDownloadAs: '', remotePackURL: '', - remotePackFile: null + remotePackFile: null, } }, - provide () { + provide() { return { emojiAddr: this.emojiAddr } }, computed: { ...SharedComputedObject(), - pack () { + pack() { return this.packName !== '' ? this.knownPacks[this.packName] : undefined }, - packMeta () { + packMeta() { if (this.packName === '') return {} if (this.editedMetadata[this.packName] === undefined) { this.editedMetadata[this.packName] = clone(this.pack.pack) @@ -72,31 +67,36 @@ const EmojiTab = { return this.editedMetadata[this.packName] }, - knownPacks () { + knownPacks() { // Copy the object itself but not the children, so they are still passed by reference and modified const result = clone(this.knownLocalPacks) for (const instName in this.knownRemotePacks) { for (const instPack in this.knownRemotePacks[instName]) { - result[`${instPack}@${instName}`] = this.knownRemotePacks[instName][instPack] + result[`${instPack}@${instName}`] = + this.knownRemotePacks[instName][instPack] } } return result }, - downloadWillReplaceLocal () { - return (this.remotePackDownloadAs.trim() === '' && this.pack.remote && this.pack.remote.baseName in this.knownLocalPacks) || - (this.remotePackDownloadAs in this.knownLocalPacks) - } + downloadWillReplaceLocal() { + return ( + (this.remotePackDownloadAs.trim() === '' && + this.pack.remote && + this.pack.remote.baseName in this.knownLocalPacks) || + this.remotePackDownloadAs in this.knownLocalPacks + ) + }, }, methods: { - reloadEmoji () { + reloadEmoji() { this.$store.state.api.backendInteractor.reloadEmoji() }, - importFromFS () { + importFromFS() { this.$store.state.api.backendInteractor.importEmojiFromFS() }, - emojiAddr (name) { + emojiAddr(name) { if (this.pack.remote !== undefined) { // Remote pack return `${this.pack.remote.instance}/emoji/${encodeURIComponent(this.pack.remote.baseName)}/${name}` @@ -105,115 +105,141 @@ const EmojiTab = { } }, - createEmojiPack () { - this.$store.state.api.backendInteractor.createEmojiPack( - { name: this.newPackName } - ).then(resp => resp.json()).then(resp => { - if (resp === 'ok') { - return this.refreshPackList() - } else { - this.displayError(resp.error) - return Promise.reject(resp) - } - }).then(() => { - this.packName = this.newPackName - this.newPackName = '' - }) + createEmojiPack() { + this.$store.state.api.backendInteractor + .createEmojiPack({ name: this.newPackName }) + .then((resp) => resp.json()) + .then((resp) => { + if (resp === 'ok') { + return this.refreshPackList() + } else { + this.displayError(resp.error) + return Promise.reject(resp) + } + }) + .then(() => { + this.packName = this.newPackName + this.newPackName = '' + }) }, - deleteEmojiPack () { - this.$store.state.api.backendInteractor.deleteEmojiPack( - { name: this.packName } - ).then(resp => resp.json()).then(resp => { - if (resp === 'ok') { - return this.refreshPackList() - } else { - this.displayError(resp.error) - return Promise.reject(resp) - } - }).then(() => { - delete this.editedMetadata[this.packName] + deleteEmojiPack() { + this.$store.state.api.backendInteractor + .deleteEmojiPack({ name: this.packName }) + .then((resp) => resp.json()) + .then((resp) => { + if (resp === 'ok') { + return this.refreshPackList() + } else { + this.displayError(resp.error) + return Promise.reject(resp) + } + }) + .then(() => { + delete this.editedMetadata[this.packName] - this.deleteModalVisible = false - this.packName = '' - }) + this.deleteModalVisible = false + this.packName = '' + }) }, - metaEdited (prop) { + metaEdited(prop) { if (!this.pack) return const def = this.pack.pack[prop] || '' const edited = this.packMeta[prop] || '' return edited !== def }, - savePackMetadata () { - this.$store.state.api.backendInteractor.saveEmojiPackMetadata({ name: this.packName, newData: this.packMeta }).then( - resp => resp.json() - ).then(resp => { - if (resp.error !== undefined) { - this.displayError(resp.error) - return - } + savePackMetadata() { + this.$store.state.api.backendInteractor + .saveEmojiPackMetadata({ name: this.packName, newData: this.packMeta }) + .then((resp) => resp.json()) + .then((resp) => { + if (resp.error !== undefined) { + this.displayError(resp.error) + return + } - // Update actual pack data - this.pack.pack = resp - // Delete edited pack data, should auto-update itself - delete this.editedMetadata[this.packName] - }) + // Update actual pack data + this.pack.pack = resp + // Delete edited pack data, should auto-update itself + delete this.editedMetadata[this.packName] + }) }, - updatePackFiles (newFiles, packName) { + updatePackFiles(newFiles, packName) { this.knownPacks[packName].files = newFiles this.sortPackFiles(packName) }, - loadPacksPaginated (listFunction) { + loadPacksPaginated(listFunction) { const pageSize = 25 const allPacks = {} - return listFunction({ instance: this.remotePackInstance, page: 1, pageSize: 0 }) - .then(data => data.json()) - .then(data => { - if (data.error !== undefined) { return Promise.reject(data.error) } + return listFunction({ + instance: this.remotePackInstance, + page: 1, + pageSize: 0, + }) + .then((data) => data.json()) + .then((data) => { + if (data.error !== undefined) { + return Promise.reject(data.error) + } let resultingPromise = Promise.resolve({}) for (let i = 0; i < Math.ceil(data.count / pageSize); i++) { - resultingPromise = resultingPromise.then(() => listFunction({ instance: this.remotePackInstance, page: i, pageSize }) - ).then(data => data.json()).then(pageData => { - if (pageData.error !== undefined) { return Promise.reject(pageData.error) } + resultingPromise = resultingPromise + .then(() => + listFunction({ + instance: this.remotePackInstance, + page: i, + pageSize, + }), + ) + .then((data) => data.json()) + .then((pageData) => { + if (pageData.error !== undefined) { + return Promise.reject(pageData.error) + } - assign(allPacks, pageData.packs) - }) + assign(allPacks, pageData.packs) + }) } return resultingPromise }) .then(() => allPacks) - .catch(data => { + .catch((data) => { this.displayError(data) }) }, - refreshPackList () { - this.loadPacksPaginated(this.$store.state.api.backendInteractor.listEmojiPacks) - .then(allPacks => { - this.knownLocalPacks = allPacks - for (const name of Object.keys(this.knownLocalPacks)) { - this.sortPackFiles(name) - } - }) + refreshPackList() { + this.loadPacksPaginated( + this.$store.state.api.backendInteractor.listEmojiPacks, + ).then((allPacks) => { + this.knownLocalPacks = allPacks + for (const name of Object.keys(this.knownLocalPacks)) { + this.sortPackFiles(name) + } + }) }, - listRemotePacks () { - this.loadPacksPaginated(this.$store.state.api.backendInteractor.listRemoteEmojiPacks) - .then(allPacks => { + listRemotePacks() { + this.loadPacksPaginated( + this.$store.state.api.backendInteractor.listRemoteEmojiPacks, + ) + .then((allPacks) => { let inst = this.remotePackInstance - if (!inst.startsWith('http')) { inst = 'https://' + inst } + if (!inst.startsWith('http')) { + inst = 'https://' + inst + } const instUrl = new URL(inst) inst = instUrl.host for (const packName in allPacks) { allPacks[packName].remote = { baseName: packName, - instance: instUrl.origin + instance: instUrl.origin, } } @@ -222,89 +248,101 @@ const EmojiTab = { this.sortPackFiles(`${pack}@${inst}`) } }) - .catch(data => { + .catch((data) => { this.displayError(data) }) }, - downloadRemotePack () { + downloadRemotePack() { if (this.remotePackDownloadAs.trim() === '') { this.remotePackDownloadAs = this.pack.remote.baseName } - this.$store.state.api.backendInteractor.downloadRemoteEmojiPack({ - instance: this.pack.remote.instance, packName: this.pack.remote.baseName, as: this.remotePackDownloadAs - }) - .then(data => data.json()) - .then(resp => { + this.$store.state.api.backendInteractor + .downloadRemoteEmojiPack({ + instance: this.pack.remote.instance, + packName: this.pack.remote.baseName, + as: this.remotePackDownloadAs, + }) + .then((data) => data.json()) + .then((resp) => { if (resp === 'ok') { return this.refreshPackList() } else { this.displayError(resp.error) return Promise.reject(resp) } - }).then(() => { + }) + .then(() => { this.packName = this.remotePackDownloadAs this.remotePackDownloadAs = '' }) }, - downloadRemoteURLPack () { - this.$store.state.api.backendInteractor.downloadRemoteEmojiPackZIP({ - url: this.remotePackURL, packName: this.newPackName - }) - .then(data => data.json()) - .then(resp => { + downloadRemoteURLPack() { + this.$store.state.api.backendInteractor + .downloadRemoteEmojiPackZIP({ + url: this.remotePackURL, + packName: this.newPackName, + }) + .then((data) => data.json()) + .then((resp) => { if (resp === 'ok') { return this.refreshPackList() } else { this.displayError(resp.error) return Promise.reject(resp) } - }).then(() => { + }) + .then(() => { this.packName = this.newPackName this.newPackName = '' this.remotePackURL = '' }) }, - downloadRemoteFilePack () { - this.$store.state.api.backendInteractor.downloadRemoteEmojiPackZIP({ - file: this.remotePackFile[0], packName: this.newPackName - }) - .then(data => data.json()) - .then(resp => { + downloadRemoteFilePack() { + this.$store.state.api.backendInteractor + .downloadRemoteEmojiPackZIP({ + file: this.remotePackFile[0], + packName: this.newPackName, + }) + .then((data) => data.json()) + .then((resp) => { if (resp === 'ok') { return this.refreshPackList() } else { this.displayError(resp.error) return Promise.reject(resp) } - }).then(() => { + }) + .then(() => { this.packName = this.newPackName this.newPackName = '' this.remotePackURL = '' }) }, - displayError (msg) { + displayError(msg) { useInterfaceStore().pushGlobalNotice({ messageKey: 'admin_dash.emoji.error', messageArgs: [msg], - level: 'error' + level: 'error', }) }, - sortPackFiles (nameOfPack) { + sortPackFiles(nameOfPack) { // Sort by key - const sorted = Object.keys(this.knownPacks[nameOfPack].files).sort().reduce((acc, key) => { - if (key.length === 0) return acc - acc[key] = this.knownPacks[nameOfPack].files[key] - return acc - }, {}) + const sorted = Object.keys(this.knownPacks[nameOfPack].files) + .sort() + .reduce((acc, key) => { + if (key.length === 0) return acc + acc[key] = this.knownPacks[nameOfPack].files[key] + return acc + }, {}) this.knownPacks[nameOfPack].files = sorted - } + }, }, - mounted () { + mounted() { this.refreshPackList() - } + }, } export default EmojiTab diff --git a/src/components/settings_modal/admin_tabs/federation_tab.js b/src/components/settings_modal/admin_tabs/federation_tab.js index 97b4e0040..55d708a92 100644 --- a/src/components/settings_modal/admin_tabs/federation_tab.js +++ b/src/components/settings_modal/admin_tabs/federation_tab.js @@ -10,10 +10,10 @@ import MapSetting from '../helpers/map_setting.vue' import SharedComputedObject from '../helpers/shared_computed_object.js' const FederationTab = { - provide () { + provide() { return { defaultDraftMode: true, - defaultSource: 'admin' + defaultSource: 'admin', } }, components: { @@ -24,11 +24,11 @@ const FederationTab = { ListSetting, ListTupleSetting, GroupSetting, - MapSetting + MapSetting, }, computed: { - ...SharedComputedObject() - } + ...SharedComputedObject(), + }, } export default FederationTab diff --git a/src/components/settings_modal/admin_tabs/frontends_tab.js b/src/components/settings_modal/admin_tabs/frontends_tab.js index c63e76214..befe6fbad 100644 --- a/src/components/settings_modal/admin_tabs/frontends_tab.js +++ b/src/components/settings_modal/admin_tabs/frontends_tab.js @@ -9,24 +9,20 @@ import { useInterfaceStore } from 'src/stores/interface' import SharedComputedObject from '../helpers/shared_computed_object.js' import { library } from '@fortawesome/fontawesome-svg-core' -import { - faGlobe -} from '@fortawesome/free-solid-svg-icons' +import { faGlobe } from '@fortawesome/free-solid-svg-icons' -library.add( - faGlobe -) +library.add(faGlobe) const FrontendsTab = { - provide () { + provide() { return { defaultDraftMode: true, - defaultSource: 'admin' + defaultSource: 'admin', } }, - data () { + data() { return { - working: false + working: false, } }, components: { @@ -36,26 +32,26 @@ const FrontendsTab = { StringSetting, GroupSetting, PanelLoading, - Popover + Popover, }, - created () { + created() { if (this.user.rights.admin) { this.$store.dispatch('loadFrontendsStuff') } }, computed: { ...SharedComputedObject(), - frontends () { + frontends() { return this.$store.state.adminSettings.frontends - } + }, }, methods: { - canInstall (frontend) { - const fe = this.frontends.find(f => f.name === frontend.name) + canInstall(frontend) { + const fe = this.frontends.find((f) => f.name === frontend.name) if (!fe) return false return fe.refs.includes(frontend.ref) }, - getSuggestedRef (frontend) { + getSuggestedRef(frontend) { if (this.adminDraft) { const defaultFe = this.adminDraft[':pleroma'][':frontends'][':primary'] if (defaultFe?.name === frontend.name && this.canInstall(defaultFe)) { @@ -67,13 +63,14 @@ const FrontendsTab = { return frontend.refs[0] } }, - update (frontend, suggestRef) { + update(frontend, suggestRef) { const ref = suggestRef || this.getSuggestedRef(frontend) const { name } = frontend const payload = { name, ref } this.working = true - this.$store.state.api.backendInteractor.installFrontend({ payload }) + this.$store.state.api.backendInteractor + .installFrontend({ payload }) .finally(() => { this.working = false }) @@ -86,29 +83,32 @@ const FrontendsTab = { messageKey: 'admin_dash.frontend.failure_installing_frontend', messageArgs: { version: name + '/' + ref, - reason: reason.error + reason: reason.error, }, - timeout: 5000 + timeout: 5000, }) } else { useInterfaceStore().pushGlobalNotice({ level: 'success', messageKey: 'admin_dash.frontend.success_installing_frontend', messageArgs: { - version: name + '/' + ref + version: name + '/' + ref, }, - timeout: 2000 + timeout: 2000, }) } }) }, - setDefault (frontend, suggestRef) { + setDefault(frontend, suggestRef) { const ref = suggestRef || this.getSuggestedRef(frontend) const { name } = frontend - this.$store.commit('updateAdminDraft', { path: [':pleroma', ':frontends', ':primary'], value: { name, ref } }) - } - } + this.$store.commit('updateAdminDraft', { + path: [':pleroma', ':frontends', ':primary'], + value: { name, ref }, + }) + }, + }, } export default FrontendsTab diff --git a/src/components/settings_modal/admin_tabs/http_tab.js b/src/components/settings_modal/admin_tabs/http_tab.js index 82e2bb010..010a95869 100644 --- a/src/components/settings_modal/admin_tabs/http_tab.js +++ b/src/components/settings_modal/admin_tabs/http_tab.js @@ -13,10 +13,10 @@ import SharedComputedObject from '../helpers/shared_computed_object.js' import { get } from 'lodash' const HTTPTab = { - provide () { + provide() { return { defaultDraftMode: true, - defaultSource: 'admin' + defaultSource: 'admin', } }, components: { @@ -29,18 +29,23 @@ const HTTPTab = { GroupSetting, ListSetting, TupleSetting, - ProxySetting + ProxySetting, }, computed: { ...SharedComputedObject(), - sslOptions () { - const desc = get(this.$store.state.adminSettings.descriptions, ':pleroma.:http.:adapter.:ssl_options.:versions') - return new Set(desc.suggestions.map(option => ({ - label: option.replace(':tlsv', 'TLS v'), - value: option - }))) + sslOptions() { + const desc = get( + this.$store.state.adminSettings.descriptions, + ':pleroma.:http.:adapter.:ssl_options.:versions', + ) + return new Set( + desc.suggestions.map((option) => ({ + label: option.replace(':tlsv', 'TLS v'), + value: option, + })), + ) }, - } + }, } export default HTTPTab diff --git a/src/components/settings_modal/admin_tabs/instance_tab.js b/src/components/settings_modal/admin_tabs/instance_tab.js index ddbd58a88..d0e90ecf0 100644 --- a/src/components/settings_modal/admin_tabs/instance_tab.js +++ b/src/components/settings_modal/admin_tabs/instance_tab.js @@ -13,10 +13,10 @@ import SharedComputedObject from '../helpers/shared_computed_object.js' import { get } from 'lodash' const InstanceTab = { - provide () { + provide() { return { defaultDraftMode: true, - defaultSource: 'admin' + defaultSource: 'admin', } }, components: { @@ -29,25 +29,40 @@ const InstanceTab = { ListSetting, PWAManifestIconsSetting, MapSetting, - GroupSetting + GroupSetting, }, computed: { ...SharedComputedObject(), - providersOptions () { - const desc = get(this.$store.state.adminSettings.descriptions, [':pleroma', 'Pleroma.Web.Metadata', ':providers']) - return new Set(desc.suggestions.map(option => ({ - label: option.replace('Pleroma.Web.Metadata.Providers.', ''), - value: option - }))) + providersOptions() { + const desc = get(this.$store.state.adminSettings.descriptions, [ + ':pleroma', + 'Pleroma.Web.Metadata', + ':providers', + ]) + return new Set( + desc.suggestions.map((option) => ({ + label: option.replace('Pleroma.Web.Metadata.Providers.', ''), + value: option, + })), + ) }, - limitLocalContentOptions () { - const desc = get(this.$store.state.adminSettings.descriptions, [':pleroma', ':instance', ':limit_to_local_content']) - return new Set(desc.suggestions.map(option => ({ - label: option !== 'false' ? this.$t('admin_dash.instance.' + option) : this.$t('general.no'), - value: option - }))) - } - } + limitLocalContentOptions() { + const desc = get(this.$store.state.adminSettings.descriptions, [ + ':pleroma', + ':instance', + ':limit_to_local_content', + ]) + return new Set( + desc.suggestions.map((option) => ({ + label: + option !== 'false' + ? this.$t('admin_dash.instance.' + option) + : this.$t('general.no'), + value: option, + })), + ) + }, + }, } export default InstanceTab diff --git a/src/components/settings_modal/admin_tabs/job_queues_tab.js b/src/components/settings_modal/admin_tabs/job_queues_tab.js index b0f583c55..c59c743b6 100644 --- a/src/components/settings_modal/admin_tabs/job_queues_tab.js +++ b/src/components/settings_modal/admin_tabs/job_queues_tab.js @@ -10,10 +10,10 @@ import ListSetting from '../helpers/list_setting.vue' import SharedComputedObject from '../helpers/shared_computed_object.js' const JobQueuesTab = { - provide () { + provide() { return { defaultDraftMode: true, - defaultSource: 'admin' + defaultSource: 'admin', } }, components: { @@ -24,11 +24,11 @@ const JobQueuesTab = { TupleSetting, AttachmentSetting, GroupSetting, - ListSetting + ListSetting, }, computed: { - ...SharedComputedObject() - } + ...SharedComputedObject(), + }, } export default JobQueuesTab diff --git a/src/components/settings_modal/admin_tabs/limits_tab.js b/src/components/settings_modal/admin_tabs/limits_tab.js index c1126f49c..e5b784289 100644 --- a/src/components/settings_modal/admin_tabs/limits_tab.js +++ b/src/components/settings_modal/admin_tabs/limits_tab.js @@ -10,11 +10,11 @@ const LimitsTab = { BooleanSetting, ChoiceSetting, IntegerSetting, - StringSetting + StringSetting, }, computed: { - ...SharedComputedObject() - } + ...SharedComputedObject(), + }, } export default LimitsTab diff --git a/src/components/settings_modal/admin_tabs/links_tab.js b/src/components/settings_modal/admin_tabs/links_tab.js index 026b099a5..1ecbc9409 100644 --- a/src/components/settings_modal/admin_tabs/links_tab.js +++ b/src/components/settings_modal/admin_tabs/links_tab.js @@ -12,10 +12,10 @@ import SharedComputedObject from '../helpers/shared_computed_object.js' import { get } from 'lodash' const LinksTab = { - provide () { + provide() { return { defaultDraftMode: true, - defaultSource: 'admin' + defaultSource: 'admin', } }, components: { @@ -26,84 +26,109 @@ const LinksTab = { AttachmentSetting, GroupSetting, ListSetting, - Checkbox + Checkbox, }, computed: { - classIsPresent () { - return this.$store.state.adminSettings.draft[':pleroma']['Pleroma.Formatter'][':class'] !== false + classIsPresent() { + return ( + this.$store.state.adminSettings.draft[':pleroma']['Pleroma.Formatter'][ + ':class' + ] !== false + ) }, - relIsPresent () { - return this.$store.state.adminSettings.draft[':pleroma']['Pleroma.Formatter'][':rel'] !== false + relIsPresent() { + return ( + this.$store.state.adminSettings.draft[':pleroma']['Pleroma.Formatter'][ + ':rel' + ] !== false + ) }, - truncateIsPresent () { - return this.$store.state.adminSettings.draft[':pleroma']['Pleroma.Formatter'][':truncate'] !== false + truncateIsPresent() { + return ( + this.$store.state.adminSettings.draft[':pleroma']['Pleroma.Formatter'][ + ':truncate' + ] !== false + ) }, - truncateDescription () { - return get(this.$store.state.adminSettings.descriptions, [':pleroma', 'Pleroma.Formatter', ':truncate']) + truncateDescription() { + return get(this.$store.state.adminSettings.descriptions, [ + ':pleroma', + 'Pleroma.Formatter', + ':truncate', + ]) }, - ttlSettersOptions () { - const desc = get(this.$store.state.adminSettings.descriptions, ':pleroma.:rich_media.:ttl_setters') - return new Set(desc.suggestions.map(option => ({ - label: option.replace('Pleroma.Web.RichMedia.Parser.TTL.', ''), - value: option - }))) + ttlSettersOptions() { + const desc = get( + this.$store.state.adminSettings.descriptions, + ':pleroma.:rich_media.:ttl_setters', + ) + return new Set( + desc.suggestions.map((option) => ({ + label: option.replace('Pleroma.Web.RichMedia.Parser.TTL.', ''), + value: option, + })), + ) }, - parsersOptions () { - const desc = get(this.$store.state.adminSettings.descriptions, ':pleroma.:rich_media.:parsers') - return new Set(desc.suggestions.map(option => ({ - label: option.replace('Pleroma.Web.RichMedia.Parsers.', ''), - value: option - }))) + parsersOptions() { + const desc = get( + this.$store.state.adminSettings.descriptions, + ':pleroma.:rich_media.:parsers', + ) + return new Set( + desc.suggestions.map((option) => ({ + label: option.replace('Pleroma.Web.RichMedia.Parsers.', ''), + value: option, + })), + ) }, - validateTLDOptions () { - return [{ - label: this.$t('general.yes'), - value: true - }, { - label: this.$t('general.no'), - value: false - }, { - label: this.$t('admin_dash.links.no_scheme'), - value: ':no_scheme' - }] + validateTLDOptions() { + return [ + { + label: this.$t('general.yes'), + value: true, + }, + { + label: this.$t('general.no'), + value: false, + }, + { + label: this.$t('admin_dash.links.no_scheme'), + value: ':no_scheme', + }, + ] }, - mediaProxyEnabled () { - return this.$store.state.adminSettings.draft[':pleroma'][':media_proxy'][':enabled'] + mediaProxyEnabled() { + return this.$store.state.adminSettings.draft[':pleroma'][':media_proxy'][ + ':enabled' + ] }, - mediaInvalidationProvider () { - return this.$store.state.adminSettings.draft[':pleroma'][':media_proxy'][':invalidation'][':provider'] + mediaInvalidationProvider() { + return this.$store.state.adminSettings.draft[':pleroma'][':media_proxy'][ + ':invalidation' + ][':provider'] }, - ...SharedComputedObject() + ...SharedComputedObject(), }, methods: { - checkRel (e) { - this.$store.commit( - 'updateAdminDraft', - { - path: [':pleroma','Pleroma.Formatter',':rel'], - value: e ? '' : false - } - ) + checkRel(e) { + this.$store.commit('updateAdminDraft', { + path: [':pleroma', 'Pleroma.Formatter', ':rel'], + value: e ? '' : false, + }) }, - checkClass (e) { - this.$store.commit( - 'updateAdminDraft', - { - path: [':pleroma','Pleroma.Formatter',':class'], - value: e ? '' : false - } - ) + checkClass(e) { + this.$store.commit('updateAdminDraft', { + path: [':pleroma', 'Pleroma.Formatter', ':class'], + value: e ? '' : false, + }) }, - checkTruncate (e) { - this.$store.commit( - 'updateAdminDraft', - { - path: [':pleroma','Pleroma.Formatter',':truncate'], - value: e ? 20 : false - } - ) - } - } + checkTruncate(e) { + this.$store.commit('updateAdminDraft', { + path: [':pleroma', 'Pleroma.Formatter', ':truncate'], + value: e ? 20 : false, + }) + }, + }, } export default LinksTab diff --git a/src/components/settings_modal/admin_tabs/mailer_tab.js b/src/components/settings_modal/admin_tabs/mailer_tab.js index f6cd854b4..424451ec4 100644 --- a/src/components/settings_modal/admin_tabs/mailer_tab.js +++ b/src/components/settings_modal/admin_tabs/mailer_tab.js @@ -9,10 +9,10 @@ import AttachmentSetting from '../helpers/attachment_setting.vue' import SharedComputedObject from '../helpers/shared_computed_object.js' const MailerTab = { - provide () { + provide() { return { defaultDraftMode: true, - defaultSource: 'admin' + defaultSource: 'admin', } }, components: { @@ -22,44 +22,50 @@ const MailerTab = { StringSetting, AttachmentSetting, ColorSetting, - GroupSetting + GroupSetting, }, computed: { - adaptersLabels () { + adaptersLabels() { const prefix = 'Swoosh.Adapters.' const descriptions = this.$store.state.adminSettings.descriptions - const options = descriptions[':pleroma']['Pleroma.Emails.Mailer'][':adapter'].suggestions + const options = + descriptions[':pleroma']['Pleroma.Emails.Mailer'][':adapter'] + .suggestions - return Object.fromEntries(options.map(value => [ - value, value.replace(prefix, '') - ])) + return Object.fromEntries( + options.map((value) => [value, value.replace(prefix, '')]), + ) }, - startTLSLabels () { + startTLSLabels() { return { ':always': this.$t('admin_dash.generic_enforcement.always'), ':if_available': this.$t('admin_dash.generic_enforcement.if_available'), - ':never': this.$t('admin_dash.generic_enforcement.never') + ':never': this.$t('admin_dash.generic_enforcement.never'), } // return Object.fromEntries(options.map(value => [ // value, value.replace(prefix, '') // ])) }, - adapter () { - return this.$store.state.adminSettings.draft[':pleroma']['Pleroma.Emails.Mailer'][':adapter'] + adapter() { + return this.$store.state.adminSettings.draft[':pleroma'][ + 'Pleroma.Emails.Mailer' + ][':adapter'] }, - mailerEnabled () { - return this.$store.state.adminSettings.draft[':pleroma']['Pleroma.Emails.Mailer'][':enabled'] + mailerEnabled() { + return this.$store.state.adminSettings.draft[':pleroma'][ + 'Pleroma.Emails.Mailer' + ][':enabled'] }, - ...SharedComputedObject() + ...SharedComputedObject(), }, methods: { - adapterHasKey (key) { + adapterHasKey(key) { const descriptions = this.$store.state.adminSettings.descriptions const mailerStuff = descriptions[':pleroma']['Pleroma.Emails.Mailer'] const adapterStuff = mailerStuff[':subgroup,' + this.adapter] return Object.hasOwn(adapterStuff, key) - } - } + }, + }, } export default MailerTab diff --git a/src/components/settings_modal/admin_tabs/media_proxy_tab.js b/src/components/settings_modal/admin_tabs/media_proxy_tab.js index af82593bc..279a606c5 100644 --- a/src/components/settings_modal/admin_tabs/media_proxy_tab.js +++ b/src/components/settings_modal/admin_tabs/media_proxy_tab.js @@ -9,10 +9,10 @@ import ListSetting from '../helpers/list_setting.vue' import SharedComputedObject from '../helpers/shared_computed_object.js' const MediaProxyTab = { - provide () { + provide() { return { defaultDraftMode: true, - defaultSource: 'admin' + defaultSource: 'admin', } }, components: { @@ -22,17 +22,21 @@ const MediaProxyTab = { StringSetting, AttachmentSetting, GroupSetting, - ListSetting + ListSetting, }, computed: { - mediaProxyEnabled () { - return this.$store.state.adminSettings.draft[':pleroma'][':media_proxy'][':enabled'] + mediaProxyEnabled() { + return this.$store.state.adminSettings.draft[':pleroma'][':media_proxy'][ + ':enabled' + ] }, - mediaInvalidationProvider () { - return this.$store.state.adminSettings.draft[':pleroma'][':media_proxy'][':invalidation'][':provider'] + mediaInvalidationProvider() { + return this.$store.state.adminSettings.draft[':pleroma'][':media_proxy'][ + ':invalidation' + ][':provider'] }, - ...SharedComputedObject() - } + ...SharedComputedObject(), + }, } export default MediaProxyTab diff --git a/src/components/settings_modal/admin_tabs/monitoring_tab.js b/src/components/settings_modal/admin_tabs/monitoring_tab.js index 8593b9d69..056f67589 100644 --- a/src/components/settings_modal/admin_tabs/monitoring_tab.js +++ b/src/components/settings_modal/admin_tabs/monitoring_tab.js @@ -8,19 +8,15 @@ import ListSetting from '../helpers/list_setting.vue' import SharedComputedObject from '../helpers/shared_computed_object.js' import { library } from '@fortawesome/fontawesome-svg-core' -import { - faGlobe -} from '@fortawesome/free-solid-svg-icons' +import { faGlobe } from '@fortawesome/free-solid-svg-icons' -library.add( - faGlobe -) +library.add(faGlobe) const MonitoringTab = { - provide () { + provide() { return { defaultDraftMode: true, - defaultSource: 'admin' + defaultSource: 'admin', } }, components: { @@ -30,13 +26,12 @@ const MonitoringTab = { StringSetting, AttachmentSetting, GroupSetting, - ListSetting + ListSetting, }, computed: { - ...SharedComputedObject() + ...SharedComputedObject(), }, - methods: { - } + methods: {}, } export default MonitoringTab diff --git a/src/components/settings_modal/admin_tabs/other_tab.js b/src/components/settings_modal/admin_tabs/other_tab.js index dc6550d27..a7b3ea30a 100644 --- a/src/components/settings_modal/admin_tabs/other_tab.js +++ b/src/components/settings_modal/admin_tabs/other_tab.js @@ -12,10 +12,10 @@ import MapSetting from '../helpers/map_setting.vue' import SharedComputedObject from '../helpers/shared_computed_object.js' const OtherTab = { - provide () { + provide() { return { defaultDraftMode: true, - defaultSource: 'admin' + defaultSource: 'admin', } }, components: { @@ -28,11 +28,11 @@ const OtherTab = { ListSetting, PWAManifestIconsSetting, MapSetting, - GroupSetting + GroupSetting, }, computed: { - ...SharedComputedObject() - } + ...SharedComputedObject(), + }, } export default OtherTab diff --git a/src/components/settings_modal/admin_tabs/posts_tab.js b/src/components/settings_modal/admin_tabs/posts_tab.js index d9f213594..0a6ceaeee 100644 --- a/src/components/settings_modal/admin_tabs/posts_tab.js +++ b/src/components/settings_modal/admin_tabs/posts_tab.js @@ -12,10 +12,10 @@ import MapSetting from '../helpers/map_setting.vue' import SharedComputedObject from '../helpers/shared_computed_object.js' const PostsTab = { - provide () { + provide() { return { defaultDraftMode: true, - defaultSource: 'admin' + defaultSource: 'admin', } }, components: { @@ -28,11 +28,11 @@ const PostsTab = { ListSetting, PWAManifestIconsSetting, MapSetting, - GroupSetting + GroupSetting, }, computed: { - ...SharedComputedObject() - } + ...SharedComputedObject(), + }, } export default PostsTab diff --git a/src/components/settings_modal/admin_tabs/rates_tab.js b/src/components/settings_modal/admin_tabs/rates_tab.js index c602dcc8a..812c25b00 100644 --- a/src/components/settings_modal/admin_tabs/rates_tab.js +++ b/src/components/settings_modal/admin_tabs/rates_tab.js @@ -3,18 +3,18 @@ import RateSetting from '../helpers/rate_setting.vue' import SharedComputedObject from '../helpers/shared_computed_object.js' const RatesTab = { - provide () { + provide() { return { defaultDraftMode: true, - defaultSource: 'admin' + defaultSource: 'admin', } }, components: { RateSetting, }, computed: { - ...SharedComputedObject() - } + ...SharedComputedObject(), + }, } export default RatesTab diff --git a/src/components/settings_modal/admin_tabs/registrations_tab.js b/src/components/settings_modal/admin_tabs/registrations_tab.js index 3ce6c8044..e01114c78 100644 --- a/src/components/settings_modal/admin_tabs/registrations_tab.js +++ b/src/components/settings_modal/admin_tabs/registrations_tab.js @@ -10,10 +10,10 @@ import ListSetting from '../helpers/list_setting.vue' import SharedComputedObject from '../helpers/shared_computed_object.js' const RegistrationsTab = { - provide () { + provide() { return { defaultDraftMode: true, - defaultSource: 'admin' + defaultSource: 'admin', } }, components: { @@ -24,11 +24,11 @@ const RegistrationsTab = { TupleSetting, AttachmentSetting, GroupSetting, - ListSetting + ListSetting, }, computed: { - ...SharedComputedObject() - } + ...SharedComputedObject(), + }, } export default RegistrationsTab diff --git a/src/components/settings_modal/admin_tabs/uploads_tab.js b/src/components/settings_modal/admin_tabs/uploads_tab.js index 40a184db1..a3e9c5d5e 100644 --- a/src/components/settings_modal/admin_tabs/uploads_tab.js +++ b/src/components/settings_modal/admin_tabs/uploads_tab.js @@ -6,41 +6,47 @@ import StringSetting from '../helpers/string_setting.vue' import SharedComputedObject from '../helpers/shared_computed_object.js' const UploadsTab = { - provide () { + provide() { return { defaultDraftMode: true, - defaultSource: 'admin' + defaultSource: 'admin', } }, - data () { + data() { return { - uploaders: [{ - key: 'Pleroma.Uploaders.Local', - value: 'Pleroma.Uploaders.Local', - label: this.$t('admin_dash.uploads.local_uploader') - }, { - key: 'Pleroma.Uploaders.IPFS', - value: 'Pleroma.Uploaders.IPFS', - label: 'IPFS' - }, { - key: 'Pleroma.Uploaders.S3', - value: 'Pleroma.Uploaders.S3', - label: 'S3' - }] + uploaders: [ + { + key: 'Pleroma.Uploaders.Local', + value: 'Pleroma.Uploaders.Local', + label: this.$t('admin_dash.uploads.local_uploader'), + }, + { + key: 'Pleroma.Uploaders.IPFS', + value: 'Pleroma.Uploaders.IPFS', + label: 'IPFS', + }, + { + key: 'Pleroma.Uploaders.S3', + value: 'Pleroma.Uploaders.S3', + label: 'S3', + }, + ], } }, components: { BooleanSetting, ChoiceSetting, IntegerSetting, - StringSetting + StringSetting, }, computed: { - uploader () { - return this.$store.state.adminSettings.draft[':pleroma']['Pleroma.Upload'][':uploader'] + uploader() { + return this.$store.state.adminSettings.draft[':pleroma'][ + 'Pleroma.Upload' + ][':uploader'] }, - ...SharedComputedObject() - } + ...SharedComputedObject(), + }, } export default UploadsTab diff --git a/src/components/settings_modal/helpers/attachment_setting.js b/src/components/settings_modal/helpers/attachment_setting.js index c4c04b2b6..cc9ce5e5f 100644 --- a/src/components/settings_modal/helpers/attachment_setting.js +++ b/src/components/settings_modal/helpers/attachment_setting.js @@ -11,34 +11,36 @@ export default { acceptTypes: { type: String, required: false, - default: 'image/*' - } + default: 'image/*', + }, }, components: { ...Setting.components, MediaUpload, - Attachment + Attachment, }, computed: { ...Setting.computed, - attachment () { + attachment() { const path = this.realDraftMode ? this.draft : this.state // The "server" part is primarily for local dev, but could be useful for alt-domain or multiuser usage. - const url = path.includes('://') ? path : this.$store.state.instance.server + path + const url = path.includes('://') + ? path + : this.$store.state.instance.server + path return { mimetype: fileTypeExt(url), - url + url, } - } + }, }, methods: { ...Setting.methods, - setMediaFile (fileInfo) { + setMediaFile(fileInfo) { if (this.realDraftMode) { this.draft = fileInfo.url } else { this.configSink(this.path, fileInfo.url) } - } - } + }, + }, } diff --git a/src/components/settings_modal/helpers/boolean_setting.js b/src/components/settings_modal/helpers/boolean_setting.js index 199d3d0f0..e7b56f58c 100644 --- a/src/components/settings_modal/helpers/boolean_setting.js +++ b/src/components/settings_modal/helpers/boolean_setting.js @@ -5,27 +5,27 @@ export default { ...Setting, props: { ...Setting.props, - indeterminateState: [String, Object] + indeterminateState: [String, Object], }, components: { ...Setting.components, - Checkbox + Checkbox, }, computed: { ...Setting.computed, - isIndeterminate () { + isIndeterminate() { return this.visibleState === this.indeterminateState - } + }, }, methods: { ...Setting.methods, - getValue (e) { + getValue(e) { // Basic tri-state toggle implementation if (!!this.indeterminateState && !e && this.visibleState === true) { // If we have indeterminate state, switching from true to false first goes through indeterminate return this.indeterminateState } return e - } - } + }, + }, } diff --git a/src/components/settings_modal/helpers/choice_setting.js b/src/components/settings_modal/helpers/choice_setting.js index 10e043082..e5bc8420e 100644 --- a/src/components/settings_modal/helpers/choice_setting.js +++ b/src/components/settings_modal/helpers/choice_setting.js @@ -5,50 +5,50 @@ export default { ...Setting, components: { ...Setting.components, - Select + Select, }, props: { ...Setting.props, overrideOptions: { type: Boolean, - required: false + required: false, }, options: { type: Array, - required: false + required: false, }, optionLabelMap: { type: Object, required: false, - default: {} - } + default: {}, + }, }, computed: { ...Setting.computed, - realOptions () { + realOptions() { if (this.overrideOptions) { return this.options } if (this.realSource === 'admin') { if ( !this.backendDescriptionSuggestions?.length || - this.backendDescriptionSuggestions?.length === 0 + this.backendDescriptionSuggestions?.length === 0 ) { return this.options } - return this.backendDescriptionSuggestions.map(x => ({ + return this.backendDescriptionSuggestions.map((x) => ({ key: x, value: x, - label: this.optionLabelMap[x] || x + label: this.optionLabelMap[x] || x, })) } return this.options - } + }, }, methods: { ...Setting.methods, - getValue (e) { + getValue(e) { return e - } - } + }, + }, } diff --git a/src/components/settings_modal/helpers/color_setting.js b/src/components/settings_modal/helpers/color_setting.js index 40fa038fe..8cbacc147 100644 --- a/src/components/settings_modal/helpers/color_setting.js +++ b/src/components/settings_modal/helpers/color_setting.js @@ -5,12 +5,12 @@ export default { ...Setting, components: { ...Setting.components, - ColorInput + ColorInput, }, methods: { ...Setting.methods, - getValue (e) { + getValue(e) { return e - } - } + }, + }, } diff --git a/src/components/settings_modal/helpers/draft_buttons.vue b/src/components/settings_modal/helpers/draft_buttons.vue index b07ea1ec4..b30508a90 100644 --- a/src/components/settings_modal/helpers/draft_buttons.vue +++ b/src/components/settings_modal/helpers/draft_buttons.vue @@ -61,13 +61,11 @@ import Popover from 'src/components/popover/popover.vue' import { library } from '@fortawesome/fontawesome-svg-core' import { faWrench } from '@fortawesome/free-solid-svg-icons' -library.add( - faWrench -) +library.add(faWrench) export default { components: { Popover }, - props: ['changed'] + props: ['changed'], } diff --git a/src/components/settings_modal/helpers/emoji_editing_popover.vue b/src/components/settings_modal/helpers/emoji_editing_popover.vue index d25b02da2..fd5f8fa2a 100644 --- a/src/components/settings_modal/helpers/emoji_editing_popover.vue +++ b/src/components/settings_modal/helpers/emoji_editing_popover.vue @@ -160,53 +160,53 @@ export default { props: { placement: { type: String, - required: true + required: true, }, newUpload: Boolean, title: { type: String, - required: true + required: true, }, packName: { type: String, - required: true + required: true, }, shortcode: { type: String, // Only exists when this is not a new upload - default: '' + default: '', }, file: { type: String, // Only exists when this is not a new upload - default: '' + default: '', }, // Only exists for emojis from remote packs remote: { type: Object, - default: undefined + default: undefined, }, knownLocalPacks: { type: Object, - default: undefined - } + default: undefined, + }, }, emits: ['updatePackFiles', 'displayError'], - data () { + data() { return { uploadFile: [], uploadURL: '', editedShortcode: this.shortcode, editedFile: this.file, deleteModalVisible: false, - copyToPack: '' + copyToPack: '', } }, computed: { - emojiPreview () { + emojiPreview() { if (this.newUpload && this.uploadFile.length > 0) { return URL.createObjectURL(this.uploadFile[0]) } else if (this.newUpload && this.uploadURL !== '') { @@ -217,73 +217,92 @@ export default { return null }, - isEdited () { - return !this.newUpload && (this.editedShortcode !== this.shortcode || this.editedFile !== this.file) + isEdited() { + return ( + !this.newUpload && + (this.editedShortcode !== this.shortcode || + this.editedFile !== this.file) + ) }, saveButtonDisabled() { if (this.remote === undefined) - return this.newUpload ? (this.uploadURL === "" && this.uploadFile.length == 0) : !this.isEdited - else - return this.copyToPack === "" - } + return this.newUpload + ? this.uploadURL === '' && this.uploadFile.length == 0 + : !this.isEdited + else return this.copyToPack === '' + }, }, methods: { - saveEditedEmoji () { + saveEditedEmoji() { if (!this.isEdited) return - this.$store.state.api.backendInteractor.updateEmojiFile( - { packName: this.packName, shortcode: this.shortcode, newShortcode: this.editedShortcode, newFilename: this.editedFile, force: false } - ).then(resp => { - if (resp.error !== undefined) { - this.$emit('displayError', resp.error) - return Promise.reject(resp.error) - } + this.$store.state.api.backendInteractor + .updateEmojiFile({ + packName: this.packName, + shortcode: this.shortcode, + newShortcode: this.editedShortcode, + newFilename: this.editedFile, + force: false, + }) + .then((resp) => { + if (resp.error !== undefined) { + this.$emit('displayError', resp.error) + return Promise.reject(resp.error) + } - return resp.json() - }).then(resp => this.$emit('updatePackFiles', resp)) + return resp.json() + }) + .then((resp) => this.$emit('updatePackFiles', resp)) }, - uploadEmoji () { + uploadEmoji() { let packName = this.remote === undefined ? this.packName : this.copyToPack - this.$store.state.api.backendInteractor.addNewEmojiFile({ - packName: packName, - file: this.remote === undefined - ? (this.uploadURL !== "" ? this.uploadURL : this.uploadFile[0]) - : this.emojiAddr(this.file), - shortcode: this.editedShortcode, - filename: this.editedFile - }).then(resp => resp.json()).then(resp => { - if (resp.error !== undefined) { - this.$emit('displayError', resp.error) - return - } + this.$store.state.api.backendInteractor + .addNewEmojiFile({ + packName: packName, + file: + this.remote === undefined + ? this.uploadURL !== '' + ? this.uploadURL + : this.uploadFile[0] + : this.emojiAddr(this.file), + shortcode: this.editedShortcode, + filename: this.editedFile, + }) + .then((resp) => resp.json()) + .then((resp) => { + if (resp.error !== undefined) { + this.$emit('displayError', resp.error) + return + } - this.$emit('updatePackFiles', resp, packName) - this.$refs.emojiPopover.hidePopover() + this.$emit('updatePackFiles', resp, packName) + this.$refs.emojiPopover.hidePopover() - this.editedFile = '' - this.editedShortcode = '' - this.uploadFile = [] - }) + this.editedFile = '' + this.editedShortcode = '' + this.uploadFile = [] + }) }, - revertEmoji () { + revertEmoji() { this.editedFile = this.file this.editedShortcode = this.shortcode }, - deleteEmoji () { + deleteEmoji() { this.deleteModalVisible = false - this.$store.state.api.backendInteractor.deleteEmojiFile( - { packName: this.packName, shortcode: this.shortcode } - ).then(resp => resp.json()).then(resp => { - if (resp.error !== undefined) { - this.$emit('displayError', resp.error) - return - } + this.$store.state.api.backendInteractor + .deleteEmojiFile({ packName: this.packName, shortcode: this.shortcode }) + .then((resp) => resp.json()) + .then((resp) => { + if (resp.error !== undefined) { + this.$emit('displayError', resp.error) + return + } - this.$emit('updatePackFiles', resp, this.packName) - }) - } - } + this.$emit('updatePackFiles', resp, this.packName) + }) + }, + }, } diff --git a/src/components/settings_modal/helpers/float_setting.vue b/src/components/settings_modal/helpers/float_setting.vue index 15edb3c3e..a1c495cd1 100644 --- a/src/components/settings_modal/helpers/float_setting.vue +++ b/src/components/settings_modal/helpers/float_setting.vue @@ -10,7 +10,7 @@ import NumberSetting from './number_setting.vue' export default { components: { - NumberSetting - } + NumberSetting, + }, } diff --git a/src/components/settings_modal/helpers/help_indicator.vue b/src/components/settings_modal/helpers/help_indicator.vue index c48f97b70..2ce804cba 100644 --- a/src/components/settings_modal/helpers/help_indicator.vue +++ b/src/components/settings_modal/helpers/help_indicator.vue @@ -22,12 +22,10 @@ import Popover from 'src/components/popover/popover.vue' import { library } from '@fortawesome/fontawesome-svg-core' import { faCircleQuestion } from '@fortawesome/free-solid-svg-icons' -library.add( - faCircleQuestion -) +library.add(faCircleQuestion) export default { - components: { Popover } + components: { Popover }, } diff --git a/src/components/settings_modal/helpers/integer_setting.vue b/src/components/settings_modal/helpers/integer_setting.vue index 8206c0c45..e9b227645 100644 --- a/src/components/settings_modal/helpers/integer_setting.vue +++ b/src/components/settings_modal/helpers/integer_setting.vue @@ -11,7 +11,7 @@ import NumberSetting from './number_setting.vue' export default { components: { - NumberSetting - } + NumberSetting, + }, } diff --git a/src/components/settings_modal/helpers/list_setting.js b/src/components/settings_modal/helpers/list_setting.js index def6064d0..c1d504cd5 100644 --- a/src/components/settings_modal/helpers/list_setting.js +++ b/src/components/settings_modal/helpers/list_setting.js @@ -3,43 +3,43 @@ import Setting from './setting.js' export default { ...Setting, - data () { + data() { return { newValue: '', } }, components: { ...Setting.components, - Checkbox + Checkbox, }, props: { ...Setting.props, ignoreSuggestions: { required: false, - type: Boolean + type: Boolean, }, overrideAvailableOptions: { required: false, - type: Boolean + type: Boolean, }, options: { required: false, - type: Set + type: Set, }, allowNew: { required: false, type: Boolean, - default: true + default: true, }, forceNew: { required: false, type: Boolean, - default: false - } + default: false, + }, }, computed: { ...Setting.computed, - showNew () { + showNew() { if (this.forceNew) return true if (!this.allowNew) return false @@ -52,10 +52,10 @@ export default { return true } }, - valueSet () { + valueSet() { return new Set(this.visibleState) }, - suggestionsSet () { + suggestionsSet() { const suggestions = this.backendDescriptionSuggestions if (suggestions) { return new Set(suggestions) @@ -63,14 +63,14 @@ export default { return new Set() } }, - extraEntries () { + extraEntries() { if (this.ignoreSuggestions) return [...this.valueSet.values()] if (!this.suggestionsSet) return [] return [...this.valueSet.values()].filter((x) => { return !this.builtinEntriesValueSet.has(x) }) }, - builtinEntries () { + builtinEntries() { if (this.ignoreSuggestions) return [] if (this.overrideAvailableOptions) { return [...this.options] @@ -80,19 +80,19 @@ export default { const builtins = [...this.suggestionsSet.values()] return builtins.map((option) => ({ label: option, - value: option + value: option, })) }, - builtinEntriesValueSet () { - return new Set(this.builtinEntries.map(x => x.value)) - } + builtinEntriesValueSet() { + return new Set(this.builtinEntries.map((x) => x.value)) + }, }, methods: { ...Setting.methods, - optionPresent (option) { + optionPresent(option) { return this.valueSet.has(option) }, - getValue ({ event, value, index, eventType }) { + getValue({ event, value, index, eventType }) { switch (eventType) { case 'toggle': { this.newValue = '' @@ -128,6 +128,6 @@ export default { return [...pre, string, ...post] } } - } - } + }, + }, } diff --git a/src/components/settings_modal/helpers/list_tuple_setting.js b/src/components/settings_modal/helpers/list_tuple_setting.js index 2cbd93788..33cc725b1 100644 --- a/src/components/settings_modal/helpers/list_tuple_setting.js +++ b/src/components/settings_modal/helpers/list_tuple_setting.js @@ -2,14 +2,14 @@ import ListSetting from './list_setting.js' export default { ...ListSetting, - data () { + data() { return { - newValue: ['',''] + newValue: ['', ''], } }, methods: { ...ListSetting.methods, - getValue ({ event, index, eventType, tuple }) { + getValue({ event, index, eventType, tuple }) { switch (eventType) { case 'add': { if (!this.newValue[0] || !this.newValue[1]) return this.visibleState @@ -39,6 +39,6 @@ export default { } } } - } - } + }, + }, } diff --git a/src/components/settings_modal/helpers/map_setting.js b/src/components/settings_modal/helpers/map_setting.js index ad93ceacf..8615c90ca 100644 --- a/src/components/settings_modal/helpers/map_setting.js +++ b/src/components/settings_modal/helpers/map_setting.js @@ -7,28 +7,31 @@ export default { allowNew: { required: false, type: Boolean, - default: true - } + default: true, + }, }, - data () { + data() { return { - newValue: ['',''] // avoiding extra complexity by just using an array instead of an object + newValue: ['', ''], // avoiding extra complexity by just using an array instead of an object } }, computed: { ...Setting.computed, // state that we'll show in the UI, i.e. transforming map into list - displayState () { + displayState() { return Object.entries(this.visibleState) - } + }, }, methods: { ...Setting.methods, - getValue ({ event, key, eventType, isKey }) { + getValue({ event, key, eventType, isKey }) { switch (eventType) { case 'add': { if (key === '') return this.visibleState - const res = {...this.visibleState, ...Object.fromEntries([this.newValue])} + const res = { + ...this.visibleState, + ...Object.fromEntries([this.newValue]), + } this.newValue = ['', ''] return res } @@ -36,35 +39,35 @@ export default { case 'remove': { // initial state for this type is empty array if (Array.isArray(this.visibleState)) return this.visibleState - const newEntries = Object.entries(this.visibleState).filter(([k]) => k !== key) + const newEntries = Object.entries(this.visibleState).filter( + ([k]) => k !== key, + ) - if (newEntries.length === 0 ) return [] + if (newEntries.length === 0) return [] return Object.fromEntries(newEntries) } case 'edit': { const string = event.target.value - const newEntries = Object - .entries(this.visibleState) - .map(([k, v]) => { - if (isKey) { - if (k === key) { - return [string, v] - } else { - return [k, v] - } + const newEntries = Object.entries(this.visibleState).map(([k, v]) => { + if (isKey) { + if (k === key) { + return [string, v] } else { - if (k === key) { - return [k, string] - } else { - return [k, v] - } + return [k, v] } - }) + } else { + if (k === key) { + return [k, string] + } else { + return [k, v] + } + } + }) return Object.fromEntries(newEntries) } } - } - } + }, + }, } diff --git a/src/components/settings_modal/helpers/modified_indicator.vue b/src/components/settings_modal/helpers/modified_indicator.vue index a747cebd2..e58d8a6af 100644 --- a/src/components/settings_modal/helpers/modified_indicator.vue +++ b/src/components/settings_modal/helpers/modified_indicator.vue @@ -27,9 +27,7 @@ import Popover from 'src/components/popover/popover.vue' import { library } from '@fortawesome/fontawesome-svg-core' import { faWrench } from '@fortawesome/free-solid-svg-icons' -library.add( - faWrench -) +library.add(faWrench) export default { components: { Popover }, @@ -37,9 +35,9 @@ export default { changed: Boolean, messageKey: { type: String, - default: 'settings.setting_changed' - } - } + default: 'settings.setting_changed', + }, + }, } diff --git a/src/components/settings_modal/helpers/number_setting.js b/src/components/settings_modal/helpers/number_setting.js index afbf5ed58..e037ece28 100644 --- a/src/components/settings_modal/helpers/number_setting.js +++ b/src/components/settings_modal/helpers/number_setting.js @@ -7,33 +7,33 @@ export default { min: { type: Number, required: false, - default: 1 + default: 1, }, max: { type: Number, required: false, - default: 1 + default: 1, }, step: { type: Number, required: false, - default: 1 + default: 1, }, truncate: { type: Number, required: false, - default: 1 - } + default: 1, + }, }, methods: { ...Setting.methods, - getValue (e) { + getValue(e) { if (!this.truncate === 1) { return parseInt(e.target.value) } else if (this.truncate > 1) { return Math.trunc(e.target.value / this.truncate) * this.truncate } return parseFloat(e.target.value) - } - } + }, + }, } diff --git a/src/components/settings_modal/helpers/profile_setting_indicator.vue b/src/components/settings_modal/helpers/profile_setting_indicator.vue index d160781b1..a90dbf8fc 100644 --- a/src/components/settings_modal/helpers/profile_setting_indicator.vue +++ b/src/components/settings_modal/helpers/profile_setting_indicator.vue @@ -27,13 +27,11 @@ import Popover from 'src/components/popover/popover.vue' import { library } from '@fortawesome/fontawesome-svg-core' import { faServer } from '@fortawesome/free-solid-svg-icons' -library.add( - faServer -) +library.add(faServer) export default { components: { Popover }, - props: ['isProfile'] + props: ['isProfile'], } diff --git a/src/components/settings_modal/helpers/proxy_setting.js b/src/components/settings_modal/helpers/proxy_setting.js index ca717b60b..4195b8430 100644 --- a/src/components/settings_modal/helpers/proxy_setting.js +++ b/src/components/settings_modal/helpers/proxy_setting.js @@ -1,18 +1,19 @@ import Checkbox from 'src/components/checkbox/checkbox.vue' import Setting from './setting.js' -const getUrl = state => state?.tuple ? state.tuple[1] + ':' + state.tuple[2] : state -const getSocks = state => state?.tuple +const getUrl = (state) => + state?.tuple ? state.tuple[1] + ':' + state.tuple[2] : state +const getSocks = (state) => state?.tuple export default { ...Setting, - data () { + data() { return { urlField: '', - socksField: false + socksField: false, } }, - created () { + created() { Setting.created() this.urlField = getUrl(this.realDraftMode ? this.draft : this.state) this.socksField = getSocks(this.realDraftMode ? this.draft : this.state) @@ -20,23 +21,23 @@ export default { computed: { ...Setting.computed, // state that we'll show in the UI, i.e. transforming map into list - displayState () { + displayState() { if (this.visibleState?.tuple) { return this.visibleState.tuple[1] + ':' + this.visibleState.tuple[2] } return this.visibleState }, - socksState () { + socksState() { return getSocks(this.visibleState) - } + }, }, components: { ...Setting.components, - Checkbox + Checkbox, }, methods: { ...Setting.methods, - getValue ({ event, isProxy}) { + getValue({ event, isProxy }) { if (isProxy) { this.socksField = event } else { @@ -44,10 +45,10 @@ export default { } if (this.socksField) { - return { tuple: [ ':socks5', ...this.urlField.split(':') ] } + return { tuple: [':socks5', ...this.urlField.split(':')] } } else { return this.urlField } - } - } + }, + }, } diff --git a/src/components/settings_modal/helpers/pwa_manifest_icons_setting.js b/src/components/settings_modal/helpers/pwa_manifest_icons_setting.js index 9986f6f39..30530f642 100644 --- a/src/components/settings_modal/helpers/pwa_manifest_icons_setting.js +++ b/src/components/settings_modal/helpers/pwa_manifest_icons_setting.js @@ -12,60 +12,62 @@ export default { ...Setting.components, Select, Attachment, - MediaUpload + MediaUpload, }, computed: { ...Setting.computed, - purposeOptions () { - return ['any','monochrome','maskable'].map(value => ({ + purposeOptions() { + return ['any', 'monochrome', 'maskable'].map((value) => ({ value, key: value, - label: this.$t('admin_dash.instance.pwa.icon.' + value) + label: this.$t('admin_dash.instance.pwa.icon.' + value), })) - } + }, }, methods: { ...Setting.methods, - attachment (e) { + attachment(e) { const path = e[':src'] if (!path) { return { mimetype: '', - url: '' + url: '', } } - const url = path.includes('://') ? path : this.$store.state.instance.server + path + const url = path.includes('://') + ? path + : this.$store.state.instance.server + path return { mimetype: fileTypeExt(url), - url + url, } }, - setMediaFile ({ event, index }) { + setMediaFile({ event, index }) { this.update({ event: { target: { - value: event.url + value: event.url, }, }, index, eventType: 'edit', - field: ':src' + field: ':src', }) }, - setPurpose ({ event, index }) { + setPurpose({ event, index }) { this.update({ event: { target: { - value: event + value: event, }, }, index, eventType: 'edit', - field: ':purpose' + field: ':purpose', }) }, - getValue ({ event, field, index, eventType }) { + getValue({ event, field, index, eventType }) { switch (eventType) { case 'add': { const res = [...this.visibleState, {}] @@ -85,7 +87,7 @@ export default { const item = clone(this.visibleState[index]) const string = event.target.value - if (!string) { + if (!string) { delete item[field] } else { item[field] = string @@ -94,6 +96,6 @@ export default { return [...pre, item, ...post] } } - } - } + }, + }, } diff --git a/src/components/settings_modal/helpers/rate_setting.js b/src/components/settings_modal/helpers/rate_setting.js index 7425cdba3..0c37abb87 100644 --- a/src/components/settings_modal/helpers/rate_setting.js +++ b/src/components/settings_modal/helpers/rate_setting.js @@ -3,40 +3,40 @@ import Setting from './setting.js' export default { ...Setting, - data () { + data() { return { newValue: '', } }, components: { ...Setting.components, - Checkbox + Checkbox, }, props: { - ...Setting.props + ...Setting.props, }, computed: { ...Setting.computed, - isSeparate () { + isSeparate() { // [[a1, b1], [a2, b2]] vs [a, b] return Array.isArray(this.visibleState[0]) }, - normalizedState () { + normalizedState() { if (this.isSeparate) { - return this.visibleState.map(y => y.map(x => Number(x) || 0)) + return this.visibleState.map((y) => y.map((x) => Number(x) || 0)) } else { - return [this.visibleState.map(x => Number(x) || 0)] + return [this.visibleState.map((x) => Number(x) || 0)] } - } + }, }, methods: { ...Setting.methods, - getValue ({ event, side, index, eventType }) { + getValue({ event, side, index, eventType }) { if (eventType === 'edit') { const value = Number(event.target.value) if (Number.isNaN(value)) return this.visibleState - const newVal = [...this.normalizedState.map(x => [...x])] + const newVal = [...this.normalizedState.map((x) => [...x])] newVal[side][index] = value return newVal } @@ -48,6 +48,6 @@ export default { return [this.normalizedState[0]] } } - } - } + }, + }, } diff --git a/src/components/settings_modal/helpers/setting.js b/src/components/settings_modal/helpers/setting.js index b1b9bf7e7..0459dff60 100644 --- a/src/components/settings_modal/helpers/setting.js +++ b/src/components/settings_modal/helpers/setting.js @@ -7,119 +7,126 @@ export default { components: { ModifiedIndicator, DraftButtons, - ProfileSettingIndicator + ProfileSettingIndicator, }, props: { modelValue: { type: String, - default: null + default: null, }, path: { type: [String, Array], - required: false + required: false, }, showDescription: { type: Boolean, - required: false + required: false, }, descriptionPathOverride: { type: [String, Array], - required: false + required: false, }, suggestions: { type: [String, Array], - required: false + required: false, }, subgroup: { type: String, - required: false + required: false, }, disabled: { type: Boolean, - default: false + default: false, }, parentPath: { - type: [String, Array] + type: [String, Array], }, parentInvert: { type: Boolean, - default: false + default: false, }, expert: { type: [Number, String], - default: 0 + default: 0, }, source: { type: String, - default: undefined + default: undefined, }, - hideDraftButtons: { // this is for the weird backend hybrid (Boolean|String or Boolean|Number) settings + hideDraftButtons: { + // this is for the weird backend hybrid (Boolean|String or Boolean|Number) settings required: false, - type: Boolean + type: Boolean, }, hideLabel: { - type: Boolean + type: Boolean, }, hideDescription: { - type: Boolean + type: Boolean, }, swapDescriptionAndLabel: { - type: Boolean + type: Boolean, }, backendDescriptionPath: { - type: [String, Array] + type: [String, Array], }, overrideBackendDescription: { - type: Boolean + type: Boolean, }, overrideBackendDescriptionLabel: { - type: [Boolean, String] + type: [Boolean, String], }, draftMode: { type: Boolean, - default: undefined + default: undefined, }, timedApplyMode: { type: Boolean, - default: false - } + default: false, + }, }, inject: { defaultSource: { - default: 'default' + default: 'default', }, defaultDraftMode: { - default: false - } + default: false, + }, }, - data () { + data() { return { - localDraft: null + localDraft: null, } }, - created () { - if (this.realDraftMode && (this.realSource !== 'admin' || this.path == null)) { + created() { + if ( + this.realDraftMode && + (this.realSource !== 'admin' || this.path == null) + ) { this.draft = cloneDeep(this.state) } }, computed: { draft: { - get () { + get() { if (this.realSource === 'admin' || this.path == null) { return get(this.$store.state.adminSettings.draft, this.canonPath) } else { return this.localDraft } }, - set (value) { + set(value) { if (this.realSource === 'admin' || this.path == null) { - this.$store.commit('updateAdminDraft', { path: this.canonPath, value }) + this.$store.commit('updateAdminDraft', { + path: this.canonPath, + value, + }) } else { this.localDraft = value } - } + }, }, - state () { + state() { if (this.path == null) { return this.modelValue } @@ -130,71 +137,93 @@ export default { return value } }, - visibleState () { + visibleState() { return this.realDraftMode ? this.draft : this.state }, - realSource () { + realSource() { return this.source || this.defaultSource }, - realDraftMode () { - return typeof this.draftMode === 'undefined' ? this.defaultDraftMode : this.draftMode + realDraftMode() { + return typeof this.draftMode === 'undefined' + ? this.defaultDraftMode + : this.draftMode }, - backendDescription () { - return get(this.$store.state.adminSettings.descriptions, this.descriptionPath) + backendDescription() { + return get( + this.$store.state.adminSettings.descriptions, + this.descriptionPath, + ) }, - backendDescriptionLabel () { + backendDescriptionLabel() { if (this.realSource !== 'admin') return '' - if (this.overrideBackendDescriptionLabel !== '' && typeof this.overrideBackendDescriptionLabel === 'string') { + if ( + this.overrideBackendDescriptionLabel !== '' && + typeof this.overrideBackendDescriptionLabel === 'string' + ) { return this.overrideBackendDescriptionLabel } if (!this.backendDescription || this.overrideBackendDescriptionLabel) { - return this.$t([ - 'admin_dash', - 'temp_overrides', - ...this.canonPath.map(p => p.replace(/\./g, '_DOT_')), - 'label' - ].join('.')) + return this.$t( + [ + 'admin_dash', + 'temp_overrides', + ...this.canonPath.map((p) => p.replace(/\./g, '_DOT_')), + 'label', + ].join('.'), + ) } else { return this.swapDescriptionAndLabel ? this.backendDescription?.description : this.backendDescription?.label } }, - backendDescriptionDescription () { + backendDescriptionDescription() { if (this.description) return this.description if (this.realSource !== 'admin') return '' if (this.hideDescription) return null if (!this.backendDescription || this.overrideBackendDescription) { - return this.$t([ - 'admin_dash', - 'temp_overrides', - ...this.canonPath.map(p => p.replace(/\./g, '_DOT_')), - 'description' - ].join('.')) + return this.$t( + [ + 'admin_dash', + 'temp_overrides', + ...this.canonPath.map((p) => p.replace(/\./g, '_DOT_')), + 'description', + ].join('.'), + ) } else { return this.swapDescriptionAndLabel ? this.backendDescription?.label : this.backendDescription?.description } }, - backendDescriptionSuggestions () { + backendDescriptionSuggestions() { return this.backendDescription?.suggestions || this.suggestions }, - shouldBeDisabled () { + shouldBeDisabled() { if (this.path == null) { return this.disabled } let parentValue = null if (this.parentPath !== undefined && this.realSource === 'admin') { if (this.realDraftMode) { - parentValue = get(this.$store.state.adminSettings.draft, this.parentPath) + parentValue = get( + this.$store.state.adminSettings.draft, + this.parentPath, + ) } else { parentValue = get(this.configSource, this.parentPath) } } - return this.disabled || (parentValue !== null ? (this.parentInvert ? parentValue : !parentValue) : false) + return ( + this.disabled || + (parentValue !== null + ? this.parentInvert + ? parentValue + : !parentValue + : false) + ) }, - configSource () { + configSource() { switch (this.realSource) { case 'profile': return this.$store.state.profileConfig @@ -204,24 +233,31 @@ export default { return this.$store.getters.mergedConfig } }, - configSink () { + configSink() { if (this.path == null) { return (k, v) => this.$emit('update:modelValue', v) } switch (this.realSource) { case 'profile': - return (k, v) => this.$store.dispatch('setProfileOption', { name: k, value: v }) + return (k, v) => + this.$store.dispatch('setProfileOption', { name: k, value: v }) case 'admin': - return (k, v) => this.$store.dispatch('pushAdminSetting', { path: k, value: v }) + return (k, v) => + this.$store.dispatch('pushAdminSetting', { path: k, value: v }) default: if (this.timedApplyMode) { - return (k, v) => this.$store.dispatch('setOptionTemporarily', { name: k, value: v }) + return (k, v) => + this.$store.dispatch('setOptionTemporarily', { + name: k, + value: v, + }) } else { - return (k, v) => this.$store.dispatch('setOption', { name: k, value: v }) + return (k, v) => + this.$store.dispatch('setOption', { name: k, value: v }) } } }, - defaultState () { + defaultState() { switch (this.realSource) { case 'profile': return {} @@ -229,10 +265,10 @@ export default { return get(this.$store.getters.defaultConfig, this.path) } }, - isProfileSetting () { + isProfileSetting() { return this.realSource === 'profile' }, - isChanged () { + isChanged() { if (this.path == null) return false switch (this.realSource) { case 'profile': @@ -242,24 +278,24 @@ export default { return this.state !== this.defaultState } }, - canonPath () { + canonPath() { if (this.path == null) return null return Array.isArray(this.path) ? this.path : this.path.split('.') }, - descriptionPath () { + descriptionPath() { if (this.path == null) return null if (this.descriptionPathOverride) return this.descriptionPathOverride const path = Array.isArray(this.path) ? this.path : this.path.split('.') if (this.subgroup) { return [ - ...path.slice(0, path.length - 1), - ':subgroup,' + this.subgroup, - ...path.slice(path.length - 1) + ...path.slice(0, path.length - 1), + ':subgroup,' + this.subgroup, + ...path.slice(path.length - 1), ] } return path }, - isDirty () { + isDirty() { if (this.path == null) return false if (this.realSource === 'admin' && this.canonPath.length > 3) { return false // should not show draft buttons for "grouped" values @@ -267,47 +303,59 @@ export default { return this.realDraftMode && !isEqual(this.draft, this.state) } }, - canHardReset () { - return this.realSource === 'admin' && this.$store.state.adminSettings.modifiedPaths?.has(this.canonPath.join(' -> ')) + canHardReset() { + return ( + this.realSource === 'admin' && + this.$store.state.adminSettings.modifiedPaths?.has( + this.canonPath.join(' -> '), + ) + ) }, - matchesExpertLevel () { + matchesExpertLevel() { const settingExpertLevel = this.expert || 0 const userToggleExpert = this.$store.state.config.expertLevel || 0 return settingExpertLevel <= userToggleExpert - } + }, }, methods: { - getValue (e) { + getValue(e) { return e.target.value }, - update (e) { + update(e) { if (this.realDraftMode) { this.draft = this.getValue(e) } else { this.configSink(this.path, this.getValue(e)) } }, - commitDraft () { + commitDraft() { if (this.realDraftMode) { this.configSink(this.path, this.draft) } }, - reset () { + reset() { if (this.realDraftMode) { this.draft = cloneDeep(this.state) } else { - set(this.$store.getters.mergedConfig, this.path, cloneDeep(this.defaultState)) + set( + this.$store.getters.mergedConfig, + this.path, + cloneDeep(this.defaultState), + ) } }, - hardReset () { + hardReset() { switch (this.realSource) { case 'admin': - return this.$store.dispatch('resetAdminSetting', { path: this.path }) - .then(() => { this.draft = this.state }) + return this.$store + .dispatch('resetAdminSetting', { path: this.path }) + .then(() => { + this.draft = this.state + }) default: console.warn('Hard reset not implemented yet!') } - } - } + }, + }, } diff --git a/src/components/settings_modal/helpers/shared_computed_object.js b/src/components/settings_modal/helpers/shared_computed_object.js index bb3d36ac4..b8197b639 100644 --- a/src/components/settings_modal/helpers/shared_computed_object.js +++ b/src/components/settings_modal/helpers/shared_computed_object.js @@ -1,19 +1,19 @@ const SharedComputedObject = () => ({ - user () { + user() { return this.$store.state.users.currentUser }, - expertLevel () { + expertLevel() { return this.$store.getters.mergedConfig.expertLevel > 0 }, - mergedConfig () { + mergedConfig() { return this.$store.getters.mergedConfig }, - adminConfig () { + adminConfig() { return this.$store.state.adminSettings.config }, - adminDraft () { + adminDraft() { return this.$store.state.adminSettings.draft - } + }, }) export default SharedComputedObject diff --git a/src/components/settings_modal/helpers/string_setting.js b/src/components/settings_modal/helpers/string_setting.js index b368cfc8c..327654ff1 100644 --- a/src/components/settings_modal/helpers/string_setting.js +++ b/src/components/settings_modal/helpers/string_setting.js @@ -1,5 +1,5 @@ import Setting from './setting.js' export default { - ...Setting + ...Setting, } diff --git a/src/components/settings_modal/helpers/tuple_setting.js b/src/components/settings_modal/helpers/tuple_setting.js index f6f4753c0..88019c901 100644 --- a/src/components/settings_modal/helpers/tuple_setting.js +++ b/src/components/settings_modal/helpers/tuple_setting.js @@ -4,13 +4,13 @@ export default { ...Setting, methods: { ...Setting.methods, - getValue ({ e, side }) { + getValue({ e, side }) { const [a, b] = this.visibleState || [] if (side === 0) { - return { tuple: [e.target.value, b]} + return { tuple: [e.target.value, b] } } else { - return { tuple: [a, e.target.value]} + return { tuple: [a, e.target.value] } } - } - } + }, + }, } diff --git a/src/components/settings_modal/helpers/unit_setting.js b/src/components/settings_modal/helpers/unit_setting.js index daeddd813..99f4ac38e 100644 --- a/src/components/settings_modal/helpers/unit_setting.js +++ b/src/components/settings_modal/helpers/unit_setting.js @@ -1,7 +1,23 @@ import Select from 'src/components/select/select.vue' import Setting from './setting.js' -export const allCssUnits = ['cm', 'mm', 'in', 'px', 'pt', 'pc', 'em', 'ex', 'ch', 'rem', 'vw', 'vh', 'vmin', 'vmax', '%'] +export const allCssUnits = [ + 'cm', + 'mm', + 'in', + 'px', + 'pt', + 'pc', + 'em', + 'ex', + 'ch', + 'rem', + 'vw', + 'vh', + 'vmin', + 'vmax', + '%', +] export const defaultHorizontalUnits = ['px', 'rem', 'vw'] export const defaultVerticalUnits = ['px', 'rem', 'vh'] @@ -9,47 +25,51 @@ export default { ...Setting, components: { ...Setting.components, - Select + Select, }, props: { ...Setting.props, min: Number, units: { type: Array, - default: () => allCssUnits + default: () => allCssUnits, }, unitSet: { type: String, - default: 'none' + default: 'none', }, step: { type: Number, - default: 1 + default: 1, }, resetDefault: { type: Object, - default: null - } + default: null, + }, }, computed: { ...Setting.computed, - stateUnit () { - return typeof this.state === 'string' ? this.state.replace(/[0-9,.]+/, '') : '' + stateUnit() { + return typeof this.state === 'string' + ? this.state.replace(/[0-9,.]+/, '') + : '' + }, + stateValue() { + return typeof this.state === 'string' + ? this.state.replace(/[^0-9,.]+/, '') + : '' }, - stateValue () { - return typeof this.state === 'string' ? this.state.replace(/[^0-9,.]+/, '') : '' - } }, methods: { ...Setting.methods, - getUnitString (value) { + getUnitString(value) { if (this.unitSet === 'none') return value return this.$t(['settings', 'units', this.unitSet, value].join('.')) }, - updateValue (e) { + updateValue(e) { this.configSink(this.path, parseFloat(e.target.value) + this.stateUnit) }, - updateUnit (e) { + updateUnit(e) { let value = this.stateValue const newUnit = e.target.value if (this.resetDefault) { @@ -59,6 +79,6 @@ export default { } } this.configSink(this.path, value + newUnit) - } - } + }, + }, } diff --git a/src/components/settings_modal/helpers/vertical_tab_switcher.jsx b/src/components/settings_modal/helpers/vertical_tab_switcher.jsx index de0e1af26..8f9112438 100644 --- a/src/components/settings_modal/helpers/vertical_tab_switcher.jsx +++ b/src/components/settings_modal/helpers/vertical_tab_switcher.jsx @@ -8,7 +8,7 @@ import { FontAwesomeIcon as FAIcon } from '@fortawesome/vue-fontawesome' import './vertical_tab_switcher.scss' import { useInterfaceStore } from 'src/stores/interface' -const findFirstUsable = (slots) => slots.findIndex(_ => _.props) +const findFirstUsable = (slots) => slots.findIndex((_) => _.props) export default { name: 'VerticalTabSwitcher', @@ -16,30 +16,30 @@ export default { renderOnlyFocused: { required: false, type: Boolean, - default: false + default: false, }, onSwitch: { required: false, type: Function, - default: undefined + default: undefined, }, activeTab: { required: false, type: String, - default: undefined + default: undefined, }, bodyScrollLock: { required: false, type: Boolean, - default: false + default: false, }, parentCollapsed: { required: false, type: Boolean, - default: null - } + default: null, + }, }, - data () { + data() { return { active: findFirstUsable(this.slots()), resizeHandler: null, @@ -47,82 +47,89 @@ export default { } }, computed: { - activeIndex () { + activeIndex() { // In case of controlled component if (this.activeTab) { - return this.slots().findIndex(slot => slot && slot.props && this.activeTab === slot.props.key) + return this.slots().findIndex( + (slot) => slot && slot.props && this.activeTab === slot.props.key, + ) } else { return this.active } }, - isActive () { - return tabName => { - const isWanted = slot => slot.props && slot.props['data-tab-name'] === tabName + isActive() { + return (tabName) => { + const isWanted = (slot) => + slot.props && slot.props['data-tab-name'] === tabName return this.$slots.default().findIndex(isWanted) === this.activeIndex } }, ...mapPiniaState(useInterfaceStore, { - mobileLayout: store => store.layoutType === 'mobile' + mobileLayout: (store) => store.layoutType === 'mobile', }), }, - beforeUpdate () { + beforeUpdate() { const currentSlot = this.slots()[this.active] if (!currentSlot.props) { this.active = findFirstUsable(this.slots()) } }, methods: { - clickTab (index) { + clickTab(index) { return (e) => { e.preventDefault() this.setTab(index) } }, - setTab (index) { + setTab(index) { if (typeof this.onSwitch === 'function') { this.onSwitch.call(null, this.slots()[index].key) } this.active = index this.changeNavSide('content') }, - changeNavSide (side) { + changeNavSide(side) { if (this.navSide !== side) { this.navSide = side } }, // DO NOT put it to computed, it doesn't work (caching?) - slots () { + slots() { if (this.$slots.default()[0].type === Fragment) { return this.$slots.default()[0].children } return this.$slots.default() - } + }, }, - render () { - const tabs = this.slots() - .map((slot, index) => { - const props = slot.props - if (!props) return - const classesTab = ['vertical-tab', 'menu-item'] - if (this.activeIndex === index && useInterfaceStore().layoutType !== 'mobile') { - classesTab.push('-active') - } - return ( - - ) - }) + render() { + const tabs = this.slots().map((slot, index) => { + const props = slot.props + if (!props) return + const classesTab = ['vertical-tab', 'menu-item'] + if ( + this.activeIndex === index && + useInterfaceStore().layoutType !== 'mobile' + ) { + classesTab.push('-active') + } + return ( + + ) + }) const contents = this.slots().map((slot, index) => { const props = slot.props @@ -134,9 +141,8 @@ export default { slot.props['delay-render'] = false delayRender = false } - const renderSlot = (!delayRender && (!this.renderOnlyFocused || active)) - ? slot - : '' + const renderSlot = + !delayRender && (!this.renderOnlyFocused || active) ? slot : '' const headerClasses = ['tab-content-label'] const header = ( @@ -147,17 +153,16 @@ export default { title={this.$t('nav.back')} class="button-unstyled" > - + {props.label} ) - const wrapperClasses = ['tab-content-wrapper', active ? '-active' : '-hidden' ] + const wrapperClasses = [ + 'tab-content-wrapper', + active ? '-active' : '-hidden', + ] const contentClasses = ['tab-content'] if (props['full-width'] || props['full-width'] === '') { contentClasses.push('-full-width') @@ -168,14 +173,10 @@ export default { wrapperClasses.push('-full-height') } return ( -
-
- {header} -
+
+
{header}
-
- {renderSlot} -
+
{renderSlot}
) @@ -193,12 +194,8 @@ export default { } return ( -
-
+
+
{tabs}
) - } + }, } diff --git a/src/components/settings_modal/settings_modal.js b/src/components/settings_modal/settings_modal.js index 789ebd0ee..f51f2107a 100644 --- a/src/components/settings_modal/settings_modal.js +++ b/src/components/settings_modal/settings_modal.js @@ -10,17 +10,15 @@ import { cloneDeep, isEqual } from 'lodash' import { mapState, mapActions } from 'pinia' import { newImporter, - newExporter + newExporter, } from 'src/services/export_import/export_import.js' import { faTimes, faFileUpload, faFileDownload, - faChevronDown + faChevronDown, } from '@fortawesome/free-solid-svg-icons' -import { - faWindowMinimize -} from '@fortawesome/free-regular-svg-icons' +import { faWindowMinimize } from '@fortawesome/free-regular-svg-icons' import { useInterfaceStore } from 'src/stores/interface' const PLEROMAFE_SETTINGS_MAJOR_VERSION = 1 @@ -31,25 +29,25 @@ library.add( faWindowMinimize, faFileUpload, faFileDownload, - faChevronDown + faChevronDown, ) const SettingsModal = { - data () { + data() { return { dataImporter: newImporter({ validator: this.importValidator, onImport: this.onImport, - onImportFailure: this.onImportFailure + onImportFailure: this.onImportFailure, }), dataThemeExporter: newExporter({ filename: 'pleromafe_settings.full', - getExportedObject: () => this.generateExport(true) + getExportedObject: () => this.generateExport(true), }), dataExporter: newExporter({ filename: 'pleromafe_settings', - getExportedObject: () => this.generateExport() - }) + getExportedObject: () => this.generateExport(), + }), } }, components: { @@ -62,29 +60,29 @@ const SettingsModal = { { loadingComponent: PanelLoading, errorComponent: AsyncComponentError, - delay: 0 - } + delay: 0, + }, ), SettingsModalAdminContent: getResettableAsyncComponent( () => import('./settings_modal_admin_content.vue'), { loadingComponent: PanelLoading, errorComponent: AsyncComponentError, - delay: 0 - } - ) + delay: 0, + }, + ), }, methods: { - closeModal () { + closeModal() { useInterfaceStore().closeSettingsModal() }, - peekModal () { + peekModal() { useInterfaceStore().togglePeekSettingsModal() }, - importValidator (data) { + importValidator(data) { if (!Array.isArray(data._pleroma_settings_version)) { return { - messageKey: 'settings.file_import_export.invalid_file' + messageKey: 'settings.file_import_export.invalid_file', } } @@ -95,8 +93,8 @@ const SettingsModal = { messageKey: 'settings.file_export_import.errors.file_too_new', messageArgs: { fileMajor: major, - feMajor: PLEROMAFE_SETTINGS_MAJOR_VERSION - } + feMajor: PLEROMAFE_SETTINGS_MAJOR_VERSION, + }, } } @@ -105,94 +103,106 @@ const SettingsModal = { messageKey: 'settings.file_export_import.errors.file_too_old', messageArgs: { fileMajor: major, - feMajor: PLEROMAFE_SETTINGS_MAJOR_VERSION - } + feMajor: PLEROMAFE_SETTINGS_MAJOR_VERSION, + }, } } if (minor > PLEROMAFE_SETTINGS_MINOR_VERSION) { useInterfaceStore().pushGlobalNotice({ level: 'warning', - messageKey: 'settings.file_export_import.errors.file_slightly_new' + messageKey: 'settings.file_export_import.errors.file_slightly_new', }) } return true }, - onImportFailure (result) { + onImportFailure(result) { if (result.error) { - useInterfaceStore().pushGlobalNotice({ messageKey: 'settings.invalid_settings_imported', level: 'error' }) + useInterfaceStore().pushGlobalNotice({ + messageKey: 'settings.invalid_settings_imported', + level: 'error', + }) } else { - useInterfaceStore().pushGlobalNotice({ ...result.validationResult, level: 'error' }) + useInterfaceStore().pushGlobalNotice({ + ...result.validationResult, + level: 'error', + }) } }, - onImport (data) { - if (data) { this.$store.dispatch('loadSettings', data) } + onImport(data) { + if (data) { + this.$store.dispatch('loadSettings', data) + } }, - restore () { + restore() { this.dataImporter.importData() }, - backup () { + backup() { this.dataExporter.exportData() }, - backupWithTheme () { + backupWithTheme() { this.dataThemeExporter.exportData() }, - generateExport (theme = false) { + generateExport(theme = false) { const { config } = this.$store.state let sample = config if (!theme) { const ignoreList = new Set([ 'customTheme', 'customThemeSource', - 'colors' + 'colors', ]) sample = Object.fromEntries( - Object - .entries(sample) - .filter(([key]) => !ignoreList.has(key)) + Object.entries(sample).filter(([key]) => !ignoreList.has(key)), ) } const clone = cloneDeep(sample) clone._pleroma_settings_version = [ PLEROMAFE_SETTINGS_MAJOR_VERSION, - PLEROMAFE_SETTINGS_MINOR_VERSION + PLEROMAFE_SETTINGS_MINOR_VERSION, ] return clone }, - resetAdminDraft () { + resetAdminDraft() { this.$store.commit('resetAdminDraft') }, - pushAdminDraft () { + pushAdminDraft() { this.$store.dispatch('pushAdminDraft') }, - ...mapActions(useInterfaceStore, ['temporaryChangesRevert', 'temporaryChangesConfirm']) + ...mapActions(useInterfaceStore, [ + 'temporaryChangesRevert', + 'temporaryChangesConfirm', + ]), }, computed: { ...mapState(useInterfaceStore, { - temporaryChangesCountdown: store => store.temporaryChangesCountdown, - currentSaveStateNotice: store => store.settings.currentSaveStateNotice, - modalActivated: store => store.settingsModalState !== 'hidden', - modalMode: store => store.settingsModalMode, - modalOpenedOnceUser: store => store.settingsModalLoadedUser, - modalOpenedOnceAdmin: store => store.settingsModalLoadedAdmin, - modalPeeked: store => store.settingsModalState === 'minimized' + temporaryChangesCountdown: (store) => store.temporaryChangesCountdown, + currentSaveStateNotice: (store) => store.settings.currentSaveStateNotice, + modalActivated: (store) => store.settingsModalState !== 'hidden', + modalMode: (store) => store.settingsModalMode, + modalOpenedOnceUser: (store) => store.settingsModalLoadedUser, + modalOpenedOnceAdmin: (store) => store.settingsModalLoadedAdmin, + modalPeeked: (store) => store.settingsModalState === 'minimized', }), expertLevel: { - get () { + get() { return this.$store.state.config.expertLevel > 0 }, - set (value) { - this.$store.dispatch('setOption', { name: 'expertLevel', value: value ? 1 : 0 }) - } + set(value) { + this.$store.dispatch('setOption', { + name: 'expertLevel', + value: value ? 1 : 0, + }) + }, }, - adminDraftAny () { + adminDraftAny() { return !isEqual( this.$store.state.adminSettings.config, - this.$store.state.adminSettings.draft + this.$store.state.adminSettings.draft, ) - } - } + }, + }, } export default SettingsModal diff --git a/src/components/settings_modal/settings_modal_admin_content.js b/src/components/settings_modal/settings_modal_admin_content.js index 77a3d6b81..f1902eae9 100644 --- a/src/components/settings_modal/settings_modal_admin_content.js +++ b/src/components/settings_modal/settings_modal_admin_content.js @@ -36,7 +36,7 @@ import { faUpload, faMessage, faEllipsis, - faGauge + faGauge, } from '@fortawesome/free-solid-svg-icons' library.add( @@ -55,7 +55,7 @@ library.add( faUpload, faMessage, faEllipsis, - faGauge + faGauge, ) const SettingsModalAdminContent = { @@ -78,44 +78,46 @@ const SettingsModalAdminContent = { MonitoringTab, RatesTab, OtherTab, - PostsTab + PostsTab, }, computed: { - user () { + user() { return this.$store.state.users.currentUser }, - isLoggedIn () { + isLoggedIn() { return !!this.$store.state.users.currentUser }, - open () { + open() { return useInterfaceStore().settingsModalState !== 'hidden' }, - bodyLock () { + bodyLock() { return useInterfaceStore().settingsModalState === 'visible' }, - adminDbLoaded () { + adminDbLoaded() { return this.$store.state.adminSettings.loaded }, - adminDescriptionsLoaded () { + adminDescriptionsLoaded() { return this.$store.state.adminSettings.descriptions !== null }, - noDb () { + noDb() { return this.$store.state.adminSettings.dbConfigEnabled === false - } + }, }, - created () { + created() { if (this.user.rights.admin) { this.$store.dispatch('loadAdminStuff') } }, methods: { - onOpen () { + onOpen() { const targetTab = useInterfaceStore().settingsModalTargetTab // We're being told to open in specific tab if (targetTab) { - const tabIndex = this.$refs.tabSwitcher.$slots.default().findIndex(elm => { - return elm.props && elm.props['data-tab-name'] === targetTab - }) + const tabIndex = this.$refs.tabSwitcher.$slots + .default() + .findIndex((elm) => { + return elm.props && elm.props['data-tab-name'] === targetTab + }) if (tabIndex >= 0) { this.$refs.tabSwitcher.setTab(tabIndex) } @@ -123,16 +125,16 @@ const SettingsModalAdminContent = { // Clear the state of target tab, so that next time settings is opened // it doesn't force it. useInterfaceStore().clearSettingsModalTargetTab() - } + }, }, - mounted () { + mounted() { this.onOpen() }, watch: { open: function (value) { if (value) this.onOpen() - } - } + }, + }, } export default SettingsModalAdminContent diff --git a/src/components/settings_modal/settings_modal_user_content.js b/src/components/settings_modal/settings_modal_user_content.js index 51718ab26..201e3b8be 100644 --- a/src/components/settings_modal/settings_modal_user_content.js +++ b/src/components/settings_modal/settings_modal_user_content.js @@ -31,7 +31,7 @@ import { faCode, faBroom, faLock, - faColumns + faColumns, } from '@fortawesome/free-solid-svg-icons' import { useInterfaceStore } from 'src/stores/interface' @@ -49,7 +49,7 @@ library.add( faDownload, faPalette, faPaintBrush, - faCode + faCode, ) const SettingsModalContent = { @@ -70,36 +70,38 @@ const SettingsModalContent = { AppearanceTab, StyleTab, DeveloperTab, - OldThemeTab + OldThemeTab, }, computed: { - isLoggedIn () { + isLoggedIn() { return !!this.$store.state.users.currentUser }, - open () { + open() { return useInterfaceStore().settingsModalState !== 'hidden' }, - bodyLock () { + bodyLock() { return useInterfaceStore().settingsModalState === 'visible' }, - expertLevel () { + expertLevel() { return this.$store.state.config.expertLevel - } + }, }, - data () { + data() { return { navCollapsed: false, - navHideHeader: false + navHideHeader: false, } }, methods: { - onOpen () { + onOpen() { const targetTab = useInterfaceStore().settingsModalTargetTab // We're being told to open in specific tab if (targetTab) { - const tabIndex = this.$refs.tabSwitcher.$slots.default().findIndex(elm => { - return elm.props && elm.props['data-tab-name'] === targetTab - }) + const tabIndex = this.$refs.tabSwitcher.$slots + .default() + .findIndex((elm) => { + return elm.props && elm.props['data-tab-name'] === targetTab + }) if (tabIndex >= 0) { this.$refs.tabSwitcher.setTab(tabIndex) } @@ -107,16 +109,16 @@ const SettingsModalContent = { // Clear the state of target tab, so that next time settings is opened // it doesn't force it. useInterfaceStore().clearSettingsModalTargetTab() - } + }, }, - mounted () { + mounted() { this.onOpen() }, watch: { open: function (value) { if (value) this.onOpen() - } - } + }, + }, } export default SettingsModalContent diff --git a/src/components/settings_modal/tabs/appearance_tab.js b/src/components/settings_modal/tabs/appearance_tab.js index db37774aa..48fa839b8 100644 --- a/src/components/settings_modal/tabs/appearance_tab.js +++ b/src/components/settings_modal/tabs/appearance_tab.js @@ -9,11 +9,12 @@ import Preview from './old_theme_tab/theme_preview.vue' import { newImporter } from 'src/services/export_import/export_import.js' import { convertTheme2To3 } from 'src/services/theme_data/theme2_to_theme3.js' import { init } from 'src/services/theme_data/theme_data_3.service.js' -import { - getCssRules -} from 'src/services/theme_data/css_utils.js' +import { getCssRules } from 'src/services/theme_data/css_utils.js' import { deserialize } from 'src/services/theme_data/iss_deserializer.js' -import { createStyleSheet, adoptStyleSheets } from 'src/services/style_setter/style_setter.js' +import { + createStyleSheet, + adoptStyleSheets, +} from 'src/services/style_setter/style_setter.js' import fileSizeFormatService from 'src/components/../services/file_size_format/file_size_format.js' import SharedComputedObject from '../helpers/shared_computed_object.js' @@ -23,7 +24,7 @@ import { mapActions } from 'pinia' import { useInterfaceStore, normalizeThemeData } from 'src/stores/interface' const AppearanceTab = { - data () { + data() { return { availableThemesV3: [], availableThemesV2: [], @@ -34,7 +35,7 @@ const AppearanceTab = { validator: this.importValidator, onImport: this.onImport, parser: this.importParser, - onImportFailure: this.onImportFailure + onImportFailure: this.onImportFailure, }), palettesKeys: [ 'bg', @@ -44,19 +45,25 @@ const AppearanceTab = { 'cRed', 'cGreen', 'cBlue', - 'cOrange' + 'cOrange', ], userPalette: {}, intersectionObserver: null, - forcedRoundnessOptions: ['disabled', 'sharp', 'nonsharp', 'round'].map((mode, i) => ({ - key: mode, - value: i - 1, - label: this.$t(`settings.style.themes3.hacks.forced_roundness_mode_${mode}`) - })), + forcedRoundnessOptions: ['disabled', 'sharp', 'nonsharp', 'round'].map( + (mode, i) => ({ + key: mode, + value: i - 1, + label: this.$t( + `settings.style.themes3.hacks.forced_roundness_mode_${mode}`, + ), + }), + ), underlayOverrideModes: ['none', 'opaque', 'transparent'].map((mode) => ({ key: mode, value: mode, - label: this.$t(`settings.style.themes3.hacks.underlay_override_mode_${mode}`) + label: this.$t( + `settings.style.themes3.hacks.underlay_override_mode_${mode}`, + ), })), backgroundUploading: false, background: null, @@ -71,9 +78,9 @@ const AppearanceTab = { UnitSetting, ProfileSettingIndicator, Preview, - PaletteEditor + PaletteEditor, }, - mounted () { + mounted() { useInterfaceStore().getThemeData() const updateIndex = (resource) => { @@ -87,120 +94,151 @@ const AppearanceTab = { promise = useInterfaceStore()[`fetch${capitalizedResource}sIndex`]() } - return promise.then(index => { - return Object - .entries(index) - .map(([k, func]) => [k, func()]) + return promise.then((index) => { + return Object.entries(index).map(([k, func]) => [k, func()]) }) } - updateIndex('style').then(styles => { - styles.forEach(([key, stylePromise]) => stylePromise.then(data => { - const meta = data.find(x => x.component === '@meta') - this.availableThemesV3.push({ key, data, name: meta.directives.name, version: 'v3' }) - })) + updateIndex('style').then((styles) => { + styles.forEach(([key, stylePromise]) => + stylePromise.then((data) => { + const meta = data.find((x) => x.component === '@meta') + this.availableThemesV3.push({ + key, + data, + name: meta.directives.name, + version: 'v3', + }) + }), + ) }) - updateIndex('theme').then(themes => { - themes.forEach(([key, themePromise]) => themePromise.then(data => { - if (!data) { - console.warn(`Theme with key ${key} is empty or malformed`) - } else if (Array.isArray(data)) { - console.warn(`Theme with key ${key} is a v1 theme and should be moved to static/palettes/index.json`) - } else if (!data.source && !data.theme) { - console.warn(`Theme with key ${key} is malformed`) - } else { - this.availableThemesV2.push({ key, data, name: data.name, version: 'v2' }) - } - })) + updateIndex('theme').then((themes) => { + themes.forEach(([key, themePromise]) => + themePromise.then((data) => { + if (!data) { + console.warn(`Theme with key ${key} is empty or malformed`) + } else if (Array.isArray(data)) { + console.warn( + `Theme with key ${key} is a v1 theme and should be moved to static/palettes/index.json`, + ) + } else if (!data.source && !data.theme) { + console.warn(`Theme with key ${key} is malformed`) + } else { + this.availableThemesV2.push({ + key, + data, + name: data.name, + version: 'v2', + }) + } + }), + ) }) this.userPalette = useInterfaceStore().paletteDataUsed || {} - updateIndex('palette').then(bundledPalettes => { - bundledPalettes.forEach(([key, palettePromise]) => palettePromise.then(v => { - let palette - if (Array.isArray(v)) { - const [ - name, - bg, - fg, - text, - link, - cRed = '#FF0000', - cGreen = '#00FF00', - cBlue = '#0000FF', - cOrange = '#E3FF00' - ] = v - palette = { key, name, bg, fg, text, link, cRed, cBlue, cGreen, cOrange } - } else { - palette = { key, ...v } - } - if (!palette.key.startsWith('style.')) { - this.bundledPalettes.push(palette) - } - })) + updateIndex('palette').then((bundledPalettes) => { + bundledPalettes.forEach(([key, palettePromise]) => + palettePromise.then((v) => { + let palette + if (Array.isArray(v)) { + const [ + name, + bg, + fg, + text, + link, + cRed = '#FF0000', + cGreen = '#00FF00', + cBlue = '#0000FF', + cOrange = '#E3FF00', + ] = v + palette = { + key, + name, + bg, + fg, + text, + link, + cRed, + cBlue, + cGreen, + cOrange, + } + } else { + palette = { key, ...v } + } + if (!palette.key.startsWith('style.')) { + this.bundledPalettes.push(palette) + } + }), + ) }) this.previewTheme('stock', 'v3') if (window.IntersectionObserver) { - this.intersectionObserver = new IntersectionObserver((entries, observer) => { - entries.forEach(({ target, isIntersecting }) => { - if (!isIntersecting) return - const theme = this.availableStyles.find(x => x.key === target.dataset.themeKey) - this.$nextTick(() => { - if (theme) this.previewTheme(theme.key, theme.version, theme.data) + this.intersectionObserver = new IntersectionObserver( + (entries, observer) => { + entries.forEach(({ target, isIntersecting }) => { + if (!isIntersecting) return + const theme = this.availableStyles.find( + (x) => x.key === target.dataset.themeKey, + ) + this.$nextTick(() => { + if (theme) this.previewTheme(theme.key, theme.version, theme.data) + }) + observer.unobserve(target) }) - observer.unobserve(target) - }) - }, { - root: this.$refs.themeList - }) + }, + { + root: this.$refs.themeList, + }, + ) } else { - this.availableStyles.forEach(theme => this.previewTheme(theme.key, theme.version, theme.data)) + this.availableStyles.forEach((theme) => + this.previewTheme(theme.key, theme.version, theme.data), + ) } }, - updated () { + updated() { this.$nextTick(() => { - this.$refs.themeList.querySelectorAll('.theme-preview').forEach(node => { - this.intersectionObserver.observe(node) - }) + this.$refs.themeList + .querySelectorAll('.theme-preview') + .forEach((node) => { + this.intersectionObserver.observe(node) + }) }) }, watch: { - paletteDataUsed () { + paletteDataUsed() { this.userPalette = this.paletteDataUsed || {} - } + }, }, computed: { - isDefaultBackground () { - return !(this.$store.state.users.currentUser.background_image) + isDefaultBackground() { + return !this.$store.state.users.currentUser.background_image }, - switchInProgress () { + switchInProgress() { return useInterfaceStore().themeChangeInProgress }, - paletteDataUsed () { + paletteDataUsed() { return useInterfaceStore().paletteDataUsed }, - availableStyles () { - return [ - ...this.availableThemesV3, - ...this.availableThemesV2 - ] + availableStyles() { + return [...this.availableThemesV3, ...this.availableThemesV2] }, - availablePalettes () { - return [ - ...this.bundledPalettes, - ...this.stylePalettes - ] + availablePalettes() { + return [...this.bundledPalettes, ...this.stylePalettes] }, - stylePalettes () { + stylePalettes() { const ruleset = useInterfaceStore().styleDataUsed || [] if (!ruleset && ruleset.length === 0) return - const meta = ruleset.find(x => x.component === '@meta') - const result = ruleset.filter(x => x.component.startsWith('@palette')) - .map(x => { + const meta = ruleset.find((x) => x.component === '@meta') + const result = ruleset + .filter((x) => x.component.startsWith('@palette')) + .map((x) => { const { variant, directives } = x const { bg, @@ -212,7 +250,7 @@ const AppearanceTab = { cBlue, cGreen, cOrange, - wallpaper + wallpaper, } = directives const result = { @@ -227,94 +265,103 @@ const AppearanceTab = { cBlue, cGreen, cOrange, - wallpaper + wallpaper, } return Object.fromEntries(Object.entries(result).filter(([, v]) => v)) }) return result }, - noIntersectionObserver () { + noIntersectionObserver() { return !window.IntersectionObserver }, - instanceWallpaper () { + instanceWallpaper() { this.$store.state.instance.background }, - instanceWallpaperUsed () { - return this.$store.state.instance.background && + instanceWallpaperUsed() { + return ( + this.$store.state.instance.background && !this.$store.state.users.currentUser.background_image + ) }, - customThemeVersion () { + customThemeVersion() { const { themeVersion } = useInterfaceStore() return themeVersion }, - isCustomThemeUsed () { + isCustomThemeUsed() { const { customTheme, customThemeSource } = this.mergedConfig return customTheme != null || customThemeSource != null }, - isCustomStyleUsed () { + isCustomStyleUsed() { const { styleCustomData } = this.mergedConfig return styleCustomData != null }, - ...SharedComputedObject() + ...SharedComputedObject(), }, methods: { - importFile () { + importFile() { this.fileImporter.importData() }, - importParser (file, filename) { + importParser(file, filename) { if (filename.endsWith('.json')) { return JSON.parse(file) } else if (filename.endsWith('.iss')) { return deserialize(file) } }, - onImport (parsed, filename) { + onImport(parsed, filename) { if (filename.endsWith('.json')) { useInterfaceStore().setThemeCustom(parsed.source || parsed.theme) } else if (filename.endsWith('.iss')) { useInterfaceStore().setStyleCustom(parsed) } }, - onImportFailure (result) { + onImportFailure(result) { console.error('Failure importing theme:', result) - useInterfaceStore().pushGlobalNotice({ messageKey: 'settings.invalid_theme_imported', level: 'error' }) + useInterfaceStore().pushGlobalNotice({ + messageKey: 'settings.invalid_theme_imported', + level: 'error', + }) }, - importValidator (parsed, filename) { + importValidator(parsed, filename) { if (filename.endsWith('.json')) { const version = parsed._pleroma_theme_version return version >= 1 || version <= 2 } else if (filename.endsWith('.iss')) { if (!Array.isArray(parsed)) return false if (parsed.length < 1) return false - if (parsed.find(x => x.component === '@meta') == null) return false + if (parsed.find((x) => x.component === '@meta') == null) return false return true } }, - isThemeActive (key) { - return key === (this.mergedConfig.theme || this.$store.state.instance.theme) + isThemeActive(key) { + return ( + key === (this.mergedConfig.theme || this.$store.state.instance.theme) + ) }, - isStyleActive (key) { - return key === (this.mergedConfig.style || this.$store.state.instance.style) + isStyleActive(key) { + return ( + key === (this.mergedConfig.style || this.$store.state.instance.style) + ) }, - isPaletteActive (key) { - return key === (this.mergedConfig.palette || this.$store.state.instance.palette) + isPaletteActive(key) { + return ( + key === + (this.mergedConfig.palette || this.$store.state.instance.palette) + ) }, - ...mapActions(useInterfaceStore, [ - 'setStyle', - 'setTheme' - ]), - setPalette (name, data) { + ...mapActions(useInterfaceStore, ['setStyle', 'setTheme']), + setPalette(name, data) { useInterfaceStore().setPalette(name) this.userPalette = data }, - setPaletteCustom (data) { + setPaletteCustom(data) { useInterfaceStore().setPaletteCustom(data) this.userPalette = data }, - resetTheming () { + resetTheming() { useInterfaceStore().setStyle('stock') }, - previewTheme (key, version, input) { + previewTheme(key, version, input) { let theme3 if (this.compilationCache[key]) { theme3 = this.compilationCache[key] @@ -327,10 +374,10 @@ const AppearanceTab = { ultimateBackgroundColor: '#000000', liteMode: true, debug: true, - onlyNormalState: true + onlyNormalState: true, }) } else if (version === 'v3') { - const palette = input.find(x => x.component === '@palette') + const palette = input.find((x) => x.component === '@palette') let paletteRule if (palette) { const { directives } = palette @@ -339,21 +386,20 @@ const AppearanceTab = { paletteRule = { component: 'Root', directives: Object.fromEntries( - Object - .entries(directives) + Object.entries(directives) .filter(([k]) => k && k !== 'name') - .map(([k, v]) => ['--' + k, 'color | ' + v]) - ) + .map(([k, v]) => ['--' + k, 'color | ' + v]), + ), } } else { paletteRule = null } theme3 = init({ - inputRuleset: [...input, paletteRule].filter(x => x), + inputRuleset: [...input, paletteRule].filter((x) => x), ultimateBackgroundColor: '#000000', liteMode: true, - onlyNormalState: true + onlyNormalState: true, }) } } else { @@ -361,7 +407,7 @@ const AppearanceTab = { inputRuleset: [], ultimateBackgroundColor: '#000000', liteMode: true, - onlyNormalState: true + onlyNormalState: true, }) } @@ -369,22 +415,29 @@ const AppearanceTab = { this.compilationCache[key] = theme3 } - const sheet = createStyleSheet('appearance-tab-previews', 90) - sheet.addRule([ - '#theme-preview-', key, ' {\n', - getCssRules(theme3.eager).join('\n'), - '\n}' - ].join('')) + sheet.addRule( + [ + '#theme-preview-', + key, + ' {\n', + getCssRules(theme3.eager).join('\n'), + '\n}', + ].join(''), + ) sheet.ready = true adoptStyleSheets() }, - uploadFile (slot, e) { + uploadFile(slot, e) { const file = e.target.files[0] - if (!file) { return } + if (!file) { + return + } if (file.size > this.$store.state.instance[slot + 'limit']) { const filesize = fileSizeFormatService.fileSizeFormat(file.size) - const allowedsize = fileSizeFormatService.fileSizeFormat(this.$store.state.instance[slot + 'limit']) + const allowedsize = fileSizeFormatService.fileSizeFormat( + this.$store.state.instance[slot + 'limit'], + ) useInterfaceStore().pushGlobalNotice({ messageKey: 'upload.error.message', messageArgs: [ @@ -392,10 +445,10 @@ const AppearanceTab = { filesize: filesize.num, filesizeunit: filesize.unit, allowedsize: allowedsize.num, - allowedsizeunit: allowedsize.unit - }) + allowedsizeunit: allowedsize.unit, + }), ], - level: 'error' + level: 'error', }) return } @@ -408,29 +461,36 @@ const AppearanceTab = { } reader.readAsDataURL(file) }, - resetBackground () { - const confirmed = window.confirm(this.$t('settings.reset_background_confirm')) + resetBackground() { + const confirmed = window.confirm( + this.$t('settings.reset_background_confirm'), + ) if (confirmed) { this.submitBackground('') } }, - resetUploadedBackground () { + resetUploadedBackground() { this.backgroundPreview = null }, - submitBackground (background) { - if (!this.backgroundPreview && background !== '') { return } + submitBackground(background) { + if (!this.backgroundPreview && background !== '') { + return + } this.backgroundUploading = true - this.$store.state.api.backendInteractor.updateProfileImages({ background }) + this.$store.state.api.backendInteractor + .updateProfileImages({ background }) .then((data) => { this.$store.commit('addNewUsers', [data]) this.$store.commit('setCurrentUser', data) this.backgroundPreview = null }) .catch(this.displayUploadError) - .finally(() => { this.backgroundUploading = false }) + .finally(() => { + this.backgroundUploading = false + }) }, - } + }, } export default AppearanceTab diff --git a/src/components/settings_modal/tabs/clutter_tab.js b/src/components/settings_modal/tabs/clutter_tab.js index d4edf8f66..e33031d6b 100644 --- a/src/components/settings_modal/tabs/clutter_tab.js +++ b/src/components/settings_modal/tabs/clutter_tab.js @@ -1,6 +1,6 @@ import { mapState, mapActions } from 'pinia' import { mapState as mapVuexState } from 'vuex' -import { v4 as uuidv4 } from 'uuid'; +import { v4 as uuidv4 } from 'uuid' import { useServerSideStorageStore } from 'src/stores/serverSideStorage' @@ -14,7 +14,6 @@ import Select from 'src/components/select/select.vue' import SharedComputedObject from '../helpers/shared_computed_object.js' - const ClutterTab = { components: { BooleanSetting, @@ -23,23 +22,23 @@ const ClutterTab = { IntegerSetting, Checkbox, Select, - HelpIndicator + HelpIndicator, }, computed: { - instanceSpecificPanelPresent () { return this.$store.state.instance.showInstanceSpecificPanel }, + instanceSpecificPanelPresent() { + return this.$store.state.instance.showInstanceSpecificPanel + }, ...SharedComputedObject(), - ...mapState( - useServerSideStorageStore, - { - muteFilters: store => Object.entries(store.prefsStorage.simple.muteFilters), - muteFiltersObject: store => store.prefsStorage.simple.muteFilters - } - ), + ...mapState(useServerSideStorageStore, { + muteFilters: (store) => + Object.entries(store.prefsStorage.simple.muteFilters), + muteFiltersObject: (store) => store.prefsStorage.simple.muteFilters, + }), ...mapVuexState({ - blockExpirationSupported: state => state.instance.blockExpiration + blockExpirationSupported: (state) => state.instance.blockExpiration, }), onMuteDefaultActionLv1: { - get () { + get() { const value = this.$store.state.config.onMuteDefaultAction if (value === 'ask' || value === 'forever') { return value @@ -47,16 +46,19 @@ const ClutterTab = { return 'temporarily' } }, - set (value) { + set(value) { let realValue = value if (value !== 'ask' && value !== 'forever') { realValue = '14d' } - this.$store.dispatch('setOption', { name: 'onMuteDefaultAction', value: realValue }) - } + this.$store.dispatch('setOption', { + name: 'onMuteDefaultAction', + value: realValue, + }) + }, }, onBlockDefaultActionLv1: { - get () { + get() { const value = this.$store.state.config.onBlockDefaultAction if (value === 'ask' || value === 'forever') { return value @@ -64,29 +66,36 @@ const ClutterTab = { return 'temporarily' } }, - set (value) { + set(value) { let realValue = value if (value !== 'ask' && value !== 'forever') { realValue = '14d' } - this.$store.dispatch('setOption', { name: 'onBlockDefaultAction', value: realValue }) - } + this.$store.dispatch('setOption', { + name: 'onBlockDefaultAction', + value: realValue, + }) + }, }, - muteFiltersDraft () { + muteFiltersDraft() { return Object.entries(this.muteFiltersDraftObject) }, - muteFiltersExpired () { + muteFiltersExpired() { const now = Date.now() - return Object - .entries(this.muteFiltersDraftObject) - .filter(([, { expires }]) => expires != null && expires <= now) - } + return Object.entries(this.muteFiltersDraftObject).filter( + ([, { expires }]) => expires != null && expires <= now, + ) + }, }, methods: { - ...mapActions(useServerSideStorageStore, ['setPreference', 'unsetPreference', 'pushServerSideStorage']), - getDatetimeLocal (timestamp) { + ...mapActions(useServerSideStorageStore, [ + 'setPreference', + 'unsetPreference', + 'pushServerSideStorage', + ]), + getDatetimeLocal(timestamp) { const date = new Date(timestamp) - const fmt = new Intl.NumberFormat("en-US", {minimumIntegerDigits: 2}) + const fmt = new Intl.NumberFormat('en-US', { minimumIntegerDigits: 2 }) const datetime = [ date.getFullYear(), '-', @@ -96,11 +105,11 @@ const ClutterTab = { 'T', fmt.format(date.getHours()), ':', - fmt.format(date.getMinutes()) + fmt.format(date.getMinutes()), ].join('') return datetime }, - checkRegexValid (id) { + checkRegexValid(id) { const filter = this.muteFiltersObject[id] if (filter.type !== 'regexp') return true if (filter.type !== 'user_regexp') return true @@ -114,19 +123,21 @@ const ClutterTab = { } return valid }, - createFilter (filter = { - type: 'word', - value: '', - name: 'New Filter', - enabled: true, - expires: null, - hide: false, - }) { + createFilter( + filter = { + type: 'word', + value: '', + name: 'New Filter', + enabled: true, + expires: null, + hide: false, + }, + ) { const newId = uuidv4() filter.order = this.muteFilters.length + 2 this.muteFiltersDraftObject[newId] = filter - this.setPreference({ path: 'simple.muteFilters.' + newId , value: filter }) + this.setPreference({ path: 'simple.muteFilters.' + newId, value: filter }) this.pushServerSideStorage() }, exportFilter(id) { @@ -137,23 +148,23 @@ const ClutterTab = { importFilter() { this.filterImporter.importData() }, - copyFilter (id) { + copyFilter(id) { const filter = { ...this.muteFiltersDraftObject[id] } const newId = uuidv4() this.muteFiltersDraftObject[newId] = filter - this.setPreference({ path: 'simple.muteFilters.' + newId , value: filter }) + this.setPreference({ path: 'simple.muteFilters.' + newId, value: filter }) this.pushServerSideStorage() }, - deleteFilter (id) { + deleteFilter(id) { delete this.muteFiltersDraftObject[id] - this.unsetPreference({ path: 'simple.muteFilters.' + id , value: null }) + this.unsetPreference({ path: 'simple.muteFilters.' + id, value: null }) this.pushServerSideStorage() }, - purgeExpiredFilters () { + purgeExpiredFilters() { this.muteFiltersExpired.forEach(([id]) => { delete this.muteFiltersDraftObject[id] - this.unsetPreference({ path: 'simple.muteFilters.' + id , value: null }) + this.unsetPreference({ path: 'simple.muteFilters.' + id, value: null }) }) this.pushServerSideStorage() }, @@ -177,17 +188,20 @@ const ClutterTab = { this.muteFiltersDraftDirty[id] = true }, saveFilter(id) { - this.setPreference({ path: 'simple.muteFilters.' + id , value: this.muteFiltersDraftObject[id] }) + this.setPreference({ + path: 'simple.muteFilters.' + id, + value: this.muteFiltersDraftObject[id], + }) this.pushServerSideStorage() this.muteFiltersDraftDirty[id] = false }, }, // Updating nested properties watch: { - replyVisibility () { + replyVisibility() { this.$store.dispatch('queueFlushAll') - } - } + }, + }, } export default ClutterTab diff --git a/src/components/settings_modal/tabs/composing_tab.js b/src/components/settings_modal/tabs/composing_tab.js index 7efbe7047..581b4d71b 100644 --- a/src/components/settings_modal/tabs/composing_tab.js +++ b/src/components/settings_modal/tabs/composing_tab.js @@ -21,69 +21,78 @@ import { faMessage, faPenAlt, faDatabase, - faSliders + faSliders, } from '@fortawesome/free-solid-svg-icons' -library.add( - faGlobe, - faMessage, - faPenAlt, - faDatabase, - faSliders -) +library.add(faGlobe, faMessage, faPenAlt, faDatabase, faSliders) const ComposingTab = { props: { parentCollapsed: { required: true, - type: Boolean - } + type: Boolean, + }, }, - data () { + data() { return { - subjectLineOptions: ['email', 'noop', 'masto'].map(mode => ({ + subjectLineOptions: ['email', 'noop', 'masto'].map((mode) => ({ key: mode, value: mode, - label: this.$t(`settings.subject_line_${mode === 'masto' ? 'mastodon' : mode}`) + label: this.$t( + `settings.subject_line_${mode === 'masto' ? 'mastodon' : mode}`, + ), })), - conversationDisplayOptions: ['tree', 'linear'].map(mode => ({ + conversationDisplayOptions: ['tree', 'linear'].map((mode) => ({ key: mode, value: mode, - label: this.$t(`settings.conversation_display_${mode}`) + label: this.$t(`settings.conversation_display_${mode}`), })), - absoluteTime12hOptions: ['24h', '12h'].map(mode => ({ + absoluteTime12hOptions: ['24h', '12h'].map((mode) => ({ key: mode, value: mode, - label: this.$t(`settings.absolute_time_format_12h_${mode}`) + label: this.$t(`settings.absolute_time_format_12h_${mode}`), })), - conversationOtherRepliesButtonOptions: ['below', 'inside'].map(mode => ({ + conversationOtherRepliesButtonOptions: ['below', 'inside'].map( + (mode) => ({ + key: mode, + value: mode, + label: this.$t(`settings.conversation_other_replies_button_${mode}`), + }), + ), + mentionLinkDisplayOptions: ['short', 'full_for_remote', 'full'].map( + (mode) => ({ + key: mode, + value: mode, + label: this.$t(`settings.mention_link_display_${mode}`), + }), + ), + userPopoverAvatarActionOptions: ['close', 'zoom', 'open'].map((mode) => ({ key: mode, value: mode, - label: this.$t(`settings.conversation_other_replies_button_${mode}`) + label: this.$t(`settings.user_popover_avatar_action_${mode}`), })), - mentionLinkDisplayOptions: ['short', 'full_for_remote', 'full'].map(mode => ({ + unsavedPostActionOptions: ['save', 'discard', 'confirm'].map((mode) => ({ key: mode, value: mode, - label: this.$t(`settings.mention_link_display_${mode}`) - })), - userPopoverAvatarActionOptions: ['close', 'zoom', 'open'].map(mode => ({ - key: mode, - value: mode, - label: this.$t(`settings.user_popover_avatar_action_${mode}`) - })), - unsavedPostActionOptions: ['save', 'discard', 'confirm'].map(mode => ({ - key: mode, - value: mode, - label: this.$t(`settings.unsaved_post_action_${mode}`) + label: this.$t(`settings.unsaved_post_action_${mode}`), })), loopSilentAvailable: - // Firefox - Object.getOwnPropertyDescriptor(HTMLVideoElement.prototype, 'mozHasAudio') || - // Chrome-likes - Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, 'webkitAudioDecodedByteCount') || - // Future spec, still not supported in Nightly 63 as of 08/2018 - Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, 'audioTracks'), - emailLanguage: this.$store.state.users.currentUser.language || [''] + // Firefox + Object.getOwnPropertyDescriptor( + HTMLVideoElement.prototype, + 'mozHasAudio', + ) || + // Chrome-likes + Object.getOwnPropertyDescriptor( + HTMLMediaElement.prototype, + 'webkitAudioDecodedByteCount', + ) || + // Future spec, still not supported in Nightly 63 as of 08/2018 + Object.getOwnPropertyDescriptor( + HTMLMediaElement.prototype, + 'audioTracks', + ), + emailLanguage: this.$store.state.users.currentUser.language || [''], } }, components: { @@ -96,61 +105,68 @@ const ComposingTab = { ProfileSettingIndicator, ScopeSelector, Select, - FontControl + FontControl, }, computed: { - postFormats () { + postFormats() { return this.$store.state.instance.postFormats || [] }, - postContentOptions () { - return this.postFormats.map(format => ({ + postContentOptions() { + return this.postFormats.map((format) => ({ key: format, value: format, - label: this.$t(`post_status.content_type["${format}"]`) + label: this.$t(`post_status.content_type["${format}"]`), })) }, language: { - get: function () { return this.$store.getters.mergedConfig.interfaceLanguage }, + get: function () { + return this.$store.getters.mergedConfig.interfaceLanguage + }, set: function (val) { - this.$store.dispatch('setOption', { name: 'interfaceLanguage', value: val }) - } + this.$store.dispatch('setOption', { + name: 'interfaceLanguage', + value: val, + }) + }, }, ...SharedComputedObject(), ...mapState({ - blockExpirationSupported: state => state.instance.blockExpiration, - }) + blockExpirationSupported: (state) => state.instance.blockExpiration, + }), }, methods: { - changeDefaultScope (value) { + changeDefaultScope(value) { this.$store.dispatch('setProfileOption', { name: 'defaultScope', value }) }, - clearCache (key) { + clearCache(key) { clearCache(key) .then(() => { this.$store.dispatch('settingsSaved', { success: true }) }) - .catch(error => { + .catch((error) => { this.$store.dispatch('settingsSaved', { error }) }) }, - tooSmall () { + tooSmall() { this.$emit('tooSmall') }, - tooBig () { + tooBig() { this.$emit('tooBig') }, - getNavMode () { + getNavMode() { return this.$refs.tabSwitcher.getNavMode() }, - clearAssetCache () { + clearAssetCache() { this.clearCache(cacheKey) }, - clearEmojiCache () { + clearEmojiCache() { this.clearCache(emojiCacheKey) }, - updateProfile () { + updateProfile() { const params = { - language: localeService.internalToBackendLocaleMulti(this.emailLanguage) + language: localeService.internalToBackendLocaleMulti( + this.emailLanguage, + ), } this.$store.state.api.backendInteractor @@ -160,19 +176,19 @@ const ComposingTab = { this.$store.commit('setCurrentUser', user) }) }, - updateFont (key, value) { + updateFont(key, value) { this.$store.dispatch('setOption', { name: 'theme3hacks', value: { ...this.mergedConfig.theme3hacks, fonts: { ...this.mergedConfig.theme3hacks.fonts, - [key]: value - } - } + [key]: value, + }, + }, }) }, - } + }, } export default ComposingTab diff --git a/src/components/settings_modal/tabs/data_import_export_tab.js b/src/components/settings_modal/tabs/data_import_export_tab.js index 4304e59c0..5b089350f 100644 --- a/src/components/settings_modal/tabs/data_import_export_tab.js +++ b/src/components/settings_modal/tabs/data_import_export_tab.js @@ -5,81 +5,84 @@ import { mapState } from 'vuex' import { useOAuthTokensStore } from 'src/stores/oauth_tokens' const DataImportExportTab = { - data () { + data() { return { activeTab: 'profile', newDomainToMute: '', listBackupsError: false, addBackupError: false, addedBackup: false, - backups: [] + backups: [], } }, - created () { + created() { useOAuthTokensStore().fetchTokens() this.fetchBackups() }, components: { Importer, Exporter, - Checkbox + Checkbox, }, computed: { ...mapState({ backendInteractor: (state) => state.api.backendInteractor, - user: (state) => state.users.currentUser - }) + user: (state) => state.users.currentUser, + }), }, methods: { - getFollowsContent () { - return this.backendInteractor.exportFriends({ id: this.user.id }) + getFollowsContent() { + return this.backendInteractor + .exportFriends({ id: this.user.id }) .then(this.generateExportableUsersContent) }, - getBlocksContent () { - return this.backendInteractor.fetchBlocks() + getBlocksContent() { + return this.backendInteractor + .fetchBlocks() .then(this.generateExportableUsersContent) }, - getMutesContent () { - return this.backendInteractor.fetchMutes() + getMutesContent() { + return this.backendInteractor + .fetchMutes() .then(this.generateExportableUsersContent) }, - importFollows (file) { - return this.backendInteractor.importFollows({ file }) - .then((status) => { - if (!status) { - throw new Error('failed') - } - }) - }, - importBlocks (file) { - return this.backendInteractor.importBlocks({ file }) - .then((status) => { - if (!status) { - throw new Error('failed') - } - }) - }, - importMutes (file) { - return this.backendInteractor.importMutes({ file }) - .then((status) => { - if (!status) { - throw new Error('failed') - } - }) - }, - generateExportableUsersContent (users) { - // Get addresses - return users.map((user) => { - // check is it's a local user - if (user && user.is_local) { - // append the instance address - return user.screen_name + '@' + location.hostname + importFollows(file) { + return this.backendInteractor.importFollows({ file }).then((status) => { + if (!status) { + throw new Error('failed') } - return user.screen_name - }).join('\n') + }) }, - addBackup () { - this.$store.state.api.backendInteractor.addBackup() + importBlocks(file) { + return this.backendInteractor.importBlocks({ file }).then((status) => { + if (!status) { + throw new Error('failed') + } + }) + }, + importMutes(file) { + return this.backendInteractor.importMutes({ file }).then((status) => { + if (!status) { + throw new Error('failed') + } + }) + }, + generateExportableUsersContent(users) { + // Get addresses + return users + .map((user) => { + // check is it's a local user + if (user && user.is_local) { + // append the instance address + return user.screen_name + '@' + location.hostname + } + return user.screen_name + }) + .join('\n') + }, + addBackup() { + this.$store.state.api.backendInteractor + .addBackup() .then(() => { this.addedBackup = true this.addBackupError = false @@ -90,8 +93,9 @@ const DataImportExportTab = { }) .then(() => this.fetchBackups()) }, - fetchBackups () { - this.$store.state.api.backendInteractor.listBackups() + fetchBackups() { + this.$store.state.api.backendInteractor + .listBackups() .then((res) => { this.backups = res this.listBackupsError = false @@ -99,8 +103,8 @@ const DataImportExportTab = { .catch((error) => { this.listBackupsError = error.error }) - } - } + }, + }, } export default DataImportExportTab diff --git a/src/components/settings_modal/tabs/developer_tab.js b/src/components/settings_modal/tabs/developer_tab.js index f8f21e9f4..6416f717a 100644 --- a/src/components/settings_modal/tabs/developer_tab.js +++ b/src/components/settings_modal/tabs/developer_tab.js @@ -4,43 +4,44 @@ import SharedComputedObject from '../helpers/shared_computed_object.js' import { clearCache, cacheKey, emojiCacheKey } from 'src/services/sw/sw.js' -const pleromaFeCommitUrl = 'https://git.pleroma.social/pleroma/pleroma-fe/commit/' +const pleromaFeCommitUrl = + 'https://git.pleroma.social/pleroma/pleroma-fe/commit/' const VersionTab = { - data () { + data() { const instance = this.$store.state.instance return { backendVersion: instance.backendVersion, backendRepository: instance.backendRepository, - frontendVersion: instance.frontendVersion + frontendVersion: instance.frontendVersion, } }, components: { - BooleanSetting + BooleanSetting, }, computed: { - frontendVersionLink () { + frontendVersionLink() { return pleromaFeCommitUrl + this.frontendVersion }, ...SharedComputedObject(), }, methods: { - clearAssetCache () { + clearAssetCache() { this.clearCache(cacheKey) }, - clearEmojiCache () { + clearEmojiCache() { this.clearCache(emojiCacheKey) }, - clearCache (key) { + clearCache(key) { clearCache(key) .then(() => { this.$store.dispatch('settingsSaved', { success: true }) }) - .catch(error => { + .catch((error) => { this.$store.dispatch('settingsSaved', { error }) }) - } - } + }, + }, } export default VersionTab diff --git a/src/components/settings_modal/tabs/filtering_tab.js b/src/components/settings_modal/tabs/filtering_tab.js index 594ce3f58..150b22ac6 100644 --- a/src/components/settings_modal/tabs/filtering_tab.js +++ b/src/components/settings_modal/tabs/filtering_tab.js @@ -1,14 +1,14 @@ import { cloneDeep } from 'lodash' import { mapState, mapActions } from 'pinia' import { mapState as mapVuexState } from 'vuex' -import { v4 as uuidv4 } from 'uuid'; +import { v4 as uuidv4 } from 'uuid' import { useServerSideStorageStore } from 'src/stores/serverSideStorage' import { useInterfaceStore } from 'src/stores/interface' import { newImporter, - newExporter + newExporter, } from 'src/services/export_import/export_import.js' import BooleanSetting from '../helpers/boolean_setting.vue' @@ -24,27 +24,29 @@ import SharedComputedObject from '../helpers/shared_computed_object.js' const SUPPORTED_TYPES = new Set(['word', 'regexp', 'user', 'user_regexp']) const FilteringTab = { - data () { + data() { return { - replyVisibilityOptions: ['all', 'following', 'self'].map(mode => ({ + replyVisibilityOptions: ['all', 'following', 'self'].map((mode) => ({ key: mode, value: mode, - label: this.$t(`settings.reply_visibility_${mode}`) + label: this.$t(`settings.reply_visibility_${mode}`), })), - muteBlockLv1Options: ['ask', 'forever', 'temporarily'].map(mode => ({ + muteBlockLv1Options: ['ask', 'forever', 'temporarily'].map((mode) => ({ key: mode, value: mode, - label: this.$t(`user_card.mute_block_${mode}`) + label: this.$t(`user_card.mute_block_${mode}`), })), - muteFiltersDraftObject: cloneDeep(useServerSideStorageStore().prefsStorage.simple.muteFilters), + muteFiltersDraftObject: cloneDeep( + useServerSideStorageStore().prefsStorage.simple.muteFilters, + ), muteFiltersDraftDirty: Object.fromEntries( Object.entries( - useServerSideStorageStore().prefsStorage.simple.muteFilters - ).map(([k]) => [k, false]) + useServerSideStorageStore().prefsStorage.simple.muteFilters, + ).map(([k]) => [k, false]), ), exportedFilter: null, filterImporter: newImporter({ - validator (parsed) { + validator(parsed) { if (Array.isArray(parsed)) return false if (!SUPPORTED_TYPES.has(parsed.type)) return false return true @@ -55,7 +57,7 @@ const FilteringTab = { expires = null, hide = false, name = '', - value = '' + value = '', } = data this.createFilter({ @@ -63,22 +65,21 @@ const FilteringTab = { expires, hide, name, - value + value, }) }, - onImportFailure (result) { + onImportFailure(result) { console.error('Failure importing filter:', result) - useInterfaceStore() - .pushGlobalNotice({ - messageKey: 'settings.filter.import_failure', - level: 'error' - }) - } + useInterfaceStore().pushGlobalNotice({ + messageKey: 'settings.filter.import_failure', + level: 'error', + }) + }, }), filterExporter: newExporter({ filename: 'pleromafe_mute-filter', - getExportedObject: () => this.exportedFilter - }) + getExportedObject: () => this.exportedFilter, + }), } }, components: { @@ -88,23 +89,23 @@ const FilteringTab = { IntegerSetting, Checkbox, Select, - HelpIndicator + HelpIndicator, }, computed: { - instanceSpecificPanelPresent () { return this.$store.state.instance.showInstanceSpecificPanel }, + instanceSpecificPanelPresent() { + return this.$store.state.instance.showInstanceSpecificPanel + }, ...SharedComputedObject(), - ...mapState( - useServerSideStorageStore, - { - muteFilters: store => Object.entries(store.prefsStorage.simple.muteFilters), - muteFiltersObject: store => store.prefsStorage.simple.muteFilters - } - ), + ...mapState(useServerSideStorageStore, { + muteFilters: (store) => + Object.entries(store.prefsStorage.simple.muteFilters), + muteFiltersObject: (store) => store.prefsStorage.simple.muteFilters, + }), ...mapVuexState({ - blockExpirationSupported: state => state.instance.blockExpiration + blockExpirationSupported: (state) => state.instance.blockExpiration, }), onMuteDefaultActionLv1: { - get () { + get() { const value = this.$store.state.config.onMuteDefaultAction if (value === 'ask' || value === 'forever') { return value @@ -112,16 +113,19 @@ const FilteringTab = { return 'temporarily' } }, - set (value) { + set(value) { let realValue = value if (value !== 'ask' && value !== 'forever') { realValue = '14d' } - this.$store.dispatch('setOption', { name: 'onMuteDefaultAction', value: realValue }) - } + this.$store.dispatch('setOption', { + name: 'onMuteDefaultAction', + value: realValue, + }) + }, }, onBlockDefaultActionLv1: { - get () { + get() { const value = this.$store.state.config.onBlockDefaultAction if (value === 'ask' || value === 'forever') { return value @@ -129,29 +133,36 @@ const FilteringTab = { return 'temporarily' } }, - set (value) { + set(value) { let realValue = value if (value !== 'ask' && value !== 'forever') { realValue = '14d' } - this.$store.dispatch('setOption', { name: 'onBlockDefaultAction', value: realValue }) - } + this.$store.dispatch('setOption', { + name: 'onBlockDefaultAction', + value: realValue, + }) + }, }, - muteFiltersDraft () { + muteFiltersDraft() { return Object.entries(this.muteFiltersDraftObject) }, - muteFiltersExpired () { + muteFiltersExpired() { const now = Date.now() - return Object - .entries(this.muteFiltersDraftObject) - .filter(([, { expires }]) => expires != null && expires <= now) - } + return Object.entries(this.muteFiltersDraftObject).filter( + ([, { expires }]) => expires != null && expires <= now, + ) + }, }, methods: { - ...mapActions(useServerSideStorageStore, ['setPreference', 'unsetPreference', 'pushServerSideStorage']), - getDatetimeLocal (timestamp) { + ...mapActions(useServerSideStorageStore, [ + 'setPreference', + 'unsetPreference', + 'pushServerSideStorage', + ]), + getDatetimeLocal(timestamp) { const date = new Date(timestamp) - const fmt = new Intl.NumberFormat("en-US", {minimumIntegerDigits: 2}) + const fmt = new Intl.NumberFormat('en-US', { minimumIntegerDigits: 2 }) const datetime = [ date.getFullYear(), '-', @@ -161,11 +172,11 @@ const FilteringTab = { 'T', fmt.format(date.getHours()), ':', - fmt.format(date.getMinutes()) + fmt.format(date.getMinutes()), ].join('') return datetime }, - checkRegexValid (id) { + checkRegexValid(id) { const filter = this.muteFiltersObject[id] if (filter.type !== 'regexp') return true if (filter.type !== 'user_regexp') return true @@ -179,19 +190,21 @@ const FilteringTab = { } return valid }, - createFilter (filter = { - type: 'word', - value: '', - name: 'New Filter', - enabled: true, - expires: null, - hide: false, - }) { + createFilter( + filter = { + type: 'word', + value: '', + name: 'New Filter', + enabled: true, + expires: null, + hide: false, + }, + ) { const newId = uuidv4() filter.order = this.muteFilters.length + 2 this.muteFiltersDraftObject[newId] = filter - this.setPreference({ path: 'simple.muteFilters.' + newId , value: filter }) + this.setPreference({ path: 'simple.muteFilters.' + newId, value: filter }) this.pushServerSideStorage() }, exportFilter(id) { @@ -202,23 +215,23 @@ const FilteringTab = { importFilter() { this.filterImporter.importData() }, - copyFilter (id) { + copyFilter(id) { const filter = { ...this.muteFiltersDraftObject[id] } const newId = uuidv4() this.muteFiltersDraftObject[newId] = filter - this.setPreference({ path: 'simple.muteFilters.' + newId , value: filter }) + this.setPreference({ path: 'simple.muteFilters.' + newId, value: filter }) this.pushServerSideStorage() }, - deleteFilter (id) { + deleteFilter(id) { delete this.muteFiltersDraftObject[id] - this.unsetPreference({ path: 'simple.muteFilters.' + id , value: null }) + this.unsetPreference({ path: 'simple.muteFilters.' + id, value: null }) this.pushServerSideStorage() }, - purgeExpiredFilters () { + purgeExpiredFilters() { this.muteFiltersExpired.forEach(([id]) => { delete this.muteFiltersDraftObject[id] - this.unsetPreference({ path: 'simple.muteFilters.' + id , value: null }) + this.unsetPreference({ path: 'simple.muteFilters.' + id, value: null }) }) this.pushServerSideStorage() }, @@ -242,17 +255,20 @@ const FilteringTab = { this.muteFiltersDraftDirty[id] = true }, saveFilter(id) { - this.setPreference({ path: 'simple.muteFilters.' + id , value: this.muteFiltersDraftObject[id] }) + this.setPreference({ + path: 'simple.muteFilters.' + id, + value: this.muteFiltersDraftObject[id], + }) this.pushServerSideStorage() this.muteFiltersDraftDirty[id] = false }, }, // Updating nested properties watch: { - replyVisibility () { + replyVisibility() { this.$store.dispatch('queueFlushAll') - } - } + }, + }, } export default FilteringTab diff --git a/src/components/settings_modal/tabs/general_tab.js b/src/components/settings_modal/tabs/general_tab.js index 4d3b01c46..801d0aa26 100644 --- a/src/components/settings_modal/tabs/general_tab.js +++ b/src/components/settings_modal/tabs/general_tab.js @@ -16,17 +16,17 @@ const GeneralTab = { props: { parentCollapsed: { required: true, - type: Boolean - } + type: Boolean, + }, }, - data () { + data() { return { - absoluteTime12hOptions: ['24h', '12h'].map(mode => ({ + absoluteTime12hOptions: ['24h', '12h'].map((mode) => ({ key: mode, value: mode, - label: this.$t(`settings.absolute_time_format_12h_${mode}`) + label: this.$t(`settings.absolute_time_format_12h_${mode}`), })), - emailLanguage: this.$store.state.users.currentUser.language || [''] + emailLanguage: this.$store.state.users.currentUser.language || [''], } }, components: { @@ -36,24 +36,31 @@ const GeneralTab = { FloatSetting, FontControl, InterfaceLanguageSwitcher, - ProfileSettingIndicator + ProfileSettingIndicator, }, computed: { language: { - get: function () { return this.$store.getters.mergedConfig.interfaceLanguage }, + get: function () { + return this.$store.getters.mergedConfig.interfaceLanguage + }, set: function (val) { - this.$store.dispatch('setOption', { name: 'interfaceLanguage', value: val }) - } + this.$store.dispatch('setOption', { + name: 'interfaceLanguage', + value: val, + }) + }, }, ...SharedComputedObject(), ...mapState({ - blockExpirationSupported: state => state.instance.blockExpiration, - }) + blockExpirationSupported: (state) => state.instance.blockExpiration, + }), }, methods: { - updateProfile () { + updateProfile() { const params = { - language: localeService.internalToBackendLocaleMulti(this.emailLanguage) + language: localeService.internalToBackendLocaleMulti( + this.emailLanguage, + ), } this.$store.state.api.backendInteractor @@ -63,19 +70,19 @@ const GeneralTab = { this.$store.commit('setCurrentUser', user) }) }, - updateFont (key, value) { + updateFont(key, value) { this.$store.dispatch('setOption', { name: 'theme3hacks', value: { ...this.mergedConfig.theme3hacks, fonts: { ...this.mergedConfig.theme3hacks.fonts, - [key]: value - } - } + [key]: value, + }, + }, }) }, - } + }, } export default GeneralTab diff --git a/src/components/settings_modal/tabs/layout_tab.js b/src/components/settings_modal/tabs/layout_tab.js index 48b6eb971..bc48bb62c 100644 --- a/src/components/settings_modal/tabs/layout_tab.js +++ b/src/components/settings_modal/tabs/layout_tab.js @@ -9,42 +9,49 @@ const GeneralTab = { props: { parentCollapsed: { required: true, - type: Boolean - } + type: Boolean, + }, }, - data () { + data() { return { - thirdColumnModeOptions: ['none', 'notifications', 'postform'].map(mode => ({ - key: mode, - value: mode, - label: this.$t(`settings.third_column_mode_${mode}`) - })) + thirdColumnModeOptions: ['none', 'notifications', 'postform'].map( + (mode) => ({ + key: mode, + value: mode, + label: this.$t(`settings.third_column_mode_${mode}`), + }), + ), } }, components: { BooleanSetting, ChoiceSetting, UnitSetting, - ProfileSettingIndicator + ProfileSettingIndicator, }, computed: { - postFormats () { + postFormats() { return this.$store.state.instance.postFormats || [] }, - instanceShoutboxPresent () { return this.$store.state.instance.shoutAvailable }, - columns () { + instanceShoutboxPresent() { + return this.$store.state.instance.shoutAvailable + }, + columns() { const mode = this.$store.getters.mergedConfig.thirdColumnMode const notif = mode === 'none' ? [] : ['notifs'] - if (this.$store.getters.mergedConfig.sidebarRight || mode === 'postform') { + if ( + this.$store.getters.mergedConfig.sidebarRight || + mode === 'postform' + ) { return [...notif, 'content', 'sidebar'] } else { return ['sidebar', 'content', ...notif] } }, ...SharedComputedObject(), - } + }, } export default GeneralTab diff --git a/src/components/settings_modal/tabs/mutes_and_blocks_tab.js b/src/components/settings_modal/tabs/mutes_and_blocks_tab.js index 36dc8a090..fbedad790 100644 --- a/src/components/settings_modal/tabs/mutes_and_blocks_tab.js +++ b/src/components/settings_modal/tabs/mutes_and_blocks_tab.js @@ -15,31 +15,33 @@ import { useOAuthTokensStore } from 'src/stores/oauth_tokens' const BlockList = withLoadMore({ fetch: (props, $store) => $store.dispatch('fetchBlocks'), - select: (props, $store) => get($store.state.users.currentUser, 'blockIds', []), + select: (props, $store) => + get($store.state.users.currentUser, 'blockIds', []), destroy: () => {}, - childPropName: 'items' + childPropName: 'items', })(SelectableList) const MuteList = withLoadMore({ fetch: (props, $store) => $store.dispatch('fetchMutes'), select: (props, $store) => get($store.state.users.currentUser, 'muteIds', []), destroy: () => {}, - childPropName: 'items' + childPropName: 'items', })(SelectableList) const DomainMuteList = withSubscription({ fetch: (props, $store) => $store.dispatch('fetchDomainMutes'), - select: (props, $store) => get($store.state.users.currentUser, 'domainMutes', []), - childPropName: 'items' + select: (props, $store) => + get($store.state.users.currentUser, 'domainMutes', []), + childPropName: 'items', })(SelectableList) const MutesAndBlocks = { - data () { + data() { return { - activeTab: 'profile' + activeTab: 'profile', } }, - created () { + created() { useOAuthTokensStore().fetchTokens() this.$store.dispatch('getKnownDomains') }, @@ -53,87 +55,94 @@ const MutesAndBlocks = { DomainMuteCard, ProgressButton, Autosuggest, - Checkbox + Checkbox, }, computed: { - knownDomains () { + knownDomains() { return this.$store.state.instance.knownDomains }, - user () { + user() { return this.$store.state.users.currentUser - } + }, }, methods: { - importFollows (file) { - return this.$store.state.api.backendInteractor.importFollows({ file }) + importFollows(file) { + return this.$store.state.api.backendInteractor + .importFollows({ file }) .then((status) => { if (!status) { throw new Error('failed') } }) }, - importBlocks (file) { - return this.$store.state.api.backendInteractor.importBlocks({ file }) + importBlocks(file) { + return this.$store.state.api.backendInteractor + .importBlocks({ file }) .then((status) => { if (!status) { throw new Error('failed') } }) }, - generateExportableUsersContent (users) { + generateExportableUsersContent(users) { // Get addresses - return users.map((user) => { - // check is it's a local user - if (user && user.is_local) { - // append the instance address - return user.screen_name + '@' + location.hostname - } - return user.screen_name - }).join('\n') + return users + .map((user) => { + // check is it's a local user + if (user && user.is_local) { + // append the instance address + return user.screen_name + '@' + location.hostname + } + return user.screen_name + }) + .join('\n') }, - activateTab (tabName) { + activateTab(tabName) { this.activeTab = tabName }, - filterUnblockedUsers (userIds) { + filterUnblockedUsers(userIds) { return reject(userIds, (userId) => { const relationship = this.$store.getters.relationship(this.userId) return relationship.blocking || userId === this.user.id }) }, - filterUnMutedUsers (userIds) { + filterUnMutedUsers(userIds) { return reject(userIds, (userId) => { const relationship = this.$store.getters.relationship(this.userId) return relationship.muting || userId === this.user.id }) }, - queryUserIds (query) { - return this.$store.dispatch('searchUsers', { query }) + queryUserIds(query) { + return this.$store + .dispatch('searchUsers', { query }) .then((users) => map(users, 'id')) }, - blockUsers (ids) { + blockUsers(ids) { return this.$store.dispatch('blockUsers', ids) }, - unblockUsers (ids) { + unblockUsers(ids) { return this.$store.dispatch('unblockUsers', ids) }, - muteUsers (ids) { + muteUsers(ids) { return this.$store.dispatch('muteUsers', ids) }, - unmuteUsers (ids) { + unmuteUsers(ids) { return this.$store.dispatch('unmuteUsers', ids) }, - filterUnMutedDomains (urls) { - return urls.filter(url => !this.user.domainMutes.includes(url)) + filterUnMutedDomains(urls) { + return urls.filter((url) => !this.user.domainMutes.includes(url)) }, - queryKnownDomains (query) { + queryKnownDomains(query) { return new Promise((resolve) => { - resolve(this.knownDomains.filter(url => url.toLowerCase().includes(query))) + resolve( + this.knownDomains.filter((url) => url.toLowerCase().includes(query)), + ) }) }, - unmuteDomains (domains) { + unmuteDomains(domains) { return this.$store.dispatch('unmuteDomains', domains) - } - } + }, + }, } export default MutesAndBlocks diff --git a/src/components/settings_modal/tabs/notifications_tab.js b/src/components/settings_modal/tabs/notifications_tab.js index c53b5889d..c863a7b9f 100644 --- a/src/components/settings_modal/tabs/notifications_tab.js +++ b/src/components/settings_modal/tabs/notifications_tab.js @@ -2,32 +2,36 @@ import BooleanSetting from '../helpers/boolean_setting.vue' import SharedComputedObject from '../helpers/shared_computed_object.js' const NotificationsTab = { - data () { + data() { return { activeTab: 'profile', - notificationSettings: this.$store.state.users.currentUser.notification_settings, - newDomainToMute: '' + notificationSettings: + this.$store.state.users.currentUser.notification_settings, + newDomainToMute: '', } }, components: { - BooleanSetting + BooleanSetting, }, computed: { - user () { + user() { return this.$store.state.users.currentUser }, - canReceiveReports () { - if (!this.user) { return false } + canReceiveReports() { + if (!this.user) { + return false + } return this.user.privileges.includes('reports_manage_reports') }, - ...SharedComputedObject() + ...SharedComputedObject(), }, methods: { - updateNotificationSettings () { - this.$store.state.api.backendInteractor - .updateNotificationSettings({ settings: this.notificationSettings }) - } - } + updateNotificationSettings() { + this.$store.state.api.backendInteractor.updateNotificationSettings({ + settings: this.notificationSettings, + }) + }, + }, } export default NotificationsTab 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 4b558471f..abf658792 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 @@ -2,15 +2,13 @@ import { rgb2hex, hex2rgb, getContrastRatioLayers, - relativeLuminance + relativeLuminance, } from 'src/services/color_convert/color_convert.js' import { newImporter, - newExporter + newExporter, } from 'src/services/export_import/export_import.js' -import { - SLOT_INHERITANCE -} from 'src/services/theme_data/pleromafe.js' +import { SLOT_INHERITANCE } from 'src/services/theme_data/pleromafe.js' import { CURRENT_VERSION, OPACITIES, @@ -22,16 +20,19 @@ import { generateRadii, generateFonts, shadows2to3, - colors2to3 + colors2to3, } from 'src/services/theme_data/theme_data.service.js' import { convertTheme2To3 } from 'src/services/theme_data/theme2_to_theme3.js' import { init } from 'src/services/theme_data/theme_data_3.service.js' import { getCssRules, - getScopedVersion + getScopedVersion, } from 'src/services/theme_data/css_utils.js' -import { createStyleSheet, adoptStyleSheets } from 'src/services/style_setter/style_setter.js' +import { + createStyleSheet, + adoptStyleSheets, +} from 'src/services/style_setter/style_setter.js' import ColorInput from 'src/components/color_input/color_input.vue' import RangeInput from 'src/components/range_input/range_input.vue' @@ -55,8 +56,8 @@ const v1OnlyNames = [ 'cRed', 'cGreen', 'cBlue', - 'cOrange' -].map(_ => _ + 'ColorLocal') + 'cOrange', +].map((_) => _ + 'ColorLocal') const colorConvert = (color) => { if (color.startsWith('--') || color === 'transparent') { @@ -67,16 +68,16 @@ const colorConvert = (color) => { } export default { - data () { + data() { return { themeImporter: newImporter({ validator: this.importValidator, onImport: this.onImport, - onImportFailure: this.onImportFailure + onImportFailure: this.onImportFailure, }), themeExporter: newExporter({ filename: 'pleroma_theme', - getExportedObject: () => this.exportedTheme + getExportedObject: () => this.exportedTheme, }), availableStyles: [], selected: '', @@ -98,12 +99,18 @@ export default { keepFonts: false, ...Object.keys(SLOT_INHERITANCE) - .map(key => [key, '']) - .reduce((acc, [key, val]) => ({ ...acc, [key + 'ColorLocal']: val }), {}), + .map((key) => [key, '']) + .reduce( + (acc, [key, val]) => ({ ...acc, [key + 'ColorLocal']: val }), + {}, + ), ...Object.keys(OPACITIES) - .map(key => [key, '']) - .reduce((acc, [key, val]) => ({ ...acc, [key + 'OpacityLocal']: val }), {}), + .map((key) => [key, '']) + .reduce( + (acc, [key, val]) => ({ ...acc, [key + 'OpacityLocal']: val }), + {}, + ), shadowSelected: undefined, shadowsLocal: {}, @@ -117,10 +124,10 @@ export default { avatarAltRadiusLocal: '', attachmentRadiusLocal: '', tooltipRadiusLocal: '', - chatMessageRadiusLocal: '' + chatMessageRadiusLocal: '', } }, - created () { + created() { const currentIndex = this.$store.state.instance.themesIndex let promise @@ -130,50 +137,48 @@ export default { promise = useInterfaceStore().fetchThemesIndex() } - promise.then(themesIndex => { - Object - .values(themesIndex) - .forEach(themeFunc => { - themeFunc().then(themeData => themeData && this.availableStyles.push(themeData)) - }) + promise.then((themesIndex) => { + Object.values(themesIndex).forEach((themeFunc) => { + themeFunc().then( + (themeData) => themeData && this.availableStyles.push(themeData), + ) + }) }) }, - mounted () { + mounted() { if (typeof this.shadowSelected === 'undefined') { this.shadowSelected = this.shadowsAvailable[0] } }, computed: { - themeWarningHelp () { + themeWarningHelp() { if (!this.themeWarning) return const t = this.$t const pre = 'settings.style.switcher.help.' - const { - origin, - themeEngineVersion, - type, - noActionsPossible - } = this.themeWarning + const { origin, themeEngineVersion, type, noActionsPossible } = + this.themeWarning if (origin === 'file') { // Loaded v2 theme from file if (themeEngineVersion === 2 && type === 'wrong_version') { return t(pre + 'v2_imported') } if (themeEngineVersion > CURRENT_VERSION) { - return t(pre + 'future_version_imported') + ' ' + - ( - noActionsPossible - ? t(pre + 'snapshot_missing') - : t(pre + 'snapshot_present') - ) + return ( + t(pre + 'future_version_imported') + + ' ' + + (noActionsPossible + ? t(pre + 'snapshot_missing') + : t(pre + 'snapshot_present')) + ) } if (themeEngineVersion < CURRENT_VERSION) { - return t(pre + 'future_version_imported') + ' ' + - ( - noActionsPossible - ? t(pre + 'snapshot_missing') - : t(pre + 'snapshot_present') - ) + return ( + t(pre + 'future_version_imported') + + ' ' + + (noActionsPossible + ? t(pre + 'snapshot_missing') + : t(pre + 'snapshot_present')) + ) } } else if (origin === 'localStorage') { if (type === 'snapshot_source_mismatch') { @@ -185,38 +190,40 @@ export default { } // Admin downgraded FE if (themeEngineVersion > CURRENT_VERSION) { - return t(pre + 'fe_downgraded') + ' ' + - ( - noActionsPossible - ? t(pre + 'migration_snapshot_ok') - : t(pre + 'migration_snapshot_gone') - ) + return ( + t(pre + 'fe_downgraded') + + ' ' + + (noActionsPossible + ? t(pre + 'migration_snapshot_ok') + : t(pre + 'migration_snapshot_gone')) + ) } // Admin upgraded FE if (themeEngineVersion < CURRENT_VERSION) { - return t(pre + 'fe_upgraded') + ' ' + - ( - noActionsPossible - ? t(pre + 'migration_snapshot_ok') - : t(pre + 'migration_snapshot_gone') - ) + return ( + t(pre + 'fe_upgraded') + + ' ' + + (noActionsPossible + ? t(pre + 'migration_snapshot_ok') + : t(pre + 'migration_snapshot_gone')) + ) } } }, - selectedVersion () { + selectedVersion() { return Array.isArray(this.selectedTheme) ? 1 : 2 }, - currentColors () { + currentColors() { return Object.keys(SLOT_INHERITANCE) - .map(key => [key, this[key + 'ColorLocal']]) + .map((key) => [key, this[key + 'ColorLocal']]) .reduce((acc, [key, val]) => ({ ...acc, [key]: val }), {}) }, - currentOpacity () { + currentOpacity() { return Object.keys(OPACITIES) - .map(key => [key, this[key + 'OpacityLocal']]) + .map((key) => [key, this[key + 'OpacityLocal']]) .reduce((acc, [key, val]) => ({ ...acc, [key]: val }), {}) }, - currentRadii () { + currentRadii() { return { btn: this.btnRadiusLocal, input: this.inputRadiusLocal, @@ -226,11 +233,11 @@ export default { avatarAlt: this.avatarAltRadiusLocal, tooltip: this.tooltipRadiusLocal, attachment: this.attachmentRadiusLocal, - chatMessage: this.chatMessageRadiusLocal + chatMessage: this.chatMessageRadiusLocal, } }, // This needs optimization maybe - previewContrast () { + previewContrast() { try { if (!this.previewTheme.colors.bg) return {} const colors = this.previewTheme.colors @@ -243,113 +250,124 @@ export default { aaa: ratio >= 7, // same but for 18pt+ texts laa: ratio >= 3, - laaa: ratio >= 4.5 + laaa: ratio >= 4.5, }) - const colorsConverted = Object.entries(colors).reduce((acc, [key, value]) => ({ ...acc, [key]: colorConvert(value) }), {}) + const colorsConverted = Object.entries(colors).reduce( + (acc, [key, value]) => ({ ...acc, [key]: colorConvert(value) }), + {}, + ) - const ratios = Object.entries(SLOT_INHERITANCE).reduce((acc, [key, value]) => { - const slotIsBaseText = key === 'text' || key === 'link' - const slotIsText = slotIsBaseText || ( - typeof value === 'object' && value !== null && value.textColor - ) - if (!slotIsText) return acc - const { layer, variant } = slotIsBaseText ? { layer: 'bg' } : value - const background = variant || layer - const opacitySlot = getOpacitySlot(background) - const textColors = [ - key, - ...(background === 'bg' ? ['cRed', 'cGreen', 'cBlue', 'cOrange'] : []) - ] + const ratios = Object.entries(SLOT_INHERITANCE).reduce( + (acc, [key, value]) => { + const slotIsBaseText = key === 'text' || key === 'link' + const slotIsText = + slotIsBaseText || + (typeof value === 'object' && value !== null && value.textColor) + if (!slotIsText) return acc + const { layer, variant } = slotIsBaseText ? { layer: 'bg' } : value + const background = variant || layer + const opacitySlot = getOpacitySlot(background) + const textColors = [ + key, + ...(background === 'bg' + ? ['cRed', 'cGreen', 'cBlue', 'cOrange'] + : []), + ] - const layers = getLayers( - layer, - variant || layer, - opacitySlot, - colorsConverted, - opacity - ) + const layers = getLayers( + layer, + variant || layer, + opacitySlot, + colorsConverted, + opacity, + ) - // Temporary patch for null-y value errors - if (layers.flat().some(v => v == null)) return acc + // Temporary patch for null-y value errors + if (layers.flat().some((v) => v == null)) return acc - return { - ...acc, - ...textColors.reduce((acc, textColorKey) => { - const newKey = slotIsBaseText - ? 'bg' + textColorKey[0].toUpperCase() + textColorKey.slice(1) - : textColorKey - return { - ...acc, - [newKey]: getContrastRatioLayers( - colorsConverted[textColorKey], - layers, - colorsConverted[textColorKey] - ) - } - }, {}) - } + return { + ...acc, + ...textColors.reduce((acc, textColorKey) => { + const newKey = slotIsBaseText + ? 'bg' + textColorKey[0].toUpperCase() + textColorKey.slice(1) + : textColorKey + return { + ...acc, + [newKey]: getContrastRatioLayers( + colorsConverted[textColorKey], + layers, + colorsConverted[textColorKey], + ), + } + }, {}), + } + }, + {}, + ) + + return Object.entries(ratios).reduce((acc, [k, v]) => { + acc[k] = hints(v) + return acc }, {}) - - return Object.entries(ratios).reduce((acc, [k, v]) => { acc[k] = hints(v); return acc }, {}) } catch (e) { console.warn('Failure computing contrasts', e) return {} } }, - themeDataUsed () { + themeDataUsed() { return useInterfaceStore().themeDataUsed }, - shadowsAvailable () { + shadowsAvailable() { return Object.keys(DEFAULT_SHADOWS).sort() }, currentShadowOverriden: { - get () { + get() { return !!this.currentShadow }, - set (val) { + set(val) { if (val) { - this.shadowsLocal[this.shadowSelected] = (this.currentShadowFallback || []) - .map(s => ({ - name: null, - x: 0, - y: 0, - blur: 0, - spread: 0, - inset: false, - color: '#000000', - alpha: 1, - ...s - })) + this.shadowsLocal[this.shadowSelected] = ( + this.currentShadowFallback || [] + ).map((s) => ({ + name: null, + x: 0, + y: 0, + blur: 0, + spread: 0, + inset: false, + color: '#000000', + alpha: 1, + ...s, + })) } else { delete this.shadowsLocal[this.shadowSelected] } - } + }, }, - currentShadowFallback () { + currentShadowFallback() { return (this.previewTheme.shadows || {})[this.shadowSelected] }, currentShadow: { - get () { + get() { return this.shadowsLocal[this.shadowSelected] }, - set (v) { + set(v) { this.shadowsLocal[this.shadowSelected] = v - } + }, }, - themeValid () { + themeValid() { return !this.shadowsInvalid && !this.colorsInvalid && !this.radiiInvalid }, - exportedTheme () { - const saveEverything = ( + exportedTheme() { + const saveEverything = !this.keepFonts && !this.keepShadows && !this.keepOpacity && !this.keepRoundness && !this.keepColor - ) const source = { - themeEngineVersion: CURRENT_VERSION + themeEngineVersion: CURRENT_VERSION, } if (this.keepFonts || saveEverything) { @@ -370,18 +388,20 @@ export default { const theme = { themeEngineVersion: CURRENT_VERSION, - ...this.previewTheme + ...this.previewTheme, } return { // To separate from other random JSON files and possible future source formats - _pleroma_theme_version: 2, theme, source + _pleroma_theme_version: 2, + theme, + source, } }, - isActive () { + isActive() { const tabSwitcher = this.$parent return tabSwitcher ? tabSwitcher.isActive('theme') : false - } + }, }, components: { ColorInput, @@ -393,70 +413,65 @@ export default { TabSwitcher, Preview, Checkbox, - Select + Select, }, methods: { - loadTheme ( - { - theme, - source, - _pleroma_theme_version: fileVersion - }, + loadTheme( + { theme, source, _pleroma_theme_version: fileVersion }, origin, - forceUseSource = false + forceUseSource = false, ) { this.dismissWarning() - const version = (origin === 'localStorage' && !theme.colors) - ? 'l1' - : fileVersion + const version = + origin === 'localStorage' && !theme.colors ? 'l1' : fileVersion const snapshotEngineVersion = (theme || {}).themeEngineVersion const themeEngineVersion = (source || {}).themeEngineVersion || 2 const versionsMatch = themeEngineVersion === CURRENT_VERSION - const sourceSnapshotMismatch = ( + const sourceSnapshotMismatch = theme !== undefined && - source !== undefined && - themeEngineVersion !== snapshotEngineVersion - ) + source !== undefined && + themeEngineVersion !== snapshotEngineVersion // Force loading of source if user requested it or if snapshot // is unavailable const forcedSourceLoad = (source && forceUseSource) || !theme - if (!(versionsMatch && !sourceSnapshotMismatch) && - !forcedSourceLoad && - version !== 'l1' && - origin !== 'defaults' + if ( + !(versionsMatch && !sourceSnapshotMismatch) && + !forcedSourceLoad && + version !== 'l1' && + origin !== 'defaults' ) { if (sourceSnapshotMismatch && origin === 'localStorage') { this.themeWarning = { origin, themeEngineVersion, - type: 'snapshot_source_mismatch' + type: 'snapshot_source_mismatch', } } else if (!theme) { this.themeWarning = { origin, noActionsPossible: true, themeEngineVersion, - type: 'no_snapshot_old_version' + type: 'no_snapshot_old_version', } } else if (!versionsMatch) { this.themeWarning = { origin, noActionsPossible: !source, themeEngineVersion, - type: 'wrong_version' + type: 'wrong_version', } } } this.normalizeLocalState(theme, version, source, forcedSourceLoad) }, - forceLoadLocalStorage () { + forceLoadLocalStorage() { this.loadThemeFromLocalStorage(true) }, - dismissWarning () { + dismissWarning() { this.themeWarning = undefined this.tempImportFile = undefined }, - forceLoad () { + forceLoad() { const { origin } = this.themeWarning switch (origin) { case 'localStorage': @@ -468,7 +483,7 @@ export default { } this.dismissWarning() }, - forceSnapshot () { + forceSnapshot() { const { origin } = this.themeWarning switch (origin) { case 'localStorage': @@ -480,25 +495,25 @@ export default { } this.dismissWarning() }, - loadThemeFromLocalStorage (confirmLoadSource = false) { + loadThemeFromLocalStorage(confirmLoadSource = false) { const theme = this.themeDataUsed?.source if (theme) { this.loadTheme( { - theme + theme, }, 'localStorage', - confirmLoadSource + confirmLoadSource, ) } }, - setCustomTheme () { + setCustomTheme() { useInterfaceStore().setThemeV2({ customTheme: { ignore: true, themeFileVersion: this.selectedVersion, themeEngineVersion: CURRENT_VERSION, - ...this.previewTheme + ...this.previewTheme, }, customThemeSource: { themeFileVersion: this.selectedVersion, @@ -507,77 +522,84 @@ export default { fonts: this.fontsLocal, opacity: this.currentOpacity, colors: this.currentColors, - radii: this.currentRadii - } + radii: this.currentRadii, + }, }) }, - updatePreviewColors () { + updatePreviewColors() { const result = generateColors({ opacity: this.currentOpacity, - colors: this.currentColors + colors: this.currentColors, }) this.previewTheme.colors = result.theme.colors this.previewTheme.opacity = result.theme.opacity }, - updatePreviewShadows () { + updatePreviewShadows() { this.previewTheme.shadows = generateShadows( { shadows: this.shadowsLocal, opacity: this.previewTheme.opacity, - themeEngineVersion: this.engineVersion + themeEngineVersion: this.engineVersion, }, this.previewTheme.colors, - relativeLuminance(this.previewTheme.colors.bg) < 0.5 ? 1 : -1 + relativeLuminance(this.previewTheme.colors.bg) < 0.5 ? 1 : -1, ).theme.shadows }, - importTheme () { this.themeImporter.importData() }, - exportTheme () { this.themeExporter.exportData() }, - onImport (parsed, forceSource = false) { + importTheme() { + this.themeImporter.importData() + }, + exportTheme() { + this.themeExporter.exportData() + }, + onImport(parsed, forceSource = false) { this.tempImportFile = parsed this.loadTheme(parsed, 'file', forceSource) }, - onImportFailure () { - useInterfaceStore().pushGlobalNotice({ messageKey: 'settings.invalid_theme_imported', level: 'error' }) + onImportFailure() { + useInterfaceStore().pushGlobalNotice({ + messageKey: 'settings.invalid_theme_imported', + level: 'error', + }) }, - importValidator (parsed) { + importValidator(parsed) { const version = parsed._pleroma_theme_version return version >= 1 || version <= 2 }, - clearAll () { + clearAll() { this.loadThemeFromLocalStorage() }, // Clears all the extra stuff when loading V1 theme - clearV1 () { + clearV1() { Object.keys(this.$data) - .filter(_ => _.endsWith('ColorLocal') || _.endsWith('OpacityLocal')) - .filter(_ => !v1OnlyNames.includes(_)) - .forEach(key => { + .filter((_) => _.endsWith('ColorLocal') || _.endsWith('OpacityLocal')) + .filter((_) => !v1OnlyNames.includes(_)) + .forEach((key) => { this.$data[key] = undefined }) }, - clearRoundness () { + clearRoundness() { Object.keys(this.$data) - .filter(_ => _.endsWith('RadiusLocal')) - .forEach(key => { + .filter((_) => _.endsWith('RadiusLocal')) + .forEach((key) => { this.$data[key] = undefined }) }, - clearOpacity () { + clearOpacity() { Object.keys(this.$data) - .filter(_ => _.endsWith('OpacityLocal')) - .forEach(key => { + .filter((_) => _.endsWith('OpacityLocal')) + .forEach((key) => { this.$data[key] = undefined }) }, - clearShadows () { + clearShadows() { this.shadowsLocal = {} }, - clearFonts () { + clearFonts() { this.fontsLocal = {} }, @@ -594,7 +616,7 @@ export default { * @param {Boolean} source - by default source won't be used if version doesn't match since it might render differently * this allows importing source anyway */ - normalizeLocalState (theme, version = 0, source, forceSource = false) { + normalizeLocalState(theme, version = 0, source, forceSource = false) { let input if (typeof source !== 'undefined') { if (forceSource || source?.themeEngineVersion === CURRENT_VERSION) { @@ -618,11 +640,17 @@ export default { if (version === 0) { if (input.version) version = input.version // Old v1 naming: fg is text, btn is foreground - if (typeof colors.text === 'undefined' && typeof colors.fg !== 'undefined') { + if ( + typeof colors.text === 'undefined' && + typeof colors.fg !== 'undefined' + ) { version = 1 } // New v2 naming: text is text, fg is foreground - if (typeof colors.text !== 'undefined' && typeof colors.fg !== 'undefined') { + if ( + typeof colors.text !== 'undefined' && + typeof colors.fg !== 'undefined' + ) { version = 2 } } @@ -648,7 +676,7 @@ export default { .add('cOrange') } - keys.forEach(key => { + keys.forEach((key) => { const color = colors[key] const hex = rgb2hex(colors[key]) this[key + 'ColorLocal'] = hex === '#aN' ? color : hex @@ -689,33 +717,32 @@ export default { this.fontsLocal = fonts } }, - updateTheme3Preview () { + updateTheme3Preview() { const theme2 = convertTheme2To3(this.previewTheme) const theme3 = init({ inputRuleset: theme2, ultimateBackgroundColor: '#000000', - liteMode: true + liteMode: true, }) const sheet = createStyleSheet('theme-tab-overall-preview', 90) - const rule = getScopedVersion( - getCssRules(theme3.eager), - '&' - ).join('\n') + const rule = getScopedVersion(getCssRules(theme3.eager), '&').join('\n') sheet.clear() sheet.addRule('#theme-preview {\n' + rule + '\n}') sheet.ready = true adoptStyleSheets() - } + }, }, watch: { - themeDataUsed () { + themeDataUsed() { this.loadThemeFromLocalStorage() }, - currentRadii () { + currentRadii() { try { - this.previewTheme.radii = generateRadii({ radii: this.currentRadii }).theme.radii + this.previewTheme.radii = generateRadii({ + radii: this.currentRadii, + }).theme.radii this.radiiInvalid = false } catch (e) { this.radiiInvalid = true @@ -723,7 +750,7 @@ export default { } }, shadowsLocal: { - handler () { + handler() { try { this.updatePreviewShadows() this.shadowsInvalid = false @@ -732,21 +759,23 @@ export default { console.warn(e) } }, - deep: true + deep: true, }, fontsLocal: { - handler () { + handler() { try { - this.previewTheme.fonts = generateFonts({ fonts: this.fontsLocal }).theme.fonts + this.previewTheme.fonts = generateFonts({ + fonts: this.fontsLocal, + }).theme.fonts this.fontsInvalid = false } catch (e) { this.fontsInvalid = true console.warn(e) } }, - deep: true + deep: true, }, - currentColors () { + currentColors() { try { this.updatePreviewColors() this.colorsInvalid = false @@ -755,23 +784,25 @@ export default { console.warn(e) } }, - currentOpacity () { + currentOpacity() { try { this.updatePreviewColors() } catch (e) { console.warn(e) } }, - selected () { - this.selectedTheme = Object.entries(this.availableStyles).find(([, s]) => { - if (Array.isArray(s)) { - return s[0] === this.selected - } else { - return s.name === this.selected - } - })[1] + selected() { + this.selectedTheme = Object.entries(this.availableStyles).find( + ([, s]) => { + if (Array.isArray(s)) { + return s[0] === this.selected + } else { + return s.name === this.selected + } + }, + )[1] }, - selectedTheme () { + selectedTheme() { this.dismissWarning() if (this.selectedVersion === 1) { if (!this.keepRoundness) { @@ -799,8 +830,12 @@ export default { this.cOrangeColorLocal = this.selectedTheme[8] } } else if (this.selectedVersion >= 2) { - this.normalizeLocalState(this.selectedTheme.theme, 2, this.selectedTheme.source) + this.normalizeLocalState( + this.selectedTheme.theme, + 2, + this.selectedTheme.source, + ) } - } - } + }, + }, } diff --git a/src/components/settings_modal/tabs/old_theme_tab/theme_preview.vue b/src/components/settings_modal/tabs/old_theme_tab/theme_preview.vue index df2d9f9e9..4c05b27c3 100644 --- a/src/components/settings_modal/tabs/old_theme_tab/theme_preview.vue +++ b/src/components/settings_modal/tabs/old_theme_tab/theme_preview.vue @@ -124,20 +124,15 @@ import { faTimes, faStar, faRetweet, - faReply + faReply, } from '@fortawesome/free-solid-svg-icons' -library.add( - faTimes, - faStar, - faRetweet, - faReply -) +library.add(faTimes, faStar, faRetweet, faReply) export default { components: { - Checkbox - } + Checkbox, + }, } diff --git a/src/components/settings_modal/tabs/posts_tab.js b/src/components/settings_modal/tabs/posts_tab.js index 094583c15..ec0dd0659 100644 --- a/src/components/settings_modal/tabs/posts_tab.js +++ b/src/components/settings_modal/tabs/posts_tab.js @@ -10,43 +10,56 @@ const GeneralTab = { props: { parentCollapsed: { required: true, - type: Boolean - } + type: Boolean, + }, }, - data () { + data() { return { - conversationDisplayOptions: ['tree', 'linear'].map(mode => ({ + conversationDisplayOptions: ['tree', 'linear'].map((mode) => ({ key: mode, value: mode, - label: this.$t(`settings.conversation_display_${mode}`) + label: this.$t(`settings.conversation_display_${mode}`), })), - conversationOtherRepliesButtonOptions: ['below', 'inside'].map(mode => ({ + conversationOtherRepliesButtonOptions: ['below', 'inside'].map( + (mode) => ({ + key: mode, + value: mode, + label: this.$t(`settings.conversation_other_replies_button_${mode}`), + }), + ), + mentionLinkDisplayOptions: ['short', 'full_for_remote', 'full'].map( + (mode) => ({ + key: mode, + value: mode, + label: this.$t(`settings.mention_link_display_${mode}`), + }), + ), + userPopoverAvatarActionOptions: ['close', 'zoom', 'open'].map((mode) => ({ key: mode, value: mode, - label: this.$t(`settings.conversation_other_replies_button_${mode}`) + label: this.$t(`settings.user_popover_avatar_action_${mode}`), })), - mentionLinkDisplayOptions: ['short', 'full_for_remote', 'full'].map(mode => ({ + unsavedPostActionOptions: ['save', 'discard', 'confirm'].map((mode) => ({ key: mode, value: mode, - label: this.$t(`settings.mention_link_display_${mode}`) - })), - userPopoverAvatarActionOptions: ['close', 'zoom', 'open'].map(mode => ({ - key: mode, - value: mode, - label: this.$t(`settings.user_popover_avatar_action_${mode}`) - })), - unsavedPostActionOptions: ['save', 'discard', 'confirm'].map(mode => ({ - key: mode, - value: mode, - label: this.$t(`settings.unsaved_post_action_${mode}`) + label: this.$t(`settings.unsaved_post_action_${mode}`), })), loopSilentAvailable: - // Firefox - Object.getOwnPropertyDescriptor(HTMLVideoElement.prototype, 'mozHasAudio') || - // Chrome-likes - Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, 'webkitAudioDecodedByteCount') || - // Future spec, still not supported in Nightly 63 as of 08/2018 - Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, 'audioTracks') + // Firefox + Object.getOwnPropertyDescriptor( + HTMLVideoElement.prototype, + 'mozHasAudio', + ) || + // Chrome-likes + Object.getOwnPropertyDescriptor( + HTMLMediaElement.prototype, + 'webkitAudioDecodedByteCount', + ) || + // Future spec, still not supported in Nightly 63 as of 08/2018 + Object.getOwnPropertyDescriptor( + HTMLMediaElement.prototype, + 'audioTracks', + ), } }, components: { @@ -54,25 +67,25 @@ const GeneralTab = { ChoiceSetting, IntegerSetting, FontControl, - ProfileSettingIndicator + ProfileSettingIndicator, }, computed: { ...SharedComputedObject(), }, methods: { - updateFont (key, value) { + updateFont(key, value) { this.$store.dispatch('setOption', { name: 'theme3hacks', value: { ...this.mergedConfig.theme3hacks, fonts: { ...this.mergedConfig.theme3hacks.fonts, - [key]: value - } - } + [key]: value, + }, + }, }) }, - } + }, } export default GeneralTab diff --git a/src/components/settings_modal/tabs/profile_tab.js b/src/components/settings_modal/tabs/profile_tab.js index 9e13eed69..e6f1a68d2 100644 --- a/src/components/settings_modal/tabs/profile_tab.js +++ b/src/components/settings_modal/tabs/profile_tab.js @@ -8,17 +8,13 @@ import { library } from '@fortawesome/fontawesome-svg-core' import { faTimes, faPlus, - faCircleNotch + faCircleNotch, } from '@fortawesome/free-solid-svg-icons' -library.add( - faTimes, - faPlus, - faCircleNotch -) +library.add(faTimes, faPlus, faCircleNotch) const ProfileTab = { - data () { + data() { return { // Whether user is locked or not locked: this.$store.state.users.currentUser.locked, @@ -28,18 +24,18 @@ const ProfileTab = { UserCard, Checkbox, BooleanSetting, - ProfileSettingIndicator + ProfileSettingIndicator, }, computed: { - user () { + user() { return this.$store.state.users.currentUser }, - ...SharedComputedObject() + ...SharedComputedObject(), }, methods: { - updateProfile () { + updateProfile() { const params = { - locked: this.locked + locked: this.locked, } this.$store.state.api.backendInteractor @@ -51,13 +47,13 @@ const ProfileTab = { .catch((error) => { this.displayUploadError(error) }) - } + }, }, watch: { - locked () { + locked() { this.updateProfile() - } - } + }, + }, } export default ProfileTab diff --git a/src/components/settings_modal/tabs/security_tab/confirm.js b/src/components/settings_modal/tabs/security_tab/confirm.js index 0f4ddfc9b..050530240 100644 --- a/src/components/settings_modal/tabs/security_tab/confirm.js +++ b/src/components/settings_modal/tabs/security_tab/confirm.js @@ -2,8 +2,12 @@ const Confirm = { props: ['disabled'], data: () => ({}), methods: { - confirm () { this.$emit('confirm') }, - cancel () { this.$emit('cancel') } - } + confirm() { + this.$emit('confirm') + }, + cancel() { + this.$emit('cancel') + }, + }, } export default Confirm diff --git a/src/components/settings_modal/tabs/security_tab/mfa.js b/src/components/settings_modal/tabs/security_tab/mfa.js index b24a35cd4..7004a7bdc 100644 --- a/src/components/settings_modal/tabs/security_tab/mfa.js +++ b/src/components/settings_modal/tabs/security_tab/mfa.js @@ -6,113 +6,124 @@ import { mapState } from 'vuex' const Mfa = { data: () => ({ - settings: { // current settings of MFA + settings: { + // current settings of MFA available: false, enabled: false, - totp: false + totp: false, }, - setupState: { // setup mfa + setupState: { + // setup mfa state: '', // state of setup. '' -> 'getBackupCodes' -> 'setupOTP' -> 'complete' - setupOTPState: '' // state of setup otp. '' -> 'prepare' -> 'confirm' -> 'complete' + setupOTPState: '', // state of setup otp. '' -> 'prepare' -> 'confirm' -> 'complete' }, backupCodes: { getNewCodes: false, inProgress: false, // progress of fetch codes - codes: [] + codes: [], }, - otpSettings: { // pre-setup setting of OTP. secret key, qrcode url. + otpSettings: { + // pre-setup setting of OTP. secret key, qrcode url. provisioning_uri: '', - key: '' + key: '', }, currentPassword: null, otpConfirmToken: null, error: null, - readyInit: false + readyInit: false, }), components: { 'recovery-codes': RecoveryCodes, 'totp-item': TOTP, qrcode: VueQrcode, - confirm: Confirm + confirm: Confirm, }, computed: { - canSetupOTP () { + canSetupOTP() { return ( - (this.setupInProgress && this.backupCodesPrepared) || - this.settings.enabled - ) && !this.settings.totp && !this.setupOTPInProgress + ((this.setupInProgress && this.backupCodesPrepared) || + this.settings.enabled) && + !this.settings.totp && + !this.setupOTPInProgress + ) }, - setupInProgress () { - return this.setupState.state !== '' && this.setupState.state !== 'complete' + setupInProgress() { + return ( + this.setupState.state !== '' && this.setupState.state !== 'complete' + ) }, - setupOTPInProgress () { + setupOTPInProgress() { return this.setupState.state === 'setupOTP' && !this.completedOTP }, - prepareOTP () { + prepareOTP() { return this.setupState.setupOTPState === 'prepare' }, - confirmOTP () { + confirmOTP() { return this.setupState.setupOTPState === 'confirm' }, - completedOTP () { + completedOTP() { return this.setupState.setupOTPState === 'completed' }, - backupCodesPrepared () { + backupCodesPrepared() { return !this.backupCodes.inProgress && this.backupCodes.codes.length > 0 }, - confirmNewBackupCodes () { + confirmNewBackupCodes() { return this.backupCodes.getNewCodes }, ...mapState({ - backendInteractor: (state) => state.api.backendInteractor - }) + backendInteractor: (state) => state.api.backendInteractor, + }), }, methods: { - activateOTP () { + activateOTP() { if (!this.settings.enabled) { this.setupState.state = 'getBackupcodes' this.fetchBackupCodes() } }, - fetchBackupCodes () { + fetchBackupCodes() { this.backupCodes.inProgress = true this.backupCodes.codes = [] - return this.backendInteractor.generateMfaBackupCodes() - .then((res) => { - this.backupCodes.codes = res.codes - this.backupCodes.inProgress = false - }) + return this.backendInteractor.generateMfaBackupCodes().then((res) => { + this.backupCodes.codes = res.codes + this.backupCodes.inProgress = false + }) }, - getBackupCodes () { // get a new backup codes + getBackupCodes() { + // get a new backup codes this.backupCodes.getNewCodes = true }, - confirmBackupCodes () { // confirm getting new backup codes + confirmBackupCodes() { + // confirm getting new backup codes this.fetchBackupCodes().then(() => { this.backupCodes.getNewCodes = false }) }, - cancelBackupCodes () { // cancel confirm form of new backup codes + cancelBackupCodes() { + // cancel confirm form of new backup codes this.backupCodes.getNewCodes = false }, // Setup OTP - setupOTP () { // prepare setup OTP + setupOTP() { + // prepare setup OTP this.setupState.state = 'setupOTP' this.setupState.setupOTPState = 'prepare' - this.backendInteractor.mfaSetupOTP() - .then((res) => { - this.otpSettings = res - this.setupState.setupOTPState = 'confirm' - }) - }, - doConfirmOTP () { // handler confirm enable OTP - this.error = null - this.backendInteractor.mfaConfirmOTP({ - token: this.otpConfirmToken, - password: this.currentPassword + this.backendInteractor.mfaSetupOTP().then((res) => { + this.otpSettings = res + this.setupState.setupOTPState = 'confirm' }) + }, + doConfirmOTP() { + // handler confirm enable OTP + this.error = null + this.backendInteractor + .mfaConfirmOTP({ + token: this.otpConfirmToken, + password: this.currentPassword, + }) .then((res) => { if (res.error) { this.error = res.error @@ -122,14 +133,15 @@ const Mfa = { }) }, - completeSetup () { + completeSetup() { this.setupState.setupOTPState = 'complete' this.setupState.state = 'complete' this.currentPassword = null this.error = null this.fetchSettings() }, - cancelSetup () { // cancel setup + cancelSetup() { + // cancel setup this.setupState.setupOTPState = '' this.setupState.state = '' this.currentPassword = null @@ -138,18 +150,18 @@ const Mfa = { // end Setup OTP // fetch settings from server - async fetchSettings () { + async fetchSettings() { const result = await this.backendInteractor.settingsMFA() if (result.error) return this.settings = result.settings this.settings.available = true return result - } + }, }, - mounted () { + mounted() { this.fetchSettings().then(() => { this.readyInit = true }) - } + }, } export default Mfa diff --git a/src/components/settings_modal/tabs/security_tab/mfa_backup_codes.js b/src/components/settings_modal/tabs/security_tab/mfa_backup_codes.js index f0a984ecd..cdd9d8d06 100644 --- a/src/components/settings_modal/tabs/security_tab/mfa_backup_codes.js +++ b/src/components/settings_modal/tabs/security_tab/mfa_backup_codes.js @@ -4,14 +4,20 @@ export default { type: Object, default: () => ({ inProgress: false, - codes: [] - }) - } + codes: [], + }), + }, }, data: () => ({}), computed: { - inProgress () { return this.backupCodes.inProgress }, - ready () { return this.backupCodes.codes.length > 0 }, - displayTitle () { return this.inProgress || this.ready } - } + inProgress() { + return this.backupCodes.inProgress + }, + ready() { + return this.backupCodes.codes.length > 0 + }, + displayTitle() { + return this.inProgress || this.ready + }, + }, } diff --git a/src/components/settings_modal/tabs/security_tab/mfa_totp.js b/src/components/settings_modal/tabs/security_tab/mfa_totp.js index b0adb530d..8af4cbcc7 100644 --- a/src/components/settings_modal/tabs/security_tab/mfa_totp.js +++ b/src/components/settings_modal/tabs/security_tab/mfa_totp.js @@ -7,34 +7,38 @@ export default { error: false, currentPassword: '', deactivate: false, - inProgress: false // progress peform request to disable otp method + inProgress: false, // progress peform request to disable otp method }), components: { - confirm: Confirm + confirm: Confirm, }, computed: { - isActivated () { + isActivated() { return this.settings.totp }, ...mapState({ - backendInteractor: (state) => state.api.backendInteractor - }) + backendInteractor: (state) => state.api.backendInteractor, + }), }, methods: { - doActivate () { + doActivate() { this.$emit('activate') }, - cancelDeactivate () { this.deactivate = false }, - doDeactivate () { + cancelDeactivate() { + this.deactivate = false + }, + doDeactivate() { this.error = null this.deactivate = true }, - confirmDeactivate () { // confirm deactivate TOTP method + confirmDeactivate() { + // confirm deactivate TOTP method this.error = null this.inProgress = true - this.backendInteractor.mfaDisableOTP({ - password: this.currentPassword - }) + this.backendInteractor + .mfaDisableOTP({ + password: this.currentPassword, + }) .then((res) => { this.inProgress = false if (res.error) { @@ -44,6 +48,6 @@ export default { this.deactivate = false this.$emit('deactivate') }) - } - } + }, + }, } diff --git a/src/components/settings_modal/tabs/security_tab/security_tab.js b/src/components/settings_modal/tabs/security_tab/security_tab.js index 04e0328cd..06cd17306 100644 --- a/src/components/settings_modal/tabs/security_tab/security_tab.js +++ b/src/components/settings_modal/tabs/security_tab/security_tab.js @@ -5,7 +5,7 @@ import localeService from 'src/services/locale/locale.service.js' import { useOAuthTokensStore } from 'src/stores/oauth_tokens' const SecurityTab = { - data () { + data() { return { newEmail: '', changeEmailError: false, @@ -25,41 +25,44 @@ const SecurityTab = { listAliasesError: false, addAliasTarget: '', addedAlias: false, - addAliasError: false + addAliasError: false, } }, - created () { + created() { useOAuthTokensStore().fetchTokens() this.fetchAliases() }, components: { ProgressButton, Mfa, - Checkbox + Checkbox, }, computed: { - user () { + user() { return this.$store.state.users.currentUser }, - pleromaExtensionsAvailable () { + pleromaExtensionsAvailable() { return this.$store.state.instance.pleromaExtensionsAvailable }, - oauthTokens () { - return useOAuthTokensStore().tokens.map(oauthToken => { + oauthTokens() { + return useOAuthTokensStore().tokens.map((oauthToken) => { return { id: oauthToken.id, appName: oauthToken.app_name, - validUntil: new Date(oauthToken.valid_until).toLocaleDateString(localeService.internalToBrowserLocale(this.$i18n.locale)) + validUntil: new Date(oauthToken.valid_until).toLocaleDateString( + localeService.internalToBrowserLocale(this.$i18n.locale), + ), } }) - } + }, }, methods: { - confirmDelete () { + confirmDelete() { this.deletingAccount = true }, - deleteAccount () { - this.$store.state.api.backendInteractor.deleteAccount({ password: this.deleteAccountConfirmPasswordInput }) + deleteAccount() { + this.$store.state.api.backendInteractor + .deleteAccount({ password: this.deleteAccountConfirmPasswordInput }) .then((res) => { if (res.status === 'success') { this.$store.dispatch('logout') @@ -69,13 +72,14 @@ const SecurityTab = { } }) }, - changePassword () { + changePassword() { const params = { password: this.changePasswordInputs[0], newPassword: this.changePasswordInputs[1], - newPasswordConfirmation: this.changePasswordInputs[2] + newPasswordConfirmation: this.changePasswordInputs[2], } - this.$store.state.api.backendInteractor.changePassword(params) + this.$store.state.api.backendInteractor + .changePassword(params) .then((res) => { if (res.status === 'success') { this.changedPassword = true @@ -87,12 +91,13 @@ const SecurityTab = { } }) }, - changeEmail () { + changeEmail() { const params = { email: this.newEmail, - password: this.changeEmailPassword + password: this.changeEmailPassword, } - this.$store.state.api.backendInteractor.changeEmail(params) + this.$store.state.api.backendInteractor + .changeEmail(params) .then((res) => { if (res.status === 'success') { this.changedEmail = true @@ -103,12 +108,13 @@ const SecurityTab = { } }) }, - moveAccount () { + moveAccount() { const params = { targetAccount: this.moveAccountTarget, - password: this.moveAccountPassword + password: this.moveAccountPassword, } - this.$store.state.api.backendInteractor.moveAccount(params) + this.$store.state.api.backendInteractor + .moveAccount(params) .then((res) => { if (res.status === 'success') { this.movedAccount = true @@ -119,12 +125,14 @@ const SecurityTab = { } }) }, - removeAlias (alias) { - this.$store.state.api.backendInteractor.deleteAlias({ alias }) + removeAlias(alias) { + this.$store.state.api.backendInteractor + .deleteAlias({ alias }) .then(() => this.fetchAliases()) }, - addAlias () { - this.$store.state.api.backendInteractor.addAlias({ alias: this.addAliasTarget }) + addAlias() { + this.$store.state.api.backendInteractor + .addAlias({ alias: this.addAliasTarget }) .then(() => { this.addedAlias = true this.addAliasError = false @@ -136,8 +144,9 @@ const SecurityTab = { }) .then(() => this.fetchAliases()) }, - fetchAliases () { - this.$store.state.api.backendInteractor.listAliases() + fetchAliases() { + this.$store.state.api.backendInteractor + .listAliases() .then((res) => { this.aliases = res.aliases this.listAliasesError = false @@ -146,16 +155,16 @@ const SecurityTab = { this.listAliasesError = error.error }) }, - logout () { + logout() { this.$store.dispatch('logout') this.$router.replace('/') }, - revokeToken (id) { + revokeToken(id) { if (window.confirm(`${this.$i18n.t('settings.revoke_token')}?`)) { useOAuthTokensStore().revokeToken(id) } - } - } + }, + }, } export default SecurityTab diff --git a/src/components/settings_modal/tabs/style_tab/style_tab.js b/src/components/settings_modal/tabs/style_tab/style_tab.js index 35626a103..72ddefedd 100644 --- a/src/components/settings_modal/tabs/style_tab/style_tab.js +++ b/src/components/settings_modal/tabs/style_tab/style_tab.js @@ -1,4 +1,11 @@ -import { ref, reactive, computed, watch, provide, getCurrentInstance } from 'vue' +import { + ref, + reactive, + computed, + watch, + provide, + getCurrentInstance, +} from 'vue' import { useInterfaceStore } from 'src/stores/interface' import { get, set, unset, throttle } from 'lodash' @@ -19,19 +26,28 @@ import Preview from '../old_theme_tab/theme_preview.vue' import VirtualDirectivesTab from './virtual_directives_tab.vue' -import { createStyleSheet, adoptStyleSheets } from 'src/services/style_setter/style_setter.js' -import { init, findColor } from 'src/services/theme_data/theme_data_3.service.js' +import { + createStyleSheet, + adoptStyleSheets, +} from 'src/services/style_setter/style_setter.js' +import { + init, + findColor, +} from 'src/services/theme_data/theme_data_3.service.js' import { getCssRules } from 'src/services/theme_data/css_utils.js' import { serialize } from 'src/services/theme_data/iss_serializer.js' -import { deserializeShadow, deserialize } from 'src/services/theme_data/iss_deserializer.js' +import { + deserializeShadow, + deserialize, +} from 'src/services/theme_data/iss_deserializer.js' import { rgb2hex, hex2rgb, - getContrastRatio + getContrastRatio, } from 'src/services/color_convert/color_convert.js' import { newImporter, - newExporter + newExporter, } from 'src/services/export_import/export_import.js' import { library } from '@fortawesome/fontawesome-svg-core' @@ -40,7 +56,7 @@ import { faFolderOpen, faFile, faArrowsRotate, - faCheck + faCheck, } from '@fortawesome/free-solid-svg-icons' // helper for debugging @@ -48,15 +64,10 @@ import { const toValue = (x) => JSON.parse(JSON.stringify(x === undefined ? 'null' : x)) // helper to make states comparable -const normalizeStates = (states) => ['normal', ...(states?.filter(x => x !== 'normal') || [])].join(':') +const normalizeStates = (states) => + ['normal', ...(states?.filter((x) => x !== 'normal') || [])].join(':') -library.add( - faFile, - faFloppyDisk, - faFolderOpen, - faArrowsRotate, - faCheck -) +library.add(faFile, faFloppyDisk, faFolderOpen, faArrowsRotate, faCheck) export default { components: { @@ -74,18 +85,22 @@ export default { RoundnessInput, ContrastRatio, Preview, - VirtualDirectivesTab + VirtualDirectivesTab, }, - setup () { + setup() { const exports = {} const interfaceStore = useInterfaceStore() // All rules that are made by editor const allEditedRules = ref(interfaceStore.styleDataUsed || {}) const styleDataUsed = computed(() => interfaceStore.styleDataUsed) - watch([styleDataUsed], () => { - onImport(interfaceStore.styleDataUsed) - }, { once: true }) + watch( + [styleDataUsed], + () => { + onImport(interfaceStore.styleDataUsed) + }, + { once: true }, + ) exports.isActive = computed(() => { const tabSwitcher = getCurrentInstance().parent.ctx @@ -105,7 +120,7 @@ export default { ` author: ${exports.author.value};`, ` license: ${exports.license.value};`, ` website: ${exports.website.value};`, - '}' + '}', ].join('\n') }) @@ -115,8 +130,8 @@ export default { name: exports.name.value, author: exports.author.value, license: exports.license.value, - website: exports.website.value - } + website: exports.website.value, + }, })) // ## Palette stuff @@ -131,7 +146,7 @@ export default { cRed: '#FF0000', cBlue: '#0095ff', cGreen: '#0fa00f', - cOrange: '#ffa500' + cOrange: '#ffa500', }, { name: 'light', @@ -144,8 +159,8 @@ export default { cRed: '#d31014', cGreen: '#0fa00f', cOrange: '#ffa500', - border: '#d8e6f9' - } + border: '#d8e6f9', + }, ]) exports.palettes = palettes @@ -163,12 +178,12 @@ export default { const selectedPaletteId = ref(0) const selectedPalette = computed({ - get () { + get() { return palettes[selectedPaletteId.value] }, - set (newPalette) { + set(newPalette) { palettes[selectedPaletteId.value] = newPalette - } + }, }) exports.selectedPaletteId = selectedPaletteId exports.selectedPalette = selectedPalette @@ -186,49 +201,50 @@ export default { cRed: '#FF0000', cBlue: '#0095ff', cGreen: '#0fa00f', - cOrange: '#ffa500' + cOrange: '#ffa500', }) // Raw format const palettesRule = computed(() => { - return palettes.map(palette => { + return palettes.map((palette) => { const { name, ...rest } = palette return { component: '@palette', variant: name, - directives: Object - .entries(rest) + directives: Object.entries(rest) .filter(([k, v]) => v && k) - .reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {}) + .reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {}), } }) }) // Text format const palettesOut = computed(() => { - return palettes.map(({ name, ...palette }) => { - const entries = Object - .entries(palette) - .filter(([k, v]) => v && k) - .map(([slot, data]) => ` ${slot}: ${data};`) - .join('\n') + return palettes + .map(({ name, ...palette }) => { + const entries = Object.entries(palette) + .filter(([k, v]) => v && k) + .map(([slot, data]) => ` ${slot}: ${data};`) + .join('\n') - return `@palette.${name} {\n${entries}\n}` - }).join('\n\n') + return `@palette.${name} {\n${entries}\n}` + }) + .join('\n\n') }) // ## Components stuff // Getting existing components const componentsContext = import.meta.glob( ['/src/**/*.style.js', '/src/**/*.style.json'], - { eager: true } + { eager: true }, ) const componentKeysAll = Object.keys(componentsContext) const componentsMap = new Map( componentKeysAll - .map( - key => [key, componentsContext[key].default] - ).filter(([, component]) => !component.virtual && !component.notEditable) + .map((key) => [key, componentsContext[key].default]) + .filter( + ([, component]) => !component.virtual && !component.notEditable, + ), ) exports.componentsMap = componentsMap const componentKeys = [...componentsMap.keys()] @@ -238,16 +254,24 @@ export default { const selectedComponentKey = ref(componentsMap.keys().next().value) exports.selectedComponentKey = selectedComponentKey - const selectedComponent = computed(() => componentsMap.get(selectedComponentKey.value)) + const selectedComponent = computed(() => + componentsMap.get(selectedComponentKey.value), + ) const selectedComponentName = computed(() => selectedComponent.value.name) // Selection basis exports.selectedComponentVariants = computed(() => { - return Object.keys({ normal: null, ...(selectedComponent.value.variants || {}) }) + return Object.keys({ + normal: null, + ...(selectedComponent.value.variants || {}), + }) }) exports.selectedComponentStates = computed(() => { - const all = Object.keys({ normal: null, ...(selectedComponent.value.states || {}) }) - return all.filter(x => x !== 'normal') + const all = Object.keys({ + normal: null, + ...(selectedComponent.value.states || {}), + }) + return all.filter((x) => x !== 'normal') }) // selection @@ -269,51 +293,49 @@ export default { selectedState.clear() } - watch( - selectedComponentName, - updateSelectedComponent - ) + watch(selectedComponentName, updateSelectedComponent) // ### Rules stuff aka meat and potatoes // The native structure of separate rules and the child -> parent // relation isn't very convenient for editor, we replace the array // and child -> parent structure with map and parent -> child structure - const rulesToEditorFriendly = (rules, root = {}) => rules.reduce((acc, rule) => { - const { parent: rParent, component: rComponent } = rule - const parent = rParent ?? rule - const hasChildren = !!rParent - const child = hasChildren ? rule : null + const rulesToEditorFriendly = (rules, root = {}) => + rules.reduce((acc, rule) => { + const { parent: rParent, component: rComponent } = rule + const parent = rParent ?? rule + const hasChildren = !!rParent + const child = hasChildren ? rule : null - const { - component: pComponent, - variant: pVariant = 'normal', - state: pState = [] // no relation to Intel CPUs whatsoever - } = parent - - const pPath = `${hasChildren ? pComponent : rComponent}.${pVariant}.${normalizeStates(pState)}` - - let output = get(acc, pPath) - if (!output) { - set(acc, pPath, {}) - output = get(acc, pPath) - } - - if (hasChildren) { - output._children = output._children ?? {} const { - component: cComponent, - variant: cVariant = 'normal', - state: cState = [], - directives - } = child + component: pComponent, + variant: pVariant = 'normal', + state: pState = [], // no relation to Intel CPUs whatsoever + } = parent - const cPath = `${cComponent}.${cVariant}.${normalizeStates(cState)}` - set(output._children, cPath, { directives }) - } else { - output.directives = parent.directives - } - return acc - }, root) + const pPath = `${hasChildren ? pComponent : rComponent}.${pVariant}.${normalizeStates(pState)}` + + let output = get(acc, pPath) + if (!output) { + set(acc, pPath, {}) + output = get(acc, pPath) + } + + if (hasChildren) { + output._children = output._children ?? {} + const { + component: cComponent, + variant: cVariant = 'normal', + state: cState = [], + directives, + } = child + + const cPath = `${cComponent}.${cVariant}.${normalizeStates(cState)}` + set(output._children, cPath, { directives }) + } else { + output.directives = parent.directives + } + return acc + }, root) const editorFriendlyFallbackStructure = computed(() => { const root = {} @@ -323,7 +345,7 @@ export default { const { defaultRules, name } = componentValue rulesToEditorFriendly( defaultRules.map((rule) => ({ ...rule, component: name })), - root + root, ) }) @@ -333,79 +355,111 @@ export default { // Checking whether component can support some "directives" which // are actually virtual subcomponents, i.e. Text, Link etc exports.componentHas = (subComponent) => { - return !!selectedComponent.value.validInnerComponents?.find(x => x === subComponent) + return !!selectedComponent.value.validInnerComponents?.find( + (x) => x === subComponent, + ) } // Path for lodash's get and set const getPath = (component, directive) => { - const pathSuffix = component ? `._children.${component}.normal.normal` : '' + const pathSuffix = component + ? `._children.${component}.normal.normal` + : '' const path = `${selectedComponentName.value}.${selectedVariant.value}.${normalizeStates([...selectedState])}${pathSuffix}.directives.${directive}` return path } // Templates for directives - const isElementPresent = (component, directive, defaultValue = '') => computed({ - get () { - return get(allEditedRules.value, getPath(component, directive)) != null - }, - set (value) { - if (value) { - const fallback = get( - editorFriendlyFallbackStructure.value, - getPath(component, directive) + const isElementPresent = (component, directive, defaultValue = '') => + computed({ + get() { + return ( + get(allEditedRules.value, getPath(component, directive)) != null ) - set(allEditedRules.value, getPath(component, directive), fallback ?? defaultValue) - } else { - unset(allEditedRules.value, getPath(component, directive)) - } - exports.updateOverallPreview() - } - }) + }, + set(value) { + if (value) { + const fallback = get( + editorFriendlyFallbackStructure.value, + getPath(component, directive), + ) + set( + allEditedRules.value, + getPath(component, directive), + fallback ?? defaultValue, + ) + } else { + unset(allEditedRules.value, getPath(component, directive)) + } + exports.updateOverallPreview() + }, + }) - const getEditedElement = (component, directive, postProcess = x => x) => computed({ - get () { - let usedRule - const fallback = editorFriendlyFallbackStructure.value - const real = allEditedRules.value - const path = getPath(component, directive) + const getEditedElement = (component, directive, postProcess = (x) => x) => + computed({ + get() { + let usedRule + const fallback = editorFriendlyFallbackStructure.value + const real = allEditedRules.value + const path = getPath(component, directive) - usedRule = get(real, path) // get real - if (usedRule === '') { - return usedRule - } - if (!usedRule) { - usedRule = get(fallback, path) - } + usedRule = get(real, path) // get real + if (usedRule === '') { + return usedRule + } + if (!usedRule) { + usedRule = get(fallback, path) + } - return postProcess(usedRule) - }, - set (value) { - if (value != null) { - set(allEditedRules.value, getPath(component, directive), value) - } else { - unset(allEditedRules.value, getPath(component, directive)) - } - exports.updateOverallPreview() - } - }) + return postProcess(usedRule) + }, + set(value) { + if (value != null) { + set(allEditedRules.value, getPath(component, directive), value) + } else { + unset(allEditedRules.value, getPath(component, directive)) + } + exports.updateOverallPreview() + }, + }) // All the editable stuff for the component exports.editedBackgroundColor = getEditedElement(null, 'background') - exports.isBackgroundColorPresent = isElementPresent(null, 'background', '#FFFFFF') + exports.isBackgroundColorPresent = isElementPresent( + null, + 'background', + '#FFFFFF', + ) exports.editedOpacity = getEditedElement(null, 'opacity') exports.isOpacityPresent = isElementPresent(null, 'opacity', 1) exports.editedRoundness = getEditedElement(null, 'roundness') exports.isRoundnessPresent = isElementPresent(null, 'roundness', '0') exports.editedTextColor = getEditedElement('Text', 'textColor') - exports.isTextColorPresent = isElementPresent('Text', 'textColor', '#000000') + exports.isTextColorPresent = isElementPresent( + 'Text', + 'textColor', + '#000000', + ) exports.editedTextAuto = getEditedElement('Text', 'textAuto') exports.isTextAutoPresent = isElementPresent('Text', 'textAuto', '#000000') exports.editedLinkColor = getEditedElement('Link', 'textColor') - exports.isLinkColorPresent = isElementPresent('Link', 'textColor', '#000080') + exports.isLinkColorPresent = isElementPresent( + 'Link', + 'textColor', + '#000080', + ) exports.editedIconColor = getEditedElement('Icon', 'textColor') - exports.isIconColorPresent = isElementPresent('Icon', 'textColor', '#909090') + exports.isIconColorPresent = isElementPresent( + 'Icon', + 'textColor', + '#909090', + ) exports.editedBorderColor = getEditedElement('Border', 'textColor') - exports.isBorderColorPresent = isElementPresent('Border', 'textColor', '#909090') + exports.isBorderColorPresent = isElementPresent( + 'Border', + 'textColor', + '#909090', + ) const getContrast = (bg, text) => { try { @@ -422,7 +476,7 @@ export default { aaa: ratio >= 7, // same but for 18pt+ texts laa: ratio >= 3, - laaa: ratio >= 4.5 + laaa: ratio >= 4.5, } } catch (e) { console.warn('Failure computing contrast', e) @@ -431,7 +485,7 @@ export default { } const normalizeShadows = (shadows) => { - return shadows?.map(shadow => { + return shadows?.map((shadow) => { if (typeof shadow === 'object') { return shadow } @@ -455,7 +509,8 @@ export default { const editedSubShadowId = ref(null) exports.editedSubShadowId = editedSubShadowId const editedSubShadow = computed(() => { - if (editedShadow.value == null || editedSubShadowId.value == null) return null + if (editedShadow.value == null || editedSubShadowId.value == null) + return null return editedShadow.value[editedSubShadowId.value] }) exports.editedSubShadow = editedSubShadow @@ -473,7 +528,7 @@ export default { newEditedShadow[editedSubShadowId.value] = { ...newEditedShadow[editedSubShadowId.value], - [axis]: value + [axis]: value, } editedShadow.value = newEditedShadow @@ -513,12 +568,12 @@ export default { component, variant, state, - directives: stateData.directives || {} + directives: stateData.directives || {}, } if (parent) { result.parent = { - component: parent + component: parent, } } @@ -526,13 +581,15 @@ export default { // Currently we only support single depth for simplicity's sake if (!parent) { - Object.entries(stateData._children || {}).forEach(([cName, child]) => convert(cName, child, component)) + Object.entries(stateData._children || {}).forEach( + ([cName, child]) => convert(cName, child, component), + ) } }) }) } - [...componentsMap.values()].forEach(({ name }) => { + ;[...componentsMap.values()].forEach(({ name }) => { convert(name, allEditedRules.value[name]) }) @@ -540,21 +597,20 @@ export default { }) const allCustomVirtualDirectives = [...componentsMap.values()] - .map(c => { - return c - .defaultRules - .filter(c => c.component === 'Root') - .map(x => Object.entries(x.directives)) + .map((c) => { + return c.defaultRules + .filter((c) => c.component === 'Root') + .map((x) => Object.entries(x.directives)) .flat() }) - .filter(x => x) + .filter((x) => x) .flat() .map(([name, value]) => { const [valType, valVal] = value.split('|') return { name: name.substring(2), valType: valType?.trim(), - value: valVal?.trim() + value: valVal?.trim(), } }) @@ -568,8 +624,11 @@ export default { const virtualDirectivesRule = computed(() => ({ component: 'Root', directives: Object.fromEntries( - virtualDirectives.value.map(vd => [`--${vd.name}`, `${vd.valType} | ${vd.value}`]) - ) + virtualDirectives.value.map((vd) => [ + `--${vd.name}`, + `${vd.valType} | ${vd.value}`, + ]), + ), })) // Text format @@ -577,16 +636,19 @@ export default { return [ 'Root {', ...virtualDirectives.value - .filter(vd => vd.name && vd.valType && vd.value) - .map(vd => ` --${vd.name}: ${vd.valType} | ${vd.value};`), - '}' + .filter((vd) => vd.name && vd.valType && vd.value) + .map((vd) => ` --${vd.name}: ${vd.valType} | ${vd.value};`), + '}', ].join('\n') }) exports.computeColor = (color) => { let computedColor try { - computedColor = findColor(color, { dynamicVars: dynamicVars.value, staticVars: staticVars.value }) + computedColor = findColor(color, { + dynamicVars: dynamicVars.value, + staticVars: staticVars.value, + }) if (computedColor) { return rgb2hex(computedColor) } @@ -600,7 +662,7 @@ export default { exports.contrast = computed(() => { return getContrast( exports.computeColor(previewColors.value.background), - exports.computeColor(previewColors.value.text) + exports.computeColor(previewColors.value.text), ) }) @@ -609,30 +671,38 @@ export default { filename: () => exports.name.value ?? 'pleroma_theme', mime: 'text/plain', extension: 'iss', - getExportedObject: () => exportStyleData.value + getExportedObject: () => exportStyleData.value, }) - const onImport = parsed => { - const editorComponents = parsed.filter(x => x.component.startsWith('@')) - const rootComponent = parsed.find(x => x.component === 'Root') - const rules = parsed.filter(x => !x.component.startsWith('@') && x.component !== 'Root') - const metaIn = editorComponents.find(x => x.component === '@meta').directives - const palettesIn = editorComponents.filter(x => x.component === '@palette') + const onImport = (parsed) => { + const editorComponents = parsed.filter((x) => x.component.startsWith('@')) + const rootComponent = parsed.find((x) => x.component === 'Root') + const rules = parsed.filter( + (x) => !x.component.startsWith('@') && x.component !== 'Root', + ) + const metaIn = editorComponents.find( + (x) => x.component === '@meta', + ).directives + const palettesIn = editorComponents.filter( + (x) => x.component === '@palette', + ) exports.name.value = metaIn.name exports.license.value = metaIn.license exports.author.value = metaIn.author exports.website.value = metaIn.website - const newVirtualDirectives = Object - .entries(rootComponent.directives) - .map(([name, value]) => { - const [valType, valVal] = value.split('|').map(x => x.trim()) + const newVirtualDirectives = Object.entries(rootComponent.directives).map( + ([name, value]) => { + const [valType, valVal] = value.split('|').map((x) => x.trim()) return { name: name.substring(2), valType, value: valVal } - }) + }, + ) virtualDirectives.value = newVirtualDirectives - onPalettesUpdate(palettesIn.map(x => ({ name: x.variant, ...x.directives }))) + onPalettesUpdate( + palettesIn.map((x) => ({ name: x.variant, ...x.directives })), + ) allEditedRules.value = rulesToEditorFriendly(rules) @@ -641,12 +711,17 @@ export default { const styleImporter = newImporter({ accept: '.iss', - parser (string) { return deserialize(string) }, - onImportFailure (result) { - console.error('Failure importing style:', result) - useInterfaceStore().pushGlobalNotice({ messageKey: 'settings.invalid_theme_imported', level: 'error' }) + parser(string) { + return deserialize(string) }, - onImport + onImportFailure(result) { + console.error('Failure importing style:', result) + useInterfaceStore().pushGlobalNotice({ + messageKey: 'settings.invalid_theme_imported', + level: 'error', + }) + }, + onImport, }) // Raw format @@ -654,7 +729,7 @@ export default { metaRule.value, ...palettesRule.value, virtualDirectivesRule.value, - ...editorFriendlyToOriginal.value + ...editorFriendlyToOriginal.value, ]) // Text format @@ -663,7 +738,7 @@ export default { metaOut.value, palettesOut.value, virtualDirectivesOut.value, - serialize(editorFriendlyToOriginal.value) + serialize(editorFriendlyToOriginal.value), ].join('\n\n') }) @@ -689,7 +764,9 @@ export default { watch([overallPreviewRules], () => { let css = null try { - css = getCssRules(overallPreviewRules.value).map(r => r.replace('html', '&')) + css = getCssRules(overallPreviewRules.value).map((r) => + r.replace('html', '&'), + ) } catch (e) { console.error(e) return @@ -698,11 +775,9 @@ export default { const sheet = createStyleSheet('style-tab-overall-preview', 90) sheet.clear() - sheet.addRule([ - '#edited-style-preview {\n', - css.join('\n'), - '\n}' - ].join('')) + sheet.addRule( + ['#edited-style-preview {\n', css.join('\n'), '\n}'].join(''), + ) sheet.ready = true adoptStyleSheets() }) @@ -715,15 +790,14 @@ export default { { component: 'Root', directives: Object.fromEntries( - Object - .entries(selectedPalette.value) + Object.entries(selectedPalette.value) .filter(([k, v]) => k && v && k !== 'name') - .map(([k, v]) => [`--${k}`, `color | ${v}`]) - ) - } + .map(([k, v]) => [`--${k}`, `color | ${v}`]), + ), + }, ], ultimateBackgroundColor: '#000000', - debug: true + debug: true, }).eager } catch (e) { console.error('Could not compile preview theme', e) @@ -733,30 +807,32 @@ export default { // // Apart from "hover" we can't really show how component looks like in // certain states, so we have to fake them. - const simulatePseudoSelectors = (css, prefix) => css - .replace(prefix, '.preview-block') - .replace(':active', '.preview-active') - .replace(':hover', '.preview-hover') - .replace(':active', '.preview-active') - .replace(':focus', '.preview-focus') - .replace(':focus-within', '.preview-focus-within') - .replace(':disabled', '.preview-disabled') + const simulatePseudoSelectors = (css, prefix) => + css + .replace(prefix, '.preview-block') + .replace(':active', '.preview-active') + .replace(':hover', '.preview-hover') + .replace(':active', '.preview-active') + .replace(':focus', '.preview-focus') + .replace(':focus-within', '.preview-focus-within') + .replace(':disabled', '.preview-disabled') const previewRules = computed(() => { - const filtered = overallPreviewRules.value.filter(r => { + const filtered = overallPreviewRules.value.filter((r) => { const componentMatch = r.component === selectedComponentName.value - const parentComponentMatch = r.parent?.component === selectedComponentName.value + const parentComponentMatch = + r.parent?.component === selectedComponentName.value if (!componentMatch && !parentComponentMatch) return false const rule = parentComponentMatch ? r.parent : r if (rule.component !== selectedComponentName.value) return false if (rule.variant !== selectedVariant.value) return false - const ruleState = new Set(rule.state.filter(x => x !== 'normal')) - const differenceA = [...ruleState].filter(x => !selectedState.has(x)) - const differenceB = [...selectedState].filter(x => !ruleState.has(x)) - return (differenceA.length + differenceB.length) === 0 + const ruleState = new Set(rule.state.filter((x) => x !== 'normal')) + const differenceA = [...ruleState].filter((x) => !selectedState.has(x)) + const differenceB = [...selectedState].filter((x) => !ruleState.has(x)) + return differenceA.length + differenceB.length === 0 }) const sorted = [...filtered] - .filter(x => x.component === selectedComponentName.value) + .filter((x) => x.component === selectedComponentName.value) .sort((a, b) => { const aSelectorLength = a.selector.split(/ /g).length const bSelectorLength = b.selector.split(/ /g).length @@ -765,27 +841,32 @@ export default { const prefix = sorted[0].selector - return filtered.filter(x => x.selector.startsWith(prefix)) + return filtered.filter((x) => x.selector.startsWith(prefix)) }) exports.previewClass = computed(() => { const selectors = [] - if (!!selectedComponent.value.variants?.normal || selectedVariant.value !== 'normal') { + if ( + !!selectedComponent.value.variants?.normal || + selectedVariant.value !== 'normal' + ) { selectors.push(selectedComponent.value.variants[selectedVariant.value]) } if (selectedState.size > 0) { - selectedState.forEach(state => { + selectedState.forEach((state) => { const original = selectedComponent.value.states[state] selectors.push(simulatePseudoSelectors(original)) }) } - return selectors.map(x => x.substring(1)).join('') + return selectors.map((x) => x.substring(1)).join('') }) exports.previewCss = computed(() => { try { const prefix = previewRules.value[0].selector - const scoped = getCssRules(previewRules.value).map(x => simulatePseudoSelectors(x, prefix)) + const scoped = getCssRules(previewRules.value).map((x) => + simulatePseudoSelectors(x, prefix), + ) return scoped.join('\n') } catch (e) { console.error('Invalid ruleset', e) @@ -798,7 +879,7 @@ export default { }) const staticVars = computed(() => { - const rootComponent = overallPreviewRules.value.find(r => { + const rootComponent = overallPreviewRules.value.find((r) => { return r.component === 'Root' }) const rootDirectivesEntries = Object.entries(rootComponent.directives) @@ -807,7 +888,10 @@ export default { .filter(([k, v]) => k.startsWith('--') && v.startsWith('color | ')) .map(([k, v]) => [k.substring(2), v.substring('color | '.length)]) .forEach(([k, v]) => { - directives[k] = findColor(v, { dynamicVars: {}, staticVars: directives }) + directives[k] = findColor(v, { + dynamicVars: {}, + staticVars: directives, + }) }) return directives }) @@ -816,13 +900,18 @@ export default { const previewColors = computed(() => { const stacked = dynamicVars.value.stacked - const background = typeof stacked === 'string' ? stacked : rgb2hex(stacked) + const background = + typeof stacked === 'string' ? stacked : rgb2hex(stacked) return { - text: previewRules.value.find(r => r.component === 'Text')?.virtualDirectives['--text'], - link: previewRules.value.find(r => r.component === 'Link')?.virtualDirectives['--link'], - border: previewRules.value.find(r => r.component === 'Border')?.virtualDirectives['--border'], - icon: previewRules.value.find(r => r.component === 'Icon')?.virtualDirectives['--icon'], - background + text: previewRules.value.find((r) => r.component === 'Text') + ?.virtualDirectives['--text'], + link: previewRules.value.find((r) => r.component === 'Link') + ?.virtualDirectives['--link'], + border: previewRules.value.find((r) => r.component === 'Border') + ?.virtualDirectives['--border'], + icon: previewRules.value.find((r) => r.component === 'Icon') + ?.virtualDirectives['--icon'], + background, } }) exports.previewColors = previewColors @@ -836,11 +925,11 @@ export default { palettes, selectedPalette, selectedState, - selectedVariant + selectedVariant, ], - updateOverallPreview + updateOverallPreview, ) return exports - } + }, } diff --git a/src/components/settings_modal/tabs/style_tab/virtual_directives_tab.js b/src/components/settings_modal/tabs/style_tab/virtual_directives_tab.js index d98dea615..be054fd68 100644 --- a/src/components/settings_modal/tabs/style_tab/virtual_directives_tab.js +++ b/src/components/settings_modal/tabs/style_tab/virtual_directives_tab.js @@ -16,11 +16,11 @@ export default { Select, SelectMotion, ShadowControl, - ColorInput + ColorInput, }, props: ['modelValue'], emits: ['update:modelValue'], - setup (props, context) { + setup(props, context) { const exports = {} const emit = context.emit @@ -32,23 +32,23 @@ export default { exports.selectedVirtualDirectiveId = selectedVirtualDirectiveId const selectedVirtualDirective = computed({ - get () { + get() { return props.modelValue[selectedVirtualDirectiveId.value] }, - set (value) { + set(value) { const newVD = [...props.modelValue] newVD[selectedVirtualDirectiveId.value] = value emit('update:modelValue', newVD) - } + }, }) exports.selectedVirtualDirective = selectedVirtualDirective exports.selectedVirtualDirectiveValType = computed({ - get () { + get() { return props.modelValue[selectedVirtualDirectiveId.value].valType }, - set (value) { + set(value) { const newValType = value let newValue switch (value) { @@ -65,9 +65,9 @@ export default { props.modelValue[selectedVirtualDirectiveId.value] = { name: newName, value: newValue, - valType: newValType + valType: newValType, } - } + }, }) const draftVirtualDirectiveValid = ref(true) @@ -83,7 +83,9 @@ export default { if (Array.isArray(directive.value)) { draftVirtualDirective.value = normalizeShadows(directive.value) } else { - const splitShadow = directive.value.split(/,/g).map(x => x.trim()) + const splitShadow = directive.value + .split(/,/g) + .map((x) => x.trim()) draftVirtualDirective.value = normalizeShadows(splitShadow) } break @@ -96,7 +98,7 @@ export default { break } }, - { immediate: true } + { immediate: true }, ) watch( @@ -106,11 +108,12 @@ export default { switch (selectedVirtualDirective.value.valType) { case 'shadow': { props.modelValue[selectedVirtualDirectiveId.value].value = - directive.map(x => serializeShadow(x)).join(', ') + directive.map((x) => serializeShadow(x)).join(', ') break } default: - props.modelValue[selectedVirtualDirectiveId.value].value = directive + props.modelValue[selectedVirtualDirectiveId.value].value = + directive } draftVirtualDirectiveValid.value = true } catch (e) { @@ -118,15 +121,15 @@ export default { draftVirtualDirectiveValid.value = false } }, - { immediate: true } + { immediate: true }, ) exports.getNewVirtualDirective = () => ({ name: 'newDirective', valType: 'generic', - value: 'foobar' + value: 'foobar', }) return exports - } + }, } diff --git a/src/components/shadow_control/shadow_control.js b/src/components/shadow_control/shadow_control.js index 1722454db..f0693599f 100644 --- a/src/components/shadow_control/shadow_control.js +++ b/src/components/shadow_control/shadow_control.js @@ -8,23 +8,24 @@ import ComponentPreview from 'src/components/component_preview/component_preview import { rgb2hex } from 'src/services/color_convert/color_convert.js' import { serializeShadow } from 'src/services/theme_data/iss_serializer.js' import { deserializeShadow } from 'src/services/theme_data/iss_deserializer.js' -import { getCssShadow, getCssShadowFilter } from 'src/services/theme_data/css_utils.js' -import { findShadow, findColor } from 'src/services/theme_data/theme_data_3.service.js' +import { + getCssShadow, + getCssShadowFilter, +} from 'src/services/theme_data/css_utils.js' +import { + findShadow, + findColor, +} from 'src/services/theme_data/theme_data_3.service.js' import { library } from '@fortawesome/fontawesome-svg-core' import { throttle, flattenDeep } from 'lodash' import { faTimes, faChevronDown, faChevronUp, - faPlus + faPlus, } from '@fortawesome/free-solid-svg-icons' -library.add( - faChevronDown, - faChevronUp, - faTimes, - faPlus -) +library.add(faChevronDown, faChevronUp, faTimes, faPlus) const toModel = (input) => { if (typeof input === 'object') { @@ -36,7 +37,7 @@ const toModel = (input) => { inset: false, color: '#000000', alpha: 1, - ...input + ...input, } } else if (typeof input === 'string') { return input @@ -51,13 +52,13 @@ export default { 'noPreview', 'disabled', 'staticVars', - 'compact' + 'compact', ], emits: ['update:modelValue', 'subShadowSelected'], - data () { + data() { return { selectedId: 0, - invalid: false + invalid: false, } }, components: { @@ -67,27 +68,27 @@ export default { SelectMotion, Checkbox, Popover, - ComponentPreview + ComponentPreview, }, computed: { cValue: { - get () { + get() { return (this.modelValue ?? this.fallback ?? []).map(toModel) }, - set (newVal) { + set(newVal) { this.$emit('update:modelValue', newVal) - } + }, }, selectedType: { - get () { + get() { return typeof this.selected }, - set (newType) { + set(newType) { this.selected = toModel(newType === 'object' ? {} : '') - } + }, }, selected: { - get () { + get() { const selected = this.cValue[this.selectedId] if (selected && typeof selected === 'object') { return { ...selected } @@ -96,24 +97,28 @@ export default { } return null }, - set (value) { + set(value) { this.cValue[this.selectedId] = toModel(value) this.$emit('update:modelValue', this.cValue) - } + }, }, - present () { + present() { return this.selected != null && this.modelValue != null }, - shadowsAreNull () { + shadowsAreNull() { return this.modelValue == null }, - currentFallback () { + currentFallback() { return this.fallback?.[this.selectedId] }, - getColorFallback () { + getColorFallback() { if (this.staticVars && this.selected?.color) { try { - const computedColor = findColor(this.selected.color, { dynamicVars: {}, staticVars: this.staticVars }, true) + const computedColor = findColor( + this.selected.color, + { dynamicVars: {}, staticVars: this.staticVars }, + true, + ) if (computedColor) return rgb2hex(computedColor) return null } catch (e) { @@ -124,22 +129,30 @@ export default { return this.currentFallback?.color } }, - style () { + style() { try { let result - const serialized = this.cValue.map(x => serializeShadow(x)).join(',') + const serialized = this.cValue.map((x) => serializeShadow(x)).join(',') serialized.split(/,/).map(deserializeShadow) // validate - const expandedShadow = flattenDeep(findShadow(this.cValue, { dynamicVars: {}, staticVars: this.staticVars })) - const fixedShadows = expandedShadow.map(x => ({ ...x, color: rgb2hex(x.color) })) + const expandedShadow = flattenDeep( + findShadow(this.cValue, { + dynamicVars: {}, + staticVars: this.staticVars, + }), + ) + const fixedShadows = expandedShadow.map((x) => ({ + ...x, + color: rgb2hex(x.color), + })) if (this.separateInset) { result = { filter: getCssShadowFilter(fixedShadows), - boxShadow: getCssShadow(fixedShadows, true) + boxShadow: getCssShadow(fixedShadows, true), } } else { result = { - boxShadow: getCssShadow(fixedShadows) + boxShadow: getCssShadow(fixedShadows), } } this.invalid = false @@ -148,23 +161,26 @@ export default { console.error('Invalid shadow', e) this.invalid = true } - } + }, }, watch: { - selected () { + selected() { this.$emit('subShadowSelected', this.selectedId) - } + }, }, methods: { - getNewSubshadow () { + getNewSubshadow() { return toModel(this.selected) }, - onSelectChange (id) { + onSelectChange(id) { this.selectedId = id }, - getSubshadowLabel (shadow, index) { + getSubshadowLabel(shadow, index) { if (typeof shadow === 'object') { - return shadow?.name ?? this.$t('settings.style.shadows.shadow_id', { value: index }) + return ( + shadow?.name ?? + this.$t('settings.style.shadows.shadow_id', { value: index }) + ) } else if (typeof shadow === 'string') { return shadow || this.$t('settings.style.shadows.empty_expression') } @@ -175,6 +191,6 @@ export default { this.cValue[this.selectedId].spread = 0 } this.$emit('update:modelValue', this.cValue) - }, 100) - } + }, 100), + }, } diff --git a/src/components/shout_panel/shout_panel.js b/src/components/shout_panel/shout_panel.js index af5e90364..72712ee50 100644 --- a/src/components/shout_panel/shout_panel.js +++ b/src/components/shout_panel/shout_panel.js @@ -1,54 +1,55 @@ import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' import { library } from '@fortawesome/fontawesome-svg-core' -import { - faBullhorn, - faTimes -} from '@fortawesome/free-solid-svg-icons' +import { faBullhorn, faTimes } from '@fortawesome/free-solid-svg-icons' import { useShoutStore } from 'src/stores/shout' -library.add( - faBullhorn, - faTimes -) +library.add(faBullhorn, faTimes) const shoutPanel = { props: ['floating'], - data () { + data() { return { currentMessage: '', channel: null, - collapsed: true + collapsed: true, } }, computed: { - messages () { + messages() { return useShoutStore().messages - } + }, }, methods: { - submit (message) { + submit(message) { useShoutStore().channel.push('new_msg', { text: message }, 10000) this.currentMessage = '' }, - togglePanel () { + togglePanel() { this.collapsed = !this.collapsed }, - userProfileLink (user) { - return generateProfileLink(user.id, user.username, this.$store.state.instance.restrictedNicknames) - } + userProfileLink(user) { + return generateProfileLink( + user.id, + user.username, + this.$store.state.instance.restrictedNicknames, + ) + }, }, watch: { - messages () { + messages() { const scrollEl = this.$el.querySelector('.chat-window') if (!scrollEl) return - if (scrollEl.scrollTop + scrollEl.offsetHeight + 20 > scrollEl.scrollHeight) { + if ( + scrollEl.scrollTop + scrollEl.offsetHeight + 20 > + scrollEl.scrollHeight + ) { this.$nextTick(() => { if (!scrollEl) return scrollEl.scrollTop = scrollEl.scrollHeight - scrollEl.offsetHeight }) } - } - } + }, + }, } export default shoutPanel diff --git a/src/components/side_drawer/side_drawer.js b/src/components/side_drawer/side_drawer.js index 7dbd38cb5..80bf95796 100644 --- a/src/components/side_drawer/side_drawer.js +++ b/src/components/side_drawer/side_drawer.js @@ -19,7 +19,7 @@ import { faInfoCircle, faCompass, faList, - faFilePen + faFilePen, } from '@fortawesome/free-solid-svg-icons' import { useShoutStore } from 'src/stores/shout' import { useInterfaceStore } from 'src/stores/interface' @@ -39,17 +39,20 @@ library.add( faInfoCircle, faCompass, faList, - faFilePen + faFilePen, ) const SideDrawer = { props: ['logout'], data: () => ({ closed: true, - closeGesture: undefined + closeGesture: undefined, }), - created () { - this.closeGesture = GestureService.swipeGesture(GestureService.DIRECTION_LEFT, this.toggleDrawer) + created() { + this.closeGesture = GestureService.swipeGesture( + GestureService.DIRECTION_LEFT, + this.toggleDrawer, + ) if (this.currentUser && this.currentUser.locked) { this.$store.dispatch('startFetchingFollowRequests') @@ -57,38 +60,40 @@ const SideDrawer = { }, components: { UserCard }, computed: { - currentUser () { + currentUser() { return this.$store.state.users.currentUser }, - shout () { return useShoutStore().joined }, - unseenNotifications () { + shout() { + return useShoutStore().joined + }, + unseenNotifications() { return unseenNotificationsFromStore(this.$store) }, - unseenNotificationsCount () { + unseenNotificationsCount() { return this.unseenNotifications.length }, - suggestionsEnabled () { + suggestionsEnabled() { return this.$store.state.instance.suggestionsEnabled }, - logo () { + logo() { return this.$store.state.instance.logo }, - hideSitename () { + hideSitename() { return this.$store.state.instance.hideSitename }, - sitename () { + sitename() { return this.$store.state.instance.name }, - followRequestCount () { + followRequestCount() { return this.$store.state.api.followRequests.length }, - privateMode () { + privateMode() { return this.$store.state.instance.private }, - federating () { + federating() { return this.$store.state.instance.federating }, - timelinesRoute () { + timelinesRoute() { let name if (useInterfaceStore().lastTimeline) { name = useInterfaceStore().lastTimeline @@ -101,35 +106,36 @@ const SideDrawer = { } }, ...mapPiniaState(useAnnouncementsStore, { - supportsAnnouncements: store => store.supportsAnnouncements, - unreadAnnouncementCount: 'unreadAnnouncementCount' + supportsAnnouncements: (store) => store.supportsAnnouncements, + unreadAnnouncementCount: 'unreadAnnouncementCount', }), ...mapState({ - pleromaChatMessagesAvailable: state => state.instance.pleromaChatMessagesAvailable + pleromaChatMessagesAvailable: (state) => + state.instance.pleromaChatMessagesAvailable, }), - ...mapGetters(['unreadChatCount', 'draftCount']) + ...mapGetters(['unreadChatCount', 'draftCount']), }, methods: { - toggleDrawer () { + toggleDrawer() { this.closed = !this.closed }, - doLogout () { + doLogout() { this.logout() this.toggleDrawer() }, - touchStart (e) { + touchStart(e) { GestureService.beginSwipe(e, this.closeGesture) }, - touchMove (e) { + touchMove(e) { GestureService.updateSwipe(e, this.closeGesture) }, - openSettingsModal () { + openSettingsModal() { useInterfaceStore().openSettingsModal('user') }, - openAdminModal () { + openAdminModal() { useInterfaceStore().openSettingsModal('admin') - } - } + }, + }, } export default SideDrawer diff --git a/src/components/staff_panel/staff_panel.js b/src/components/staff_panel/staff_panel.js index 46a92ac78..26b34f38f 100644 --- a/src/components/staff_panel/staff_panel.js +++ b/src/components/staff_panel/staff_panel.js @@ -4,30 +4,32 @@ import { mapGetters, mapState } from 'vuex' import BasicUserCard from '../basic_user_card/basic_user_card.vue' const StaffPanel = { - created () { + created() { const nicknames = this.$store.state.instance.staffAccounts - nicknames.forEach(nickname => this.$store.dispatch('fetchUserIfMissing', nickname)) + nicknames.forEach((nickname) => + this.$store.dispatch('fetchUserIfMissing', nickname), + ) }, components: { - BasicUserCard + BasicUserCard, }, computed: { - groupedStaffAccounts () { - const staffAccounts = map(this.staffAccounts, this.findUserByName).filter(_ => _) + groupedStaffAccounts() { + const staffAccounts = map(this.staffAccounts, this.findUserByName).filter( + (_) => _, + ) const groupedStaffAccounts = groupBy(staffAccounts, 'role') return [ { role: 'admin', users: groupedStaffAccounts.admin }, - { role: 'moderator', users: groupedStaffAccounts.moderator } - ].filter(group => group.users) + { role: 'moderator', users: groupedStaffAccounts.moderator }, + ].filter((group) => group.users) }, - ...mapGetters([ - 'findUserByName' - ]), + ...mapGetters(['findUserByName']), ...mapState({ - staffAccounts: state => state.instance.staffAccounts - }) - } + staffAccounts: (state) => state.instance.staffAccounts, + }), + }, } export default StaffPanel diff --git a/src/components/status/post.style.js b/src/components/status/post.style.js index 43e21a16e..a988aef76 100644 --- a/src/components/status/post.style.js +++ b/src/components/status/post.style.js @@ -2,7 +2,7 @@ export default { name: 'Post', selector: '.Status', states: { - selected: '.-focused' + selected: '.-focused', }, validInnerComponents: [ 'Text', @@ -10,19 +10,19 @@ export default { 'Icon', 'Border', 'Avatar', - 'PollGraph' + 'PollGraph', ], defaultRules: [ { directives: { - background: '--bg' - } + background: '--bg', + }, }, { state: ['selected'], directives: { - background: '--inheritedBackground, 10' - } - } - ] + background: '--inheritedBackground, 10', + }, + }, + ], } diff --git a/src/components/status/status.js b/src/components/status/status.js index 4112ed44e..84a80ac07 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -13,7 +13,10 @@ import MentionsLine from 'src/components/mentions_line/mentions_line.vue' import MentionLink from 'src/components/mention_link/mention_link.vue' import StatusActionButtons from 'src/components/status_action_buttons/status_action_buttons.vue' import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' -import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js' +import { + highlightClass, + highlightStyle, +} from '../../services/user_highlighter/user_highlighter.js' import { muteFilterHits } from '../../services/status_parser/status_parser.js' import { unescape, uniqBy } from 'lodash' import { useServerSideStorageStore } from 'src/stores/serverSideStorage' @@ -38,7 +41,7 @@ import { faChevronUp, faChevronDown, faAngleDoubleRight, - faPlay + faPlay, } from '@fortawesome/free-solid-svg-icons' library.add( @@ -60,21 +63,26 @@ library.add( faChevronUp, faChevronDown, faAngleDoubleRight, - faPlay + faPlay, ) -const camelCase = name => name.charAt(0).toUpperCase() + name.slice(1) +const camelCase = (name) => name.charAt(0).toUpperCase() + name.slice(1) -const controlledOrUncontrolledGetters = list => list.reduce((res, name) => { - const camelized = camelCase(name) - const toggle = `controlledToggle${camelized}` - const controlledName = `controlled${camelized}` - const uncontrolledName = `uncontrolled${camelized}` - res[name] = function () { - return ((this.$data[toggle] !== undefined || this.$props[toggle] !== undefined) && this[toggle]) ? this[controlledName] : this[uncontrolledName] - } - return res -}, {}) +const controlledOrUncontrolledGetters = (list) => + list.reduce((res, name) => { + const camelized = camelCase(name) + const toggle = `controlledToggle${camelized}` + const controlledName = `controlled${camelized}` + const uncontrolledName = `uncontrolled${camelized}` + res[name] = function () { + return (this.$data[toggle] !== undefined || + this.$props[toggle] !== undefined) && + this[toggle] + ? this[controlledName] + : this[uncontrolledName] + } + return res + }, {}) const controlledOrUncontrolledToggle = (obj, name) => { const camelized = camelCase(name) @@ -114,7 +122,7 @@ const Status = { MentionsLine, UserPopover, UserLink, - StatusActionButtons + StatusActionButtons, }, props: [ 'statusoid', @@ -147,10 +155,10 @@ const Status = { 'controlledToggleReplying', 'controlledMediaPlaying', 'controlledSetMediaPlaying', - 'dive' + 'dive', ], emits: ['interacted'], - data () { + data() { return { uncontrolledReplying: false, unmuted: false, @@ -159,128 +167,164 @@ const Status = { suspendable: true, error: null, headTailLinks: null, - displayQuote: !this.inQuote + displayQuote: !this.inQuote, } }, computed: { ...controlledOrUncontrolledGetters(['replying', 'mediaPlaying']), - showReasonMutedThread () { + showReasonMutedThread() { return ( - this.status.thread_muted || - (this.status.reblog && this.status.reblog.thread_muted) - ) && !this.inConversation + (this.status.thread_muted || + (this.status.reblog && this.status.reblog.thread_muted)) && + !this.inConversation + ) }, - repeaterClass () { + repeaterClass() { const user = this.statusoid.user return highlightClass(user) }, - userClass () { - const user = this.retweet ? (this.statusoid.retweeted_status.user) : this.statusoid.user + userClass() { + const user = this.retweet + ? this.statusoid.retweeted_status.user + : this.statusoid.user return highlightClass(user) }, - deleted () { + deleted() { return this.statusoid.deleted }, - repeaterStyle () { + repeaterStyle() { const user = this.statusoid.user const highlight = this.mergedConfig.highlight return highlightStyle(highlight[user.screen_name]) }, - userStyle () { + userStyle() { if (this.noHeading) return - const user = this.retweet ? (this.statusoid.retweeted_status.user) : this.statusoid.user + const user = this.retweet + ? this.statusoid.retweeted_status.user + : this.statusoid.user const highlight = this.mergedConfig.highlight return highlightStyle(highlight[user.screen_name]) }, - userProfileLink () { - return this.generateUserProfileLink(this.status.user.id, this.status.user.screen_name) + userProfileLink() { + return this.generateUserProfileLink( + this.status.user.id, + this.status.user.screen_name, + ) }, - replyProfileLink () { + replyProfileLink() { if (this.isReply) { - const user = this.$store.getters.findUser(this.status.in_reply_to_user_id) + const user = this.$store.getters.findUser( + this.status.in_reply_to_user_id, + ) // FIXME Why user not found sometimes??? return user ? user.statusnet_profile_url : 'NOT_FOUND' } }, - retweet () { return !!this.statusoid.retweeted_status }, - retweeterUser () { return this.statusoid.user }, - retweeter () { return this.statusoid.user.name || this.statusoid.user.screen_name_ui }, - retweeterHtml () { return this.statusoid.user.name }, - retweeterProfileLink () { return this.generateUserProfileLink(this.statusoid.user.id, this.statusoid.user.screen_name) }, - status () { + retweet() { + return !!this.statusoid.retweeted_status + }, + retweeterUser() { + return this.statusoid.user + }, + retweeter() { + return this.statusoid.user.name || this.statusoid.user.screen_name_ui + }, + retweeterHtml() { + return this.statusoid.user.name + }, + retweeterProfileLink() { + return this.generateUserProfileLink( + this.statusoid.user.id, + this.statusoid.user.screen_name, + ) + }, + status() { if (this.retweet) { return this.statusoid.retweeted_status } else { return this.statusoid } }, - statusFromGlobalRepository () { + statusFromGlobalRepository() { // NOTE: Consider to replace status with statusFromGlobalRepository return this.$store.state.statuses.allStatusesObject[this.status.id] }, - loggedIn () { + loggedIn() { return !!this.currentUser }, - muteFilterHits () { + muteFilterHits() { return muteFilterHits( - Object.values(useServerSideStorageStore().prefsStorage.simple.muteFilters), - this.status + Object.values( + useServerSideStorageStore().prefsStorage.simple.muteFilters, + ), + this.status, ) }, - botStatus () { + botStatus() { return this.status.user.actor_type === 'Service' }, - showActorTypeIndicator () { + showActorTypeIndicator() { return !this.hideBotIndication }, - sensitiveStatus () { + sensitiveStatus() { return this.status.nsfw }, - mentionsLine () { + mentionsLine() { if (!this.headTailLinks) return [] - const writtenSet = new Set(this.headTailLinks.writtenMentions.map(_ => _.url)) - return this.status.attentions.filter(attn => { - // no reply user - return attn.id !== this.status.in_reply_to_user_id && - // no self-replies - attn.statusnet_profile_url !== this.status.user.statusnet_profile_url && - // don't include if mentions is written - !writtenSet.has(attn.statusnet_profile_url) - }).map(attn => ({ - url: attn.statusnet_profile_url, - content: attn.screen_name, - userId: attn.id - })) + const writtenSet = new Set( + this.headTailLinks.writtenMentions.map((_) => _.url), + ) + return this.status.attentions + .filter((attn) => { + // no reply user + return ( + attn.id !== this.status.in_reply_to_user_id && + // no self-replies + attn.statusnet_profile_url !== + this.status.user.statusnet_profile_url && + // don't include if mentions is written + !writtenSet.has(attn.statusnet_profile_url) + ) + }) + .map((attn) => ({ + url: attn.statusnet_profile_url, + content: attn.screen_name, + userId: attn.id, + })) }, - hasMentionsLine () { + hasMentionsLine() { return this.mentionsLine.length > 0 }, - muteReasons () { + muteReasons() { return [ this.userIsMuted ? 'user' : null, this.status.thread_muted ? 'thread' : null, - (this.muteFilterHits.length > 0) ? 'filtered' : null, - (this.muteBotStatuses && this.botStatus) ? 'bot' : null, - (this.muteSensitiveStatuses && this.sensitiveStatus) ? 'nsfw' : null - ].filter(_ => _) + this.muteFilterHits.length > 0 ? 'filtered' : null, + this.muteBotStatuses && this.botStatus ? 'bot' : null, + this.muteSensitiveStatuses && this.sensitiveStatus ? 'nsfw' : null, + ].filter((_) => _) }, - muteLocalized () { + muteLocalized() { if (this.muteReasons.length === 0) return null const mainReason = () => { switch (this.muteReasons[0]) { - case 'user': return this.$t('status.muted_user') - case 'thread': return this.$t('status.thread_muted') + case 'user': + return this.$t('status.muted_user') + case 'thread': + return this.$t('status.thread_muted') case 'filtered': return this.$t( 'status.muted_filters', { name: this.muteFilterHits[0].name, - filterMore: this.muteFilterHits.length - 1 + filterMore: this.muteFilterHits.length - 1, }, - this.muteFilterHits.length + this.muteFilterHits.length, ) - case 'bot': return this.$t('status.bot_muted') - case 'nsfw': return this.$t('status.sensitive_muted') + case 'bot': + return this.$t('status.bot_muted') + case 'nsfw': + return this.$t('status.sensitive_muted') } } if (this.muteReasons.length > 1) { @@ -288,72 +332,74 @@ const Status = { 'status.multi_reason_mute', { main: mainReason(), - numReasonsMore: this.muteReasons.length - 1 + numReasonsMore: this.muteReasons.length - 1, }, - this.muteReasons.length - 1 + this.muteReasons.length - 1, ) } else { return mainReason() } }, - muted () { + muted() { if (this.statusoid.user.id === this.currentUser.id) return false return !this.unmuted && !this.shouldNotMute && this.muteReasons.length > 0 }, - userIsMuted () { + userIsMuted() { if (this.statusoid.user.id === this.currentUser.id) return false const { status } = this const { reblog } = status const relationship = this.$store.getters.relationship(status.user.id) - const relationshipReblog = reblog && this.$store.getters.relationship(reblog.user.id) - return (status.muted && !status.thread_muted) || + const relationshipReblog = + reblog && this.$store.getters.relationship(reblog.user.id) + return ( + (status.muted && !status.thread_muted) || // Reprööt of a muted post according to BE (reblog && reblog.muted && !reblog.thread_muted) || // Muted user relationship.muting || // Muted user of a reprööt (relationshipReblog && relationshipReblog.muting) + ) }, - shouldNotMute () { + shouldNotMute() { if (this.isFocused) return true const { status } = this const { reblog } = status return ( - ( - this.inProfile && ( - // Don't mute user's posts on user timeline (except reblogs) - (!reblog && status.user.id === this.profileUserId) || + ((this.inProfile && + // Don't mute user's posts on user timeline (except reblogs) + ((!reblog && status.user.id === this.profileUserId) || // Same as above but also allow self-reblogs - (reblog && reblog.user.id === this.profileUserId) - ) - ) || - // Don't mute statuses in muted conversation when said conversation is opened - (this.inConversation && status.thread_muted) + (reblog && reblog.user.id === this.profileUserId))) || + // Don't mute statuses in muted conversation when said conversation is opened + (this.inConversation && status.thread_muted)) && // No excuses if post has muted words - ) && !this.muteFilterHits.length > 0 - }, - hideMutedUsers () { - return this.mergedConfig.hideMutedPosts - }, - hideMutedThreads () { - return this.mergedConfig.hideMutedThreads - }, - hideFilteredStatuses () { - return this.mergedConfig.hideFilteredStatuses - }, - hideWordFilteredPosts () { - return this.mergedConfig.hideWordFilteredPosts - }, - hideStatus () { - return (!this.shouldNotMute) && ( - (this.muted && this.hideFilteredStatuses) || - (this.userIsMuted && this.hideMutedUsers) || - (this.status.thread_muted && this.hideMutedThreads) || - (this.muteFilterHits.length > 0 && this.hideWordFilteredPosts) || - (this.muteFilterHits.some(x => x.hide)) + !this.muteFilterHits.length > 0 ) }, - isFocused () { + hideMutedUsers() { + return this.mergedConfig.hideMutedPosts + }, + hideMutedThreads() { + return this.mergedConfig.hideMutedThreads + }, + hideFilteredStatuses() { + return this.mergedConfig.hideFilteredStatuses + }, + hideWordFilteredPosts() { + return this.mergedConfig.hideWordFilteredPosts + }, + hideStatus() { + return ( + !this.shouldNotMute && + ((this.muted && this.hideFilteredStatuses) || + (this.userIsMuted && this.hideMutedUsers) || + (this.status.thread_muted && this.hideMutedThreads) || + (this.muteFilterHits.length > 0 && this.hideWordFilteredPosts) || + this.muteFilterHits.some((x) => x.hide)) + ) + }, + isFocused() { // retweet or root of an expanded conversation if (this.focused) { return true @@ -363,18 +409,22 @@ const Status = { // use conversation highlight only when in conversation return this.status.id === this.highlight }, - isReply () { - return !!(this.status.in_reply_to_status_id && this.status.in_reply_to_user_id) + isReply() { + return !!( + this.status.in_reply_to_status_id && this.status.in_reply_to_user_id + ) }, - replyToName () { + replyToName() { if (this.status.in_reply_to_screen_name) { return this.status.in_reply_to_screen_name } else { - const user = this.$store.getters.findUser(this.status.in_reply_to_user_id) + const user = this.$store.getters.findUser( + this.status.in_reply_to_user_id, + ) return user && user.screen_name_ui } }, - replySubject () { + replySubject() { if (!this.status.summary) return '' const decodedSummary = unescape(this.status.summary) const behavior = this.mergedConfig.subjectLineBehavior @@ -387,70 +437,80 @@ const Status = { return '' } }, - combinedFavsAndRepeatsUsers () { + combinedFavsAndRepeatsUsers() { // Use the status from the global status repository since favs and repeats are saved in it const combinedUsers = [].concat( this.statusFromGlobalRepository.favoritedBy, - this.statusFromGlobalRepository.rebloggedBy + this.statusFromGlobalRepository.rebloggedBy, ) return uniqBy(combinedUsers, 'id') }, - tags () { + tags() { // eslint-disable-next-line no-prototype-builtins - return this.status.tags.filter(tagObj => Object.hasOwn(tagObj, 'name')).map(tagObj => tagObj.name).join(' ') + return this.status.tags + .filter((tagObj) => Object.hasOwn(tagObj, 'name')) + .map((tagObj) => tagObj.name) + .join(' ') }, - hidePostStats () { + hidePostStats() { return this.mergedConfig.hidePostStats }, - shouldDisplayFavsAndRepeats () { - return !this.hidePostStats && this.isFocused && (this.combinedFavsAndRepeatsUsers.length > 0 || this.statusFromGlobalRepository.quotes_count) + shouldDisplayFavsAndRepeats() { + return ( + !this.hidePostStats && + this.isFocused && + (this.combinedFavsAndRepeatsUsers.length > 0 || + this.statusFromGlobalRepository.quotes_count) + ) }, - muteBotStatuses () { + muteBotStatuses() { return this.mergedConfig.muteBotStatuses }, - muteSensitiveStatuses () { + muteSensitiveStatuses() { return this.mergedConfig.muteSensitiveStatuses }, - hideBotIndication () { + hideBotIndication() { return this.mergedConfig.hideBotIndication }, - currentUser () { + currentUser() { return this.$store.state.users.currentUser }, - mergedConfig () { + mergedConfig() { return this.$store.getters.mergedConfig }, - isSuspendable () { + isSuspendable() { return !this.replying && this.mediaPlaying.length === 0 }, - inThreadForest () { + inThreadForest() { return !!this.controlledThreadDisplayStatus }, - threadShowing () { + threadShowing() { return this.controlledThreadDisplayStatus === 'showing' }, - visibilityLocalized () { + visibilityLocalized() { return this.$i18n.t('general.scope_in_timeline.' + this.status.visibility) }, - isEdited () { + isEdited() { return this.status.edited_at !== null }, - editingAvailable () { + editingAvailable() { return this.$store.state.instance.editingAvailable }, - hasVisibleQuote () { + hasVisibleQuote() { return this.status.quote_url && this.status.quote_visible }, - hasInvisibleQuote () { + hasInvisibleQuote() { return this.status.quote_url && !this.status.quote_visible }, - quotedStatus () { - return this.status.quote_id ? this.$store.state.statuses.allStatusesObject[this.status.quote_id] : undefined + quotedStatus() { + return this.status.quote_id + ? this.$store.state.statuses.allStatusesObject[this.status.quote_id] + : undefined }, - shouldDisplayQuote () { + shouldDisplayQuote() { return this.quotedStatus && this.displayQuote }, - scrobblePresent () { + scrobblePresent() { if (this.mergedConfig.hideScrobbles) return false if (!this.status.user?.latestScrobble) return false const value = this.mergedConfig.hideScrobblesAfter.match(/\d+/gs)[0] @@ -473,12 +533,12 @@ const Status = { if (age > maxAge) return false return this.status.user.latestScrobble.artist }, - scrobble () { + scrobble() { return this.status.user?.latestScrobble - } + }, }, methods: { - visibilityIcon (visibility) { + visibilityIcon(visibility) { switch (visibility) { case 'private': return 'lock' @@ -492,14 +552,14 @@ const Status = { return 'globe' } }, - showError (error) { + showError(error) { this.error = error }, - clearError () { + clearError() { this.$emit('interacted') this.error = undefined }, - toggleReplying () { + toggleReplying() { this.$emit('interacted') if (this.replying) { this.$refs.postStatusForm.requestClose() @@ -507,39 +567,51 @@ const Status = { this.doToggleReplying() } }, - doToggleReplying () { + doToggleReplying() { controlledOrUncontrolledToggle(this, 'replying') }, - gotoOriginal (id) { + gotoOriginal(id) { if (this.inConversation) { this.$emit('goto', id) } }, - toggleExpanded () { + toggleExpanded() { this.$emit('toggleExpanded') }, - toggleMute () { + toggleMute() { this.unmuted = !this.unmuted }, - toggleUserExpanded () { + toggleUserExpanded() { this.userExpanded = !this.userExpanded }, - generateUserProfileLink (id, name) { - return generateProfileLink(id, name, this.$store.state.instance.restrictedNicknames) + generateUserProfileLink(id, name) { + return generateProfileLink( + id, + name, + this.$store.state.instance.restrictedNicknames, + ) }, - addMediaPlaying (id) { - controlledOrUncontrolledSet(this, 'mediaPlaying', this.mediaPlaying.concat(id)) + addMediaPlaying(id) { + controlledOrUncontrolledSet( + this, + 'mediaPlaying', + this.mediaPlaying.concat(id), + ) }, - removeMediaPlaying (id) { - controlledOrUncontrolledSet(this, 'mediaPlaying', this.mediaPlaying.filter(mediaId => mediaId !== id)) + removeMediaPlaying(id) { + controlledOrUncontrolledSet( + this, + 'mediaPlaying', + this.mediaPlaying.filter((mediaId) => mediaId !== id), + ) }, - setHeadTailLinks (headTailLinks) { + setHeadTailLinks(headTailLinks) { this.headTailLinks = headTailLinks }, - toggleThreadDisplay () { + toggleThreadDisplay() { this.controlledToggleThreadDisplay() }, - scrollIfHighlighted (highlightId) { + scrollIfHighlighted(highlightId) { if (this.$el.getBoundingClientRect == null) return const id = highlightId if (this.status.id === id) { @@ -547,7 +619,7 @@ const Status = { if (rect.top < 100) { // Post is above screen, match its top to screen top window.scrollBy(0, rect.top - 100) - } else if (rect.height >= (window.innerHeight - 50)) { + } else if (rect.height >= window.innerHeight - 50) { // Post we want to see is taller than screen so match its top to screen top window.scrollBy(0, rect.top - 100) } else if (rect.bottom > window.innerHeight - 50) { @@ -556,18 +628,17 @@ const Status = { } } }, - toggleDisplayQuote () { + toggleDisplayQuote() { if (this.shouldDisplayQuote) { this.displayQuote = false } else if (!this.quotedStatus) { - this.$store.dispatch('fetchStatus', this.status.quote_id) - .then(() => { - this.displayQuote = true - }) + this.$store.dispatch('fetchStatus', this.status.quote_id).then(() => { + this.displayQuote = true + }) } else { this.displayQuote = true } - } + }, }, watch: { highlight: function (id) { @@ -575,20 +646,28 @@ const Status = { }, 'status.repeat_num': function (num) { // refetch repeats when repeat_num is changed in any way - if (this.isFocused && this.statusFromGlobalRepository.rebloggedBy && this.statusFromGlobalRepository.rebloggedBy.length !== num) { + if ( + this.isFocused && + this.statusFromGlobalRepository.rebloggedBy && + this.statusFromGlobalRepository.rebloggedBy.length !== num + ) { this.$store.dispatch('fetchRepeats', this.status.id) } }, 'status.fave_num': function (num) { // refetch favs when fave_num is changed in any way - if (this.isFocused && this.statusFromGlobalRepository.favoritedBy && this.statusFromGlobalRepository.favoritedBy.length !== num) { + if ( + this.isFocused && + this.statusFromGlobalRepository.favoritedBy && + this.statusFromGlobalRepository.favoritedBy.length !== num + ) { this.$store.dispatch('fetchFavs', this.status.id) } }, isSuspendable: function (val) { this.suspendable = val - } - } + }, + }, } export default Status diff --git a/src/components/status_action_buttons/action_button.js b/src/components/status_action_buttons/action_button.js index 8a65b9184..1edc57fc4 100644 --- a/src/components/status_action_buttons/action_button.js +++ b/src/components/status_action_buttons/action_button.js @@ -9,25 +9,22 @@ import { faCheck, faTimes, faWrench, - faChevronRight, faChevronUp, - faReply, faRetweet, faStar, faSmileBeam, - faBookmark, faEyeSlash, faThumbtack, faShareAlt, faExternalLinkAlt, - faHistory + faHistory, } from '@fortawesome/free-solid-svg-icons' import { faStar as faStarRegular, - faBookmark as faBookmarkRegular + faBookmark as faBookmarkRegular, } from '@fortawesome/free-regular-svg-icons' library.add( @@ -52,7 +49,7 @@ library.add( faThumbtack, faShareAlt, faExternalLinkAlt, - faHistory + faHistory, ) export default { @@ -65,66 +62,73 @@ export default { 'getClass', 'getComponent', 'doAction', - 'outerClose' - ], - emits: [ - 'interacted' + 'outerClose', ], + emits: ['interacted'], components: { StatusBookmarkFolderMenu, EmojiPicker, - Popover + Popover, }, data: () => ({ - animationState: false + animationState: false, }), computed: { - buttonClass () { + buttonClass() { return [ this.button.name + '-button', { '-with-extra': this.button.name === 'bookmark', '-extra': this.extra, - '-quick': !this.extra - } + '-quick': !this.extra, + }, ] }, - userIsMuted () { + userIsMuted() { return this.$store.getters.relationship(this.status.user.id).muting }, - threadIsMuted () { + threadIsMuted() { return this.status.thread_muted }, - hideCustomEmoji () { + hideCustomEmoji() { return !this.$store.state.instance.pleromaCustomEmojiReactionsAvailable }, - buttonInnerClass () { + buttonInnerClass() { return [ this.button.name + '-button', { 'main-button': this.extra, 'button-unstyled': !this.extra, '-active': this.button.active?.(this.funcArg), - disabled: this.button.interactive ? !this.button.interactive(this.funcArg) : false - } + disabled: this.button.interactive + ? !this.button.interactive(this.funcArg) + : false, + }, ] }, - remoteInteractionLink () { - return this.$store.getters.remoteInteractionLink({ statusId: this.status.id }) - } + remoteInteractionLink() { + return this.$store.getters.remoteInteractionLink({ + statusId: this.status.id, + }) + }, }, methods: { - addReaction (event) { + addReaction(event) { const emoji = event.insertion - const existingReaction = this.status.emoji_reactions.find(r => r.name === emoji) + const existingReaction = this.status.emoji_reactions.find( + (r) => r.name === emoji, + ) if (existingReaction && existingReaction.me) { this.$store.dispatch('unreactWithEmoji', { id: this.status.id, emoji }) } else { this.$store.dispatch('reactWithEmoji', { id: this.status.id, emoji }) } }, - doActionWrap (button, close = () => {}) { - if (this.button.interactive ? !this.button.interactive(this.funcArg) : false) return + doActionWrap(button, close = () => {}) { + if ( + this.button.interactive ? !this.button.interactive(this.funcArg) : false + ) + return this.$emit('interacted') if (button.name === 'emoji') { this.$refs.picker.showPicker() @@ -136,6 +140,6 @@ export default { }, 500) close() } - } - } + }, + }, } diff --git a/src/components/status_action_buttons/action_button_container.js b/src/components/status_action_buttons/action_button_container.js index a8f20800b..303a76377 100644 --- a/src/components/status_action_buttons/action_button_container.js +++ b/src/components/status_action_buttons/action_button_container.js @@ -7,86 +7,84 @@ import { library } from '@fortawesome/fontawesome-svg-core' import { faUser, faGlobe, - faFolderTree + faFolderTree, } from '@fortawesome/free-solid-svg-icons' -library.add( - faUser, - faGlobe, - faFolderTree -) +library.add(faUser, faGlobe, faFolderTree) export default { components: { ActionButton, Popover, MuteConfirm, - UserTimedFilterModal + UserTimedFilterModal, }, props: ['button', 'status'], emits: ['interacted'], - mounted () { + mounted() { if (this.button.name === 'mute') { this.$store.dispatch('fetchDomainMutes') } }, computed: { - buttonClass () { + buttonClass() { return [ this.button.name + '-button', { '-with-extra': this.button.name === 'bookmark', '-extra': this.extra, - '-quick': !this.extra - } + '-quick': !this.extra, + }, ] }, - user () { + user() { return this.status.user }, - userIsMuted () { + userIsMuted() { return this.$store.getters.relationship(this.user.id).muting }, - conversationIsMuted () { + conversationIsMuted() { return this.status.thread_muted }, - domain () { + domain() { return this.user.fqn.split('@')[1] }, - domainIsMuted () { - return new Set(this.$store.state.users.currentUser.domainMutes).has(this.domain) - } + domainIsMuted() { + return new Set(this.$store.state.users.currentUser.domainMutes).has( + this.domain, + ) + }, }, methods: { - unmuteUser () { + unmuteUser() { return this.$store.dispatch('unmuteUser', this.user.id) }, - unmuteConversation () { + unmuteConversation() { return this.$store.dispatch('unmuteConversation', { id: this.status.id }) }, - unmuteDomain () { + unmuteDomain() { return this.$store.dispatch('unmuteDomain', this.user.id) }, - toggleUserMute () { + toggleUserMute() { if (this.userIsMuted) { this.unmuteUser() } else { this.$refs.confirmUser.optionallyPrompt() } }, - toggleConversationMute () { + toggleConversationMute() { if (this.conversationIsMuted) { this.unmuteConversation() } else { this.$refs.confirmConversation.optionallyPrompt() } }, - toggleDomainMute () { + toggleDomainMute() { if (this.domainIsMuted) { this.unmuteDomain() } else { this.$refs.confirmDomain.optionallyPrompt() } - } - } + }, + }, } diff --git a/src/components/status_action_buttons/buttons_definitions.js b/src/components/status_action_buttons/buttons_definitions.js index bc1e45a98..d1a6845d1 100644 --- a/src/components/status_action_buttons/buttons_definitions.js +++ b/src/components/status_action_buttons/buttons_definitions.js @@ -4,257 +4,282 @@ import { useStatusHistoryStore } from 'src/stores/statusHistory.js' const PRIVATE_SCOPES = new Set(['private', 'direct']) const PUBLIC_SCOPES = new Set(['public', 'unlisted']) -export const BUTTONS = [{ - // ========= - // REPLY - // ========= - name: 'reply', - label: 'tool_tip.reply', - icon: 'reply', - active: ({ replying }) => replying, - counter: ({ status }) => status.replies_count, - anon: true, - anonLink: true, - toggleable: true, - closeIndicator: 'times', - activeIndicator: 'none', - action ({ emit }) { - emit('toggleReplying') - return Promise.resolve() - } -}, { - // ========= - // REPEAT - // ========= - name: 'retweet', - label: ({ status }) => status.repeated - ? 'tool_tip.unrepeat' - : 'tool_tip.repeat', - icon ({ status, currentUser }) { - if (currentUser.id !== status.user.id && PRIVATE_SCOPES.has(status.visibility)) { - return 'lock' - } - return 'retweet' +export const BUTTONS = [ + { + // ========= + // REPLY + // ========= + name: 'reply', + label: 'tool_tip.reply', + icon: 'reply', + active: ({ replying }) => replying, + counter: ({ status }) => status.replies_count, + anon: true, + anonLink: true, + toggleable: true, + closeIndicator: 'times', + activeIndicator: 'none', + action({ emit }) { + emit('toggleReplying') + return Promise.resolve() + }, }, - animated: true, - active: ({ status }) => status.repeated, - counter: ({ status }) => status.repeat_num, - anonLink: true, - interactive: ({ status, currentUser }) => !!currentUser && (currentUser.id === status.user.id || !PRIVATE_SCOPES.has(status.visibility)), - toggleable: true, - confirm: ({ status, getters }) => !status.repeated && getters.mergedConfig.modalOnRepeat, - confirmStrings: { - title: 'status.repeat_confirm_title', - body: 'status.repeat_confirm', - confirm: 'status.repeat_confirm_accept_button', - cancel: 'status.repeat_confirm_cancel_button' + { + // ========= + // REPEAT + // ========= + name: 'retweet', + label: ({ status }) => + status.repeated ? 'tool_tip.unrepeat' : 'tool_tip.repeat', + icon({ status, currentUser }) { + if ( + currentUser.id !== status.user.id && + PRIVATE_SCOPES.has(status.visibility) + ) { + return 'lock' + } + return 'retweet' + }, + animated: true, + active: ({ status }) => status.repeated, + counter: ({ status }) => status.repeat_num, + anonLink: true, + interactive: ({ status, currentUser }) => + !!currentUser && + (currentUser.id === status.user.id || + !PRIVATE_SCOPES.has(status.visibility)), + toggleable: true, + confirm: ({ status, getters }) => + !status.repeated && getters.mergedConfig.modalOnRepeat, + confirmStrings: { + title: 'status.repeat_confirm_title', + body: 'status.repeat_confirm', + confirm: 'status.repeat_confirm_accept_button', + cancel: 'status.repeat_confirm_cancel_button', + }, + action({ status, dispatch }) { + if (!status.repeated) { + return dispatch('retweet', { id: status.id }) + } else { + return dispatch('unretweet', { id: status.id }) + } + }, }, - action ({ status, dispatch }) { - if (!status.repeated) { - return dispatch('retweet', { id: status.id }) - } else { - return dispatch('unretweet', { id: status.id }) - } - } -}, { - // ========= - // FAVORITE - // ========= - name: 'favorite', - label: ({ status }) => status.favorited - ? 'tool_tip.unfavorite' - : 'tool_tip.favorite', - icon: ({ status }) => status.favorited - ? ['fas', 'star'] - : ['far', 'star'], - animated: true, - active: ({ status }) => status.favorited, - counter: ({ status }) => status.fave_num, - anonLink: true, - toggleable: true, - action ({ status, dispatch }) { - if (!status.favorited) { - return dispatch('favorite', { id: status.id }) - } else { - return dispatch('unfavorite', { id: status.id }) - } - } -}, { - // ========= - // EMOJI REACTIONS - // ========= - name: 'emoji', - label: 'tool_tip.add_reaction', - icon: ['far', 'smile-beam'], - anonLink: true -}, { - // ========= - // MUTE - // ========= - name: 'mute', - icon: 'eye-slash', - label: 'status.mute_ellipsis', - if: ({ loggedIn }) => loggedIn, - toggleable: true, - dropdown: true - // action ({ status, dispatch, emit }) { - // } -}, { - // ========= - // PIN STATUS - // ========= - name: 'pin', - icon: 'thumbtack', - label: ({ status }) => status.pinned - ? 'status.unpin' - : 'status.pin', - if ({ status, loggedIn, currentUser }) { - return loggedIn && - status.user.id === currentUser.id && - PUBLIC_SCOPES.has(status.visibility) + { + // ========= + // FAVORITE + // ========= + name: 'favorite', + label: ({ status }) => + status.favorited ? 'tool_tip.unfavorite' : 'tool_tip.favorite', + icon: ({ status }) => + status.favorited ? ['fas', 'star'] : ['far', 'star'], + animated: true, + active: ({ status }) => status.favorited, + counter: ({ status }) => status.fave_num, + anonLink: true, + toggleable: true, + action({ status, dispatch }) { + if (!status.favorited) { + return dispatch('favorite', { id: status.id }) + } else { + return dispatch('unfavorite', { id: status.id }) + } + }, }, - action ({ status, dispatch }) { - if (status.pinned) { - return dispatch('unpinStatus', status.id) - } else { - return dispatch('pinStatus', status.id) - } - } -}, { - // ========= - // BOOKMARK - // ========= - name: 'bookmark', - icon: ({ status }) => status.bookmarked - ? ['fas', 'bookmark'] - : ['far', 'bookmark'], - toggleable: true, - active: ({ status }) => status.bookmarked, - label: ({ status }) => status.bookmarked - ? 'status.unbookmark' - : 'status.bookmark', - if: ({ loggedIn }) => loggedIn, - action ({ status, dispatch }) { - if (status.bookmarked) { - return dispatch('unbookmark', { id: status.id }) - } else { - return dispatch('bookmark', { id: status.id }) - } - } -}, { - // ========= - // EDIT HISTORY - // ========= - name: 'editHistory', - icon: 'history', - label: 'status.status_history', - if ({ status, state }) { - return state.instance.editingAvailable && - status.edited_at !== null + { + // ========= + // EMOJI REACTIONS + // ========= + name: 'emoji', + label: 'tool_tip.add_reaction', + icon: ['far', 'smile-beam'], + anonLink: true, }, - action ({ status }) { - const originalStatus = { ...status } - const stripFieldsList = [ - 'attachments', - 'created_at', - 'emojis', - 'text', - 'raw_html', - 'nsfw', - 'poll', - 'summary', - 'summary_raw_html' - ] - stripFieldsList.forEach(p => delete originalStatus[p]) - useStatusHistoryStore().openStatusHistoryModal(originalStatus) - return Promise.resolve() - } -}, { - // ========= - // EDIT - // ========= - name: 'edit', - icon: 'pen', - label: 'status.edit', - if ({ status, loggedIn, currentUser, state }) { - return loggedIn && - state.instance.editingAvailable && - status.user.id === currentUser.id + { + // ========= + // MUTE + // ========= + name: 'mute', + icon: 'eye-slash', + label: 'status.mute_ellipsis', + if: ({ loggedIn }) => loggedIn, + toggleable: true, + dropdown: true, + // action ({ status, dispatch, emit }) { + // } }, - action ({ dispatch, status }) { - return dispatch('fetchStatusSource', { id: status.id }) - .then(data => useEditStatusStore().openEditStatusModal({ - statusId: status.id, - subject: data.spoiler_text, - statusText: data.text, - statusIsSensitive: status.nsfw, - statusPoll: status.poll, - statusFiles: [...status.attachments], - visibility: status.visibility, - statusContentType: data.content_type - })) - } -}, { - // ========= - // DELETE - // ========= - name: 'delete', - icon: 'times', - label: 'status.delete', - if ({ status, loggedIn, currentUser }) { - return loggedIn && ( - status.user.id === currentUser.id || - currentUser.privileges.includes('messages_delete') - ) + { + // ========= + // PIN STATUS + // ========= + name: 'pin', + icon: 'thumbtack', + label: ({ status }) => (status.pinned ? 'status.unpin' : 'status.pin'), + if({ status, loggedIn, currentUser }) { + return ( + loggedIn && + status.user.id === currentUser.id && + PUBLIC_SCOPES.has(status.visibility) + ) + }, + action({ status, dispatch }) { + if (status.pinned) { + return dispatch('unpinStatus', status.id) + } else { + return dispatch('pinStatus', status.id) + } + }, }, - confirm: ({ getters }) => getters.mergedConfig.modalOnDelete, - confirmStrings: { - title: 'status.delete_confirm_title', - body: 'status.delete_confirm', - confirm: 'status.delete_confirm_accept_button', - cancel: 'status.delete_confirm_cancel_button' + { + // ========= + // BOOKMARK + // ========= + name: 'bookmark', + icon: ({ status }) => + status.bookmarked ? ['fas', 'bookmark'] : ['far', 'bookmark'], + toggleable: true, + active: ({ status }) => status.bookmarked, + label: ({ status }) => + status.bookmarked ? 'status.unbookmark' : 'status.bookmark', + if: ({ loggedIn }) => loggedIn, + action({ status, dispatch }) { + if (status.bookmarked) { + return dispatch('unbookmark', { id: status.id }) + } else { + return dispatch('bookmark', { id: status.id }) + } + }, }, - action ({ dispatch, status }) { - return dispatch('deleteStatus', { id: status.id }) - } -}, { - // ========= - // SHARE/COPY - // ========= - name: 'share', - icon: 'share-alt', - label: 'status.copy_link', - action ({ state, status, router }) { - navigator.clipboard.writeText([ - state.instance.server, - router.resolve({ name: 'conversation', params: { id: status.id } }).href - ].join('')) - return Promise.resolve() - } -}, { - // ========= - // EXTERNAL - // ========= - name: 'external', - icon: 'external-link-alt', - label: 'status.external_source', - link: ({ status }) => status.external_url -}, { - // ========= - // REPORT - // ========= - name: 'report', - icon: 'flag', - label: 'user_card.report', - if: ({ loggedIn }) => loggedIn, - action ({ status }) { - return useReportsStore().openUserReportingModal({ userId: status.user.id, statusIds: [status.id] }) - } -}].map(button => { + { + // ========= + // EDIT HISTORY + // ========= + name: 'editHistory', + icon: 'history', + label: 'status.status_history', + if({ status, state }) { + return state.instance.editingAvailable && status.edited_at !== null + }, + action({ status }) { + const originalStatus = { ...status } + const stripFieldsList = [ + 'attachments', + 'created_at', + 'emojis', + 'text', + 'raw_html', + 'nsfw', + 'poll', + 'summary', + 'summary_raw_html', + ] + stripFieldsList.forEach((p) => delete originalStatus[p]) + useStatusHistoryStore().openStatusHistoryModal(originalStatus) + return Promise.resolve() + }, + }, + { + // ========= + // EDIT + // ========= + name: 'edit', + icon: 'pen', + label: 'status.edit', + if({ status, loggedIn, currentUser, state }) { + return ( + loggedIn && + state.instance.editingAvailable && + status.user.id === currentUser.id + ) + }, + action({ dispatch, status }) { + return dispatch('fetchStatusSource', { id: status.id }).then((data) => + useEditStatusStore().openEditStatusModal({ + statusId: status.id, + subject: data.spoiler_text, + statusText: data.text, + statusIsSensitive: status.nsfw, + statusPoll: status.poll, + statusFiles: [...status.attachments], + visibility: status.visibility, + statusContentType: data.content_type, + }), + ) + }, + }, + { + // ========= + // DELETE + // ========= + name: 'delete', + icon: 'times', + label: 'status.delete', + if({ status, loggedIn, currentUser }) { + return ( + loggedIn && + (status.user.id === currentUser.id || + currentUser.privileges.includes('messages_delete')) + ) + }, + confirm: ({ getters }) => getters.mergedConfig.modalOnDelete, + confirmStrings: { + title: 'status.delete_confirm_title', + body: 'status.delete_confirm', + confirm: 'status.delete_confirm_accept_button', + cancel: 'status.delete_confirm_cancel_button', + }, + action({ dispatch, status }) { + return dispatch('deleteStatus', { id: status.id }) + }, + }, + { + // ========= + // SHARE/COPY + // ========= + name: 'share', + icon: 'share-alt', + label: 'status.copy_link', + action({ state, status, router }) { + navigator.clipboard.writeText( + [ + state.instance.server, + router.resolve({ name: 'conversation', params: { id: status.id } }) + .href, + ].join(''), + ) + return Promise.resolve() + }, + }, + { + // ========= + // EXTERNAL + // ========= + name: 'external', + icon: 'external-link-alt', + label: 'status.external_source', + link: ({ status }) => status.external_url, + }, + { + // ========= + // REPORT + // ========= + name: 'report', + icon: 'flag', + label: 'user_card.report', + if: ({ loggedIn }) => loggedIn, + action({ status }) { + return useReportsStore().openUserReportingModal({ + userId: status.user.id, + statusIds: [status.id], + }) + }, + }, +].map((button) => { return Object.fromEntries( Object.entries(button).map(([k, v]) => [ k, - (typeof v === 'function' || k === 'name') ? v : () => v - ]) + typeof v === 'function' || k === 'name' ? v : () => v, + ]), ) }) diff --git a/src/components/status_action_buttons/status_action_buttons.js b/src/components/status_action_buttons/status_action_buttons.js index 6f84ce8b5..7c9c5ae86 100644 --- a/src/components/status_action_buttons/status_action_buttons.js +++ b/src/components/status_action_buttons/status_action_buttons.js @@ -10,18 +10,14 @@ import { useServerSideStorageStore } from 'src/stores/serverSideStorage' import { BUTTONS } from './buttons_definitions.js' import { library } from '@fortawesome/fontawesome-svg-core' -import { - faEllipsisH -} from '@fortawesome/free-solid-svg-icons' +import { faEllipsisH } from '@fortawesome/free-solid-svg-icons' -library.add( - faEllipsisH -) +library.add(faEllipsisH) const StatusActionButtons = { props: ['status', 'replying'], emits: ['toggleReplying', 'interacted'], - data () { + data() { return { showPin: false, showingConfirmDialog: false, @@ -29,31 +25,32 @@ const StatusActionButtons = { currentConfirmOkText: '', currentConfirmCancelText: '', currentConfirmAction: () => {}, - randomSeed: genRandomSeed() + randomSeed: genRandomSeed(), } }, components: { Popover, ConfirmModal, - ActionButtonContainer + ActionButtonContainer, }, computed: { ...mapState(useServerSideStorageStore, { - pinnedItems: store => new Set(store.prefsStorage.collections.pinnedStatusActions) + pinnedItems: (store) => + new Set(store.prefsStorage.collections.pinnedStatusActions), }), - buttons () { - return BUTTONS.filter(x => x.if ? x.if(this.funcArg) : true) + buttons() { + return BUTTONS.filter((x) => (x.if ? x.if(this.funcArg) : true)) }, - quickButtons () { - return this.buttons.filter(x => this.pinnedItems.has(x.name)) + quickButtons() { + return this.buttons.filter((x) => this.pinnedItems.has(x.name)) }, - extraButtons () { - return this.buttons.filter(x => !this.pinnedItems.has(x.name)) + extraButtons() { + return this.buttons.filter((x) => !this.pinnedItems.has(x.name)) }, - currentUser () { + currentUser() { return this.$store.state.users.currentUser }, - funcArg () { + funcArg() { return { status: this.status, replying: this.replying, @@ -63,25 +60,33 @@ const StatusActionButtons = { getters: this.$store.getters, router: this.$router, currentUser: this.currentUser, - loggedIn: !!this.currentUser + loggedIn: !!this.currentUser, } }, - triggerAttrs () { + triggerAttrs() { return { title: this.$t('status.more_actions'), 'aria-controls': `popup-menu-${this.randomSeed}`, - 'aria-haspopup': 'menu' + 'aria-haspopup': 'menu', } - } + }, }, methods: { - doAction (button) { + doAction(button) { if (button.confirm?.(this.funcArg)) { // TODO move to action_button - this.currentConfirmTitle = this.$t(button.confirmStrings(this.funcArg).title) - this.currentConfirmOkText = this.$t(button.confirmStrings(this.funcArg).confirm) - this.currentConfirmCancelText = this.$t(button.confirmStrings(this.funcArg).cancel) - this.currentConfirmBody = this.$t(button.confirmStrings(this.funcArg).body) + this.currentConfirmTitle = this.$t( + button.confirmStrings(this.funcArg).title, + ) + this.currentConfirmOkText = this.$t( + button.confirmStrings(this.funcArg).confirm, + ) + this.currentConfirmCancelText = this.$t( + button.confirmStrings(this.funcArg).cancel, + ) + this.currentConfirmBody = this.$t( + button.confirmStrings(this.funcArg).body, + ) this.currentConfirmAction = () => { this.showingConfirmDialog = false this.doActionReal(button) @@ -91,26 +96,33 @@ const StatusActionButtons = { this.doActionReal(button) } }, - doActionReal (button) { - button.action(this.funcArg) + doActionReal(button) { + button + .action(this.funcArg) .then(() => this.$emit('onSuccess')) - .catch(err => this.$emit('onError', err.error.error)) + .catch((err) => this.$emit('onError', err.error.error)) }, - onExtraClose () { + onExtraClose() { this.showPin = false }, - isPinned (button) { + isPinned(button) { return this.pinnedItems.has(button.name) }, - unpin (button) { - useServerSideStorageStore().removeCollectionPreference({ path: 'collections.pinnedStatusActions', value: button.name }) + unpin(button) { + useServerSideStorageStore().removeCollectionPreference({ + path: 'collections.pinnedStatusActions', + value: button.name, + }) useServerSideStorageStore().pushServerSideStorage() }, - pin (button) { - useServerSideStorageStore().addCollectionPreference({ path: 'collections.pinnedStatusActions', value: button.name }) + pin(button) { + useServerSideStorageStore().addCollectionPreference({ + path: 'collections.pinnedStatusActions', + value: button.name, + }) useServerSideStorageStore().pushServerSideStorage() }, - getComponent (button) { + getComponent(button) { if (!this.$store.state.users.currentUser && button.anonLink) { return 'a' } else if (button.action == null && button.link != null) { @@ -119,16 +131,18 @@ const StatusActionButtons = { return 'button' } }, - getClass (button) { + getClass(button) { return { [button.name + '-button']: true, - disabled: button.interactive ? !button.interactive(this.funcArg) : false, + disabled: button.interactive + ? !button.interactive(this.funcArg) + : false, '-pin-edit': this.showPin, '-dropdown': button.dropdown?.(), - '-active': button.active?.(this.funcArg) + '-active': button.active?.(this.funcArg), } - } - } + }, + }, } export default StatusActionButtons diff --git a/src/components/status_body/status_body.js b/src/components/status_body/status_body.js index 9a56060e8..772f7ff0e 100644 --- a/src/components/status_body/status_body.js +++ b/src/components/status_body/status_body.js @@ -7,16 +7,10 @@ import { faMusic, faImage, faLink, - faPollH + faPollH, } from '@fortawesome/free-solid-svg-icons' -library.add( - faFile, - faMusic, - faImage, - faLink, - faPollH -) +library.add(faFile, faMusic, faImage, faLink, faPollH) const StatusBody = { name: 'StatusBody', @@ -33,17 +27,17 @@ const StatusBody = { 'showingLongSubject', 'toggleShowingTall', 'toggleExpandingSubject', - 'toggleShowingLongSubject' + 'toggleShowingLongSubject', ], - data () { + data() { return { postLength: this.status.text.length, - parseReadyDone: false + parseReadyDone: false, } }, emits: ['parseReady'], computed: { - localCollapseSubjectDefault () { + localCollapseSubjectDefault() { return this.mergedConfig.collapseMessageWithSubject }, // This is a bit hacky, but we want to approximate post height before rendering @@ -53,73 +47,87 @@ const StatusBody = { // // Using max-height + overflow: auto for status components resulted in false positives // very often with japanese characters, and it was very annoying. - tallStatus () { + tallStatus() { if (this.singleLine || this.compact) return false - const lengthScore = this.status.raw_html.split(/ 20 }, - longSubject () { + longSubject() { return this.status.summary.length > 240 }, // When a status has a subject and is also tall, we should only have one show more/less button. If the default is to collapse statuses with subjects, we just treat it like a status with a subject; otherwise, we just treat it like a tall status. - mightHideBecauseSubject () { + mightHideBecauseSubject() { return !!this.status.summary && this.localCollapseSubjectDefault }, - mightHideBecauseTall () { - return this.tallStatus && !(this.status.summary && this.localCollapseSubjectDefault) + mightHideBecauseTall() { + return ( + this.tallStatus && + !(this.status.summary && this.localCollapseSubjectDefault) + ) }, - hideSubjectStatus () { + hideSubjectStatus() { return this.mightHideBecauseSubject && !this.expandingSubject }, - hideTallStatus () { + hideTallStatus() { return this.mightHideBecauseTall && !this.showingTall }, - shouldShowToggle () { + shouldShowToggle() { return this.mightHideBecauseSubject || this.mightHideBecauseTall }, - toggleButtonClasses () { + toggleButtonClasses() { return { 'cw-status-hider': !this.showingMore && this.mightHideBecauseSubject, 'tall-status-hider': !this.showingMore && this.mightHideBecauseTall, 'status-unhider': this.showingMore, } }, - toggleText () { + toggleText() { if (this.showingMore) { - return this.mightHideBecauseSubject ? this.$t('status.hide_content') : this.$t('general.show_less') + return this.mightHideBecauseSubject + ? this.$t('status.hide_content') + : this.$t('general.show_less') } else { - return this.mightHideBecauseSubject ? this.$t('status.show_content') : this.$t('general.show_more') + return this.mightHideBecauseSubject + ? this.$t('status.show_content') + : this.$t('general.show_more') } }, - showingMore () { - return (this.mightHideBecauseTall && this.showingTall) || (this.mightHideBecauseSubject && this.expandingSubject) + showingMore() { + return ( + (this.mightHideBecauseTall && this.showingTall) || + (this.mightHideBecauseSubject && this.expandingSubject) + ) }, - attachmentTypes () { - return this.status.attachments.map(file => fileType.fileType(file.mimetype)) + attachmentTypes() { + return this.status.attachments.map((file) => + fileType.fileType(file.mimetype), + ) }, - collapsedStatus () { + collapsedStatus() { return this.status.raw_html.replace(/(\n|)/g, ' ') }, - ...mapGetters(['mergedConfig']) + ...mapGetters(['mergedConfig']), }, components: { - RichContent + RichContent, }, - mounted () { - this.status.attentions && this.status.attentions.forEach(attn => { - const { id } = attn - this.$store.dispatch('fetchUserIfMissing', id) - }) + mounted() { + this.status.attentions && + this.status.attentions.forEach((attn) => { + const { id } = attn + this.$store.dispatch('fetchUserIfMissing', id) + }) }, methods: { - onParseReady (event) { + onParseReady(event) { if (this.parseReadyDone) return this.parseReadyDone = true this.$emit('parseReady', event) const { writtenMentions, invisibleMentions } = event writtenMentions - .filter(mention => !mention.notifying) - .forEach(mention => { + .filter((mention) => !mention.notifying) + .forEach((mention) => { const { content, url } = mention const cleanedString = content.replace(/<[^>]+?>/gi, '') // remove all tags if (!cleanedString.startsWith('@')) return @@ -137,17 +145,17 @@ const StatusBody = { return acc - mention.textContent.length - 1 }, this.postLength) }, - toggleShowMore () { + toggleShowMore() { if (this.mightHideBecauseTall) { this.toggleShowingTall() } else if (this.mightHideBecauseSubject) { this.toggleExpandingSubject() } }, - generateTagLink (tag) { + generateTagLink(tag) { return `/tag/${tag}` - } - } + }, + }, } export default StatusBody diff --git a/src/components/status_bookmark_folder_menu/status_bookmark_folder_menu.js b/src/components/status_bookmark_folder_menu/status_bookmark_folder_menu.js index 72d4f6fda..99a3ed06d 100644 --- a/src/components/status_bookmark_folder_menu/status_bookmark_folder_menu.js +++ b/src/components/status_bookmark_folder_menu/status_bookmark_folder_menu.js @@ -9,34 +9,32 @@ import StillImage from 'src/components/still-image/still-image.vue' library.add(faChevronRight, faFolder) const StatusBookmarkFolderMenu = { - props: [ - 'status', - 'close' - ], - data () { + props: ['status', 'close'], + data() { return {} }, components: { Popover, - StillImage + StillImage, }, computed: { ...mapState(useBookmarkFoldersStore, { - folders: store => store.allFolders + folders: (store) => store.allFolders, }), - folderId () { + folderId() { return this.status.bookmark_folder_id - } + }, }, methods: { - toggleFolder (id) { + toggleFolder(id) { const value = id === this.folderId ? null : id - this.$store.dispatch('bookmark', { id: this.status.id, bookmark_folder_id: value }) + this.$store + .dispatch('bookmark', { id: this.status.id, bookmark_folder_id: value }) .then(() => this.$emit('onSuccess')) - .catch(err => this.$emit('onError', err.error.error)) - } - } + .catch((err) => this.$emit('onError', err.error.error)) + }, + }, } export default StatusBookmarkFolderMenu diff --git a/src/components/status_content/status_content.js b/src/components/status_content/status_content.js index 364123f49..f2a157537 100644 --- a/src/components/status_content/status_content.js +++ b/src/components/status_content/status_content.js @@ -11,31 +11,29 @@ import { faMusic, faImage, faLink, - faPollH + faPollH, } from '@fortawesome/free-solid-svg-icons' import { useMediaViewerStore } from 'src/stores/media_viewer' -library.add( - faCircleNotch, - faFile, - faMusic, - faImage, - faLink, - faPollH -) +library.add(faCircleNotch, faFile, faMusic, faImage, faLink, faPollH) -const camelCase = name => name.charAt(0).toUpperCase() + name.slice(1) +const camelCase = (name) => name.charAt(0).toUpperCase() + name.slice(1) -const controlledOrUncontrolledGetters = list => list.reduce((res, name) => { - const camelized = camelCase(name) - const toggle = `controlledToggle${camelized}` - const controlledName = `controlled${camelized}` - const uncontrolledName = `uncontrolled${camelized}` - res[name] = function () { - return ((this.$data[toggle] !== undefined || this.$props[toggle] !== undefined) && this[toggle]) ? this[controlledName] : this[uncontrolledName] - } - return res -}, {}) +const controlledOrUncontrolledGetters = (list) => + list.reduce((res, name) => { + const camelized = camelCase(name) + const toggle = `controlledToggle${camelized}` + const controlledName = `controlled${camelized}` + const uncontrolledName = `uncontrolled${camelized}` + res[name] = function () { + return (this.$data[toggle] !== undefined || + this.$props[toggle] !== undefined) && + this[toggle] + ? this[controlledName] + : this[uncontrolledName] + } + return res + }, {}) const controlledOrUncontrolledToggle = (obj, name) => { const camelized = camelCase(name) @@ -63,28 +61,38 @@ const StatusContent = { 'controlledToggleShowingTall', 'controlledToggleExpandingSubject', 'controlledShowingLongSubject', - 'controlledToggleShowingLongSubject' + 'controlledToggleShowingLongSubject', ], emits: ['parseReady', 'mediaplay', 'mediapause'], - data () { + data() { return { - uncontrolledShowingTall: this.fullContent || (this.inConversation && this.focused), + uncontrolledShowingTall: + this.fullContent || (this.inConversation && this.focused), uncontrolledShowingLongSubject: false, // not as computed because it sets the initial state which will be changed later - uncontrolledExpandingSubject: !this.$store.getters.mergedConfig.collapseMessageWithSubject + uncontrolledExpandingSubject: + !this.$store.getters.mergedConfig.collapseMessageWithSubject, } }, computed: { - ...controlledOrUncontrolledGetters(['showingTall', 'expandingSubject', 'showingLongSubject']), - statusCard () { + ...controlledOrUncontrolledGetters([ + 'showingTall', + 'expandingSubject', + 'showingLongSubject', + ]), + statusCard() { if (!this.status.card) return null - return this.status.card.url === this.status.quote_url ? null : this.status.card + return this.status.card.url === this.status.quote_url + ? null + : this.status.card }, - hideAttachments () { - return (this.mergedConfig.hideAttachments && !this.inConversation) || + hideAttachments() { + return ( + (this.mergedConfig.hideAttachments && !this.inConversation) || (this.mergedConfig.hideAttachmentsInConv && this.inConversation) + ) }, - nsfwClickthrough () { + nsfwClickthrough() { if (!this.status.nsfw) { return false } @@ -93,49 +101,54 @@ const StatusContent = { } return true }, - localCollapseSubjectDefault () { + localCollapseSubjectDefault() { return this.mergedConfig.collapseMessageWithSubject }, - attachmentSize () { + attachmentSize() { if (this.compact) { return 'small' - } else if ((this.mergedConfig.hideAttachments && !this.inConversation) || + } else if ( + (this.mergedConfig.hideAttachments && !this.inConversation) || (this.mergedConfig.hideAttachmentsInConv && this.inConversation) || - (this.status.attachments.length > this.maxThumbnails)) { + this.status.attachments.length > this.maxThumbnails + ) { return 'hide' } return 'normal' }, - maxThumbnails () { + maxThumbnails() { return this.mergedConfig.maxThumbnails }, ...mapGetters(['mergedConfig']), ...mapState({ - currentUser: state => state.users.currentUser - }) + currentUser: (state) => state.users.currentUser, + }), }, components: { Attachment, Poll, Gallery, LinkPreview, - StatusBody + StatusBody, }, methods: { - toggleShowingTall () { + toggleShowingTall() { controlledOrUncontrolledToggle(this, 'showingTall') }, - toggleExpandingSubject () { + toggleExpandingSubject() { controlledOrUncontrolledToggle(this, 'expandingSubject') }, - toggleShowingLongSubject () { + toggleShowingLongSubject() { controlledOrUncontrolledToggle(this, 'showingLongSubject') }, - setMedia () { - const attachments = this.attachmentSize === 'hide' ? this.status.attachments : this.galleryAttachments + setMedia() { + const attachments = + this.attachmentSize === 'hide' + ? this.status.attachments + : this.galleryAttachments return () => useMediaViewerStore().setMedia(attachments) - } - } + }, + }, } export default StatusContent diff --git a/src/components/status_history_modal/status_history_modal.js b/src/components/status_history_modal/status_history_modal.js index 215f8152e..216f8f90e 100644 --- a/src/components/status_history_modal/status_history_modal.js +++ b/src/components/status_history_modal/status_history_modal.js @@ -6,56 +6,58 @@ import { useStatusHistoryStore } from 'src/stores/statusHistory' const StatusHistoryModal = { components: { Modal, - Status + Status, }, - data () { + data() { return { - statuses: [] + statuses: [], } }, computed: { - modalActivated () { + modalActivated() { return useStatusHistoryStore().modalActivated }, - params () { + params() { return useStatusHistoryStore().params }, - statusId () { + statusId() { return this.params.id }, - historyCount () { + historyCount() { return this.statuses.length }, - history () { + history() { return this.statuses - } + }, }, watch: { - params (newVal, oldVal) { + params(newVal, oldVal) { const newStatusId = get(newVal, 'id') !== get(oldVal, 'id') if (newStatusId) { this.resetHistory() } - if (newStatusId || get(newVal, 'edited_at') !== get(oldVal, 'edited_at')) { + if ( + newStatusId || + get(newVal, 'edited_at') !== get(oldVal, 'edited_at') + ) { this.fetchStatusHistory() } - } + }, }, methods: { - resetHistory () { + resetHistory() { this.statuses = [] }, - fetchStatusHistory () { - this.$store.dispatch('fetchStatusHistory', this.params) - .then(data => { - this.statuses = data - }) + fetchStatusHistory() { + this.$store.dispatch('fetchStatusHistory', this.params).then((data) => { + this.statuses = data + }) }, - closeModal () { + closeModal() { useStatusHistoryStore().closeStatusHistoryModal() - } - } + }, + }, } export default StatusHistoryModal diff --git a/src/components/status_popover/status_popover.js b/src/components/status_popover/status_popover.js index 2615208d2..2146c45cf 100644 --- a/src/components/status_popover/status_popover.js +++ b/src/components/status_popover/status_popover.js @@ -3,49 +3,46 @@ import { library } from '@fortawesome/fontawesome-svg-core' import { faCircleNotch } from '@fortawesome/free-solid-svg-icons' import { defineAsyncComponent } from 'vue' -library.add( - faCircleNotch -) +library.add(faCircleNotch) const StatusPopover = { name: 'StatusPopover', - props: [ - 'statusId' - ], - data () { + props: ['statusId'], + data() { return { - error: false + error: false, } }, computed: { - status () { + status() { return find(this.$store.state.statuses.allStatuses, { id: this.statusId }) - } + }, }, components: { Status: defineAsyncComponent(() => import('../status/status.vue')), - Popover: defineAsyncComponent(() => import('../popover/popover.vue')) + Popover: defineAsyncComponent(() => import('../popover/popover.vue')), }, methods: { - enter () { + enter() { if (!this.status) { if (!this.statusId) { this.error = true return } - this.$store.dispatch('fetchStatus', this.statusId) + this.$store + .dispatch('fetchStatus', this.statusId) .then(() => (this.error = false)) .catch(() => (this.error = true)) } - } + }, }, watch: { - status (newStatus, oldStatus) { + status(newStatus, oldStatus) { if (newStatus !== oldStatus) { this.$nextTick(() => this.$refs.popover.updateStyles()) } - } - } + }, + }, } export default StatusPopover diff --git a/src/components/sticker_picker/sticker_picker.js b/src/components/sticker_picker/sticker_picker.js index b06384e5f..6a6aed862 100644 --- a/src/components/sticker_picker/sticker_picker.js +++ b/src/components/sticker_picker/sticker_picker.js @@ -4,49 +4,50 @@ import TabSwitcher from '../tab_switcher/tab_switcher.jsx' const StickerPicker = { components: { - TabSwitcher + TabSwitcher, }, - data () { + data() { return { meta: { - stickers: [] + stickers: [], }, - path: '' + path: '', } }, computed: { - pack () { + pack() { return this.$store.state.instance.stickers || [] - } + }, }, methods: { - clear () { + clear() { this.meta = { - stickers: [] + stickers: [], } }, - pick (sticker, name) { + pick(sticker, name) { const store = this.$store // TODO remove this workaround by finding a way to bypass reuploads - fetch(sticker) - .then((res) => { - res.blob().then((blob) => { - const file = new File([blob], name, { mimetype: 'image/png' }) - const formData = new FormData() - formData.append('file', file) - statusPosterService.uploadMedia({ store, formData }) - .then((fileData) => { - this.$emit('uploaded', fileData) - this.clear() - }, (error) => { - console.warn("Can't attach sticker") - console.warn(error) - this.$emit('upload-failed', 'default') - }) - }) + fetch(sticker).then((res) => { + res.blob().then((blob) => { + const file = new File([blob], name, { mimetype: 'image/png' }) + const formData = new FormData() + formData.append('file', file) + statusPosterService.uploadMedia({ store, formData }).then( + (fileData) => { + this.$emit('uploaded', fileData) + this.clear() + }, + (error) => { + console.warn("Can't attach sticker") + console.warn(error) + this.$emit('upload-failed', 'default') + }, + ) }) - } - } + }) + }, + }, } export default StickerPicker diff --git a/src/components/still-image/still-image-emoji-popover.vue b/src/components/still-image/still-image-emoji-popover.vue index 8317c6a0c..ef6cb6e71 100644 --- a/src/components/still-image/still-image-emoji-popover.vue +++ b/src/components/still-image/still-image-emoji-popover.vue @@ -56,51 +56,54 @@