diff --git a/.woodpecker/build-package.yaml b/.woodpecker/build-package.yaml
new file mode 100644
index 000000000..d06a267cd
--- /dev/null
+++ b/.woodpecker/build-package.yaml
@@ -0,0 +1,36 @@
+when:
+ - event: push
+ branch: ${CI_REPO_DEFAULT_BRANCH}
+ - event: manual
+ branch: ${CI_REPO_DEFAULT_BRANCH}
+
+depends_on:
+ - test
+ - test-e2e
+
+labels:
+ platform: linux/amd64
+ memory: 'high'
+
+steps:
+ build:
+ image: docker.io/node:20-alpine
+ commands:
+ - apk add --no-cache zip git
+ - yarn --frozen-lockfile
+ - yarn build
+ - if [ "${CI_PIPELINE_EVENT}" = "push" ] || [ "${CI_PIPELINE_EVENT}" = "manual" ]; then zip -9qr ${CI_REPO_DEFAULT_BRANCH}.zip dist/; fi
+
+ upload-artifacts:
+ image: docker.io/woodpeckercommunity/plugin-gitea-package:0.5.0
+ settings:
+ user:
+ from_secret: pleroma-ci-user
+ password:
+ from_secret: pleroma-ci-password
+ update: true
+ owner: 'pleroma'
+ package_name: 'pleroma-fe-builds'
+ package_version: ${CI_REPO_DEFAULT_BRANCH}
+ file_source: ./${CI_REPO_DEFAULT_BRANCH}.zip
+ file_name: latest.zip
diff --git a/.woodpecker/build.yaml b/.woodpecker/build.yaml
index af0bb98e3..d6bd36f3e 100644
--- a/.woodpecker/build.yaml
+++ b/.woodpecker/build.yaml
@@ -6,38 +6,12 @@ when:
- event: manual
branch: ${CI_REPO_DEFAULT_BRANCH}
-depends_on:
- - test
- - test-e2e
-
labels:
platform: linux/amd64
- memory: 'high'
steps:
build:
image: docker.io/node:20-alpine
commands:
- - apk add --no-cache zip git
- yarn --frozen-lockfile
- yarn build
- - if [ "${CI_PIPELINE_EVENT}" = "push" ] || [ "${CI_PIPELINE_EVENT}" = "manual" ]; then zip -9qr ${CI_REPO_DEFAULT_BRANCH}.zip dist/; fi
-
- upload-artifacts:
- image: docker.io/woodpeckercommunity/plugin-gitea-package:0.5.0
- when:
- - event: push
- branch: ${CI_REPO_DEFAULT_BRANCH}
- - event: manual
- branch: ${CI_REPO_DEFAULT_BRANCH}
- settings:
- user:
- from_secret: pleroma-ci-user
- password:
- from_secret: pleroma-ci-password
- update: true
- owner: 'pleroma'
- package_name: 'pleroma-fe-builds'
- package_version: ${CI_REPO_DEFAULT_BRANCH}
- file_source: ./${CI_REPO_DEFAULT_BRANCH}.zip
- file_name: latest.zip
diff --git a/.woodpecker/code-analisys.yaml b/.woodpecker/code-analisys.yaml
new file mode 100644
index 000000000..11ee853ee
--- /dev/null
+++ b/.woodpecker/code-analisys.yaml
@@ -0,0 +1,45 @@
+when:
+ - event: push
+ branch: ${CI_REPO_DEFAULT_BRANCH}
+ - event: manual
+ branch: ${CI_REPO_DEFAULT_BRANCH}
+
+labels:
+ platform: linux/amd64
+ memory: 'high'
+
+depends_on:
+ - build
+ - test
+ - test-e2e
+
+clone:
+ - name: git
+ image: woodpeckerci/plugin-git
+ settings:
+ depth: 0
+ partial: false
+
+variables:
+ script_file_entrypoint: &script_file_entrypoint
+ - /bin/sh
+ - -c
+ - 'printf "%s" "$CI_SCRIPT" | base64 -d > /tmp/ci-script.sh && /bin/sh -xe /tmp/ci-script.sh'
+
+steps:
+ generate-coverage:
+ image: mcr.microsoft.com/playwright:v1.61.0-jammy
+ environment:
+ FF_NETWORK_PER_BUILD: "true"
+ PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "1"
+ entrypoint: *script_file_entrypoint
+ commands:
+ - yarn --frozen-lockfile
+ - yarn unit-ci-coverage
+
+ code-analysis:
+ image: sonarsource/sonar-scanner-cli:11
+ environment:
+ SONAR_TOKEN:
+ from_secret: sonarqube-token
+ entrypoint: sonar-scanner
diff --git a/.woodpecker/test-e2e.yaml b/.woodpecker/test-e2e.yaml
index 2a7d1511d..8f7e407f8 100644
--- a/.woodpecker/test-e2e.yaml
+++ b/.woodpecker/test-e2e.yaml
@@ -10,6 +10,10 @@ labels:
platform: linux/amd64
memory: 'high'
+depends_on:
+ - build
+ - test
+
variables:
artifacts_uploader_settings: &artifacts_uploader_settings
user:
diff --git a/.woodpecker/test.yaml b/.woodpecker/test.yaml
index 9c3c24a53..c3f2c3793 100644
--- a/.woodpecker/test.yaml
+++ b/.woodpecker/test.yaml
@@ -10,6 +10,9 @@ labels:
platform: linux/amd64
memory: 'high'
+depends_on:
+ - build
+
variables:
artifacts_uploader_settings: &artifacts_uploader_settings
user:
@@ -45,20 +48,6 @@ steps:
exit 1
fi
- code-analysis:
- image: sonarsource/sonar-scanner-cli:11
- when:
- - event: push
- branch: ${CI_REPO_DEFAULT_BRANCH}
- - event: manual
- branch: ${CI_REPO_DEFAULT_BRANCH}
- environment:
- SONAR_TOKEN:
- from_secret: sonarqube-token
- entrypoint: *script_file_entrypoint
- commands:
- - sonar-scanner
-
upload-artifacts:
image: docker.io/woodpeckercommunity/plugin-gitea-package:0.5.0
when:
diff --git a/README.md b/README.md
index 16d32dcd2..a1dabba35 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,14 @@
-# Pleroma-FE
+# Pleroma-FE
> Highly-customizable frontend designed for Pleroma.

