diff --git a/build/check-versions.mjs b/build/check-versions.mjs deleted file mode 100644 index c22004b00..000000000 --- a/build/check-versions.mjs +++ /dev/null @@ -1,41 +0,0 @@ -import chalk from 'chalk' -import semver from 'semver' - -import packageConfig from '../package.json' with { type: 'json' } - -var versionRequirements = [ - { - name: 'node', - currentVersion: semver.clean(process.version), - versionRequirement: packageConfig.engines.node, - }, -] - -export default function () { - const warnings = [] - 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), - ) - } - } - - if (warnings.length) { - 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) - } - process.exit(1) - } -} diff --git a/build/commit_hash.js b/build/commit_hash.js index c60355804..8225817ee 100644 --- a/build/commit_hash.js +++ b/build/commit_hash.js @@ -1,8 +1,8 @@ -import childProcess from 'child_process' +import childProcess from 'node:child_process' export const getCommitHash = () => { const subst = '$Format:%h$' - if (!subst.match(/Format:/)) { + if (!/Format:/.exec(subst)) { return subst } else { try { diff --git a/build/sw_plugin.js b/build/sw_plugin.js index 2f0a4819d..e6be7a738 100644 --- a/build/sw_plugin.js +++ b/build/sw_plugin.js @@ -31,7 +31,6 @@ export const buildSwPlugin = ({ swSrc, swDest }) => { name: 'build-sw-plugin', enforce: 'post', configResolved(resolvedConfig) { - resolvedConfig config = { define: resolvedConfig.define, resolve: resolvedConfig.resolve, @@ -60,7 +59,7 @@ export const buildSwPlugin = ({ swSrc, swDest }) => { sequential: true, async handler(_, bundle) { const assets = Object.keys(bundle) - .filter((name) => !/\.map$/.test(name)) + .filter((name) => !name.endsWith('.map')) .map((name) => '/' + name) config.plugins.push({ diff --git a/build/update-emoji.js b/build/update-emoji.js index 4ff7e1de8..dd965cf66 100644 --- a/build/update-emoji.js +++ b/build/update-emoji.js @@ -1,10 +1,10 @@ +import fs from 'node:fs' 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) => { + emojis[k].forEach((e) => { delete e.unicode_version delete e.emoji_version delete e.skin_tone_support_unicode_version @@ -12,11 +12,15 @@ Object.keys(emojis).map((k) => { }) const res = {} -Object.keys(emojis).map((k) => { - const groupId = k.replace('&', 'and').replace(/ /g, '-').toLowerCase() +Object.keys(emojis).forEach((k) => { + const groupId = k.replace('&', 'and').replaceAll(' ', '-').toLowerCase() res[groupId] = emojis[k] }) console.info('Updating emojis...') -fs.writeFileSync('src/assets/emoji.json', JSON.stringify(res)) -console.info('Done.') +try { + fs.writeFileSync('src/assets/emoji.json', JSON.stringify(res)) + console.info('Done.') +} catch (e) { + console.error('Failed updating emoji', e) +} diff --git a/docker/e2e/Dockerfile.e2e b/docker/e2e/Dockerfile.e2e index 7e3fbfbf1..e0b93d83d 100644 --- a/docker/e2e/Dockerfile.e2e +++ b/docker/e2e/Dockerfile.e2e @@ -1,15 +1,19 @@ FROM mcr.microsoft.com/playwright:v1.61.0-jammy +RUN npm install -g yarn@1.22.22 + +RUN adduser --system playwright --group --shell=/bin/bash + +USER playwright + WORKDIR /app ENV PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 -RUN npm install -g yarn@1.22.22 - COPY package.json yarn.lock ./ RUN yarn --frozen-lockfile -COPY . . +COPY --chown=playwright ./build ./src ./test ./public ./static ./yarn.lock ./package.json . ENV CI=1 diff --git a/index.html b/index.html index 26eeee19b..346b9f06c 100644 --- a/index.html +++ b/index.html @@ -31,7 +31,7 @@
- + A cute mascot
diff --git a/public/static/terms-of-service.html b/public/static/terms-of-service.html index 2b7bf7697..19db6408e 100644 --- a/public/static/terms-of-service.html +++ b/public/static/terms-of-service.html @@ -6,4 +6,4 @@ Pleroma install containing the real ToS for your instance.

See the Pleroma documentation for more information.


- +Pleroma Logo diff --git a/src/App.scss b/src/App.scss index 0de5a3577..ccdf636bb 100644 --- a/src/App.scss +++ b/src/App.scss @@ -1,10 +1,11 @@ -// stylelint-disable rscss/class-format -/* stylelint-disable no-descending-specificity */ @use "panel"; @import '@fortawesome/fontawesome-svg-core/styles.css'; @import '@kazvmoe-infra/pinch-zoom-element/dist/pinch-zoom.css'; +// stylelint-disable rscss/class-format +/* stylelint-disable no-descending-specificity */ + :root { --status-margin: 0.75em; --post-line-height: 1.4; @@ -934,7 +935,6 @@ option { #splash { pointer-events: none; - // transition: opacity 0.5s; #status { &.css-ok { diff --git a/src/api/admin.js b/src/api/admin.js index c33f863a7..d4a9e8ff9 100644 --- a/src/api/admin.js +++ b/src/api/admin.js @@ -39,7 +39,7 @@ const USERS_URL_LIST = ({ isAdmin && 'is_admin', isModerator && 'is_moderator', ] - .filter((x) => x) + .filter(Boolean) .join(',') return `/api/v1/pleroma/admin/users?page=${page}&page_size=${pageSize}&filters=${filters_str}&query=${query}&name=${name}&email=${email}` } @@ -236,7 +236,7 @@ export const changeStatusScope = ({ opts: { id, sensitive, visibility }, credentials, }) => { - var payload = {} + const payload = {} if (typeof sensitive !== 'undefined') { payload['sensitive'] = sensitive } diff --git a/src/api/helpers.js b/src/api/helpers.js index 4bf16e0cc..cb2f2bdd5 100644 --- a/src/api/helpers.js +++ b/src/api/helpers.js @@ -6,12 +6,12 @@ export const paramsString = (params = {}) => { if (params == null || params === undefined) return '' if (typeof params !== 'object' || Array.isArray(params)) { - throw new Error('Params are not an object!') + throw new TypeError('Params are not an object!') } const entries = (() => { if (params instanceof Map) { - return params.entries() + return [...params.entries()] } else { return Object.entries(params) } @@ -26,7 +26,7 @@ export const paramsString = (params = {}) => { (typeof v === 'object' && !Array.isArray(v)) || typeof v === 'function' ) { - throw new Error('Param cannot be non-primitive!') + throw new TypeError('Param cannot be non-primitive!') } if (Array.isArray(v)) { arrays.push([k, v]) @@ -42,7 +42,7 @@ export const paramsString = (params = {}) => { typeof v === 'function' || typeof v === 'undefined' ) - throw new Error('Array param cannot contain non-primitives!') + throw new TypeError('Array param cannot contain non-primitives!') }) }) @@ -109,7 +109,9 @@ export const promisedRequest = async ({ .get('content-type') .split(';') .map((x) => x.toLowerCase().trim()) - const contentLength = parseInt(response.headers.get('content-length')) + const contentLength = Number.parseInt( + response.headers.get('content-length'), + ) if (contentLength === 0) return null switch (contentType) { diff --git a/src/api/public.js b/src/api/public.js index e001ba749..78db90bfe 100644 --- a/src/api/public.js +++ b/src/api/public.js @@ -105,7 +105,7 @@ export const fetchUserByName = ({ name, credentials }) => }) .then(({ data }) => data.id) .catch((error) => { - if (error && error.statusCode === 404) { + if (error?.statusCode === 404) { // Either the backend does not support lookup endpoint, // or there is no user with such name. Fallback and treat name as id. return name diff --git a/src/api/user.js b/src/api/user.js index 9a55a6b93..8086d6d7f 100644 --- a/src/api/user.js +++ b/src/api/user.js @@ -1,4 +1,4 @@ -import { concat, last } from 'lodash' +import { last } from 'lodash' import { paramsString, promisedRequest } from './helpers.js' import { fetchFriends, MASTODON_STATUS_URL } from './public.js' @@ -220,7 +220,7 @@ export const postStatus = ({ }) if (pollOptions.some((option) => option !== '')) { const normalizedPoll = { - expires_in: parseInt(poll.expiresIn, 10), + expires_in: Number.parseInt(poll.expiresIn, 10), multiple: poll.multiple, } Object.keys(normalizedPoll).forEach((key) => { @@ -278,7 +278,7 @@ export const editStatus = ({ if (pollOptions.some((option) => option !== '')) { const normalizedPoll = { - expires_in: parseInt(poll.expiresIn, 10), + expires_in: Number.parseInt(poll.expiresIn, 10), multiple: poll.multiple, } Object.keys(normalizedPoll).forEach((key) => { @@ -412,7 +412,7 @@ export const exportFriends = ({ id, credentials }) => { credentials, withRelationships: true, }) - friends = concat(friends, users) + friends = [...friends, ...users] if (users.length === 0) { more = false } @@ -613,7 +613,7 @@ export const listAliases = ({ credentials }) => method: 'GET', credentials, params: { - _cacheBooster: new Date().getTime(), + _cacheBooster: Date.now(), }, }) @@ -799,7 +799,7 @@ export const listBackups = ({ credentials }) => method: 'GET', credentials, params: { - _cacheBooster: new Date().getTime(), + _cacheBooster: Date.now(), }, }) diff --git a/src/api/websocket.js b/src/api/websocket.js index d952372e5..ae2e5358f 100644 --- a/src/api/websocket.js +++ b/src/api/websocket.js @@ -141,13 +141,11 @@ export const handleMastoWS = ( if (data.result === 'success') { console.debug('[WS] Successfully authenticated') onAuthenticated() + } else if (data.error === 'already_authenticated') { + onAuthenticated() } else { - if (data.error === 'already_authenticated') { - onAuthenticated() - } else { - console.error('[WS] Unable to authenticate:', data.error) - wsEvent.target.close() - } + console.error('[WS] Unable to authenticate:', data.error) + wsEvent.target.close() } } return null diff --git a/src/boot/after_store.js b/src/boot/after_store.js index 4ccfa4d41..986586a80 100644 --- a/src/boot/after_store.js +++ b/src/boot/after_store.js @@ -63,14 +63,14 @@ const parsedInitialResults = () => { const decodeUTF8Base64 = (data) => { const rawData = atob(data) - const array = Uint8Array.from([...rawData].map((char) => char.charCodeAt(0))) + const array = Uint8Array.from([...rawData].map((char) => char.codePointAt(0))) const text = new TextDecoder().decode(array) return text } const preloadFetch = async (request) => { const data = parsedInitialResults() - if (!data || !data[request]) { + if (!data?.[request]) { return window.fetch(request) } const decoded = decodeUTF8Base64(data[request]) @@ -170,9 +170,9 @@ const setSettings = async ({ apiConfig, staticConfig, store }) => { let config = {} if (overrides.staticConfigPreference && env === 'development') { console.warn('OVERRIDING API CONFIG WITH STATIC CONFIG') - config = Object.assign({}, apiConfig, staticConfig) + config = { ...apiConfig, ...staticConfig } } else { - config = Object.assign({}, staticConfig, apiConfig) + config = { ...staticConfig, ...apiConfig } } Object.keys(INSTANCE_IDENTITY_DEFAULT_DEFINITIONS).forEach((source) => { @@ -353,19 +353,19 @@ const getNodeInfo = async ({ store }) => { const uploadLimits = metadata.uploadLimits useInstanceStore().set({ path: 'limits.uploadlimit', - value: parseInt(uploadLimits.general), + value: Number.parseInt(uploadLimits.general), }) useInstanceStore().set({ path: 'limits.avatarlimit', - value: parseInt(uploadLimits.avatar), + value: Number.parseInt(uploadLimits.avatar), }) useInstanceStore().set({ path: 'limits.backgroundlimit', - value: parseInt(uploadLimits.background), + value: Number.parseInt(uploadLimits.background), }) useInstanceStore().set({ path: 'limits.bannerlimit', - value: parseInt(uploadLimits.banner), + value: Number.parseInt(uploadLimits.banner), }) useInstanceStore().set({ path: 'limits.fieldsLimits', @@ -409,7 +409,7 @@ const getNodeInfo = async ({ store }) => { useInstanceCapabilitiesStore().set( 'tagPolicyAvailable', - typeof federation.mrf_policies === 'undefined' + federation.mrf_policies === undefined ? false : metadata.federation.mrf_policies.includes('TagPolicy'), ) @@ -420,8 +420,7 @@ const getNodeInfo = async ({ store }) => { }) useInstanceStore().set({ path: 'federating', - value: - typeof federation.enabled === 'undefined' ? true : federation.enabled, + value: federation.enabled === undefined ? true : federation.enabled, }) const accountActivationRequired = metadata.accountActivationRequired @@ -443,7 +442,7 @@ const getNodeInfo = async ({ store }) => { const setConfig = async ({ store }) => { // apiConfig, staticConfig const configInfos = await Promise.all([ - getBackendProvidedConfig({ store }), + getBackendProvidedConfig(), getStaticConfig(), ]) const apiConfig = configInfos[0] @@ -457,7 +456,7 @@ const checkOAuthToken = async ({ store }) => { if (oauth.userToken) { return store.dispatch('loginUser', oauth.userToken) } - return Promise.resolve() + return } const afterStoreSetup = async ({ pinia, store, storageError, i18n }) => { @@ -485,7 +484,7 @@ 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( + throw new TypeError( 'No stores are available. Check the code in src/boot/after_store.js', ) } @@ -495,7 +494,7 @@ const afterStoreSetup = async ({ pinia, store, storageError, i18n }) => { const isStoreName = (name) => name.startsWith('use') if (process.env.NODE_ENV === 'development') { if (Object.keys(mod).filter(isStoreName).length !== 1) { - throw new Error( + throw new TypeError( 'Each store file must export exactly one store as a named export. Check your code in src/stores/', ) } @@ -504,13 +503,13 @@ const afterStoreSetup = async ({ pinia, store, storageError, i18n }) => { if (storeFuncName && typeof mod[storeFuncName] === 'function') { const p = mod[storeFuncName]().$persistLoaded if (!(p instanceof Promise)) { - throw new Error( + throw new TypeError( `${name} store's $persistLoaded is not a Promise. The persist plugin is not applied.`, ) } await p } else { - throw new Error( + throw new TypeError( `Store module ${name} does not export a 'use...' function`, ) } @@ -518,14 +517,15 @@ const afterStoreSetup = async ({ pinia, store, storageError, i18n }) => { ) } + let newStorageError try { await waitForAllStoresToLoad() } catch (e) { console.error('Cannot load stores:', e) - storageError = e + newStorageError = e } - if (storageError) { + if (storageError || newStorageError) { useInterfaceStore().pushGlobalNotice({ messageKey: 'errors.storage_unavailable', level: 'error', @@ -547,9 +547,7 @@ const afterStoreSetup = async ({ pinia, store, storageError, i18n }) => { const overrides = window.___pleromafe_dev_overrides || {} const server = - typeof overrides.target !== 'undefined' - ? overrides.target - : window.location.origin + overrides.target !== undefined ? overrides.target : window.location.origin useInstanceStore().set({ path: 'server', value: server }) await setConfig({ store }) @@ -561,7 +559,7 @@ const afterStoreSetup = async ({ pinia, store, storageError, i18n }) => { }) } catch (e) { window.splashError(e) - return Promise.reject(e) + throw e } applyStyleConfig(useMergedConfigStore().mergedConfig, i18n.global) @@ -573,7 +571,7 @@ const afterStoreSetup = async ({ pinia, store, storageError, i18n }) => { getInstancePanel({ store }), getNodeInfo({ store }), getInstanceConfig({ store }), - ]).catch((e) => Promise.reject(e)) + ]) getTOS({ store }) getStickers({ store }) @@ -583,7 +581,7 @@ const afterStoreSetup = async ({ pinia, store, storageError, i18n }) => { routes: routes(store), scrollBehavior: (to, _from, savedPosition) => { if (to.matched.some((m) => m.meta.dontScroll)) { - return false + return {} } return savedPosition || { left: 0, top: 0 } }, diff --git a/src/components/chat_list_item/chat_list_item.js b/src/components/chat_list_item/chat_list_item.js index c95895def..8bb2afd93 100644 --- a/src/components/chat_list_item/chat_list_item.js +++ b/src/components/chat_list_item/chat_list_item.js @@ -39,7 +39,7 @@ const ChatListItem = { messageForStatusContent() { const message = this.chat.lastMessage const messageEmojis = message ? message.emojis : [] - const isYou = message && message.account_id === this.currentUser.id + const isYou = message?.account_id === this.currentUser.id const content = message ? this.attachmentInfo || message.content : '' const messagePreview = isYou ? `${this.$t('chats.you')} ${content}` diff --git a/src/components/chat_message/chat_message.js b/src/components/chat_message/chat_message.js index 78be870b4..aabdec31f 100644 --- a/src/components/chat_message/chat_message.js +++ b/src/components/chat_message/chat_message.js @@ -111,7 +111,7 @@ const ChatMessage = { const user = this.$store.getters.findUser( this.message.in_reply_to_user_id, ) - return user && user.screen_name_ui + return user?.screen_name_ui } }, replyProfileLink() { diff --git a/src/components/chat_message/chat_message.vue b/src/components/chat_message/chat_message.vue index b772a9b20..394cce4b7 100644 --- a/src/components/chat_message/chat_message.vue +++ b/src/components/chat_message/chat_message.vue @@ -53,7 +53,7 @@ :user-screen-name="message.in_reply_to_screen_name" /> -