+[](https://ci.pleroma.com/repos/2) [](https://sonarqube.pleroma.dev/dashboard?id=Pleroma-FE) [](https://sonarqube.pleroma.dev/dashboard?id=Pleroma-FE) [](https://sonarqube.pleroma.dev/dashboard?id=Pleroma-FE) [](https://sonarqube.pleroma.dev/dashboard?id=Pleroma-FE)
+
# For Translators
-To translate Pleroma-FE, use our weblate server: https://translate.pleroma.social/. If you need to add your language it should be added as a json file in [src/i18n/](https://git.pleroma.social/pleroma/pleroma-fe/src/src/i18n/) folder and added in a list within [src/i18n/languages.js](https://git.pleroma.social/pleroma/pleroma-fe/src/src/i18n/languages.js).
+To translate Pleroma-FE, use our weblate server: https://translate.pleroma.social/. If you need to add your language it should be added as a json file in [src/i18n/](https://git.pleroma.social/pleroma/pleroma-fe/src/src/i18n/) folder and added in a list within [src/i18n/languages.js](https://git.pleroma.social/pleroma/pleroma-fe/src/src/i18n/languages.js).
Pleroma-FE will set your language by your browser locale, but you can change language in settings.
@@ -32,9 +34,14 @@ yarn unit
# For Contributors:
+You can make local build proxy requests to specific instance with an environment variable `VITE_PROXY_TARGET`, generally it is a good idea to also set port for that specific proxy, if you intend on testing multiple instances, i.e.:
+
+```bash
+yarn && PORT=8080 VITE_PROXY_TARGET=https://coolinstance.tld yarn dev
+```
+
You can create file `/config/local.json` (see [example](https://git.pleroma.social/pleroma/pleroma-fe/src/config/local.example.json)) to enable some convenience dev options:
-* `target`: makes local dev server redirect to some existing instance's BE instead of local BE, useful for testing things in near-production environment and searching for real-life use-cases.
* `staticConfigPreference`: makes FE's `/static/config.json` take preference of BE-served `/api/pleroma/frontend_configurations`. Only works in dev mode.
FE Build process also leaves current commit hash in global variable `___pleromafe_commit_hash` so that you can easily see which pleroma-fe commit instance is running, also helps pinpointing which commit was used when FE was bundled into BE.
diff --git a/build/commit_hash.js b/build/commit_hash.js
index 8225817ee..2b13afb93 100644
--- a/build/commit_hash.js
+++ b/build/commit_hash.js
@@ -7,7 +7,9 @@ export const getCommitHash = () => {
} else {
try {
return childProcess
- .execSync('git rev-parse --short HEAD')
+ .execSync(
+ 'PATH=/usr/bin:/bin:/usr/local/bin:/sbin:/usr/sbin git rev-parse --short HEAD',
+ )
.toString()
.trim()
} catch (e) {
diff --git a/build/emojis_plugin.js b/build/emojis_plugin.js
index 9872f5331..b21c10048 100644
--- a/build/emojis_plugin.js
+++ b/build/emojis_plugin.js
@@ -35,7 +35,7 @@ const getAllAccessibleAnnotations = async (projectRoot) => {
}),
)
)
- .filter((k) => k)
+ .filter(Boolean)
.join(',\n')
return `
diff --git a/build/update-emoji.js b/build/update-emoji.js
index dd965cf66..b2631ec6e 100644
--- a/build/update-emoji.js
+++ b/build/update-emoji.js
@@ -3,7 +3,7 @@ import emojis from '@kazvmoe-infra/unicode-emoji-json/data-by-group.json' with {
type: 'json',
}
-Object.keys(emojis).map((k) => {
+Object.keys(emojis).forEach((k) => {
emojis[k].forEach((e) => {
delete e.unicode_version
delete e.emoji_version
diff --git a/changelog.d/server-mute-render-modify.fix b/changelog.d/server-mute-render-modify.fix
new file mode 100644
index 000000000..cb94bb826
--- /dev/null
+++ b/changelog.d/server-mute-render-modify.fix
@@ -0,0 +1 @@
+Fixed server-side domain mutes rendering/api calls
diff --git a/changelog.d/theme2-font-cache.fix b/changelog.d/theme2-font-cache.fix
new file mode 100644
index 000000000..d8782b467
--- /dev/null
+++ b/changelog.d/theme2-font-cache.fix
@@ -0,0 +1 @@
+Fix Theme 2 fonts falling back to serif after upgrade
diff --git a/changelog.d/timeline_rendering_status_index_approx.fix b/changelog.d/timeline_rendering_status_index_approx.fix
new file mode 100644
index 000000000..717464c58
--- /dev/null
+++ b/changelog.d/timeline_rendering_status_index_approx.fix
@@ -0,0 +1 @@
+Fixed status index approxmiation in timeline rendering for dynamically changing viewport geometries
diff --git a/package.json b/package.json
index f8bacaf15..50138e6a2 100644
--- a/package.json
+++ b/package.json
@@ -8,7 +8,8 @@
"dev": "node build/update-emoji.js && vite dev",
"build": "node build/update-emoji.js && vite build",
"unit": "node build/update-emoji.js && vitest --run --coverage",
- "unit-ci": "node build/update-emoji.js && vitest --run --coverage --browser.headless",
+ "unit-ci": "node build/update-emoji.js && vitest --run --browser.headless",
+ "unit-ci-coverage": "node build/update-emoji.js && vitest --run --coverage --browser.headless",
"unit:watch": "node build/update-emoji.js && vitest --coverage",
"e2e:pw": "playwright test --config test/e2e-playwright/playwright.config.mjs",
"e2e": "sh ./tools/e2e/run.sh",
diff --git a/sonar-project.properties b/sonar-project.properties
index 15030e3cc..5fae237b8 100644
--- a/sonar-project.properties
+++ b/sonar-project.properties
@@ -1,3 +1,5 @@
sonar.projectKey=Pleroma-FE
sonar.qualitygatesonar.host.url.wait=true
sonar.host.url=https://sonarqube.pleroma.dev
+sonar.sources=src,build,tools
+sonar.tests=test
diff --git a/src/api/admin.js b/src/api/admin.js
index d4a9e8ff9..8bbee8b4f 100644
--- a/src/api/admin.js
+++ b/src/api/admin.js
@@ -237,10 +237,10 @@ export const changeStatusScope = ({
credentials,
}) => {
const payload = {}
- if (typeof sensitive !== 'undefined') {
+ if (sensitive !== undefined) {
payload['sensitive'] = sensitive
}
- if (typeof visibility !== 'undefined') {
+ if (visibility !== undefined) {
payload['visibility'] = visibility
}
@@ -260,15 +260,15 @@ export const announcementToPayload = ({
}) => {
const payload = { content }
- if (typeof startsAt !== 'undefined') {
+ if (startsAt !== undefined) {
payload.starts_at = startsAt ? new Date(startsAt).toISOString() : null
}
- if (typeof endsAt !== 'undefined') {
+ if (endsAt !== undefined) {
payload.ends_at = endsAt ? new Date(endsAt).toISOString() : null
}
- if (typeof allDay !== 'undefined') {
+ if (allDay !== undefined) {
payload.all_day = allDay
}
diff --git a/src/api/chats.js b/src/api/chats.js
index 5d766dec4..b212380de 100644
--- a/src/api/chats.js
+++ b/src/api/chats.js
@@ -18,7 +18,7 @@ export const chats = ({ credentials }) =>
url: PLEROMA_CHATS_URL,
credentials,
}).then(({ data }) => ({
- data: data.map(parseChat).filter((c) => c),
+ data: data.map(parseChat).filter(Boolean),
}))
export const getOrCreateChat = ({ accountId, credentials }) =>
@@ -40,7 +40,7 @@ export const chatMessages = ({
method: 'GET',
credentials,
}).then(({ data }) => ({
- data: data.map(parseChatMessage).filter((c) => c),
+ data: data.map(parseChatMessage).filter(Boolean),
}))
}
diff --git a/src/api/helpers.js b/src/api/helpers.js
index b033d80b8..326275495 100644
--- a/src/api/helpers.js
+++ b/src/api/helpers.js
@@ -37,11 +37,7 @@ export const paramsString = (params = {}) => {
arrays.forEach(([k, array]) => {
array.forEach((v) => {
- if (
- typeof v === 'object' ||
- typeof v === 'function' ||
- typeof v === 'undefined'
- )
+ if (typeof v === 'object' || typeof v === 'function' || v === undefined)
throw new TypeError('Array param cannot contain non-primitives!')
})
})
diff --git a/src/components/announcement/announcement.js b/src/components/announcement/announcement.js
index ee427533f..6b45b2b90 100644
--- a/src/components/announcement/announcement.js
+++ b/src/components/announcement/announcement.js
@@ -29,9 +29,8 @@ const Announcement = {
currentUser: (state) => state.users.currentUser,
}),
canEditAnnouncement() {
- return (
- this.currentUser &&
- this.currentUser.privileges.has('announcements_manage_announcements')
+ return this.currentUser?.privileges.has(
+ 'announcements_manage_announcements',
)
},
content() {
diff --git a/src/components/announcements_page/announcements_page.js b/src/components/announcements_page/announcements_page.js
index 0f7933e5f..802e90cf6 100644
--- a/src/components/announcements_page/announcements_page.js
+++ b/src/components/announcements_page/announcements_page.js
@@ -33,9 +33,8 @@ const AnnouncementsPage = {
return useAnnouncementsStore().announcements
},
canPostAnnouncement() {
- return (
- this.currentUser &&
- this.currentUser.privileges.has('announcements_manage_announcements')
+ return this.currentUser?.privileges.has(
+ 'announcements_manage_announcements',
)
},
},
diff --git a/src/components/attachment/attachment.js b/src/components/attachment/attachment.js
index f7d6ebf10..6556b07a0 100644
--- a/src/components/attachment/attachment.js
+++ b/src/components/attachment/attachment.js
@@ -165,16 +165,16 @@ const Attachment = {
useMediaViewerStore().setCurrentMedia(this.attachment)
},
onEdit(event) {
- this.edit && this.edit(this.attachment, event)
+ this.edit?.(this.attachment, event)
},
onRemove() {
- this.remove && this.remove(this.attachment)
+ this.remove?.(this.attachment)
},
onShiftUp() {
- this.shiftUp && this.shiftUp(this.attachment)
+ this.shiftUp?.(this.attachment)
},
onShiftDn() {
- this.shiftDn && this.shiftDn(this.attachment)
+ this.shiftDn?.(this.attachment)
},
stopFlash() {
this.$refs.flash.closePlayer()
diff --git a/src/components/chat_message/chat_message.vue b/src/components/chat_message/chat_message.vue
index 394cce4b7..88ab2fc0a 100644
--- a/src/components/chat_message/chat_message.vue
+++ b/src/components/chat_message/chat_message.vue
@@ -1,9 +1,9 @@
@@ -40,7 +40,10 @@
-
+
{{ $t('status.broken_reply') }}
@@ -83,7 +86,10 @@
:user="author"
/>
-
+
@@ -98,7 +104,6 @@
@mouseenter="hovered = true"
@mouseleave="hovered = false"
>
-
@@ -232,7 +240,10 @@
v-else
class="chat-message-date-separator"
>
-
+
diff --git a/src/components/chat_message_list/chat_message_list.vue b/src/components/chat_message_list/chat_message_list.vue
index 5cdbf6871..7833730e7 100644
--- a/src/components/chat_message_list/chat_message_list.vue
+++ b/src/components/chat_message_list/chat_message_list.vue
@@ -7,7 +7,7 @@
:previous-item="getPreviousItem(index)"
:hovered-message-chain="chatItem.messageChainId === hoveredMessageChainId"
:focused="chatItem.id === focusedId"
- :repliedTo="chatItem.id === repliedId"
+ :replied-to="chatItem.id === repliedId"
@hover="onMessageHover"
@delete="onMessageDelete"
@reply-requested="onReplyRequested"
diff --git a/src/components/chat_view/chat_view.js b/src/components/chat_view/chat_view.js
index f48b75c65..16b2ae1d7 100644
--- a/src/components/chat_view/chat_view.js
+++ b/src/components/chat_view/chat_view.js
@@ -97,7 +97,7 @@ const Chat = {
mounted() {
window.addEventListener('resize', this.handleResize)
window.addEventListener('scroll', this.handleScroll)
- if (typeof document.hidden !== 'undefined') {
+ if (document.hidden !== undefined) {
document.addEventListener(
'visibilitychange',
this.handleVisibilityChange,
@@ -112,7 +112,7 @@ const Chat = {
unmounted() {
window.removeEventListener('scroll', this.handleScroll)
window.removeEventListener('resize', this.handleResize)
- if (typeof document.hidden !== 'undefined')
+ if (document.hidden !== undefined)
document.removeEventListener(
'visibilitychange',
this.handleVisibilityChange,
diff --git a/src/components/chat_view/chat_view.vue b/src/components/chat_view/chat_view.vue
index e6bef0f84..06862ab3c 100644
--- a/src/components/chat_view/chat_view.vue
+++ b/src/components/chat_view/chat_view.vue
@@ -24,7 +24,7 @@
v-if="messages[0]?.summary_raw_html"
:html="messages[0].summary_raw_html"
:emoji="messages[0].emojis"
- />
+ />
{{ $t('timeline.conversation') }}
diff --git a/src/components/color_input/color_input.vue b/src/components/color_input/color_input.vue
index d6667c28f..53396f532 100644
--- a/src/components/color_input/color_input.vue
+++ b/src/components/color_input/color_input.vue
@@ -11,11 +11,11 @@
{{ label }}
diff --git a/src/components/timeline/timeline.js b/src/components/timeline/timeline.js
index 17065a9ef..c3f4d381d 100644
--- a/src/components/timeline/timeline.js
+++ b/src/components/timeline/timeline.js
@@ -263,7 +263,10 @@ const Timeline = {
// Start from approximating the index of some visible status by using the
// the center of the screen on the timeline.
- let approxIndex = Math.floor(statuses.length * (centerOfScreen / height))
+ let approxIndex = Math.min(
+ Math.floor(statuses.length * (centerOfScreen / height)),
+ statuses.length - 1,
+ )
let err = statuses[approxIndex].getBoundingClientRect().y
// if we have a previous scroll index that can be used, test if it's
diff --git a/src/components/user_card/user_card.js b/src/components/user_card/user_card.js
index 9f6488b34..b6e720c6f 100644
--- a/src/components/user_card/user_card.js
+++ b/src/components/user_card/user_card.js
@@ -196,7 +196,7 @@ export default {
},
computed: {
escapedNewBio() {
- return ldEscape(this.newBio).replace(/\n/g, '
')
+ return ldEscape(this.newBio).replaceAll('\n', '
')
},
somethingToSave() {
if (this.newName !== this.user.name_unescaped) return true
diff --git a/src/i18n/compare b/src/i18n/compare
index 94e5aab55..7afe9355c 100755
--- a/src/i18n/compare
+++ b/src/i18n/compare
@@ -1,7 +1,7 @@
#!/usr/bin/env node
const arg = process.argv[2]
-if (typeof arg === 'undefined') {
+if (arg === undefined) {
console.info('This is a very simple and tiny tool that checks en.json with any other language and')
console.info('outputs all the things present in english but missing in foreign language.')
console.info('')
diff --git a/src/i18n/en.json b/src/i18n/en.json
index 60fcaf1dd..4b257be9f 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -242,6 +242,8 @@
"single_choice": "Single choice",
"multiple_choices": "Multiple choices",
"expiry": "Poll age",
+ "expiry_unit": "Expiry unit",
+ "expiry_amount": "Expiry amount",
"expires_at": "Poll ends {0}",
"expires_in": "Poll ends in {0}",
"expired": "Poll ended {0} ago",
diff --git a/src/i18n/messages.js b/src/i18n/messages.js
index 76d6b5386..51b8d920c 100644
--- a/src/i18n/messages.js
+++ b/src/i18n/messages.js
@@ -18,9 +18,9 @@ const hasLanguageFile = (code) => languages.includes(code)
const languageFileMap = import.meta.glob(['./*.json', '!./en.json'])
-const loadLanguageFile = (code) => {
+const loadLanguageFile = async (code) => {
const jsonName = langCodeToJsonName(code)
- if (jsonName === 'en') return Promise.resolve({ default: enMessages })
+ if (jsonName === 'en') return { default: enMessages }
return languageFileMap[`./${jsonName}.json`]()
}
diff --git a/src/lib/persisted_state.js b/src/lib/persisted_state.js
index f6375dfed..5fcf259aa 100644
--- a/src/lib/persisted_state.js
+++ b/src/lib/persisted_state.js
@@ -74,7 +74,7 @@ export default function createPersistedState({
if (saveImmedeatelyActions.includes(mutation.type)) {
setState(key, reducer(cloneDeep(state), paths), storage).then(
(success) => {
- if (typeof success !== 'undefined') {
+ if (success !== undefined) {
if (
mutation.type === 'setOption' ||
mutation.type === 'setCurrentUser'
@@ -185,7 +185,7 @@ export const piniaPersistPlugin =
}
const fallbackValue = await storage.getItem(vuexKey)
- if (fallbackValue && fallbackValue[id]) {
+ if (fallbackValue?.[id]) {
console.info(`Migrating ${id} store data from vuex to pinia`)
const res = fallbackValue[id]
await storage.setItem(key, res)
@@ -198,7 +198,7 @@ export const piniaPersistPlugin =
const setState = (state) => {
if (!loadedGuard.loaded) {
console.info('waiting for old state to be loaded...')
- return Promise.reject()
+ throw new Error('Waiting')
} else {
return storage.setItem(key, state)
}
diff --git a/src/modules/api.js b/src/modules/api.js
index 3d223d6cc..6d0c41d48 100644
--- a/src/modules/api.js
+++ b/src/modules/api.js
@@ -325,7 +325,7 @@ const api = {
const token = state.wsToken
if (
useInstanceCapabilitiesStore().shoutAvailable &&
- typeof token !== 'undefined' &&
+ token !== undefined &&
state.socket === null
) {
const socket = new Socket('/socket', { params: { token } })
@@ -336,7 +336,7 @@ const api = {
}
},
disconnectFromSocket({ commit, state }) {
- state.socket && state.socket.disconnect()
+ state.socket?.disconnect()
commit('setSocket', null)
},
},
diff --git a/src/modules/statuses.js b/src/modules/statuses.js
index 03469a68c..a41213d65 100644
--- a/src/modules/statuses.js
+++ b/src/modules/statuses.js
@@ -49,8 +49,8 @@ const emptyTl = (userId = 0) => ({
visibleStatuses: [],
visibleStatusesObject: {},
newStatusCount: 0,
- maxId: 0,
- minId: 0,
+ maxId: '',
+ minId: '',
minVisibleId: 0,
loading: false,
followers: [],
@@ -64,7 +64,7 @@ export const defaultState = () => ({
scrobblesNextFetch: {},
allStatusesObject: {},
conversationsObject: {},
- maxId: 0,
+ maxId: '',
favorites: new Set(),
timelines: {
mentions: emptyTl(),
@@ -134,7 +134,7 @@ const sortById = (a, b) => {
const sortTimeline = (timeline) => {
timeline.visibleStatuses = timeline.visibleStatuses.sort(sortById)
timeline.statuses = timeline.statuses.sort(sortById)
- timeline.minVisibleId = (last(timeline.visibleStatuses) || {}).id
+ timeline.minVisibleId = last(timeline.visibleStatuses)?.id
return timeline
}
@@ -222,11 +222,11 @@ const addNewStatuses = (
const newer =
timeline &&
- (maxNew > timelineObject.maxId || timelineObject.maxId === 0) &&
+ (maxNew > timelineObject.maxId || timelineObject.maxId === '') &&
statuses.length > 0
const older =
timeline &&
- (minNew < timelineObject.minId || timelineObject.minId === 0) &&
+ (minNew < timelineObject.minId || timelineObject.minId === '') &&
statuses.length > 0
if (!noIdUpdate && newer) {
@@ -525,7 +525,7 @@ export const mutations = {
},
addRepeats(state, { id, rebloggedByUsers, currentUser }) {
const newStatus = state.allStatusesObject[id]
- newStatus.rebloggedBy = rebloggedByUsers.filter((_) => _)
+ newStatus.rebloggedBy = rebloggedByUsers.filter(Boolean)
// repeats stats can be incorrect based on polling condition, let's update them using the most recent data
newStatus.repeat_num = newStatus.rebloggedBy.length
newStatus.repeated = !!newStatus.rebloggedBy.find(
@@ -534,7 +534,7 @@ export const mutations = {
},
addFavs(state, { id, favoritedByUsers, currentUser }) {
const newStatus = state.allStatusesObject[id]
- newStatus.favoritedBy = favoritedByUsers.filter((_) => _)
+ newStatus.favoritedBy = favoritedByUsers.filter(Boolean)
// favorites stats can be incorrect based on polling condition, let's update them using the most recent data
newStatus.fave_num = newStatus.favoritedBy.length
newStatus.favorited = !!newStatus.favoritedBy.find(
@@ -879,7 +879,7 @@ const statuses = {
store.commit('addNewUsers', data.accounts)
store.commit(
'addNewUsers',
- data.statuses.map((s) => s.user).filter((u) => u),
+ data.statuses.map((s) => s.user).filter(Boolean),
)
store.dispatch('addNewStatuses', {
statuses: data.statuses,
diff --git a/src/modules/users.js b/src/modules/users.js
index a145a80e0..4aef4d5dd 100644
--- a/src/modules/users.js
+++ b/src/modules/users.js
@@ -75,13 +75,13 @@ const mergeArrayLength = (oldValue, newValue) => {
}
}
-const getNotificationPermission = () => {
+const getNotificationPermission = async () => {
const Notification = window.Notification
- if (!Notification) return Promise.resolve(null)
+ if (!Notification) return null
if (Notification.permission === 'default')
return Notification.requestPermission()
- return Promise.resolve(Notification.permission)
+ return Notification.permission
}
const blockUser = (store, args) => {
@@ -271,7 +271,7 @@ export const mutations = {
state.currentUser.blockIds = blockIds
},
addBlockId(state, blockId) {
- if (state.currentUser.blockIds.indexOf(blockId) === -1) {
+ if (state.currentUser.blockIds.includes(blockId)) {
state.currentUser.blockIds.push(blockId)
}
},
@@ -285,7 +285,7 @@ export const mutations = {
state.currentUser.muteIdsMaxId = muteIdsMaxId
},
addMuteId(state, muteId) {
- if (state.currentUser.muteIds.indexOf(muteId) === -1) {
+ if (state.currentUser.muteIds.includes(muteId)) {
state.currentUser.muteIds.push(muteId)
}
},
@@ -293,7 +293,7 @@ export const mutations = {
state.currentUser.domainMutes = domainMutes
},
addDomainMute(state, domain) {
- if (state.currentUser.domainMutes.indexOf(domain) === -1) {
+ if (state.currentUser.domainMutes.includes(domain)) {
state.currentUser.domainMutes.push(domain)
}
},
@@ -385,12 +385,12 @@ const users = {
mutations,
getters,
actions: {
- fetchUserIfMissing(store, id) {
+ async fetchUserIfMissing(store, id) {
const user = store.getters.findUser(id)
if (!user) {
return store.dispatch('fetchUser', id)
} else {
- return Promise.resolve(user)
+ return user
}
},
updateUserAdminData(store, { userAdminData }) {
@@ -640,7 +640,7 @@ const users = {
},
addNewNotifications(store, { notifications }) {
const users = map(notifications, 'from_profile')
- const targetUsers = map(notifications, 'target').filter((_) => _)
+ const targetUsers = map(notifications, 'target').filter(Boolean)
const notificationIds = notifications.map((_) => _.id)
store.commit('addNewUsers', users)
store.commit('addNewUsers', targetUsers)
diff --git a/src/panel.scss b/src/panel.scss
index 8d9c2896b..6af2c9e76 100644
--- a/src/panel.scss
+++ b/src/panel.scss
@@ -148,6 +148,14 @@
background-image:
linear-gradient(to bottom, var(--background), var(--background)),
linear-gradient(to bottom, var(--__panel-background), var(--__panel-background));
+ overflow: hidden;
+ text-overflow: ellipsis;
+
+ .title {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
--_shadow: var(--shadow);
diff --git a/src/services/chat_utils/chat_utils.js b/src/services/chat_utils/chat_utils.js
index ccf91e85c..bc02981cd 100644
--- a/src/services/chat_utils/chat_utils.js
+++ b/src/services/chat_utils/chat_utils.js
@@ -12,10 +12,7 @@ export const maybeShowChatNotification = (chat) => {
body: chat.lastMessage.content,
}
- if (
- chat.lastMessage.attachment &&
- chat.lastMessage.attachment.type === 'image'
- ) {
+ if (chat.lastMessage.attachment?.type === 'image') {
opts.image = chat.lastMessage.attachment.preview_url
}
diff --git a/src/services/color_convert/color_convert.js b/src/services/color_convert/color_convert.js
index dded73da3..925969531 100644
--- a/src/services/color_convert/color_convert.js
+++ b/src/services/color_convert/color_convert.js
@@ -11,7 +11,7 @@ import { contrastRatio, convert, invertLightness } from 'chromatism'
* @param {Number} [b] - Blue component
*/
export const rgb2hex = (r, g, b) => {
- if (r === null || typeof r === 'undefined') {
+ if (r === null || r === undefined) {
return undefined
}
// TODO: clean up this mess
@@ -130,7 +130,7 @@ export const arithmeticBlend = (origin, value, operator) => {
* @returns {Object} sRGB of resulting color
*/
export const alphaBlend = (fg, fga, bg) => {
- if (fga === 1 || typeof fga === 'undefined') {
+ if (fga === 1 || fga === undefined) {
return fg
}
@@ -210,16 +210,16 @@ export const rgba2css = function (rgba) {
}
if (rgba !== null) {
- if (rgba.r !== undefined && !isNaN(rgba.r)) {
+ if (rgba.r !== undefined && !Number.isNaN(rgba.r)) {
base.r = rgba.r
}
- if (rgba.g !== undefined && !isNaN(rgba.g)) {
+ if (rgba.g !== undefined && !Number.isNaN(rgba.g)) {
base.g = rgba.g
}
- if (rgba.b !== undefined && !isNaN(rgba.b)) {
+ if (rgba.b !== undefined && !Number.isNaN(rgba.b)) {
base.b = rgba.b
}
- if (rgba.a !== undefined && !isNaN(rgba.a)) {
+ if (rgba.a !== undefined && !Number.isNaN(rgba.a)) {
base.a = rgba.a
}
} else {
diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js
index d2df5e869..5b8fa5b57 100644
--- a/src/services/entity_normalizer/entity_normalizer.service.js
+++ b/src/services/entity_normalizer/entity_normalizer.service.js
@@ -62,8 +62,8 @@ export const parseUser = (data) => {
})
output.fields_text = data.fields.map((field) => {
return {
- name: unescape(field.name.replace(/<[^>]*>/g, '')),
- value: unescape(field.value.replace(/<[^>]*>/g, '')),
+ name: unescape(field.name.replaceAll(/<[^>]*>/g, '')),
+ value: unescape(field.value.replaceAll(/<[^>]*>/g, '')),
}
})
@@ -191,7 +191,7 @@ export const parseUser = (data) => {
// Convert punycode to unicode for UI
output.screen_name_ui = output.screen_name
- if (output.screen_name && output.screen_name.includes('@')) {
+ if (output.screen_name?.includes('@')) {
const parts = output.screen_name.split('@')
const unicodeDomain = punycode.toUnicode(parts[1])
if (unicodeDomain !== parts[1]) {
diff --git a/src/services/errors/errors.js b/src/services/errors/errors.js
index 5fbb8da11..0742de3f4 100644
--- a/src/services/errors/errors.js
+++ b/src/services/errors/errors.js
@@ -3,7 +3,7 @@ import { capitalize } from 'lodash'
function humanizeErrors(errors) {
return Object.entries(errors).reduce((errs, [k, val]) => {
const message = val.reduce((acc, message) => {
- const key = capitalize(k.replace(/_/g, ' '))
+ const key = capitalize(k.replaceAll('_', ' '))
return acc + [key, message].join(' ') + '. '
}, '')
return [...errs, message]
diff --git a/src/services/file_type/file_type.service.js b/src/services/file_type/file_type.service.js
index 02f659eec..b6179cd81 100644
--- a/src/services/file_type/file_type.service.js
+++ b/src/services/file_type/file_type.service.js
@@ -43,7 +43,7 @@ export const fileTypeExt = (url) => {
}
export const fileMatchesSomeType = (types, file) =>
- types.some((type) => fileType(file.mimetype) === type)
+ types.includes(fileType(file.mimetype))
const fileTypeService = {
fileType,
diff --git a/src/services/notification_utils/notification_utils.js b/src/services/notification_utils/notification_utils.js
index 6cb3dbc19..9e443bf8b 100644
--- a/src/services/notification_utils/notification_utils.js
+++ b/src/services/notification_utils/notification_utils.js
@@ -25,7 +25,7 @@ const visibleTypes = (notificationVisibility) => {
notificationVisibility.emojiReactions && 'pleroma:emoji_reaction',
notificationVisibility.reports && 'pleroma:report',
notificationVisibility.polls && 'poll',
- ].filter((_) => _)
+ ].filter(Boolean)
}
const statusNotifications = new Set([
@@ -95,9 +95,7 @@ export const filteredNotificationsFromStore = (
types,
) => {
// map is just to clone the array since sort mutates it and it causes some issues
- const sortedNotifications = notificationsFromStore(store)
- .map((_) => _)
- .sort(sortById)
+ const sortedNotifications = notificationsFromStore(store).sort(sortById)
// TODO implement sorting elsewhere and make it optional
return sortedNotifications.filter((notification) =>
(types || visibleTypes(notificationVisibility)).includes(notification.type),
@@ -175,13 +173,7 @@ export const prepareNotificationObject = (notification, i18n) => {
}
// Shows first attached non-nsfw image, if any. Should add configuration for this somehow...
- if (
- status &&
- status.attachments &&
- status.attachments.length > 0 &&
- !status.nsfw &&
- status.attachments[0].mimetype.startsWith('image/')
- ) {
+ if (!status.nsfw && status?.attachments?.[0]?.mimetype.startsWith('image/')) {
notifObj.image = status.attachments[0].url
}
diff --git a/src/services/ruffle_service/ruffle_service.js b/src/services/ruffle_service/ruffle_service.js
index c029330aa..298f32df3 100644
--- a/src/services/ruffle_service/ruffle_service.js
+++ b/src/services/ruffle_service/ruffle_service.js
@@ -25,13 +25,13 @@ const createRuffleService = () => {
script.src = '/static/ruffle/ruffle.js'
script.type = 'text/javascript'
script.onerror = (e) => {
- reject(e)
+ reject(new Error('Ruffle script errorred', e))
}
script.onabort = (e) => {
- reject(e)
+ reject(new Error('Ruffle script aborted', e))
}
script.oncancel = (e) => {
- reject(e)
+ reject(new Error('Ruffle script cancelled', e))
}
script.onload = () => {
ruffleInstance = window.RufflePlayer
diff --git a/src/services/status_parser/status_parser.js b/src/services/status_parser/status_parser.js
index a011fe265..fc498f540 100644
--- a/src/services/status_parser/status_parser.js
+++ b/src/services/status_parser/status_parser.js
@@ -88,5 +88,5 @@ export const muteFilterHits = (muteFilters, status) => {
}
}
})
- .filter((_) => _)
+ .filter(Boolean)
}
diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js
index 43eaeb3e5..6c289590e 100644
--- a/src/services/style_setter/style_setter.js
+++ b/src/services/style_setter/style_setter.js
@@ -29,7 +29,7 @@ export const createStyleSheet = (id, priority = 1000) => {
addRule(rule) {
let newRule = rule
if (!CSS.supports?.('backdrop-filter', 'blur()')) {
- newRule = newRule.replace(/backdrop-filter:[^;]+;/g, '') // Remove backdrop-filter
+ newRule = newRule.replaceAll(/backdrop-filter:[^;]+;/g, '') // Remove backdrop-filter
}
if (newRule.startsWith('::-webkit')) {
@@ -44,7 +44,7 @@ export const createStyleSheet = (id, priority = 1000) => {
}
this.rules.push(
- newRule.replace(/var\(--shadowFilter\)[^;]*;/g, ''), // Remove shadowFilter references
+ newRule.replaceAll(/var\(--shadowFilter\)[^;]*;/g, ''), // Remove shadowFilter references
)
},
}
@@ -92,6 +92,11 @@ export const adoptStyleSheets = throttle(() => {
const EAGER_STYLE_ID = 'pleroma-eager-styles'
const LAZY_STYLE_ID = 'pleroma-lazy-styles'
+export const hasInvalidCachedThemeRules = (data) =>
+ data
+ .flat()
+ .some((rule) => /--(?:mono)?font:\s*\[object Object\](?:;|$)/i.test(rule))
+
const generateTheme = (inputRuleset, callbacks, debug) => {
const {
onNewRule = () => {
@@ -151,7 +156,8 @@ export const tryLoadCache = async () => {
if (
cache.engineChecksum === getEngineChecksum() &&
cache.checksum !== undefined &&
- cache.checksum === useMergedConfigStore().mergedConfig.themeChecksum
+ cache.checksum === useMergedConfigStore().mergedConfig.themeChecksum &&
+ !hasInvalidCachedThemeRules(cache.data)
) {
const eagerStyles = createStyleSheet(EAGER_STYLE_ID, 10)
const lazyStyles = createStyleSheet(LAZY_STYLE_ID, 20)
@@ -318,7 +324,7 @@ export const getResourcesIndex = async (url, parser = noop) => {
const resourceTransform = (resources) => {
return Object.entries(resources).map(([k, v]) => {
if (typeof v === 'object') {
- return [k, () => Promise.resolve(v)]
+ return [k, () => v]
} else if (typeof v === 'string') {
return [
k,
@@ -362,11 +368,9 @@ export const getResourcesIndex = async (url, parser = noop) => {
const total = [...custom, ...builtin]
if (total.length === 0) {
- return Promise.reject(
- new Error(
- `Resource at ${url} and ${customUrl} completely unavailable. Panicking`,
- ),
+ throw new Error(
+ `Resource at ${url} and ${customUrl} completely unavailable. Panicking`,
)
}
- return Promise.resolve(Object.fromEntries(total))
+ return Object.fromEntries(total)
}
diff --git a/src/services/sw/sw.js b/src/services/sw/sw.js
index b45409b28..ef8200a4d 100644
--- a/src/services/sw/sw.js
+++ b/src/services/sw/sw.js
@@ -1,7 +1,9 @@
/* global process */
function urlBase64ToUint8Array(base64String) {
const padding = '='.repeat((4 - (base64String.length % 4)) % 4)
- const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/')
+ const base64 = (base64String + padding)
+ .replaceAll('-', '+')
+ .replaceAll('_', '/')
const rawData = window.atob(base64)
return Uint8Array.from([...rawData].map((char) => char.codePointAt(0)))
@@ -26,10 +28,8 @@ function getOrCreateServiceWorker() {
}
function subscribePush(registration, isEnabled, vapidPublicKey) {
- if (!isEnabled)
- return Promise.reject(new Error('Web Push is disabled in config'))
- if (!vapidPublicKey)
- return Promise.reject(new Error('VAPID public key is not found'))
+ if (!isEnabled) throw new Error('Web Push is disabled in config')
+ if (!vapidPublicKey) throw new Error('VAPID public key is not found')
const subscribeOptions = {
userVisibleOnly: false,
@@ -38,10 +38,10 @@ function subscribePush(registration, isEnabled, vapidPublicKey) {
return registration.pushManager.subscribe(subscribeOptions)
}
-function unsubscribePush(registration) {
+async function unsubscribePush(registration) {
return registration.pushManager.getSubscription().then((subscription) => {
if (subscription === null) {
- return Promise.resolve('No subscription')
+ return 'No subscription'
}
return subscription.unsubscribe()
})
diff --git a/src/services/theme_data/css_utils.js b/src/services/theme_data/css_utils.js
index 990db652c..b728f68d2 100644
--- a/src/services/theme_data/css_utils.js
+++ b/src/services/theme_data/css_utils.js
@@ -165,7 +165,7 @@ export const getCssRules = (rules, debug) =>
header,
directives,
rule.component === 'Text' &&
- rule.state.indexOf('faint') < 0 &&
+ !rule.state.includes('faint') &&
rule.directives.textNoCssColor !== 'yes'
? ' color: var(--text);'
: '',
diff --git a/src/services/theme_data/iss_utils.js b/src/services/theme_data/iss_utils.js
index e5aa5cd15..d42da3780 100644
--- a/src/services/theme_data/iss_utils.js
+++ b/src/services/theme_data/iss_utils.js
@@ -24,7 +24,7 @@ export const getAllPossibleCombinations = (array) => {
const nonSelf = array.filter((x) => !selfSet.has(x))
return nonSelf.map((x) => [...self, x])
})
- const flatCombos = newCombos.reduce((acc, x) => [...acc, ...x], [])
+ const flatCombos = newCombos.flat()
const uniqueComboStrings = new Set()
const uniqueCombos = flatCombos.map(sortBy).filter((x) => {
if (uniqueComboStrings.has(x.join())) {
@@ -36,7 +36,7 @@ export const getAllPossibleCombinations = (array) => {
})
combos.push(uniqueCombos)
}
- return combos.reduce((acc, x) => [...acc, ...x], [])
+ return combos.flat()
}
/**
@@ -110,13 +110,6 @@ export const genericRuleToSelector =
let arraySelector = Array.isArray(selector) ? selector : [selector]
if (ignoreOutOfTreeSelector || liteMode)
arraySelector = [arraySelector[0]]
- arraySelector
- .sort((a) => {
- if (a.startsWith(':')) return 1
- if (/^[a-z]/.exec(a)) return -1
- else return 0
- })
- .join('')
return arraySelector
})
diff --git a/src/services/theme_data/theme2_to_theme3.js b/src/services/theme_data/theme2_to_theme3.js
index 51dc3e2c4..48afe4ad9 100644
--- a/src/services/theme_data/theme2_to_theme3.js
+++ b/src/services/theme_data/theme2_to_theme3.js
@@ -191,14 +191,16 @@ export const convertTheme2To3 = (data) => {
newRules.push(rule)
if (rule.component === 'Button') {
- newRules.push({ ...rule, component: 'ScrollbarElement' })
- newRules.push({ ...rule, component: 'Tab' })
- newRules.push({
- ...rule,
- component: 'Tab',
- state: ['active'],
- directives: { opacity: 0 },
- })
+ newRules.push(
+ { ...rule, component: 'ScrollbarElement' },
+ { ...rule, component: 'Tab' },
+ {
+ ...rule,
+ component: 'Tab',
+ state: ['active'],
+ directives: { opacity: 0 },
+ },
+ )
}
if (rule.component === 'Panel') {
newRules.push({ ...rule, component: 'Post' })
@@ -250,8 +252,10 @@ export const convertTheme2To3 = (data) => {
}
newRules.push(rule)
if (rule.component === 'Button') {
- newRules.push({ ...rule, component: 'ScrollbarElement' })
- newRules.push({ ...rule, component: 'Tab' })
+ newRules.push(
+ { ...rule, component: 'ScrollbarElement' },
+ { ...rule, component: 'Tab' },
+ )
}
})
return newRules
@@ -349,16 +353,20 @@ export const convertTheme2To3 = (data) => {
newRules.push({ ...rule, parent: { component: 'Notification' } })
}
if (key === 'buttonPressed') {
- newRules.push({ ...rule, state: ['toggled'] })
- newRules.push({ ...rule, state: ['toggled', 'focus'] })
- newRules.push({ ...rule, state: ['pressed', 'focus'] })
- newRules.push({ ...rule, state: ['toggled', 'focus', 'hover'] })
- newRules.push({ ...rule, state: ['pressed', 'focus', 'hover'] })
+ newRules.push(
+ { ...rule, state: ['toggled'] },
+ { ...rule, state: ['toggled', 'focus'] },
+ { ...rule, state: ['pressed', 'focus'] },
+ { ...rule, state: ['toggled', 'focus', 'hover'] },
+ { ...rule, state: ['pressed', 'focus', 'hover'] },
+ )
}
if (rule.component === 'Button') {
- newRules.push({ ...rule, component: 'ScrollbarElement' })
- newRules.push({ ...rule, component: 'Tab' })
+ newRules.push(
+ { ...rule, component: 'ScrollbarElement' },
+ { ...rule, component: 'Tab' },
+ )
}
})
return newRules
@@ -512,15 +520,17 @@ export const convertTheme2To3 = (data) => {
{ ...newRule, component: 'Tab' },
{ ...newRule, component: 'ScrollbarElement' },
]
- if (newRule.state?.indexOf('toggled') >= 0) {
- rules.push({ ...newRule, state: [...newRule.state, 'focused'] })
- rules.push({ ...newRule, state: [...newRule.state, 'hover'] })
- rules.push({
- ...newRule,
- state: [...newRule.state, 'hover', 'focused'],
- })
+ if (newRule.state?.includes('toggled')) {
+ rules.push(
+ { ...newRule, state: [...newRule.state, 'focused'] },
+ { ...newRule, state: [...newRule.state, 'hover'] },
+ {
+ ...newRule,
+ state: [...newRule.state, 'hover', 'focused'],
+ },
+ )
}
- if (newRule.state?.indexOf('hover') >= 0) {
+ if (newRule.state?.includes('hover')) {
rules.push({ ...newRule, state: [...newRule.state, 'focused'] })
}
return rules
@@ -559,9 +569,9 @@ export const convertTheme2To3 = (data) => {
const flatExtRules = extendedRules
.filter(Boolean)
- .reduce((acc, x) => [...acc, ...x], [])
+ .flat()
.filter(Boolean)
- .reduce((acc, x) => [...acc, ...x], [])
+ .flat()
return [
generateRoot(),
diff --git a/src/services/theme_data/theme_data.service.js b/src/services/theme_data/theme_data.service.js
index cdce2cf57..cc5553936 100644
--- a/src/services/theme_data/theme_data.service.js
+++ b/src/services/theme_data/theme_data.service.js
@@ -244,10 +244,7 @@ export const OPACITIES = Object.entries(SLOT_INHERITANCE).reduce((acc, [k]) => {
...acc,
[opacity]: {
defaultValue: DEFAULT_OPACITY[opacity] || 1,
- affectedSlots: [
- ...((acc[opacity] && acc[opacity].affectedSlots) || []),
- k,
- ],
+ affectedSlots: [...(acc[opacity]?.affectedSlots || []), k],
},
}
} else {
@@ -413,7 +410,7 @@ export const getColors = (sourceColors, sourceOpacity) =>
outputColor.a = Number(
opacityOverriden
? sourceOpacity[opacitySlot]
- : (OPACITIES[opacitySlot] || {}).defaultValue,
+ : OPACITIES[opacitySlot]?.defaultValue,
)
}
}
@@ -460,7 +457,7 @@ export const generatePreset = (input) => {
return composePreset(
colors,
generateRadii(input),
- generateShadows(input, colors.theme.colors, colors.mod),
+ generateShadows(input, colors.theme.colors),
generateFonts(input),
)
}
diff --git a/src/services/theme_data/theme_data_3.service.js b/src/services/theme_data/theme_data_3.service.js
index 626d7daeb..694e41e12 100644
--- a/src/services/theme_data/theme_data_3.service.js
+++ b/src/services/theme_data/theme_data_3.service.js
@@ -262,7 +262,7 @@ export const init = ({
...r,
})),
)
- .reduce((acc, arr) => [...acc, ...arr], []),
+ .flat(),
...inputRuleset,
].map((rule) => {
normalizeCombination(rule)
@@ -690,11 +690,11 @@ export const init = ({
.map((combination) => ['normal', ...combination])
.filter((combo) => {
// Optimization: filter out some hard-coded combinations that don't make sense
- if (combo.indexOf('disabled') >= 0) {
+ if (combo.includes('disabled')) {
return !(
- combo.indexOf('hover') >= 0 ||
- combo.indexOf('focused') >= 0 ||
- combo.indexOf('pressed') >= 0
+ combo.includes('hover') ||
+ combo.includes('focused') ||
+ combo.includes('pressed')
)
}
return true
@@ -705,13 +705,13 @@ export const init = ({
.map((variant) => {
return stateCombinations.map((state) => ({ variant, state }))
})
- .reduce((acc, x) => [...acc, ...x], [])
+ .flat()
stateVariantCombination.forEach((combination) => {
combination.component = component.name
combination.lazy = component.lazy || parent?.lazy
combination.parent = parent
- if (!liteMode && combination.state.indexOf('hover') >= 0) {
+ if (!liteMode && combination.state.includes('hover')) {
combination.lazy = true
}
diff --git a/src/services/user_highlighter/user_highlighter.js b/src/services/user_highlighter/user_highlighter.js
index 697496c91..abd0d9633 100644
--- a/src/services/user_highlighter/user_highlighter.js
+++ b/src/services/user_highlighter/user_highlighter.js
@@ -47,7 +47,7 @@ const highlightStyle = (prefs) => {
const highlightClass = (user) => {
return (
- 'USER____' + user.screen_name?.replace(/\./g, '_').replace(/@/g, '_AT_')
+ 'USER____' + user.screen_name?.replaceAll('.', '_').replaceAll('@', '_AT_')
)
}
diff --git a/src/stores/chats.js b/src/stores/chats.js
index bc5b7f101..5ade0c126 100644
--- a/src/stores/chats.js
+++ b/src/stores/chats.js
@@ -61,7 +61,7 @@ export const useChatsStore = defineStore('chats', {
addNewChats(chats) {
window.vuex.commit(
'addNewUsers',
- chats.map((k) => k.account).filter((k) => k),
+ chats.map((k) => k.account).filter(Boolean),
)
chats.forEach((updatedChat) => {
@@ -69,8 +69,7 @@ export const useChatsStore = defineStore('chats', {
if (chat) {
const isNewMessage =
- (chat.lastMessage && chat.lastMessage.id) !==
- (updatedChat.lastMessage && updatedChat.lastMessage.id)
+ chat.lastMessage?.id !== updatedChat.lastMessage?.id
chat.lastMessage = updatedChat.lastMessage
chat.unread = updatedChat.unread
chat.updated_at = updatedChat.updated_at
diff --git a/src/stores/emoji.js b/src/stores/emoji.js
index 3316f8328..8135a483e 100644
--- a/src/stores/emoji.js
+++ b/src/stores/emoji.js
@@ -120,23 +120,19 @@ export const useEmojiStore = defineStore('emoji', {
}, {})
},
standardEmojiList(state) {
- return (
- SORTED_EMOJI_GROUP_IDS.map((groupId) =>
- (this.emoji[groupId] || []).map((k) =>
- injectAnnotations(k, this.unicodeEmojiAnnotations),
- ),
- ).reduce((a, b) => a.concat(b), []) ?? []
- )
+ return SORTED_EMOJI_GROUP_IDS.map((groupId) =>
+ (this.emoji[groupId] || []).map((k) =>
+ injectAnnotations(k, this.unicodeEmojiAnnotations),
+ ),
+ ).flat()
},
standardEmojiGroupList(state) {
- return (
- SORTED_EMOJI_GROUP_IDS.map((groupId) => ({
- id: groupId,
- emojis: (this.emoji[groupId] || []).map((k) =>
- injectAnnotations(k, this.unicodeEmojiAnnotations),
- ),
- })) ?? []
- )
+ return SORTED_EMOJI_GROUP_IDS.map((groupId) => ({
+ id: groupId,
+ emojis: (this.emoji[groupId] || []).map((k) =>
+ injectAnnotations(k, this.unicodeEmojiAnnotations),
+ ),
+ }))
},
},
actions: {
@@ -146,7 +142,7 @@ export const useEmojiStore = defineStore('emoji', {
async getStaticEmoji() {
try {
// See build/emojis_plugin for more details
- const values = (await import('/src/assets/emoji.json')).default
+ const values = (await import('src/assets/emoji.json')).default
const emoji = Object.keys(values).reduce((res, groupId) => {
res[groupId] = values[groupId].map((e) => ({
@@ -231,7 +227,7 @@ export const useEmojiStore = defineStore('emoji', {
.then((allPacks) => {
// Sort by key
return Object.keys(allPacks)
- .sort()
+ .sort((a, b) => a.localeCompare(b))
.reduce((acc, key) => {
if (key.length === 0) return acc
acc[key] = allPacks[key]
diff --git a/src/stores/interface.js b/src/stores/interface.js
index 87ef65865..7be72a8d2 100644
--- a/src/stores/interface.js
+++ b/src/stores/interface.js
@@ -60,8 +60,7 @@ export const useInterfaceStore = defineStore('interface', {
},
browserSupport: {
cssFilter:
- window.CSS &&
- window.CSS.supports &&
+ window.CSS?.supports &&
(window.CSS.supports('filter', 'drop-shadow(0 0)') ||
window.CSS.supports('-webkit-filter', 'drop-shadow(0 0)')),
localFonts: typeof window.queryLocalFonts === 'function',
@@ -295,7 +294,7 @@ export const useInterfaceStore = defineStore('interface', {
path: 'palettesIndex',
value: { _error: e },
})
- return Promise.resolve({})
+ return {}
}
},
setPalette(value) {
@@ -333,7 +332,7 @@ export const useInterfaceStore = defineStore('interface', {
path: 'simple.stylesIndex',
value: { _error: e },
})
- return Promise.resolve({})
+ return {}
}
},
setStyle(value) {
@@ -376,7 +375,7 @@ export const useInterfaceStore = defineStore('interface', {
path: 'themesIndex',
value: { _error: e },
})
- return Promise.resolve({})
+ return {}
}
},
setTheme(value) {
@@ -578,7 +577,8 @@ export const useInterfaceStore = defineStore('interface', {
return { name: x.variant, ...cleanDirectives }
})
.forEach((palette) => {
- const key = 'style.' + palette.name.toLowerCase().replace(/ /g, '_')
+ const key =
+ 'style.' + palette.name.toLowerCase().replaceAll(' ', '_')
if (!firstStylePaletteName) firstStylePaletteName = key
palettesIndex[key] = () => Promise.resolve(palette)
})
diff --git a/src/stores/sync_config.js b/src/stores/sync_config.js
index 87083d850..9464ffb68 100644
--- a/src/stores/sync_config.js
+++ b/src/stores/sync_config.js
@@ -104,7 +104,7 @@ const _verifyPrefs = (state) => {
// Simple
Object.entries(defaultState.prefsStorage.simple).forEach(([k, v]) => {
- if (typeof v === 'undefined') return
+ if (v === undefined) return
if (typeof v === 'number' || typeof v === 'boolean') return
if (typeof v === 'object') return
console.warn(
@@ -195,10 +195,13 @@ export const _getRecentData = (cache, live, isTest) => {
}
export const _getAllFlags = (recent, stale) => {
+ const recentStorage = toRaw(recent?.flagStorage)
+ const staleStorage = toRaw(stale?.flagStorage)
+
return Array.from(
new Set([
- ...Object.keys(toRaw((recent || {}).flagStorage || {})),
- ...Object.keys(toRaw((stale || {}).flagStorage || {})),
+ ...Object.keys(recentStorage || {}),
+ ...Object.keys(staleStorage || {}),
]),
)
}
@@ -833,7 +836,7 @@ export const useSyncConfigStore = defineStore('sync_config', {
: [path, finalValue]
})
newState.prefsStorage.simple = Object.fromEntries(
- newEntries.filter((_) => _),
+ newEntries.filter(Boolean),
)
return newState
},
diff --git a/src/stores/user_highlight.js b/src/stores/user_highlight.js
index 0db37703d..3cd3bfe7d 100644
--- a/src/stores/user_highlight.js
+++ b/src/stores/user_highlight.js
@@ -43,7 +43,7 @@ const _verifyHighlights = (state) => {
// Simple
Object.entries(defaultState.highlight).forEach(([k, v]) => {
- if (typeof v === 'undefined') return
+ if (v === undefined) return
if (typeof v === 'object') return
console.warn(`User highlight ${k} is invalid type ${typeof v}, unsetting`)
delete state.highlight[k]
diff --git a/src/sw.js b/src/sw.js
index b7629b5f2..908e86567 100644
--- a/src/sw.js
+++ b/src/sw.js
@@ -198,6 +198,10 @@ self.addEventListener('push', async (event) => {
self.addEventListener('message', async (event) => {
await setSettings()
const { type, content } = event.data
+ if (self.location.origin !== event.origin) {
+ console.error('SW Message with strange origin received', event)
+ return
+ }
if (type === 'desktopNotification') {
const { title, ...rest } = content
diff --git a/test/e2e/runner.js b/test/e2e/runner.js
index 33fcc322c..06cae7813 100644
--- a/test/e2e/runner.js
+++ b/test/e2e/runner.js
@@ -10,10 +10,10 @@ const server = require('../../build/dev-server.js')
// For more information on Nightwatch's config file, see
// http://nightwatchjs.org/guide#settings-file
let opts = process.argv.slice(2)
-if (opts.indexOf('--config') === -1) {
+if (!opts.includes('--config')) {
opts = opts.concat(['--config', 'test/e2e/nightwatch.conf.js'])
}
-if (opts.indexOf('--env') === -1) {
+if (!opts.includes('--env')) {
opts = opts.concat(['--env', 'chrome'])
}
diff --git a/test/unit/specs/components/chat_view.spec.js b/test/unit/specs/components/chat_view.spec.js
index c308e4fbc..d03809e38 100644
--- a/test/unit/specs/components/chat_view.spec.js
+++ b/test/unit/specs/components/chat_view.spec.js
@@ -66,10 +66,10 @@ describe('ChatView methods', () => {
it("Doesn't add duplicates", () => {
component.vm.addMessages({ messages: [message1] })
component.vm.addMessages({ messages: [message1] })
- expect(component.vm.messages.length).to.eql(1)
+ expect(component.vm.messages).to.have.length(1)
component.vm.addMessages({ messages: [message2] })
- expect(component.vm.messages.length).to.eql(2)
+ expect(component.vm.messages).to.have.length(2)
})
it('Updates minId and lastMessage and newMessageCount', async () => {
@@ -127,11 +127,11 @@ describe('ChatView methods', () => {
})
}
component.vm.cullOlder()
- expect(component.vm.messages.length).to.eql(50)
+ expect(component.vm.messages).to.have.length(50)
expect(component.vm.messages[0].id).to.eql('a0.051')
expect(component.vm.minId).to.eql('a0.051')
expect(component.vm.messages[49].id).to.eql('a0.100')
- expect(Object.keys(component.vm.messagesIndex).length).to.eql(50)
+ expect(Object.keys(component.vm.messagesIndex)).to.have.length(50)
})
})
})
diff --git a/test/unit/specs/components/post_status_form.spec.js b/test/unit/specs/components/post_status_form.spec.js
index 03c0ec8b9..f89102504 100644
--- a/test/unit/specs/components/post_status_form.spec.js
+++ b/test/unit/specs/components/post_status_form.spec.js
@@ -80,8 +80,8 @@ describe('PostStatusForm', () => {
expect(wrapper.vm.refId).to.equal('status-1')
expect(wrapper.vm.quotable).to.equal(true)
expect(wrapper.vm.inReplyToStatusId).to.equal('status-1')
- expect(wrapper.vm.newStatus.quote).to.eql(null)
- expect(wrapper.vm.newStatus.poll).to.eql(null)
+ expect(wrapper.vm.newStatus.quote).to.be.null
+ expect(wrapper.vm.newStatus.poll).to.be.null
expect(wrapper.vm.newStatus.spoilerText).to.eql('')
expect(wrapper.vm.newStatus.mentions).to.eql('@replied')
expect(wrapper.vm.newStatus.status).to.eql('@replied ')
@@ -101,8 +101,8 @@ describe('PostStatusForm', () => {
expect(wrapper.vm.statusType).to.equal('reply')
expect(wrapper.vm.isReply).to.equal(true)
expect(wrapper.vm.quotable).to.equal(false)
- expect(wrapper.vm.newStatus.quote).to.eql(null)
- expect(wrapper.vm.newStatus.poll).to.eql(null)
+ expect(wrapper.vm.newStatus.quote).to.be.null
+ expect(wrapper.vm.newStatus.poll).to.be.null
expect(wrapper.vm.newStatus.spoilerText).to.eql('re: subject')
expect(wrapper.vm.newStatus.mentions).to.eql('@replied')
expect(wrapper.vm.newStatus.status).to.eql('@replied ')
@@ -114,9 +114,9 @@ describe('PostStatusForm', () => {
expect(wrapper.vm.postingOptions.sensitive).to.eql(false)
expect(wrapper.vm.postingOptions.media).to.eql([])
expect(wrapper.vm.postingOptions.inReplyToStatusId).to.eql('status-2')
- expect(wrapper.vm.postingOptions.quoteId).to.eql(null)
+ expect(wrapper.vm.postingOptions.quoteId).to.be.null
expect(wrapper.vm.postingOptions.contentType).to.eql('text/plain')
- expect(wrapper.vm.postingOptions.poll).to.eql(null)
+ expect(wrapper.vm.postingOptions.poll).to.be.null
})
it('Forces direct mode when replying to a DM, mastodon style subject handling', () => {
@@ -139,8 +139,8 @@ describe('PostStatusForm', () => {
expect(wrapper.vm.statusType).to.equal('reply')
expect(wrapper.vm.isReply).to.equal(true)
expect(wrapper.vm.quotable).to.equal(false)
- expect(wrapper.vm.newStatus.quote).to.eql(null)
- expect(wrapper.vm.newStatus.poll).to.eql(null)
+ expect(wrapper.vm.newStatus.quote).to.be.null
+ expect(wrapper.vm.newStatus.poll).to.be.null
expect(wrapper.vm.newStatus.spoilerText).to.eql('subject')
expect(wrapper.vm.newStatus.mentions).to.eql('@replied')
expect(wrapper.vm.newStatus.status).to.eql('@replied ')
@@ -207,7 +207,7 @@ describe('PostStatusForm', () => {
wrapper.vm.quoteThreadToggled = true
wrapper.vm.quoteThreadToggled = false
- expect(wrapper.vm.newStatus.quote).to.eql(null)
+ expect(wrapper.vm.newStatus.quote).to.be.null
})
it('Initializes and reset quote when toggling quote attachment', () => {
@@ -228,7 +228,7 @@ describe('PostStatusForm', () => {
url: '',
})
wrapper.vm.toggleQuoteForm()
- expect(wrapper.vm.newStatus.quote).to.eql(null)
+ expect(wrapper.vm.newStatus.quote).to.be.null
})
it('Status editing', () => {
diff --git a/test/unit/specs/components/rich_content.spec.js b/test/unit/specs/components/rich_content.spec.js
index fdf6c7f8f..cc79fe377 100644
--- a/test/unit/specs/components/rich_content.spec.js
+++ b/test/unit/specs/components/rich_content.spec.js
@@ -50,7 +50,7 @@ describe('RichContent', () => {
},
})
- expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(html))
+ expect(wrapper.html().replaceAll('\n', '')).to.eql(compwrap(html))
})
it('unescapes everything as needed', () => {
@@ -67,7 +67,7 @@ describe('RichContent', () => {
},
})
- expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(expected))
+ expect(wrapper.html().replaceAll('\n', '')).to.eql(compwrap(expected))
})
it('replaces mention with mentionsline', () => {
@@ -83,7 +83,7 @@ describe('RichContent', () => {
},
})
- expect(wrapper.html().replace(/\n/g, '')).to.eql(
+ expect(wrapper.html().replaceAll('\n', '')).to.eql(
compwrap(p(mentionsLine(1), ' how are you doing today?')),
)
})
@@ -116,7 +116,7 @@ describe('RichContent', () => {
},
})
- expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(expected))
+ expect(wrapper.html().replaceAll('\n', '')).to.eql(compwrap(expected))
})
it('Does not touch links if link handling is disabled', () => {
@@ -211,7 +211,7 @@ describe('RichContent', () => {
},
})
- expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(expected))
+ expect(wrapper.html().replaceAll('\n', '')).to.eql(compwrap(expected))
})
it("Doesn't add nonexistent emoji to post", () => {
@@ -228,7 +228,7 @@ describe('RichContent', () => {
},
})
- expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(html))
+ expect(wrapper.html().replaceAll('\n', '')).to.eql(compwrap(html))
})
it('Greentext + last mentions', () => {
@@ -279,7 +279,7 @@ describe('RichContent', () => {
},
})
- expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(expected))
+ expect(wrapper.html().replaceAll('\n', '')).to.eql(compwrap(expected))
})
it('buggy example/hashtags', () => {
@@ -315,7 +315,7 @@ describe('RichContent', () => {
},
})
- expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(expected))
+ expect(wrapper.html().replaceAll('\n', '')).to.eql(compwrap(expected))
})
it('rich contents of a mention are handled properly', () => {
@@ -365,8 +365,8 @@ describe('RichContent', () => {
expect(
wrapper
.html()
- .replace(/\n/g, '')
- .replace(//g, ''),
+ .replaceAll('\n', '')
+ .replaceAll(//g, ''),
).to.eql(compwrap(expected))
})
@@ -438,8 +438,8 @@ describe('RichContent', () => {
expect(
wrapper
.html()
- .replace(/\n/g, '')
- .replace(//g, ''),
+ .replaceAll('\n', '')
+ .replaceAll(//g, ''),
).to.eql(compwrap(expected))
})
@@ -484,7 +484,7 @@ describe('RichContent', () => {
},
})
- expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(expected))
+ expect(wrapper.html().replaceAll('\n', '')).to.eql(compwrap(expected))
})
it.skip('[INFORMATIVE] Performance testing, 10 000 simple posts', () => {
diff --git a/test/unit/specs/lib/persisted_state.spec.js b/test/unit/specs/lib/persisted_state.spec.js
index de7d92bd8..c37d0f99f 100644
--- a/test/unit/specs/lib/persisted_state.spec.js
+++ b/test/unit/specs/lib/persisted_state.spec.js
@@ -187,7 +187,7 @@ describe('piniaPersistPlugin', () => {
const test = useTestStore()
test.$patch({ a: 3 })
- expect(await mockStorage.getItem('pinia-local-test')).to.eql(undefined)
+ expect(await mockStorage.getItem('pinia-local-test')).to.be.undefined
// NOTE: it should not even have tried to save, because the subscribe function
// is called only after loading the initial state.
expect(mockStorage.setItem).not.toHaveBeenCalled()
diff --git a/test/unit/specs/modules/statuses.spec.js b/test/unit/specs/modules/statuses.spec.js
index 1315724da..04bd07ffe 100644
--- a/test/unit/specs/modules/statuses.spec.js
+++ b/test/unit/specs/modules/statuses.spec.js
@@ -107,7 +107,7 @@ describe('Statuses module', () => {
showImmediately: true,
timeline: 'public',
})
- expect(state.timelines.public.maxId).to.eql('1')
+ expect(state.timelines.public.maxId).to.equal('1')
mutations.addNewStatuses(state, {
statuses: [secondStatus],
@@ -120,7 +120,7 @@ describe('Statuses module', () => {
secondStatus,
status,
])
- expect(state.timelines.public.maxId).to.eql('1')
+ expect(state.timelines.public.maxId).to.equal('1')
})
it('keeps a descending by id order in timeline.visibleStatuses and timeline.statuses', () => {
@@ -278,7 +278,7 @@ describe('Statuses module', () => {
timeline: 'public',
})
- expect(state.timelines.public.visibleStatuses.length).to.eql(1)
+ expect(state.timelines.public.visibleStatuses).to.have.length(1)
expect(state.timelines.public.visibleStatuses[0].fave_num).to.eql(1)
expect(state.timelines.public.maxId).to.eq(favorite.id)
@@ -289,7 +289,7 @@ describe('Statuses module', () => {
timeline: 'public',
})
- expect(state.timelines.public.visibleStatuses.length).to.eql(1)
+ expect(state.timelines.public.visibleStatuses).to.have.length(1)
expect(state.timelines.public.visibleStatuses[0].fave_num).to.eql(1)
expect(state.timelines.public.maxId).to.eq(favorite.id)
@@ -314,7 +314,7 @@ describe('Statuses module', () => {
user,
})
- expect(state.timelines.public.visibleStatuses.length).to.eql(1)
+ expect(state.timelines.public.visibleStatuses).to.have.length(1)
expect(state.timelines.public.visibleStatuses[0].fave_num).to.eql(1)
expect(state.timelines.public.visibleStatuses[0].favorited).to.eql(true)
})
@@ -340,7 +340,7 @@ describe('Statuses module', () => {
expect(state.allStatusesObject['1'].emoji_reactions[0].me).to.eql(true)
expect(
state.allStatusesObject['1'].emoji_reactions[0].accounts[0].id,
- ).to.eql('me')
+ ).to.equal('me')
})
it('adds a new reaction', () => {
@@ -362,7 +362,7 @@ describe('Statuses module', () => {
expect(state.allStatusesObject['1'].emoji_reactions[0].me).to.eql(true)
expect(
state.allStatusesObject['1'].emoji_reactions[0].accounts[0].id,
- ).to.eql('me')
+ ).to.equal('me')
})
it('decreases count in existing reaction', () => {
@@ -406,7 +406,7 @@ describe('Statuses module', () => {
emoji: '😂',
currentUser: { id: 'me' },
})
- expect(state.allStatusesObject['1'].emoji_reactions.length).to.eql(0)
+ expect(state.allStatusesObject['1'].emoji_reactions).to.have.length(0)
})
})
@@ -428,9 +428,9 @@ describe('Statuses module', () => {
state.timelines.public.minId = '5'
mutations.showNewStatuses(state, { timeline: 'public' })
- expect(state.timelines.public.visibleStatuses.length).to.eql(2)
- expect(state.timelines.public.minVisibleId).to.eql('10')
- expect(state.timelines.public.minId).to.eql('10')
+ expect(state.timelines.public.visibleStatuses).to.have.length(2)
+ expect(state.timelines.public.minVisibleId).to.equal('10')
+ expect(state.timelines.public.minId).to.equal('10')
})
})
diff --git a/test/unit/specs/modules/users.spec.js b/test/unit/specs/modules/users.spec.js
index c07f817d8..1b33f8c4e 100644
--- a/test/unit/specs/modules/users.spec.js
+++ b/test/unit/specs/modules/users.spec.js
@@ -68,7 +68,7 @@ describe('The users module', () => {
},
}
const name = 'Guy'
- expect(getters.findUser(state)(name)).to.eql(undefined)
+ expect(getters.findUser(state)(name)).to.be.undefined
})
it('returns user with matching id', () => {
@@ -114,7 +114,7 @@ describe('The users module', () => {
},
}
const id = '1'
- expect(getters.findUserByName(state)(id)).to.eql(undefined)
+ expect(getters.findUserByName(state)(id)).to.be.undefined
})
})
})
diff --git a/test/unit/specs/services/entity_normalizer/entity_normalizer.spec.js b/test/unit/specs/services/entity_normalizer/entity_normalizer.spec.js
index da3f88636..c68c832a0 100644
--- a/test/unit/specs/services/entity_normalizer/entity_normalizer.spec.js
+++ b/test/unit/specs/services/entity_normalizer/entity_normalizer.spec.js
@@ -101,7 +101,7 @@ describe('API Entities normalizer', () => {
describe('Mastoapi preprocessing and converting', () => {
it("doesn't blow up", () => {
const parsed = mastoapidata.map(parseStatus)
- expect(parsed.length).to.eq(mastoapidata.length)
+ expect(parsed).to.have.length(mastoapidata.length)
})
it('processes repeats correctly', () => {
diff --git a/test/unit/specs/services/style_setter/style_setter.spec.js b/test/unit/specs/services/style_setter/style_setter.spec.js
new file mode 100644
index 000000000..c1bacdfa1
--- /dev/null
+++ b/test/unit/specs/services/style_setter/style_setter.spec.js
@@ -0,0 +1,24 @@
+import { hasInvalidCachedThemeRules } from 'src/services/style_setter/style_setter.js'
+
+describe('style setter cache', () => {
+ it('rejects cached rules containing serialized objects', () => {
+ expect(
+ hasInvalidCachedThemeRules([
+ ['html { --font: [object Object]; }'],
+ ['.post { --font: sans-serif; }'],
+ ]),
+ ).to.equal(true)
+ })
+
+ it('accepts cached rules containing valid font families', () => {
+ expect(
+ hasInvalidCachedThemeRules([
+ ['html { --font: sans-serif; }'],
+ [
+ '.post { --font: "Atkinson Hyperlegible"; }',
+ '.post::after { content: "[object Object]"; }',
+ ],
+ ]),
+ ).to.equal(false)
+ })
+})
diff --git a/test/unit/specs/services/theme_data/theme2_to_theme3.spec.js b/test/unit/specs/services/theme_data/theme2_to_theme3.spec.js
new file mode 100644
index 000000000..7165d88db
--- /dev/null
+++ b/test/unit/specs/services/theme_data/theme2_to_theme3.spec.js
@@ -0,0 +1,42 @@
+import {
+ basePaletteKeys,
+ convertTheme2To3,
+} from 'src/services/theme_data/theme2_to_theme3.js'
+
+describe('Theme 2 to Theme 3 conversion', () => {
+ it('converts font descriptors to CSS font families', () => {
+ const colors = Object.fromEntries(
+ [...basePaletteKeys].map((key) => [key, '#000000']),
+ )
+ const rules = convertTheme2To3({
+ colors,
+ fonts: {
+ interface: { family: 'sans-serif' },
+ input: { family: 'Open Sans' },
+ post: { family: 'Atkinson Hyperlegible' },
+ postCode: { family: 'monospace' },
+ },
+ })
+
+ expect(rules).to.deep.include({
+ source: '2to3',
+ component: 'Root',
+ directives: { '--font': 'generic | sans-serif' },
+ })
+ expect(rules).to.deep.include({
+ source: '2to3',
+ component: 'Root',
+ directives: { '--monoFont': 'generic | monospace' },
+ })
+ expect(rules).to.deep.include({
+ source: '2to3',
+ component: 'Input',
+ directives: { '--font': 'generic | Open Sans' },
+ })
+ expect(rules).to.deep.include({
+ source: '2to3',
+ component: 'RichContent',
+ directives: { '--font': 'generic | Atkinson Hyperlegible' },
+ })
+ })
+})
diff --git a/test/unit/specs/stores/sync_config.spec.js b/test/unit/specs/stores/sync_config.spec.js
index a045aa18a..d2e6d42d3 100644
--- a/test/unit/specs/stores/sync_config.spec.js
+++ b/test/unit/specs/stores/sync_config.spec.js
@@ -125,7 +125,7 @@ describe('The SyncConfig store', () => {
},
})
- expect(store.prefsStorage._journal.length).to.eql(500)
+ expect(store.prefsStorage._journal).to.have.length(500)
})
it('should reset local timestamp to remote if contents are the same', async () => {
@@ -173,7 +173,7 @@ describe('The SyncConfig store', () => {
}
store.setPreference({ path: 'simple.palette', value: '1' })
expect(store.prefsStorage.simple.palette).to.eql('1')
- expect(store.prefsStorage._journal.length).to.eql(1)
+ expect(store.prefsStorage._journal).to.have.length(1)
expect(store.prefsStorage._journal[0]).to.eql({
path: 'simple.palette',
operation: 'set',
@@ -199,7 +199,7 @@ describe('The SyncConfig store', () => {
store.updateCache({ username: 'test' })
expect(store.prefsStorage.simple.palette).to.eql(2)
expect(store.prefsStorage.collections.palette).to.eql([])
- expect(store.prefsStorage._journal.length).to.eql(2)
+ expect(store.prefsStorage._journal).to.have.length(2)
expect(store.prefsStorage._journal[0]).to.eql({
path: 'simple.palette',
operation: 'set',
@@ -229,7 +229,7 @@ describe('The SyncConfig store', () => {
store.updateCache({ username: 'test' })
expect(store.prefsStorage.simple.palette).to.eql(1)
expect(store.prefsStorage.collections.palette).to.eql([2])
- expect(store.prefsStorage._journal.length).to.eql(2)
+ expect(store.prefsStorage._journal).to.have.length(2)
})
// TODO We need a proper test for object-based stores
@@ -245,7 +245,7 @@ describe('The SyncConfig store', () => {
expect(store.prefsStorage.simple.fontInput).to.not.have.property(
'family',
)
- expect(store.prefsStorage._journal.length).to.eql(1)
+ expect(store.prefsStorage._journal).to.have.length(1)
})
it('should not allow unsetting depth <= 2', () => {
diff --git a/test/unit/specs/stores/user_highlight.spec.js b/test/unit/specs/stores/user_highlight.spec.js
index f80143b6e..e97f8f382 100644
--- a/test/unit/specs/stores/user_highlight.spec.js
+++ b/test/unit/specs/stores/user_highlight.spec.js
@@ -55,7 +55,7 @@ describe('The UserHighlight store', () => {
user: 'highlight@testing',
type: 'test',
})
- expect(store.highlight._journal.length).to.eql(1)
+ expect(store.highlight._journal).to.have.length(1)
expect(store.highlight._journal[0]).to.eql({
user: 'highlight@testing',
operation: 'set',
@@ -74,7 +74,7 @@ describe('The UserHighlight store', () => {
user: 'highlight@testing.xyz',
type: 'test',
})
- expect(store.highlight._journal.length).to.eql(1)
+ expect(store.highlight._journal).to.have.length(1)
expect(store.highlight._journal[0]).to.eql({
user: 'highlight@testing.xyz',
operation: 'set',
@@ -98,7 +98,7 @@ describe('The UserHighlight store', () => {
user: 'a@test.xyz',
type: 'foo',
})
- expect(store.highlight._journal.length).to.eql(1)
+ expect(store.highlight._journal).to.have.length(1)
})
})
})
diff --git a/tools/emoji_merger.js b/tools/emoji_merger.js
index b49ead471..1e37134a7 100644
--- a/tools/emoji_merger.js
+++ b/tools/emoji_merger.js
@@ -54,7 +54,7 @@ const run = () => {
// Sort by key
const sorted = Object.keys(emojisObject)
- .sort()
+ .sort((a, b) => a.localeCompare(b))
.reduce((acc, key) => {
if (key.length === 0) return acc
acc[key] = emojisObject[key]