Merge remote-tracking branch 'origin/develop' into 2-11-3-fixes
This commit is contained in:
commit
a125a9af6c
117 changed files with 591 additions and 557 deletions
36
.woodpecker/build-package.yaml
Normal file
36
.woodpecker/build-package.yaml
Normal file
|
|
@ -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
|
||||||
|
|
@ -6,38 +6,12 @@ when:
|
||||||
- event: manual
|
- event: manual
|
||||||
branch: ${CI_REPO_DEFAULT_BRANCH}
|
branch: ${CI_REPO_DEFAULT_BRANCH}
|
||||||
|
|
||||||
depends_on:
|
|
||||||
- test
|
|
||||||
- test-e2e
|
|
||||||
|
|
||||||
labels:
|
labels:
|
||||||
platform: linux/amd64
|
platform: linux/amd64
|
||||||
memory: 'high'
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
build:
|
build:
|
||||||
image: docker.io/node:20-alpine
|
image: docker.io/node:20-alpine
|
||||||
commands:
|
commands:
|
||||||
- apk add --no-cache zip git
|
|
||||||
- yarn --frozen-lockfile
|
- yarn --frozen-lockfile
|
||||||
- yarn build
|
- 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
|
|
||||||
|
|
|
||||||
45
.woodpecker/code-analisys.yaml
Normal file
45
.woodpecker/code-analisys.yaml
Normal file
|
|
@ -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
|
||||||
|
|
@ -10,6 +10,10 @@ labels:
|
||||||
platform: linux/amd64
|
platform: linux/amd64
|
||||||
memory: 'high'
|
memory: 'high'
|
||||||
|
|
||||||
|
depends_on:
|
||||||
|
- build
|
||||||
|
- test
|
||||||
|
|
||||||
variables:
|
variables:
|
||||||
artifacts_uploader_settings: &artifacts_uploader_settings
|
artifacts_uploader_settings: &artifacts_uploader_settings
|
||||||
user:
|
user:
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,9 @@ labels:
|
||||||
platform: linux/amd64
|
platform: linux/amd64
|
||||||
memory: 'high'
|
memory: 'high'
|
||||||
|
|
||||||
|
depends_on:
|
||||||
|
- build
|
||||||
|
|
||||||
variables:
|
variables:
|
||||||
artifacts_uploader_settings: &artifacts_uploader_settings
|
artifacts_uploader_settings: &artifacts_uploader_settings
|
||||||
user:
|
user:
|
||||||
|
|
@ -45,20 +48,6 @@ steps:
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
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:
|
upload-artifacts:
|
||||||
image: docker.io/woodpeckercommunity/plugin-gitea-package:0.5.0
|
image: docker.io/woodpeckercommunity/plugin-gitea-package:0.5.0
|
||||||
when:
|
when:
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
[](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) <a href="https://translate.pleroma.social/engage/pleroma/"><img src="https://translate.pleroma.social/widget/pleroma/pleroma-fe/svg-badge.svg" alt="Translation status"></a>
|
||||||
|
|
||||||
# For Translators
|
# 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).
|
||||||
|
|
@ -32,9 +34,14 @@ yarn unit
|
||||||
|
|
||||||
# For Contributors:
|
# 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:
|
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.
|
* `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.
|
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.
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,9 @@ export const getCommitHash = () => {
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
return childProcess
|
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()
|
.toString()
|
||||||
.trim()
|
.trim()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ const getAllAccessibleAnnotations = async (projectRoot) => {
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.filter((k) => k)
|
.filter(Boolean)
|
||||||
.join(',\n')
|
.join(',\n')
|
||||||
|
|
||||||
return `
|
return `
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import emojis from '@kazvmoe-infra/unicode-emoji-json/data-by-group.json' with {
|
||||||
type: 'json',
|
type: 'json',
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.keys(emojis).map((k) => {
|
Object.keys(emojis).forEach((k) => {
|
||||||
emojis[k].forEach((e) => {
|
emojis[k].forEach((e) => {
|
||||||
delete e.unicode_version
|
delete e.unicode_version
|
||||||
delete e.emoji_version
|
delete e.emoji_version
|
||||||
|
|
|
||||||
1
changelog.d/server-mute-render-modify.fix
Normal file
1
changelog.d/server-mute-render-modify.fix
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Fixed server-side domain mutes rendering/api calls
|
||||||
1
changelog.d/theme2-font-cache.fix
Normal file
1
changelog.d/theme2-font-cache.fix
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Fix Theme 2 fonts falling back to serif after upgrade
|
||||||
1
changelog.d/timeline_rendering_status_index_approx.fix
Normal file
1
changelog.d/timeline_rendering_status_index_approx.fix
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Fixed status index approxmiation in timeline rendering for dynamically changing viewport geometries
|
||||||
|
|
@ -8,7 +8,8 @@
|
||||||
"dev": "node build/update-emoji.js && vite dev",
|
"dev": "node build/update-emoji.js && vite dev",
|
||||||
"build": "node build/update-emoji.js && vite build",
|
"build": "node build/update-emoji.js && vite build",
|
||||||
"unit": "node build/update-emoji.js && vitest --run --coverage",
|
"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",
|
"unit:watch": "node build/update-emoji.js && vitest --coverage",
|
||||||
"e2e:pw": "playwright test --config test/e2e-playwright/playwright.config.mjs",
|
"e2e:pw": "playwright test --config test/e2e-playwright/playwright.config.mjs",
|
||||||
"e2e": "sh ./tools/e2e/run.sh",
|
"e2e": "sh ./tools/e2e/run.sh",
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
sonar.projectKey=Pleroma-FE
|
sonar.projectKey=Pleroma-FE
|
||||||
sonar.qualitygatesonar.host.url.wait=true
|
sonar.qualitygatesonar.host.url.wait=true
|
||||||
sonar.host.url=https://sonarqube.pleroma.dev
|
sonar.host.url=https://sonarqube.pleroma.dev
|
||||||
|
sonar.sources=src,build,tools
|
||||||
|
sonar.tests=test
|
||||||
|
|
|
||||||
|
|
@ -237,10 +237,10 @@ export const changeStatusScope = ({
|
||||||
credentials,
|
credentials,
|
||||||
}) => {
|
}) => {
|
||||||
const payload = {}
|
const payload = {}
|
||||||
if (typeof sensitive !== 'undefined') {
|
if (sensitive !== undefined) {
|
||||||
payload['sensitive'] = sensitive
|
payload['sensitive'] = sensitive
|
||||||
}
|
}
|
||||||
if (typeof visibility !== 'undefined') {
|
if (visibility !== undefined) {
|
||||||
payload['visibility'] = visibility
|
payload['visibility'] = visibility
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -260,15 +260,15 @@ export const announcementToPayload = ({
|
||||||
}) => {
|
}) => {
|
||||||
const payload = { content }
|
const payload = { content }
|
||||||
|
|
||||||
if (typeof startsAt !== 'undefined') {
|
if (startsAt !== undefined) {
|
||||||
payload.starts_at = startsAt ? new Date(startsAt).toISOString() : null
|
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
|
payload.ends_at = endsAt ? new Date(endsAt).toISOString() : null
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof allDay !== 'undefined') {
|
if (allDay !== undefined) {
|
||||||
payload.all_day = allDay
|
payload.all_day = allDay
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ export const chats = ({ credentials }) =>
|
||||||
url: PLEROMA_CHATS_URL,
|
url: PLEROMA_CHATS_URL,
|
||||||
credentials,
|
credentials,
|
||||||
}).then(({ data }) => ({
|
}).then(({ data }) => ({
|
||||||
data: data.map(parseChat).filter((c) => c),
|
data: data.map(parseChat).filter(Boolean),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
export const getOrCreateChat = ({ accountId, credentials }) =>
|
export const getOrCreateChat = ({ accountId, credentials }) =>
|
||||||
|
|
@ -40,7 +40,7 @@ export const chatMessages = ({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
credentials,
|
credentials,
|
||||||
}).then(({ data }) => ({
|
}).then(({ data }) => ({
|
||||||
data: data.map(parseChatMessage).filter((c) => c),
|
data: data.map(parseChatMessage).filter(Boolean),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,11 +37,7 @@ export const paramsString = (params = {}) => {
|
||||||
|
|
||||||
arrays.forEach(([k, array]) => {
|
arrays.forEach(([k, array]) => {
|
||||||
array.forEach((v) => {
|
array.forEach((v) => {
|
||||||
if (
|
if (typeof v === 'object' || typeof v === 'function' || v === undefined)
|
||||||
typeof v === 'object' ||
|
|
||||||
typeof v === 'function' ||
|
|
||||||
typeof v === 'undefined'
|
|
||||||
)
|
|
||||||
throw new TypeError('Array param cannot contain non-primitives!')
|
throw new TypeError('Array param cannot contain non-primitives!')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -29,9 +29,8 @@ const Announcement = {
|
||||||
currentUser: (state) => state.users.currentUser,
|
currentUser: (state) => state.users.currentUser,
|
||||||
}),
|
}),
|
||||||
canEditAnnouncement() {
|
canEditAnnouncement() {
|
||||||
return (
|
return this.currentUser?.privileges.has(
|
||||||
this.currentUser &&
|
'announcements_manage_announcements',
|
||||||
this.currentUser.privileges.has('announcements_manage_announcements')
|
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
content() {
|
content() {
|
||||||
|
|
|
||||||
|
|
@ -33,9 +33,8 @@ const AnnouncementsPage = {
|
||||||
return useAnnouncementsStore().announcements
|
return useAnnouncementsStore().announcements
|
||||||
},
|
},
|
||||||
canPostAnnouncement() {
|
canPostAnnouncement() {
|
||||||
return (
|
return this.currentUser?.privileges.has(
|
||||||
this.currentUser &&
|
'announcements_manage_announcements',
|
||||||
this.currentUser.privileges.has('announcements_manage_announcements')
|
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -165,16 +165,16 @@ const Attachment = {
|
||||||
useMediaViewerStore().setCurrentMedia(this.attachment)
|
useMediaViewerStore().setCurrentMedia(this.attachment)
|
||||||
},
|
},
|
||||||
onEdit(event) {
|
onEdit(event) {
|
||||||
this.edit && this.edit(this.attachment, event)
|
this.edit?.(this.attachment, event)
|
||||||
},
|
},
|
||||||
onRemove() {
|
onRemove() {
|
||||||
this.remove && this.remove(this.attachment)
|
this.remove?.(this.attachment)
|
||||||
},
|
},
|
||||||
onShiftUp() {
|
onShiftUp() {
|
||||||
this.shiftUp && this.shiftUp(this.attachment)
|
this.shiftUp?.(this.attachment)
|
||||||
},
|
},
|
||||||
onShiftDn() {
|
onShiftDn() {
|
||||||
this.shiftDn && this.shiftDn(this.attachment)
|
this.shiftDn?.(this.attachment)
|
||||||
},
|
},
|
||||||
stopFlash() {
|
stopFlash() {
|
||||||
this.$refs.flash.closePlayer()
|
this.$refs.flash.closePlayer()
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
v-if="isMessage"
|
v-if="isMessage"
|
||||||
|
:id="`chatmessage-${message.id}`"
|
||||||
class="chat-message-wrapper"
|
class="chat-message-wrapper"
|
||||||
:class="[classnames, { 'hovered-message-chain': hoveredMessageChain }]"
|
:class="[classnames, { 'hovered-message-chain': hoveredMessageChain }]"
|
||||||
:id="`chatmessage-${message.id}`"
|
|
||||||
@mouseover="onHover(true)"
|
@mouseover="onHover(true)"
|
||||||
@mouseleave="onHover(false)"
|
@mouseleave="onHover(false)"
|
||||||
>
|
>
|
||||||
|
|
@ -40,7 +40,10 @@
|
||||||
</template>
|
</template>
|
||||||
</i18n-t>
|
</i18n-t>
|
||||||
</StatusPopover>
|
</StatusPopover>
|
||||||
<span v-else class="reply-label">
|
<span
|
||||||
|
v-else
|
||||||
|
class="reply-label"
|
||||||
|
>
|
||||||
{{ $t('status.broken_reply') }}
|
{{ $t('status.broken_reply') }}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -83,7 +86,10 @@
|
||||||
:user="author"
|
:user="author"
|
||||||
/>
|
/>
|
||||||
</UserPopover>
|
</UserPopover>
|
||||||
<div v-else class="avatar-spacer" />
|
<div
|
||||||
|
v-else
|
||||||
|
class="avatar-spacer"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="chat-message-inner">
|
<div class="chat-message-inner">
|
||||||
<div class="message-bubble-wrapper">
|
<div class="message-bubble-wrapper">
|
||||||
|
|
@ -98,7 +104,6 @@
|
||||||
@mouseenter="hovered = true"
|
@mouseenter="hovered = true"
|
||||||
@mouseleave="hovered = false"
|
@mouseleave="hovered = false"
|
||||||
>
|
>
|
||||||
|
|
||||||
<StatusActionButtons
|
<StatusActionButtons
|
||||||
v-if="isStatus"
|
v-if="isStatus"
|
||||||
class="chat-message-toolbar"
|
class="chat-message-toolbar"
|
||||||
|
|
@ -112,9 +117,9 @@
|
||||||
@toggle-replying="$emit('replyRequested', message)"
|
@toggle-replying="$emit('replyRequested', message)"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
|
v-else
|
||||||
class="chat-message-toolbar"
|
class="chat-message-toolbar"
|
||||||
:class="{ '-visible': hovered || menuOpened }"
|
:class="{ '-visible': hovered || menuOpened }"
|
||||||
v-else
|
|
||||||
>
|
>
|
||||||
<Popover
|
<Popover
|
||||||
trigger="click"
|
trigger="click"
|
||||||
|
|
@ -221,7 +226,10 @@
|
||||||
v-if="isStatus && repliedTo"
|
v-if="isStatus && repliedTo"
|
||||||
class="reply-indicator"
|
class="reply-indicator"
|
||||||
>
|
>
|
||||||
<FAIcon class="icon" icon="reply" />
|
<FAIcon
|
||||||
|
class="icon"
|
||||||
|
icon="reply"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="end-spacer" />
|
<div class="end-spacer" />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -232,7 +240,10 @@
|
||||||
v-else
|
v-else
|
||||||
class="chat-message-date-separator"
|
class="chat-message-date-separator"
|
||||||
>
|
>
|
||||||
<ChatMessageDate :date="chatItem.date" :show-time="chatItem.isTime" />
|
<ChatMessageDate
|
||||||
|
:date="chatItem.date"
|
||||||
|
:show-time="chatItem.isTime"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
:previous-item="getPreviousItem(index)"
|
:previous-item="getPreviousItem(index)"
|
||||||
:hovered-message-chain="chatItem.messageChainId === hoveredMessageChainId"
|
:hovered-message-chain="chatItem.messageChainId === hoveredMessageChainId"
|
||||||
:focused="chatItem.id === focusedId"
|
:focused="chatItem.id === focusedId"
|
||||||
:repliedTo="chatItem.id === repliedId"
|
:replied-to="chatItem.id === repliedId"
|
||||||
@hover="onMessageHover"
|
@hover="onMessageHover"
|
||||||
@delete="onMessageDelete"
|
@delete="onMessageDelete"
|
||||||
@reply-requested="onReplyRequested"
|
@reply-requested="onReplyRequested"
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,7 @@ const Chat = {
|
||||||
mounted() {
|
mounted() {
|
||||||
window.addEventListener('resize', this.handleResize)
|
window.addEventListener('resize', this.handleResize)
|
||||||
window.addEventListener('scroll', this.handleScroll)
|
window.addEventListener('scroll', this.handleScroll)
|
||||||
if (typeof document.hidden !== 'undefined') {
|
if (document.hidden !== undefined) {
|
||||||
document.addEventListener(
|
document.addEventListener(
|
||||||
'visibilitychange',
|
'visibilitychange',
|
||||||
this.handleVisibilityChange,
|
this.handleVisibilityChange,
|
||||||
|
|
@ -112,7 +112,7 @@ const Chat = {
|
||||||
unmounted() {
|
unmounted() {
|
||||||
window.removeEventListener('scroll', this.handleScroll)
|
window.removeEventListener('scroll', this.handleScroll)
|
||||||
window.removeEventListener('resize', this.handleResize)
|
window.removeEventListener('resize', this.handleResize)
|
||||||
if (typeof document.hidden !== 'undefined')
|
if (document.hidden !== undefined)
|
||||||
document.removeEventListener(
|
document.removeEventListener(
|
||||||
'visibilitychange',
|
'visibilitychange',
|
||||||
this.handleVisibilityChange,
|
this.handleVisibilityChange,
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@
|
||||||
v-if="messages[0]?.summary_raw_html"
|
v-if="messages[0]?.summary_raw_html"
|
||||||
:html="messages[0].summary_raw_html"
|
:html="messages[0].summary_raw_html"
|
||||||
:emoji="messages[0].emojis"
|
:emoji="messages[0].emojis"
|
||||||
/>
|
/>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
{{ $t('timeline.conversation') }}
|
{{ $t('timeline.conversation') }}
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -11,11 +11,11 @@
|
||||||
{{ label }}
|
{{ label }}
|
||||||
</label>
|
</label>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
v-if="typeof fallback !== 'undefined' && showOptionalCheckbox && !hideOptionalCheckbox"
|
v-if="fallback !== undefined && showOptionalCheckbox && !hideOptionalCheckbox"
|
||||||
:model-value="present"
|
:model-value="present"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
class="opt"
|
class="opt"
|
||||||
@update:model-value="updateValue(typeof modelValue === 'undefined' ? fallback : undefined)"
|
@update:model-value="updateValue(modelValue === undefined ? fallback : undefined)"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
class="input color-input-field"
|
class="input color-input-field"
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ const sortAndFilterConversation = (conversation, statusoid) => {
|
||||||
} else {
|
} else {
|
||||||
conversation = filter(conversation, (status) => status.type !== 'retweet')
|
conversation = filter(conversation, (status) => status.type !== 'retweet')
|
||||||
}
|
}
|
||||||
return conversation.filter((_) => _).sort(sortById)
|
return conversation.filter(Boolean).sort(sortById)
|
||||||
}
|
}
|
||||||
|
|
||||||
const conversation = {
|
const conversation = {
|
||||||
|
|
@ -239,9 +239,9 @@ const conversation = {
|
||||||
depth,
|
depth,
|
||||||
},
|
},
|
||||||
walk(forest, forest[id], depth + 1, processed),
|
walk(forest, forest[id], depth + 1, processed),
|
||||||
].reduce((a, b) => a.concat(b), [])
|
].flat()
|
||||||
})
|
})
|
||||||
.reduce((a, b) => a.concat(b), [])
|
.flat()
|
||||||
|
|
||||||
const linearized = walk(
|
const linearized = walk(
|
||||||
threads.forest,
|
threads.forest,
|
||||||
|
|
@ -307,9 +307,9 @@ const conversation = {
|
||||||
(tl, cur) =>
|
(tl, cur) =>
|
||||||
tl.filter(
|
tl.filter(
|
||||||
(k) =>
|
(k) =>
|
||||||
this.getReplies(cur.id)
|
!this.getReplies(cur.id)
|
||||||
.map((v) => v.id)
|
.map((v) => v.id)
|
||||||
.indexOf(k.id) === -1,
|
.includes(k.id),
|
||||||
),
|
),
|
||||||
this.conversation,
|
this.conversation,
|
||||||
)
|
)
|
||||||
|
|
@ -375,7 +375,7 @@ const conversation = {
|
||||||
return !!(this.expanded || this.isPage)
|
return !!(this.expanded || this.isPage)
|
||||||
},
|
},
|
||||||
hiddenStyle() {
|
hiddenStyle() {
|
||||||
const height = (this.status && this.status.virtualHeight) || '120px'
|
const height = this.status?.virtualHeight || '120px'
|
||||||
return this.virtualHidden ? { height } : {}
|
return this.virtualHidden ? { height } : {}
|
||||||
},
|
},
|
||||||
threadDisplayStatus() {
|
threadDisplayStatus() {
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,9 @@
|
||||||
v-if="conversation[0]?.summary_raw_html"
|
v-if="conversation[0]?.summary_raw_html"
|
||||||
:html="conversation[0].summary_raw_html"
|
:html="conversation[0].summary_raw_html"
|
||||||
:emoji="conversation[0].emojis"
|
:emoji="conversation[0].emojis"
|
||||||
/>
|
/>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
{{ $t('timeline.conversation') }}
|
{{ $t('timeline.conversation') }}
|
||||||
</template>
|
</template>
|
||||||
</h1>
|
</h1>
|
||||||
<button
|
<button
|
||||||
|
|
|
||||||
|
|
@ -45,8 +45,7 @@ export default {
|
||||||
data: () => ({
|
data: () => ({
|
||||||
searchBarHidden: true,
|
searchBarHidden: true,
|
||||||
supportsMask:
|
supportsMask:
|
||||||
window.CSS &&
|
window.CSS?.supports &&
|
||||||
window.CSS.supports &&
|
|
||||||
(window.CSS.supports('mask-size', 'contain') ||
|
(window.CSS.supports('mask-size', 'contain') ||
|
||||||
window.CSS.supports('-webkit-mask-size', 'contain') ||
|
window.CSS.supports('-webkit-mask-size', 'contain') ||
|
||||||
window.CSS.supports('-moz-mask-size', 'contain') ||
|
window.CSS.supports('-moz-mask-size', 'contain') ||
|
||||||
|
|
|
||||||
|
|
@ -42,9 +42,7 @@ const EditStatusModal = {
|
||||||
},
|
},
|
||||||
isFormVisible(val) {
|
isFormVisible(val) {
|
||||||
if (val) {
|
if (val) {
|
||||||
this.$nextTick(
|
this.$nextTick(() => this.$el?.querySelector('textarea').focus())
|
||||||
() => this.$el && this.$el.querySelector('textarea').focus(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -188,8 +188,8 @@ const EmojiInput = {
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
names: names.filter((k) => k),
|
names: names.filter(Boolean),
|
||||||
keywords: keywords.filter((k) => k),
|
keywords: keywords.filter(Boolean),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ export const suggestUsers = ({ dispatch, state }) => {
|
||||||
|
|
||||||
const userSearch = (query) => dispatch('searchUsers', { query })
|
const userSearch = (query) => dispatch('searchUsers', { query })
|
||||||
const debounceUserSearch = (query) => {
|
const debounceUserSearch = (query) => {
|
||||||
cancelUserSearch && cancelUserSearch()
|
cancelUserSearch?.()
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
timeout = setTimeout(() => {
|
timeout = setTimeout(() => {
|
||||||
userSearch(query).then(resolve).catch(reject)
|
userSearch(query).then(resolve).catch(reject)
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ const maybeLocalizedKeywords = (emoji, languages, nameLocalizer) => {
|
||||||
languages.forEach((lang) => {
|
languages.forEach((lang) => {
|
||||||
const keywords = emoji.annotations[lang]?.keywords || []
|
const keywords = emoji.annotations[lang]?.keywords || []
|
||||||
const name = emoji.annotations[lang]?.name
|
const name = emoji.annotations[lang]?.name
|
||||||
res.push(...keywords.concat([name]).filter((k) => k))
|
res.push(...keywords.concat([name]).filter(Boolean))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
|
|
@ -408,7 +408,7 @@ const EmojiPicker = {
|
||||||
isFirstRow: index === 0,
|
isFirstRow: index === 0,
|
||||||
})),
|
})),
|
||||||
)
|
)
|
||||||
.reduce((a, c) => a.concat(c), [])
|
.flat()
|
||||||
},
|
},
|
||||||
languages() {
|
languages() {
|
||||||
return ensureFinalFallback(
|
return ensureFinalFallback(
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ const Flash = {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
closePlayer() {
|
closePlayer() {
|
||||||
this.ruffleInstance && this.ruffleInstance.remove()
|
this.ruffleInstance?.remove()
|
||||||
this.player = false
|
this.player = false
|
||||||
this.$emit('playerClosed')
|
this.$emit('playerClosed')
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ export default {
|
||||||
'sans-serif',
|
'sans-serif',
|
||||||
'monospace',
|
'monospace',
|
||||||
...(this.options || []),
|
...(this.options || []),
|
||||||
].filter((_) => _),
|
].filter(Boolean),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
<div class="font-control">
|
<div class="font-control">
|
||||||
<div class="setting-item">
|
<div class="setting-item">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
v-if="typeof fallback !== 'undefined'"
|
v-if="fallback !== undefined"
|
||||||
:id="name + '-o'"
|
:id="name + '-o'"
|
||||||
class="font-checkbox setting-control setting-label"
|
class="font-checkbox setting-control setting-label"
|
||||||
:model-value="present"
|
:model-value="present"
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ const ImageCropper = {
|
||||||
},
|
},
|
||||||
readFile() {
|
readFile() {
|
||||||
const fileInput = this.$refs.input
|
const fileInput = this.$refs.input
|
||||||
if (fileInput.files != null && fileInput.files[0] != null) {
|
if (fileInput?.files?.[0]) {
|
||||||
this.file = fileInput.files[0]
|
this.file = fileInput.files[0]
|
||||||
const reader = new window.FileReader()
|
const reader = new window.FileReader()
|
||||||
reader.onload = (e) => {
|
reader.onload = (e) => {
|
||||||
|
|
|
||||||
|
|
@ -59,12 +59,12 @@ const ListsNew = {
|
||||||
membersUsers() {
|
membersUsers() {
|
||||||
return [...this.membersUserIds, ...this.addedUserIds]
|
return [...this.membersUserIds, ...this.addedUserIds]
|
||||||
.map((userId) => this.findUser(userId))
|
.map((userId) => this.findUser(userId))
|
||||||
.filter((user) => user)
|
.filter(Boolean)
|
||||||
},
|
},
|
||||||
searchUsers() {
|
searchUsers() {
|
||||||
return this.searchUserIds
|
return this.searchUserIds
|
||||||
.map((userId) => this.findUser(userId))
|
.map((userId) => this.findUser(userId))
|
||||||
.filter((user) => user)
|
.filter(Boolean)
|
||||||
},
|
},
|
||||||
...mapState({
|
...mapState({
|
||||||
currentUser: (state) => state.users.currentUser,
|
currentUser: (state) => state.users.currentUser,
|
||||||
|
|
|
||||||
|
|
@ -75,13 +75,11 @@ const MentionLink = {
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
user() {
|
user() {
|
||||||
return (
|
return this.url && this.$store?.getters.findUserByUrl(this.url)
|
||||||
this.url && this.$store && this.$store.getters.findUserByUrl(this.url)
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
isYou() {
|
isYou() {
|
||||||
// FIXME why user !== currentUser???
|
// FIXME why user !== currentUser???
|
||||||
return this.user && this.user.id === this.currentUser.id
|
return this.user?.id === this.currentUser.id
|
||||||
},
|
},
|
||||||
userName() {
|
userName() {
|
||||||
return this.user && this.userNameFullUi.split('@')[0]
|
return this.user && this.userNameFullUi.split('@')[0]
|
||||||
|
|
@ -94,10 +92,10 @@ const MentionLink = {
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
userNameFull() {
|
userNameFull() {
|
||||||
return this.user && this.user.screen_name
|
return this.user?.screen_name
|
||||||
},
|
},
|
||||||
userNameFullUi() {
|
userNameFullUi() {
|
||||||
return this.user && this.user.screen_name_ui
|
return this.user?.screen_name_ui
|
||||||
},
|
},
|
||||||
highlightData() {
|
highlightData() {
|
||||||
return this.highlight[this.user?.screen_name]
|
return this.highlight[this.user?.screen_name]
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import { useInstanceStore } from 'src/stores/instance.js'
|
||||||
*/
|
*/
|
||||||
const toInstanceReasonObject = (instances, 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) {
|
if (info[key]?.[instance]?.reason) {
|
||||||
return { instance, reason: info[key][instance].reason }
|
return { instance, reason: info[key][instance].reason }
|
||||||
}
|
}
|
||||||
return { instance, reason: '' }
|
return { instance, reason: '' }
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,7 @@ export const filterNavigation = (
|
||||||
if (!isFederating && set.has('federating')) return false
|
if (!isFederating && set.has('federating')) return false
|
||||||
if (!currentUser && isPrivate && set.has('!private')) return false
|
if (!currentUser && isPrivate && set.has('!private')) return false
|
||||||
if (!currentUser && !(anon || anonRoute)) return false
|
if (!currentUser && !(anon || anonRoute)) return false
|
||||||
if ((!currentUser || !currentUser.locked) && set.has('lockedUser'))
|
if (!currentUser?.locked && set.has('lockedUser')) return false
|
||||||
return false
|
|
||||||
if (!hasChats && set.has('chats')) return false
|
if (!hasChats && set.has('chats')) return false
|
||||||
if (!hasAnnouncements && set.has('announcements')) return false
|
if (!hasAnnouncements && set.has('announcements')) return false
|
||||||
if (!supportsBubbleTimeline && set.has('supportsBubbleTimeline'))
|
if (!supportsBubbleTimeline && set.has('supportsBubbleTimeline'))
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
{{ label || $t('settings.style.themes3.editor.opacity') }}
|
{{ label || $t('settings.style.themes3.editor.opacity') }}
|
||||||
</label>
|
</label>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
v-if="typeof fallback !== 'undefined'"
|
v-if="fallback !== undefined"
|
||||||
:model-value="present"
|
:model-value="present"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
class="opt"
|
class="opt"
|
||||||
|
|
|
||||||
|
|
@ -39,13 +39,13 @@ export default {
|
||||||
return storePoll || {}
|
return storePoll || {}
|
||||||
},
|
},
|
||||||
options() {
|
options() {
|
||||||
return (this.poll && this.poll.options) || []
|
return this.poll?.options || []
|
||||||
},
|
},
|
||||||
expiresAt() {
|
expiresAt() {
|
||||||
return (this.poll && this.poll.expires_at) || null
|
return this.poll?.expires_at || null
|
||||||
},
|
},
|
||||||
expired() {
|
expired() {
|
||||||
return (this.poll && this.poll.expired) || false
|
return this.poll?.expired || false
|
||||||
},
|
},
|
||||||
expirationLabel() {
|
expirationLabel() {
|
||||||
if (useMergedConfigStore().mergedConfig.useAbsoluteTimeFormat) {
|
if (useMergedConfigStore().mergedConfig.useAbsoluteTimeFormat) {
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,10 @@ export default {
|
||||||
},
|
},
|
||||||
emits: ['update:modelValue'],
|
emits: ['update:modelValue'],
|
||||||
computed: {
|
computed: {
|
||||||
|
// Model value
|
||||||
|
options() {
|
||||||
|
return pollFallback(this.modelValue, 'options')
|
||||||
|
},
|
||||||
pollType: {
|
pollType: {
|
||||||
get() {
|
get() {
|
||||||
return pollFallback(this.modelValue, 'pollType')
|
return pollFallback(this.modelValue, 'pollType')
|
||||||
|
|
@ -36,14 +40,6 @@ export default {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
options: {
|
|
||||||
get() {
|
|
||||||
return pollFallback(this.modelValue, 'options')
|
|
||||||
},
|
|
||||||
set(newVal) {
|
|
||||||
this.$emit('update:modelValue', { ...this.modelValue, options: newVal })
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expiryAmount: {
|
expiryAmount: {
|
||||||
get() {
|
get() {
|
||||||
return pollFallback(this.modelValue, 'expiryAmount')
|
return pollFallback(this.modelValue, 'expiryAmount')
|
||||||
|
|
@ -66,6 +62,8 @@ export default {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Configuration
|
||||||
pollLimits() {
|
pollLimits() {
|
||||||
return useInstanceStore().limits.pollLimits
|
return useInstanceStore().limits.pollLimits
|
||||||
},
|
},
|
||||||
|
|
@ -116,19 +114,34 @@ export default {
|
||||||
},
|
},
|
||||||
addOption() {
|
addOption() {
|
||||||
if (this.options.length < this.maxOptions) {
|
if (this.options.length < this.maxOptions) {
|
||||||
this.options = [...this.options, '']
|
this.$emit('update:modelValue', {
|
||||||
|
...this.modelValue,
|
||||||
|
options: [...this.options, ''],
|
||||||
|
})
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
},
|
},
|
||||||
deleteOption(index) {
|
deleteOption(index) {
|
||||||
if (this.options.length > 2) {
|
if (this.options.length > 2) {
|
||||||
this.options.splice(index, 1)
|
const options = [...this.options]
|
||||||
this.options = this.options
|
options.splice(index, 1)
|
||||||
|
|
||||||
|
this.$emit('update:modelValue', {
|
||||||
|
...this.modelValue,
|
||||||
|
options,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
updateOption(index, value) {
|
updateOption(index, value) {
|
||||||
this.options = this.options
|
const options = [...this.options]
|
||||||
|
options[index] = value
|
||||||
|
|
||||||
|
this.$emit('update:modelValue', {
|
||||||
|
...this.modelValue,
|
||||||
|
options,
|
||||||
|
})
|
||||||
},
|
},
|
||||||
convertExpiryToUnit(unit, amount) {
|
convertExpiryToUnit(unit, amount) {
|
||||||
// Note: we want seconds and not milliseconds
|
// Note: we want seconds and not milliseconds
|
||||||
|
|
|
||||||
|
|
@ -11,14 +11,15 @@
|
||||||
<div class="input-container">
|
<div class="input-container">
|
||||||
<input
|
<input
|
||||||
:id="`poll-${index}`"
|
:id="`poll-${index}`"
|
||||||
v-model="options[index]"
|
:model-value="options[index]"
|
||||||
size="1"
|
size="1"
|
||||||
class="input poll-option-input"
|
class="input poll-option-input"
|
||||||
type="text"
|
type="text"
|
||||||
:placeholder="$t('polls.option')"
|
:placeholder="$t('polls.option')"
|
||||||
:maxlength="maxLength"
|
:maxlength="maxLength"
|
||||||
|
:aria-label="$t('polls.option')"
|
||||||
@keydown.enter.stop.prevent="nextOption(index)"
|
@keydown.enter.stop.prevent="nextOption(index)"
|
||||||
@change="updateOption"
|
@change="(e) => updateOption(index, e.target.value)"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
|
|
@ -69,6 +70,7 @@
|
||||||
class="input expiry-amount hide-number-spinner"
|
class="input expiry-amount hide-number-spinner"
|
||||||
:min="minExpirationInCurrentUnit"
|
:min="minExpirationInCurrentUnit"
|
||||||
:max="maxExpirationInCurrentUnit"
|
:max="maxExpirationInCurrentUnit"
|
||||||
|
:aria-label="$t('polls.expiry_amount')"
|
||||||
@change="expiryAmountChange"
|
@change="expiryAmountChange"
|
||||||
>
|
>
|
||||||
{{ ' ' }}
|
{{ ' ' }}
|
||||||
|
|
@ -77,6 +79,7 @@
|
||||||
unstyled="true"
|
unstyled="true"
|
||||||
class="expiry-unit"
|
class="expiry-unit"
|
||||||
@change="expiryAmountChange"
|
@change="expiryAmountChange"
|
||||||
|
:aria-label="$t('polls.expiry_unit')"
|
||||||
>
|
>
|
||||||
<option
|
<option
|
||||||
v-for="unit in expiryUnits"
|
v-for="unit in expiryUnits"
|
||||||
|
|
|
||||||
|
|
@ -129,9 +129,7 @@ const Popover = {
|
||||||
// Popover will be anchored around this element, trigger ref is the container, so
|
// 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.
|
// its children are what are inside the slot. Expect only one v-slot:trigger.
|
||||||
const anchorEl =
|
const anchorEl =
|
||||||
this.anchorEl ||
|
this.anchorEl || this.$refs.trigger?.children[0] || this.$el
|
||||||
(this.$refs.trigger && this.$refs.trigger.children[0]) ||
|
|
||||||
this.$el
|
|
||||||
// SVGs don't have offsetWidth/Height, use fallback
|
// SVGs don't have offsetWidth/Height, use fallback
|
||||||
const anchorHeight = anchorEl.offsetHeight || anchorEl.clientHeight
|
const anchorHeight = anchorEl.offsetHeight || anchorEl.clientHeight
|
||||||
const anchorWidth = anchorEl.offsetWidth || anchorEl.clientWidth
|
const anchorWidth = anchorEl.offsetWidth || anchorEl.clientWidth
|
||||||
|
|
@ -155,8 +153,7 @@ const Popover = {
|
||||||
|
|
||||||
// Minor optimization, don't call a slow reflow call if we don't have to
|
// Minor optimization, don't call a slow reflow call if we don't have to
|
||||||
const parentScreenBox =
|
const parentScreenBox =
|
||||||
this.boundTo &&
|
(this.boundTo?.x === 'container' || this.boundTo?.y === 'container') &&
|
||||||
(this.boundTo.x === 'container' || this.boundTo.y === 'container') &&
|
|
||||||
this.containerBoundingClientRect()
|
this.containerBoundingClientRect()
|
||||||
|
|
||||||
const margin = this.margin || {}
|
const margin = this.margin || {}
|
||||||
|
|
@ -164,7 +161,7 @@ const Popover = {
|
||||||
// What are the screen bounds for the popover? Viewport vs container
|
// What are the screen bounds for the popover? Viewport vs container
|
||||||
// when using viewport, using default margin values to dodge the navbar
|
// when using viewport, using default margin values to dodge the navbar
|
||||||
const xBounds =
|
const xBounds =
|
||||||
this.boundTo && this.boundTo.x === 'container'
|
this.boundTo?.x === 'container'
|
||||||
? {
|
? {
|
||||||
min: parentScreenBox.left + (margin.left || 0),
|
min: parentScreenBox.left + (margin.left || 0),
|
||||||
max: parentScreenBox.right - (margin.right || 0),
|
max: parentScreenBox.right - (margin.right || 0),
|
||||||
|
|
@ -175,7 +172,7 @@ const Popover = {
|
||||||
}
|
}
|
||||||
|
|
||||||
const yBounds =
|
const yBounds =
|
||||||
this.boundTo && this.boundTo.y === 'container'
|
this.boundTo?.y === 'container'
|
||||||
? {
|
? {
|
||||||
min: parentScreenBox.top + (margin.top || 0),
|
min: parentScreenBox.top + (margin.top || 0),
|
||||||
max: parentScreenBox.bottom - (margin.bottom || 0),
|
max: parentScreenBox.bottom - (margin.bottom || 0),
|
||||||
|
|
@ -247,12 +244,12 @@ const Popover = {
|
||||||
if (bottomBoundary + content.offsetHeight > yBounds.max) usingTop = true
|
if (bottomBoundary + content.offsetHeight > yBounds.max) usingTop = true
|
||||||
if (topBoundary - content.offsetHeight < yBounds.min) usingTop = false
|
if (topBoundary - content.offsetHeight < yBounds.min) usingTop = false
|
||||||
|
|
||||||
const yOffset = (this.offset && this.offset.y) || 0
|
const yOffset = this.offset?.y || 0
|
||||||
translateY = usingTop
|
translateY = usingTop
|
||||||
? topBoundary - yOffset - content.offsetHeight
|
? topBoundary - yOffset - content.offsetHeight
|
||||||
: bottomBoundary + yOffset
|
: bottomBoundary + yOffset
|
||||||
|
|
||||||
const xOffset = (this.offset && this.offset.x) || 0
|
const xOffset = this.offset?.x || 0
|
||||||
translateX = origin.x + horizOffset + xOffset
|
translateX = origin.x + horizOffset + xOffset
|
||||||
} else {
|
} else {
|
||||||
// Default to whatever user wished with placement prop
|
// Default to whatever user wished with placement prop
|
||||||
|
|
@ -268,12 +265,12 @@ const Popover = {
|
||||||
if (rightBoundary + content.offsetWidth > xBounds.max) usingLeft = true
|
if (rightBoundary + content.offsetWidth > xBounds.max) usingLeft = true
|
||||||
if (leftBoundary - content.offsetWidth < xBounds.min) usingLeft = false
|
if (leftBoundary - content.offsetWidth < xBounds.min) usingLeft = false
|
||||||
|
|
||||||
const xOffset = (this.offset && this.offset.x) || 0
|
const xOffset = this.offset?.x || 0
|
||||||
translateX = usingLeft
|
translateX = usingLeft
|
||||||
? leftBoundary - xOffset - content.offsetWidth
|
? leftBoundary - xOffset - content.offsetWidth
|
||||||
: rightBoundary + xOffset
|
: rightBoundary + xOffset
|
||||||
|
|
||||||
const yOffset = (this.offset && this.offset.y) || 0
|
const yOffset = this.offset?.y || 0
|
||||||
translateY = origin.y + vertOffset + yOffset
|
translateY = origin.y + vertOffset + yOffset
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -298,7 +295,7 @@ const Popover = {
|
||||||
}, 0)
|
}, 0)
|
||||||
const wasHidden = this.hidden
|
const wasHidden = this.hidden
|
||||||
this.hidden = false
|
this.hidden = false
|
||||||
this.parentPopover && this.parentPopover.onChildPopoverState(this, true)
|
this.parentPopover?.onChildPopoverState(this, true)
|
||||||
if (this.trigger === 'click' || this.stayOnClick) {
|
if (this.trigger === 'click' || this.stayOnClick) {
|
||||||
document.addEventListener('click', this.onClickOutside)
|
document.addEventListener('click', this.onClickOutside)
|
||||||
}
|
}
|
||||||
|
|
@ -316,7 +313,7 @@ const Popover = {
|
||||||
if (this.disabled) return
|
if (this.disabled) return
|
||||||
if (!this.hidden) this.$emit('close')
|
if (!this.hidden) this.$emit('close')
|
||||||
this.hidden = true
|
this.hidden = true
|
||||||
this.parentPopover && this.parentPopover.onChildPopoverState(this, false)
|
this.parentPopover?.onChildPopoverState(this, false)
|
||||||
if (this.trigger === 'click') {
|
if (this.trigger === 'click') {
|
||||||
document.removeEventListener('click', this.onClickOutside)
|
document.removeEventListener('click', this.onClickOutside)
|
||||||
}
|
}
|
||||||
|
|
@ -366,7 +363,7 @@ const Popover = {
|
||||||
onClickOutside(e) {
|
onClickOutside(e) {
|
||||||
if (this.disableClickOutside) return
|
if (this.disableClickOutside) return
|
||||||
if (this.hidden) return
|
if (this.hidden) return
|
||||||
if (this.$refs.content && this.$refs.content.contains(e.target)) return
|
if (this.$refs.content?.contains(e.target)) return
|
||||||
if (this.$el.contains(e.target)) return
|
if (this.$el.contains(e.target)) return
|
||||||
if (this.childrenShown.size > 0) return
|
if (this.childrenShown.size > 0) return
|
||||||
this.hidePopover()
|
this.hidePopover()
|
||||||
|
|
|
||||||
|
|
@ -316,7 +316,7 @@ const PostStatusForm = {
|
||||||
},
|
},
|
||||||
// -Edit
|
// -Edit
|
||||||
isEdit() {
|
isEdit() {
|
||||||
return typeof this.statusId !== 'undefined' && this.statusId.trim() !== ''
|
return this.statusId !== undefined && this.statusId.trim() !== ''
|
||||||
},
|
},
|
||||||
// -Reply
|
// -Reply
|
||||||
isReply() {
|
isReply() {
|
||||||
|
|
@ -378,8 +378,8 @@ const PostStatusForm = {
|
||||||
set(value) {
|
set(value) {
|
||||||
if (value) {
|
if (value) {
|
||||||
this.newStatus.quote = {}
|
this.newStatus.quote = {}
|
||||||
this.newStatus.quote.thread = value
|
this.newStatus.quote.thread = true
|
||||||
this.newStatus.quote.id = value ? this.repliedStatus.id : ''
|
this.newStatus.quote.id = this.repliedStatus.id
|
||||||
} else {
|
} else {
|
||||||
this.newStatus.quote = null
|
this.newStatus.quote = null
|
||||||
}
|
}
|
||||||
|
|
@ -509,9 +509,7 @@ const PostStatusForm = {
|
||||||
|
|
||||||
// Error handling
|
// Error handling
|
||||||
pollContentError() {
|
pollContentError() {
|
||||||
return (
|
return this.pollFormVisible && this.newStatus.poll?.error
|
||||||
this.pollFormVisible && this.newStatus.poll && this.newStatus.poll.error
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Featureset detection
|
// Featureset detection
|
||||||
|
|
@ -622,7 +620,7 @@ const PostStatusForm = {
|
||||||
this.newStatus.quote = null
|
this.newStatus.quote = null
|
||||||
this.newStatus.nsfw = this.defaultNewStatus.nsfw
|
this.newStatus.nsfw = this.defaultNewStatus.nsfw
|
||||||
this.newStatus.mediaDescriptions = {}
|
this.newStatus.mediaDescriptions = {}
|
||||||
this.$refs.mediaUpload && this.$refs.mediaUpload.clearFile()
|
this.$refs.mediaUpload?.clearFile()
|
||||||
if (this.preserveFocus) {
|
if (this.preserveFocus) {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.$refs.textarea.focus()
|
this.$refs.textarea.focus()
|
||||||
|
|
@ -812,7 +810,7 @@ const PostStatusForm = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
fileDrop(e) {
|
fileDrop(e) {
|
||||||
if (e.dataTransfer && e.dataTransfer.types.includes('Files')) {
|
if (e.dataTransfer?.types.includes('Files')) {
|
||||||
e.preventDefault() // allow dropping text like before
|
e.preventDefault() // allow dropping text like before
|
||||||
this.dropFiles = e.dataTransfer.files
|
this.dropFiles = e.dataTransfer.files
|
||||||
clearTimeout(this.dropStopTimeout)
|
clearTimeout(this.dropStopTimeout)
|
||||||
|
|
@ -829,7 +827,7 @@ const PostStatusForm = {
|
||||||
},
|
},
|
||||||
fileDrag(e) {
|
fileDrag(e) {
|
||||||
e.dataTransfer.dropEffect = this.uploadFileLimitReached ? 'none' : 'copy'
|
e.dataTransfer.dropEffect = this.uploadFileLimitReached ? 'none' : 'copy'
|
||||||
if (e.dataTransfer && e.dataTransfer.types.includes('Files')) {
|
if (e.dataTransfer?.types.includes('Files')) {
|
||||||
clearTimeout(this.dropStopTimeout)
|
clearTimeout(this.dropStopTimeout)
|
||||||
this.showDropIcon = 'show'
|
this.showDropIcon = 'show'
|
||||||
}
|
}
|
||||||
|
|
@ -962,6 +960,7 @@ const PostStatusForm = {
|
||||||
// Quote
|
// Quote
|
||||||
toggleQuoteForm() {
|
toggleQuoteForm() {
|
||||||
// This is for the "attach quote" button
|
// This is for the "attach quote" button
|
||||||
|
if (this.newStatus.quote?.thread) return
|
||||||
if (!this.hasQuote) {
|
if (!this.hasQuote) {
|
||||||
this.newStatus.quote = {}
|
this.newStatus.quote = {}
|
||||||
this.newStatus.quote.thread = false
|
this.newStatus.quote.thread = false
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
|
v-if="initialized"
|
||||||
ref="form"
|
ref="form"
|
||||||
class="post-status-form"
|
class="post-status-form"
|
||||||
v-if="initialized"
|
|
||||||
>
|
>
|
||||||
<form
|
<form
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
|
|
@ -176,12 +176,12 @@
|
||||||
<input
|
<input
|
||||||
v-if="mentionsLine"
|
v-if="mentionsLine"
|
||||||
:value="mentionsLineReadOnly ? mentionsString : newStatus.mentionsLine"
|
:value="mentionsLineReadOnly ? mentionsString : newStatus.mentionsLine"
|
||||||
@change="onMentionsLineUpdate"
|
|
||||||
type="text"
|
type="text"
|
||||||
:placeholder="$t('post_status.mentions_line')"
|
:placeholder="$t('post_status.mentions_line')"
|
||||||
:disabled="mentionsLineReadOnly || (posting && !optimisticPosting)"
|
:disabled="mentionsLineReadOnly || (posting && !optimisticPosting)"
|
||||||
size="1"
|
size="1"
|
||||||
class="input mentions-input form-post-mentions unstyled"
|
class="input mentions-input form-post-mentions unstyled"
|
||||||
|
@change="onMentionsLineUpdate"
|
||||||
>
|
>
|
||||||
<EmojiInput
|
<EmojiInput
|
||||||
ref="emoji-input"
|
ref="emoji-input"
|
||||||
|
|
@ -234,7 +234,7 @@
|
||||||
ref="scopeSelector"
|
ref="scopeSelector"
|
||||||
:show-all="showAllScopes"
|
:show-all="showAllScopes"
|
||||||
:user-default="userDefaultScope"
|
:user-default="userDefaultScope"
|
||||||
:original-scope="newStatus.visibility"
|
:original-scope="repliedStatus?.visibility"
|
||||||
:initial-scope="newStatus.visibility"
|
:initial-scope="newStatus.visibility"
|
||||||
@change="changeVis"
|
@change="changeVis"
|
||||||
/>
|
/>
|
||||||
|
|
@ -271,14 +271,14 @@
|
||||||
<PollForm
|
<PollForm
|
||||||
v-if="pollsAvailable"
|
v-if="pollsAvailable"
|
||||||
ref="pollForm"
|
ref="pollForm"
|
||||||
:visible="pollFormVisible"
|
|
||||||
v-model="newStatus.poll"
|
v-model="newStatus.poll"
|
||||||
|
:visible="pollFormVisible"
|
||||||
/>
|
/>
|
||||||
<QuoteForm
|
<QuoteForm
|
||||||
v-if="quotingAvailable"
|
v-if="quotingAvailable"
|
||||||
|
:id="newStatus.quote?.id"
|
||||||
ref="quoteForm"
|
ref="quoteForm"
|
||||||
:visible="quoteFormVisible"
|
:visible="quoteFormVisible"
|
||||||
:id="newStatus.quote?.id"
|
|
||||||
:url="newStatus.quote?.url"
|
:url="newStatus.quote?.url"
|
||||||
@update:url="url => newStatus.quote.url = url"
|
@update:url="url => newStatus.quote.url = url"
|
||||||
@update:id="id => newStatus.quote.id = id"
|
@update:id="id => newStatus.quote.id = id"
|
||||||
|
|
|
||||||
|
|
@ -40,9 +40,7 @@ const PostStatusModal = {
|
||||||
},
|
},
|
||||||
isFormVisible(val) {
|
isFormVisible(val) {
|
||||||
if (val) {
|
if (val) {
|
||||||
this.$nextTick(
|
this.$nextTick(() => this.$el?.querySelector('textarea').focus())
|
||||||
() => this.$el && this.$el.querySelector('textarea').focus(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,7 @@ export default {
|
||||||
type: 'statuses',
|
type: 'statuses',
|
||||||
})
|
})
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
if (data?.statuses && data.statuses.length === 1) {
|
if (data?.statuses?.length === 1) {
|
||||||
this.$emit('update:id', data.statuses[0].id)
|
this.$emit('update:id', data.statuses[0].id)
|
||||||
} else {
|
} else {
|
||||||
this.handleError(true)
|
this.handleError(true)
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
size="1"
|
size="1"
|
||||||
class="input"
|
class="input"
|
||||||
:placeholder="$t('post_status.quote_url')"
|
:placeholder="$t('post_status.quote_url')"
|
||||||
|
:aria-label="$t('post_status.quote_url')"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<Quote
|
<Quote
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
{{ label }}
|
{{ label }}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
v-if="typeof fallback !== 'undefined'"
|
v-if="fallback !== undefined"
|
||||||
:id="name + '-o'"
|
:id="name + '-o'"
|
||||||
:aria-labelledby="name + '-label'"
|
:aria-labelledby="name + '-label'"
|
||||||
class="input -checkbox opt visible-for-screenreader-only"
|
class="input -checkbox opt visible-for-screenreader-only"
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
@change="$emit('update:modelValue', !present ? fallback : undefined)"
|
@change="$emit('update:modelValue', !present ? fallback : undefined)"
|
||||||
>
|
>
|
||||||
<label
|
<label
|
||||||
v-if="typeof fallback !== 'undefined'"
|
v-if="fallback !== undefined"
|
||||||
class="opt-l"
|
class="opt-l"
|
||||||
:for="name + '-o'"
|
:for="name + '-o'"
|
||||||
:aria-hidden="true"
|
:aria-hidden="true"
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,7 @@ const registration = {
|
||||||
this.user.captcha_answer_data = this.captcha.answer_data
|
this.user.captcha_answer_data = this.captcha.answer_data
|
||||||
if (this.user.language) {
|
if (this.user.language) {
|
||||||
this.user.language = localeService.internalToBackendLocaleMulti(
|
this.user.language = localeService.internalToBackendLocaleMulti(
|
||||||
this.user.language.filter((k) => k),
|
this.user.language.filter(Boolean),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -154,7 +154,7 @@ const registration = {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
replaceNewlines(str) {
|
replaceNewlines(str) {
|
||||||
return str.replace(/\s*\n\s*/g, ' \n')
|
return str.replaceAll(/\s*\n\s*/g, ' \n')
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -181,7 +181,7 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Processor to use with html_tree_converter
|
// Processor to use with html_tree_converter
|
||||||
const processItem = (item, index, array, what) => {
|
const processItem = (item, index, array) => {
|
||||||
// Handle text nodes - just add emoji
|
// Handle text nodes - just add emoji
|
||||||
if (typeof item === 'string') {
|
if (typeof item === 'string') {
|
||||||
const emptyText = item.trim() === ''
|
const emptyText = item.trim() === ''
|
||||||
|
|
@ -251,18 +251,14 @@ export default {
|
||||||
return ['', [mentionsLinePadding, renderImage(opener)], '']
|
return ['', [mentionsLinePadding, renderImage(opener)], '']
|
||||||
} else if (Tag === 'a' && this.handleLinks) {
|
} else if (Tag === 'a' && this.handleLinks) {
|
||||||
// replace mentions with MentionLink
|
// replace mentions with MentionLink
|
||||||
if (fullAttrs.class && fullAttrs.class.includes('mention')) {
|
if (fullAttrs.class?.includes('mention')) {
|
||||||
// Handling mentions here
|
// Handling mentions here
|
||||||
return renderMention(attrs, children)
|
return renderMention(attrs, children)
|
||||||
} else {
|
} else {
|
||||||
currentMentions = null
|
currentMentions = null
|
||||||
}
|
}
|
||||||
} else if (Tag === 'span') {
|
} else if (Tag === 'span') {
|
||||||
if (
|
if (this.handleLinks && fullAttrs.class?.includes('h-card')) {
|
||||||
this.handleLinks &&
|
|
||||||
fullAttrs.class &&
|
|
||||||
fullAttrs.class.includes('h-card')
|
|
||||||
) {
|
|
||||||
return ['', children.map(processItem), '']
|
return ['', children.map(processItem), '']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -281,7 +277,7 @@ export default {
|
||||||
|
|
||||||
// Processor for back direction (for finding "last" stuff, just easier this way)
|
// Processor for back direction (for finding "last" stuff, just easier this way)
|
||||||
let encounteredTextReverse = false
|
let encounteredTextReverse = false
|
||||||
const processItemReverse = (item, index, array, what) => {
|
const processItemReverse = (item, index, array) => {
|
||||||
// Handle text nodes - just add emoji
|
// Handle text nodes - just add emoji
|
||||||
if (typeof item === 'string') {
|
if (typeof item === 'string') {
|
||||||
const emptyText = item.trim() === ''
|
const emptyText = item.trim() === ''
|
||||||
|
|
@ -300,7 +296,7 @@ export default {
|
||||||
const attrs = getAttrs(opener, () => true)
|
const attrs = getAttrs(opener, () => true)
|
||||||
// should only be this
|
// should only be this
|
||||||
if (
|
if (
|
||||||
(fullAttrs.class && fullAttrs.class.includes('hashtag')) || // Pleroma style
|
fullAttrs.class?.includes('hashtag') || // Pleroma style
|
||||||
fullAttrs.rel === 'tag' // Mastodon style
|
fullAttrs.rel === 'tag' // Mastodon style
|
||||||
) {
|
) {
|
||||||
return renderHashtag(attrs, children, encounteredTextReverse)
|
return renderHashtag(attrs, children, encounteredTextReverse)
|
||||||
|
|
@ -385,13 +381,13 @@ export default {
|
||||||
x ? 'mfm-spinX' : null,
|
x ? 'mfm-spinX' : null,
|
||||||
y ? 'mfm-spinY' : null,
|
y ? 'mfm-spinY' : null,
|
||||||
'mfm-spin',
|
'mfm-spin',
|
||||||
].filter((a) => a)[0]
|
].filter(Boolean)[0]
|
||||||
|
|
||||||
const direction = [
|
const direction = [
|
||||||
alternate ? 'alternate' : null,
|
alternate ? 'alternate' : null,
|
||||||
left ? 'reverse' : null,
|
left ? 'reverse' : null,
|
||||||
'normal',
|
'normal',
|
||||||
].filter((a) => a)[0]
|
].filter(Boolean)[0]
|
||||||
|
|
||||||
newAttrs.style = [
|
newAttrs.style = [
|
||||||
`animation-name: ${anim}`,
|
`animation-name: ${anim}`,
|
||||||
|
|
@ -479,7 +475,7 @@ export default {
|
||||||
>
|
>
|
||||||
{this.collapse
|
{this.collapse
|
||||||
? pass2.map((x) => {
|
? pass2.map((x) => {
|
||||||
if (typeof x === 'string') return x.replace(/\n/g, ' ')
|
if (typeof x === 'string') return x.replaceAll('\n', ' ')
|
||||||
if (!Array.isArray(x)) return x
|
if (!Array.isArray(x)) return x
|
||||||
return x.map((y) => (y.type === 'br' ? ' ' : y))
|
return x.map((y) => (y.type === 'br' ? ' ' : y))
|
||||||
})
|
})
|
||||||
|
|
@ -547,8 +543,8 @@ export const preProcessPerLine = (html, greentext) => {
|
||||||
(string.includes('>') || string.includes('<'))
|
(string.includes('>') || string.includes('<'))
|
||||||
) {
|
) {
|
||||||
const cleanedString = string
|
const cleanedString = string
|
||||||
.replace(/<[^>]+?>/gi, '') // remove all tags
|
.replaceAll(/<[^>]+?>/gi, '') // remove all tags
|
||||||
.replace(/@\w+/gi, '') // remove mentions (even failed ones)
|
.replaceAll(/@\w+/gi, '') // remove mentions (even failed ones)
|
||||||
.trim()
|
.trim()
|
||||||
if (cleanedString.startsWith('>')) {
|
if (cleanedString.startsWith('>')) {
|
||||||
return `<span class='greentext'>${string}</span>`
|
return `<span class='greentext'>${string}</span>`
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
{{ label }}
|
{{ label }}
|
||||||
</label>
|
</label>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
v-if="typeof fallback !== 'undefined'"
|
v-if="fallback !== undefined"
|
||||||
:model-value="present"
|
:model-value="present"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
class="opt"
|
class="opt"
|
||||||
|
|
|
||||||
|
|
@ -122,7 +122,7 @@ const Search = {
|
||||||
return 'statuses'
|
return 'statuses'
|
||||||
},
|
},
|
||||||
lastHistoryRecord(hashtag) {
|
lastHistoryRecord(hashtag) {
|
||||||
return hashtag.history && hashtag.history[0]
|
return hashtag.history?.[0]
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -122,7 +122,7 @@ const EmojiTab = {
|
||||||
return this.refreshPackList()
|
return this.refreshPackList()
|
||||||
} else {
|
} else {
|
||||||
this.displayError(resp.error)
|
this.displayError(resp.error)
|
||||||
return Promise.reject(resp)
|
throw new Error(resp)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
|
@ -139,7 +139,7 @@ const EmojiTab = {
|
||||||
return this.refreshPackList()
|
return this.refreshPackList()
|
||||||
} else {
|
} else {
|
||||||
this.displayError(resp.error)
|
this.displayError(resp.error)
|
||||||
return Promise.reject(resp)
|
throw new Error(resp)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
|
@ -239,7 +239,7 @@ const EmojiTab = {
|
||||||
return this.refreshPackList()
|
return this.refreshPackList()
|
||||||
} else {
|
} else {
|
||||||
this.displayError(resp.error)
|
this.displayError(resp.error)
|
||||||
return Promise.reject(resp)
|
throw new Error(resp)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
|
@ -259,7 +259,7 @@ const EmojiTab = {
|
||||||
return this.refreshPackList()
|
return this.refreshPackList()
|
||||||
} else {
|
} else {
|
||||||
this.displayError(resp.error)
|
this.displayError(resp.error)
|
||||||
return Promise.reject(resp)
|
throw new Error(resp)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
|
@ -280,7 +280,7 @@ const EmojiTab = {
|
||||||
return this.refreshPackList()
|
return this.refreshPackList()
|
||||||
} else {
|
} else {
|
||||||
this.displayError(resp.error)
|
this.displayError(resp.error)
|
||||||
return Promise.reject(resp)
|
throw new Error(resp)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
|
@ -300,7 +300,7 @@ const EmojiTab = {
|
||||||
sortPackFiles(nameOfPack) {
|
sortPackFiles(nameOfPack) {
|
||||||
// Sort by key
|
// Sort by key
|
||||||
const sorted = Object.keys(this.knownPacks[nameOfPack].files)
|
const sorted = Object.keys(this.knownPacks[nameOfPack].files)
|
||||||
.sort()
|
.sort((a, b) => a.localeCompare(b))
|
||||||
.reduce((acc, key) => {
|
.reduce((acc, key) => {
|
||||||
if (key.length === 0) return acc
|
if (key.length === 0) return acc
|
||||||
acc[key] = this.knownPacks[nameOfPack].files[key]
|
acc[key] = this.knownPacks[nameOfPack].files[key]
|
||||||
|
|
|
||||||
|
|
@ -262,7 +262,7 @@ export default {
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
if (resp.error !== undefined) {
|
if (resp.error !== undefined) {
|
||||||
this.$emit('displayError', resp.error)
|
this.$emit('displayError', resp.error)
|
||||||
return Promise.reject(resp.error)
|
throw new Error(resp.error)
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp.json()
|
return resp.json()
|
||||||
|
|
|
||||||
|
|
@ -28,12 +28,12 @@ export default {
|
||||||
methods: {
|
methods: {
|
||||||
...Setting.methods,
|
...Setting.methods,
|
||||||
getValue(e) {
|
getValue(e) {
|
||||||
if (!this.truncate === 1) {
|
if (this.truncate === 1) {
|
||||||
return Number.parseInt(e.target.value)
|
return Number.parseInt(e.target.value)
|
||||||
} else if (this.truncate > 1) {
|
} else if (this.truncate > 1) {
|
||||||
return Math.trunc(e.target.value / this.truncate) * this.truncate
|
return Math.trunc(e.target.value / this.truncate) * this.truncate
|
||||||
}
|
}
|
||||||
return parseFloat(e.target.value)
|
return Number.parseFloat(e.target.value)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -180,7 +180,7 @@ export default {
|
||||||
[
|
[
|
||||||
'admin_dash',
|
'admin_dash',
|
||||||
'temp_overrides',
|
'temp_overrides',
|
||||||
...this.canonPath.map((p) => p.replace(/\./g, '_DOT_')),
|
...this.canonPath.map((p) => p.replaceAll('.', '_DOT_')),
|
||||||
'label',
|
'label',
|
||||||
].join('.'),
|
].join('.'),
|
||||||
)
|
)
|
||||||
|
|
@ -199,7 +199,7 @@ export default {
|
||||||
[
|
[
|
||||||
'admin_dash',
|
'admin_dash',
|
||||||
'temp_overrides',
|
'temp_overrides',
|
||||||
...this.canonPath.map((p) => p.replace(/\./g, '_DOT_')),
|
...this.canonPath.map((p) => p.replaceAll('.', '_DOT_')),
|
||||||
'description',
|
'description',
|
||||||
].join('.'),
|
].join('.'),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,10 @@ export default {
|
||||||
return this.$t(['settings', 'units', this.unitSet, value].join('.'))
|
return this.$t(['settings', 'units', this.unitSet, value].join('.'))
|
||||||
},
|
},
|
||||||
updateValue(e) {
|
updateValue(e) {
|
||||||
this.configSink(this.path, parseFloat(e.target.value) + this.stateUnit)
|
this.configSink(
|
||||||
|
this.path,
|
||||||
|
Number.parseFloat(e.target.value) + this.stateUnit,
|
||||||
|
)
|
||||||
},
|
},
|
||||||
updateUnit(e) {
|
updateUnit(e) {
|
||||||
let value = this.stateValue
|
let value = this.stateValue
|
||||||
|
|
|
||||||
|
|
@ -236,7 +236,7 @@ const AppearanceTab = {
|
||||||
},
|
},
|
||||||
stylePalettes() {
|
stylePalettes() {
|
||||||
const ruleset = useInterfaceStore().styleDataUsed || []
|
const ruleset = useInterfaceStore().styleDataUsed || []
|
||||||
if (!ruleset?.length === 0) return
|
if (ruleset.length === 0) return
|
||||||
const meta = ruleset.find((x) => x.component === '@meta')
|
const meta = ruleset.find((x) => x.component === '@meta')
|
||||||
const result = ruleset
|
const result = ruleset
|
||||||
.filter((x) => x.component.startsWith('@palette'))
|
.filter((x) => x.component.startsWith('@palette'))
|
||||||
|
|
@ -257,7 +257,7 @@ const AppearanceTab = {
|
||||||
|
|
||||||
const result = {
|
const result = {
|
||||||
name: `${meta.directives.name || this.$t('settings.style.themes3.palette.imported')}: ${variant}`,
|
name: `${meta.directives.name || this.$t('settings.style.themes3.palette.imported')}: ${variant}`,
|
||||||
key: `style.${variant.toLowerCase().replace(/ /g, '_')}`,
|
key: `style.${variant.toLowerCase().replaceAll(' ', '_')}`,
|
||||||
bg,
|
bg,
|
||||||
fg,
|
fg,
|
||||||
text,
|
text,
|
||||||
|
|
@ -277,7 +277,7 @@ const AppearanceTab = {
|
||||||
return !window.IntersectionObserver
|
return !window.IntersectionObserver
|
||||||
},
|
},
|
||||||
instanceWallpaper() {
|
instanceWallpaper() {
|
||||||
useInstanceStore().instanceIdentity.background
|
return useInstanceStore().instanceIdentity.background
|
||||||
},
|
},
|
||||||
instanceWallpaperUsed() {
|
instanceWallpaperUsed() {
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import { mapActions, mapState } from 'pinia'
|
import { mapState } from 'pinia'
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
|
||||||
|
|
||||||
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||||
import Select from 'src/components/select/select.vue'
|
import Select from 'src/components/select/select.vue'
|
||||||
|
|
@ -12,7 +11,6 @@ import UnitSetting from '../helpers/unit_setting.vue'
|
||||||
|
|
||||||
import { useInstanceStore } from 'src/stores/instance.js'
|
import { useInstanceStore } from 'src/stores/instance.js'
|
||||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
|
||||||
|
|
||||||
const ClutterTab = {
|
const ClutterTab = {
|
||||||
components: {
|
components: {
|
||||||
|
|
@ -33,120 +31,6 @@ const ClutterTab = {
|
||||||
store.instanceIdentity.showInstanceSpecificPanel &&
|
store.instanceIdentity.showInstanceSpecificPanel &&
|
||||||
store.instanceIdentity.instanceSpecificPanelContent,
|
store.instanceIdentity.instanceSpecificPanelContent,
|
||||||
}),
|
}),
|
||||||
...mapState(useSyncConfigStore, {
|
|
||||||
muteFilters: (store) =>
|
|
||||||
Object.entries(store.prefsStorage.simple.muteFilters),
|
|
||||||
muteFiltersObject: (store) => store.prefsStorage.simple.muteFilters,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
...mapActions(useSyncConfigStore, [
|
|
||||||
'setSimplePrefAndSave',
|
|
||||||
'unsetSimplePrefAndSave',
|
|
||||||
'pushSyncConfig',
|
|
||||||
]),
|
|
||||||
getDatetimeLocal(timestamp) {
|
|
||||||
const date = new Date(timestamp)
|
|
||||||
const fmt = new Intl.NumberFormat('en-US', { minimumIntegerDigits: 2 })
|
|
||||||
const datetime = [
|
|
||||||
date.getFullYear(),
|
|
||||||
'-',
|
|
||||||
fmt.format(date.getMonth() + 1),
|
|
||||||
'-',
|
|
||||||
fmt.format(date.getDate()),
|
|
||||||
'T',
|
|
||||||
fmt.format(date.getHours()),
|
|
||||||
':',
|
|
||||||
fmt.format(date.getMinutes()),
|
|
||||||
].join('')
|
|
||||||
return datetime
|
|
||||||
},
|
|
||||||
checkRegexValid(id) {
|
|
||||||
const filter = this.muteFiltersObject[id]
|
|
||||||
if (filter.type !== 'regexp') return true
|
|
||||||
if (filter.type !== 'user_regexp') return true
|
|
||||||
const { value } = filter
|
|
||||||
let valid = true
|
|
||||||
try {
|
|
||||||
new RegExp(value)
|
|
||||||
} catch {
|
|
||||||
valid = false
|
|
||||||
console.error('Invalid RegExp: ' + value)
|
|
||||||
}
|
|
||||||
return valid
|
|
||||||
},
|
|
||||||
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.setSimplePrefAndSave({ path: 'muteFilters.' + newId, value: filter })
|
|
||||||
this.pushSyncConfig()
|
|
||||||
},
|
|
||||||
exportFilter(id) {
|
|
||||||
this.exportedFilter = { ...this.muteFiltersDraftObject[id] }
|
|
||||||
delete this.exportedFilter.order
|
|
||||||
this.filterExporter.exportData()
|
|
||||||
},
|
|
||||||
importFilter() {
|
|
||||||
this.filterImporter.importData()
|
|
||||||
},
|
|
||||||
copyFilter(id) {
|
|
||||||
const filter = { ...this.muteFiltersDraftObject[id] }
|
|
||||||
const newId = uuidv4()
|
|
||||||
|
|
||||||
this.muteFiltersDraftObject[newId] = filter
|
|
||||||
this.setSimplePrefAndSave({ path: 'muteFilters.' + newId, value: filter })
|
|
||||||
this.pushSyncConfig()
|
|
||||||
},
|
|
||||||
deleteFilter(id) {
|
|
||||||
delete this.muteFiltersDraftObject[id]
|
|
||||||
this.unsetSimplePrefAndSave({ path: 'muteFilters.' + id, value: null })
|
|
||||||
this.pushSyncConfig()
|
|
||||||
},
|
|
||||||
purgeExpiredFilters() {
|
|
||||||
this.muteFiltersExpired.forEach(([id]) => {
|
|
||||||
delete this.muteFiltersDraftObject[id]
|
|
||||||
this.unsetSimplePrefAndSave({ path: 'muteFilters.' + id, value: null })
|
|
||||||
})
|
|
||||||
this.pushSyncConfig()
|
|
||||||
},
|
|
||||||
updateFilter(id, field, value) {
|
|
||||||
const filter = { ...this.muteFiltersDraftObject[id] }
|
|
||||||
if (field === 'expires-never') {
|
|
||||||
if (!value) {
|
|
||||||
const offset = 1000 * 60 * 60 * 24 * 14 // 2 weeks
|
|
||||||
const date = Date.now() + offset
|
|
||||||
filter.expires = date
|
|
||||||
} else {
|
|
||||||
filter.expires = null
|
|
||||||
}
|
|
||||||
} else if (field === 'expires') {
|
|
||||||
const parsed = Date.parse(value)
|
|
||||||
filter.expires = parsed.valueOf()
|
|
||||||
} else {
|
|
||||||
filter[field] = value
|
|
||||||
}
|
|
||||||
this.muteFiltersDraftObject[id] = filter
|
|
||||||
this.muteFiltersDraftDirty[id] = true
|
|
||||||
},
|
|
||||||
saveFilter(id) {
|
|
||||||
this.setSimplePrefAndSave({
|
|
||||||
path: 'muteFilters.' + id,
|
|
||||||
value: this.muteFiltersDraftObject[id],
|
|
||||||
})
|
|
||||||
this.pushSyncConfig()
|
|
||||||
this.muteFiltersDraftDirty[id] = false
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
// Updating nested properties
|
// Updating nested properties
|
||||||
watch: {
|
watch: {
|
||||||
|
|
|
||||||
|
|
@ -190,21 +190,24 @@ const FilteringTab = {
|
||||||
}
|
}
|
||||||
return valid
|
return valid
|
||||||
},
|
},
|
||||||
createFilter(
|
createFilter(filter) {
|
||||||
filter = {
|
const newId = uuidv4()
|
||||||
|
const newFilter = {
|
||||||
type: 'word',
|
type: 'word',
|
||||||
value: '',
|
value: '',
|
||||||
name: 'New Filter',
|
name: 'New Filter',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
expires: null,
|
expires: null,
|
||||||
hide: false,
|
hide: false,
|
||||||
},
|
...filter,
|
||||||
) {
|
}
|
||||||
const newId = uuidv4()
|
|
||||||
|
|
||||||
filter.order = this.muteFilters.length + 2
|
newFilter.order = this.muteFilters.length + 2
|
||||||
this.muteFiltersDraftObject[newId] = filter
|
this.muteFiltersDraftObject[newId] = newFilter
|
||||||
this.setSimplePrefAndSave({ path: 'muteFilters.' + newId, value: filter })
|
this.setSimplePrefAndSave({
|
||||||
|
path: 'muteFilters.' + newId,
|
||||||
|
value: newFilter,
|
||||||
|
})
|
||||||
},
|
},
|
||||||
exportFilter(id) {
|
exportFilter(id) {
|
||||||
this.exportedFilter = { ...this.muteFiltersDraftObject[id] }
|
this.exportedFilter = { ...this.muteFiltersDraftObject[id] }
|
||||||
|
|
|
||||||
|
|
@ -153,7 +153,6 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #item="{item}">
|
<template #item="{item}">
|
||||||
{{ item }}
|
|
||||||
<DomainMuteCard :domain="item" />
|
<DomainMuteCard :domain="item" />
|
||||||
</template>
|
</template>
|
||||||
<template #empty>
|
<template #empty>
|
||||||
|
|
|
||||||
|
|
@ -319,7 +319,7 @@ export default {
|
||||||
return useInterfaceStore().themeDataUsed
|
return useInterfaceStore().themeDataUsed
|
||||||
},
|
},
|
||||||
shadowsAvailable() {
|
shadowsAvailable() {
|
||||||
return Object.keys(DEFAULT_SHADOWS).sort()
|
return Object.keys(DEFAULT_SHADOWS).sort((a, b) => a.localeCompare(b))
|
||||||
},
|
},
|
||||||
currentShadowOverriden: {
|
currentShadowOverriden: {
|
||||||
get() {
|
get() {
|
||||||
|
|
@ -346,7 +346,7 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
currentShadowFallback() {
|
currentShadowFallback() {
|
||||||
return (this.previewTheme.shadows || {})[this.shadowSelected]
|
return this.previewTheme.shadows?.[this.shadowSelected]
|
||||||
},
|
},
|
||||||
currentShadow: {
|
currentShadow: {
|
||||||
get() {
|
get() {
|
||||||
|
|
@ -425,8 +425,8 @@ export default {
|
||||||
this.dismissWarning()
|
this.dismissWarning()
|
||||||
const version =
|
const version =
|
||||||
origin === 'localStorage' && !theme.colors ? 'l1' : fileVersion
|
origin === 'localStorage' && !theme.colors ? 'l1' : fileVersion
|
||||||
const snapshotEngineVersion = (theme || {}).themeEngineVersion
|
const snapshotEngineVersion = theme?.themeEngineVersion
|
||||||
const themeEngineVersion = (source || {}).themeEngineVersion || 2
|
const themeEngineVersion = source?.themeEngineVersion || 2
|
||||||
const versionsMatch = themeEngineVersion === CURRENT_VERSION
|
const versionsMatch = themeEngineVersion === CURRENT_VERSION
|
||||||
const sourceSnapshotMismatch =
|
const sourceSnapshotMismatch =
|
||||||
theme !== undefined &&
|
theme !== undefined &&
|
||||||
|
|
@ -611,7 +611,7 @@ export default {
|
||||||
*/
|
*/
|
||||||
normalizeLocalState(theme, version = 0, source, forceSource = false) {
|
normalizeLocalState(theme, version = 0, source, forceSource = false) {
|
||||||
let input
|
let input
|
||||||
if (typeof source !== 'undefined') {
|
if (source !== undefined) {
|
||||||
if (forceSource || source?.themeEngineVersion === CURRENT_VERSION) {
|
if (forceSource || source?.themeEngineVersion === CURRENT_VERSION) {
|
||||||
input = source
|
input = source
|
||||||
version = source.themeEngineVersion
|
version = source.themeEngineVersion
|
||||||
|
|
|
||||||
|
|
@ -181,14 +181,14 @@
|
||||||
name="accentColor"
|
name="accentColor"
|
||||||
:fallback="previewTheme.colors?.link"
|
:fallback="previewTheme.colors?.link"
|
||||||
:label="$t('settings.accent')"
|
:label="$t('settings.accent')"
|
||||||
:show-optional-checkbox="typeof linkColorLocal !== 'undefined'"
|
:show-optional-checkbox="linkColorLocal !== undefined"
|
||||||
/>
|
/>
|
||||||
<ColorInput
|
<ColorInput
|
||||||
v-model="linkColorLocal"
|
v-model="linkColorLocal"
|
||||||
name="linkColor"
|
name="linkColor"
|
||||||
:fallback="previewTheme.colors?.accent"
|
:fallback="previewTheme.colors?.accent"
|
||||||
:label="$t('settings.links')"
|
:label="$t('settings.links')"
|
||||||
:show-optional-checkbox="typeof accentColorLocal !== 'undefined'"
|
:show-optional-checkbox="accentColorLocal !== undefined"
|
||||||
/>
|
/>
|
||||||
<ContrastRatio :contrast="previewContrast.bgLink" />
|
<ContrastRatio :contrast="previewContrast.bgLink" />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ const SideDrawer = {
|
||||||
this.toggleDrawer,
|
this.toggleDrawer,
|
||||||
)
|
)
|
||||||
|
|
||||||
if (this.currentUser && this.currentUser.locked) {
|
if (this.currentUser?.locked) {
|
||||||
this.$store.dispatch('startFetchingFollowRequests')
|
this.$store.dispatch('startFetchingFollowRequests')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -128,8 +128,7 @@ const Status = {
|
||||||
computed: {
|
computed: {
|
||||||
showReasonMutedThread() {
|
showReasonMutedThread() {
|
||||||
return (
|
return (
|
||||||
(this.status.thread_muted ||
|
(this.status.thread_muted || this.status.reblog?.thread_muted) &&
|
||||||
(this.status.reblog && this.status.reblog.thread_muted)) &&
|
|
||||||
!this.inConversation
|
!this.inConversation
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
@ -263,7 +262,7 @@ const Status = {
|
||||||
this.muteFilterHits.length > 0 ? 'filtered' : null,
|
this.muteFilterHits.length > 0 ? 'filtered' : null,
|
||||||
this.muteBotStatuses && this.botStatus ? 'bot' : null,
|
this.muteBotStatuses && this.botStatus ? 'bot' : null,
|
||||||
this.muteSensitiveStatuses && this.sensitiveStatus ? 'nsfw' : null,
|
this.muteSensitiveStatuses && this.sensitiveStatus ? 'nsfw' : null,
|
||||||
].filter((_) => _)
|
].filter(Boolean)
|
||||||
},
|
},
|
||||||
muteLocalized() {
|
muteLocalized() {
|
||||||
if (this.muteReasons.length === 0) return null
|
if (this.muteReasons.length === 0) return null
|
||||||
|
|
|
||||||
|
|
@ -20,9 +20,9 @@
|
||||||
:get-component="getComponent"
|
:get-component="getComponent"
|
||||||
:close="() => { /* no-op */ }"
|
:close="() => { /* no-op */ }"
|
||||||
:do-action="doAction"
|
:do-action="doAction"
|
||||||
@emoji-picker-shown="onEmojiPickerShown"
|
|
||||||
:default-button-style="useDefaultButtons"
|
:default-button-style="useDefaultButtons"
|
||||||
:hide-label="hideLabels"
|
:hide-label="hideLabels"
|
||||||
|
@emoji-picker-shown="onEmojiPickerShown"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
v-if="showPin && currentUser"
|
v-if="showPin && currentUser"
|
||||||
|
|
|
||||||
|
|
@ -147,16 +147,15 @@ const StatusBody = {
|
||||||
return this.status.attachments.map((file) => file.type)
|
return this.status.attachments.map((file) => file.type)
|
||||||
},
|
},
|
||||||
collapsedStatus() {
|
collapsedStatus() {
|
||||||
return this.status.raw_html.replace(/(\n|<br\s?\/?>)/g, ' ')
|
return this.status.raw_html.replaceAll('(\n|<br\s?\/?>)', ' ')
|
||||||
},
|
},
|
||||||
...mapState(useMergedConfigStore, ['mergedConfig']),
|
...mapState(useMergedConfigStore, ['mergedConfig']),
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.status.attentions &&
|
this.status.attentions?.forEach((attn) => {
|
||||||
this.status.attentions.forEach((attn) => {
|
const { id } = attn
|
||||||
const { id } = attn
|
this.$store.dispatch('fetchUserIfMissing', id)
|
||||||
this.$store.dispatch('fetchUserIfMissing', id)
|
})
|
||||||
})
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
onParseReady(event) {
|
onParseReady(event) {
|
||||||
|
|
@ -168,7 +167,7 @@ const StatusBody = {
|
||||||
.filter((mention) => !mention.notifying)
|
.filter((mention) => !mention.notifying)
|
||||||
.forEach((mention) => {
|
.forEach((mention) => {
|
||||||
const { content, url } = mention
|
const { content, url } = mention
|
||||||
const cleanedString = content.replace(/<[^>]+?>/gi, '') // remove all tags
|
const cleanedString = content.replaceAll(/<[^>]+?>/gi, '') // remove all tags
|
||||||
if (!cleanedString.startsWith('@')) return
|
if (!cleanedString.startsWith('@')) return
|
||||||
const handle = cleanedString.slice(1)
|
const handle = cleanedString.slice(1)
|
||||||
const host = url.replace(/^https?:\/\//, '').replace(/\/.+?$/, '')
|
const host = url.replace(/^https?:\/\//, '').replace(/\/.+?$/, '')
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ const StillImage = {
|
||||||
}
|
}
|
||||||
const image = this.$refs.src
|
const image = this.$refs.src
|
||||||
if (!image) return
|
if (!image) return
|
||||||
this.imageLoadHandler && this.imageLoadHandler(image)
|
this.imageLoadHandler?.(image)
|
||||||
const canvas = this.$refs.canvas
|
const canvas = this.$refs.canvas
|
||||||
if (!canvas) return
|
if (!canvas) return
|
||||||
const width = image.naturalWidth
|
const width = image.naturalWidth
|
||||||
|
|
@ -61,7 +61,7 @@ const StillImage = {
|
||||||
canvas.getContext('2d').drawImage(image, 0, 0, width, height)
|
canvas.getContext('2d').drawImage(image, 0, 0, width, height)
|
||||||
},
|
},
|
||||||
onError() {
|
onError() {
|
||||||
this.imageLoadError && this.imageLoadError()
|
this.imageLoadError?.()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,11 @@ export default {
|
||||||
type="button"
|
type="button"
|
||||||
role="tab"
|
role="tab"
|
||||||
>
|
>
|
||||||
<img src={props.image} title={props['image-tooltip']} />
|
<img
|
||||||
|
src={props.image}
|
||||||
|
alt={props['image-tooltip']}
|
||||||
|
title={props['image-tooltip']}
|
||||||
|
/>
|
||||||
{props.label ? '' : props.label}
|
{props.label ? '' : props.label}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -263,7 +263,10 @@ const Timeline = {
|
||||||
|
|
||||||
// Start from approximating the index of some visible status by using the
|
// Start from approximating the index of some visible status by using the
|
||||||
// the center of the screen on the timeline.
|
// 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
|
let err = statuses[approxIndex].getBoundingClientRect().y
|
||||||
|
|
||||||
// if we have a previous scroll index that can be used, test if it's
|
// if we have a previous scroll index that can be used, test if it's
|
||||||
|
|
|
||||||
|
|
@ -196,7 +196,7 @@ export default {
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
escapedNewBio() {
|
escapedNewBio() {
|
||||||
return ldEscape(this.newBio).replace(/\n/g, '<br>')
|
return ldEscape(this.newBio).replaceAll('\n', '<br>')
|
||||||
},
|
},
|
||||||
somethingToSave() {
|
somethingToSave() {
|
||||||
if (this.newName !== this.user.name_unescaped) return true
|
if (this.newName !== this.user.name_unescaped) return true
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
const arg = process.argv[2]
|
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('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('outputs all the things present in english but missing in foreign language.')
|
||||||
console.info('')
|
console.info('')
|
||||||
|
|
|
||||||
|
|
@ -242,6 +242,8 @@
|
||||||
"single_choice": "Single choice",
|
"single_choice": "Single choice",
|
||||||
"multiple_choices": "Multiple choices",
|
"multiple_choices": "Multiple choices",
|
||||||
"expiry": "Poll age",
|
"expiry": "Poll age",
|
||||||
|
"expiry_unit": "Expiry unit",
|
||||||
|
"expiry_amount": "Expiry amount",
|
||||||
"expires_at": "Poll ends {0}",
|
"expires_at": "Poll ends {0}",
|
||||||
"expires_in": "Poll ends in {0}",
|
"expires_in": "Poll ends in {0}",
|
||||||
"expired": "Poll ended {0} ago",
|
"expired": "Poll ended {0} ago",
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,9 @@ const hasLanguageFile = (code) => languages.includes(code)
|
||||||
|
|
||||||
const languageFileMap = import.meta.glob(['./*.json', '!./en.json'])
|
const languageFileMap = import.meta.glob(['./*.json', '!./en.json'])
|
||||||
|
|
||||||
const loadLanguageFile = (code) => {
|
const loadLanguageFile = async (code) => {
|
||||||
const jsonName = langCodeToJsonName(code)
|
const jsonName = langCodeToJsonName(code)
|
||||||
if (jsonName === 'en') return Promise.resolve({ default: enMessages })
|
if (jsonName === 'en') return { default: enMessages }
|
||||||
return languageFileMap[`./${jsonName}.json`]()
|
return languageFileMap[`./${jsonName}.json`]()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ export default function createPersistedState({
|
||||||
if (saveImmedeatelyActions.includes(mutation.type)) {
|
if (saveImmedeatelyActions.includes(mutation.type)) {
|
||||||
setState(key, reducer(cloneDeep(state), paths), storage).then(
|
setState(key, reducer(cloneDeep(state), paths), storage).then(
|
||||||
(success) => {
|
(success) => {
|
||||||
if (typeof success !== 'undefined') {
|
if (success !== undefined) {
|
||||||
if (
|
if (
|
||||||
mutation.type === 'setOption' ||
|
mutation.type === 'setOption' ||
|
||||||
mutation.type === 'setCurrentUser'
|
mutation.type === 'setCurrentUser'
|
||||||
|
|
@ -185,7 +185,7 @@ export const piniaPersistPlugin =
|
||||||
}
|
}
|
||||||
|
|
||||||
const fallbackValue = await storage.getItem(vuexKey)
|
const fallbackValue = await storage.getItem(vuexKey)
|
||||||
if (fallbackValue && fallbackValue[id]) {
|
if (fallbackValue?.[id]) {
|
||||||
console.info(`Migrating ${id} store data from vuex to pinia`)
|
console.info(`Migrating ${id} store data from vuex to pinia`)
|
||||||
const res = fallbackValue[id]
|
const res = fallbackValue[id]
|
||||||
await storage.setItem(key, res)
|
await storage.setItem(key, res)
|
||||||
|
|
@ -198,7 +198,7 @@ export const piniaPersistPlugin =
|
||||||
const setState = (state) => {
|
const setState = (state) => {
|
||||||
if (!loadedGuard.loaded) {
|
if (!loadedGuard.loaded) {
|
||||||
console.info('waiting for old state to be loaded...')
|
console.info('waiting for old state to be loaded...')
|
||||||
return Promise.reject()
|
throw new Error('Waiting')
|
||||||
} else {
|
} else {
|
||||||
return storage.setItem(key, state)
|
return storage.setItem(key, state)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -325,7 +325,7 @@ const api = {
|
||||||
const token = state.wsToken
|
const token = state.wsToken
|
||||||
if (
|
if (
|
||||||
useInstanceCapabilitiesStore().shoutAvailable &&
|
useInstanceCapabilitiesStore().shoutAvailable &&
|
||||||
typeof token !== 'undefined' &&
|
token !== undefined &&
|
||||||
state.socket === null
|
state.socket === null
|
||||||
) {
|
) {
|
||||||
const socket = new Socket('/socket', { params: { token } })
|
const socket = new Socket('/socket', { params: { token } })
|
||||||
|
|
@ -336,7 +336,7 @@ const api = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
disconnectFromSocket({ commit, state }) {
|
disconnectFromSocket({ commit, state }) {
|
||||||
state.socket && state.socket.disconnect()
|
state.socket?.disconnect()
|
||||||
commit('setSocket', null)
|
commit('setSocket', null)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -49,8 +49,8 @@ const emptyTl = (userId = 0) => ({
|
||||||
visibleStatuses: [],
|
visibleStatuses: [],
|
||||||
visibleStatusesObject: {},
|
visibleStatusesObject: {},
|
||||||
newStatusCount: 0,
|
newStatusCount: 0,
|
||||||
maxId: 0,
|
maxId: '',
|
||||||
minId: 0,
|
minId: '',
|
||||||
minVisibleId: 0,
|
minVisibleId: 0,
|
||||||
loading: false,
|
loading: false,
|
||||||
followers: [],
|
followers: [],
|
||||||
|
|
@ -64,7 +64,7 @@ export const defaultState = () => ({
|
||||||
scrobblesNextFetch: {},
|
scrobblesNextFetch: {},
|
||||||
allStatusesObject: {},
|
allStatusesObject: {},
|
||||||
conversationsObject: {},
|
conversationsObject: {},
|
||||||
maxId: 0,
|
maxId: '',
|
||||||
favorites: new Set(),
|
favorites: new Set(),
|
||||||
timelines: {
|
timelines: {
|
||||||
mentions: emptyTl(),
|
mentions: emptyTl(),
|
||||||
|
|
@ -134,7 +134,7 @@ const sortById = (a, b) => {
|
||||||
const sortTimeline = (timeline) => {
|
const sortTimeline = (timeline) => {
|
||||||
timeline.visibleStatuses = timeline.visibleStatuses.sort(sortById)
|
timeline.visibleStatuses = timeline.visibleStatuses.sort(sortById)
|
||||||
timeline.statuses = timeline.statuses.sort(sortById)
|
timeline.statuses = timeline.statuses.sort(sortById)
|
||||||
timeline.minVisibleId = (last(timeline.visibleStatuses) || {}).id
|
timeline.minVisibleId = last(timeline.visibleStatuses)?.id
|
||||||
return timeline
|
return timeline
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -222,11 +222,11 @@ const addNewStatuses = (
|
||||||
|
|
||||||
const newer =
|
const newer =
|
||||||
timeline &&
|
timeline &&
|
||||||
(maxNew > timelineObject.maxId || timelineObject.maxId === 0) &&
|
(maxNew > timelineObject.maxId || timelineObject.maxId === '') &&
|
||||||
statuses.length > 0
|
statuses.length > 0
|
||||||
const older =
|
const older =
|
||||||
timeline &&
|
timeline &&
|
||||||
(minNew < timelineObject.minId || timelineObject.minId === 0) &&
|
(minNew < timelineObject.minId || timelineObject.minId === '') &&
|
||||||
statuses.length > 0
|
statuses.length > 0
|
||||||
|
|
||||||
if (!noIdUpdate && newer) {
|
if (!noIdUpdate && newer) {
|
||||||
|
|
@ -525,7 +525,7 @@ export const mutations = {
|
||||||
},
|
},
|
||||||
addRepeats(state, { id, rebloggedByUsers, currentUser }) {
|
addRepeats(state, { id, rebloggedByUsers, currentUser }) {
|
||||||
const newStatus = state.allStatusesObject[id]
|
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
|
// 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.repeat_num = newStatus.rebloggedBy.length
|
||||||
newStatus.repeated = !!newStatus.rebloggedBy.find(
|
newStatus.repeated = !!newStatus.rebloggedBy.find(
|
||||||
|
|
@ -534,7 +534,7 @@ export const mutations = {
|
||||||
},
|
},
|
||||||
addFavs(state, { id, favoritedByUsers, currentUser }) {
|
addFavs(state, { id, favoritedByUsers, currentUser }) {
|
||||||
const newStatus = state.allStatusesObject[id]
|
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
|
// 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.fave_num = newStatus.favoritedBy.length
|
||||||
newStatus.favorited = !!newStatus.favoritedBy.find(
|
newStatus.favorited = !!newStatus.favoritedBy.find(
|
||||||
|
|
@ -879,7 +879,7 @@ const statuses = {
|
||||||
store.commit('addNewUsers', data.accounts)
|
store.commit('addNewUsers', data.accounts)
|
||||||
store.commit(
|
store.commit(
|
||||||
'addNewUsers',
|
'addNewUsers',
|
||||||
data.statuses.map((s) => s.user).filter((u) => u),
|
data.statuses.map((s) => s.user).filter(Boolean),
|
||||||
)
|
)
|
||||||
store.dispatch('addNewStatuses', {
|
store.dispatch('addNewStatuses', {
|
||||||
statuses: data.statuses,
|
statuses: data.statuses,
|
||||||
|
|
|
||||||
|
|
@ -75,13 +75,13 @@ const mergeArrayLength = (oldValue, newValue) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const getNotificationPermission = () => {
|
const getNotificationPermission = async () => {
|
||||||
const Notification = window.Notification
|
const Notification = window.Notification
|
||||||
|
|
||||||
if (!Notification) return Promise.resolve(null)
|
if (!Notification) return null
|
||||||
if (Notification.permission === 'default')
|
if (Notification.permission === 'default')
|
||||||
return Notification.requestPermission()
|
return Notification.requestPermission()
|
||||||
return Promise.resolve(Notification.permission)
|
return Notification.permission
|
||||||
}
|
}
|
||||||
|
|
||||||
const blockUser = (store, args) => {
|
const blockUser = (store, args) => {
|
||||||
|
|
@ -271,7 +271,7 @@ export const mutations = {
|
||||||
state.currentUser.blockIds = blockIds
|
state.currentUser.blockIds = blockIds
|
||||||
},
|
},
|
||||||
addBlockId(state, blockId) {
|
addBlockId(state, blockId) {
|
||||||
if (state.currentUser.blockIds.indexOf(blockId) === -1) {
|
if (state.currentUser.blockIds.includes(blockId)) {
|
||||||
state.currentUser.blockIds.push(blockId)
|
state.currentUser.blockIds.push(blockId)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -285,7 +285,7 @@ export const mutations = {
|
||||||
state.currentUser.muteIdsMaxId = muteIdsMaxId
|
state.currentUser.muteIdsMaxId = muteIdsMaxId
|
||||||
},
|
},
|
||||||
addMuteId(state, muteId) {
|
addMuteId(state, muteId) {
|
||||||
if (state.currentUser.muteIds.indexOf(muteId) === -1) {
|
if (state.currentUser.muteIds.includes(muteId)) {
|
||||||
state.currentUser.muteIds.push(muteId)
|
state.currentUser.muteIds.push(muteId)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -293,7 +293,7 @@ export const mutations = {
|
||||||
state.currentUser.domainMutes = domainMutes
|
state.currentUser.domainMutes = domainMutes
|
||||||
},
|
},
|
||||||
addDomainMute(state, domain) {
|
addDomainMute(state, domain) {
|
||||||
if (state.currentUser.domainMutes.indexOf(domain) === -1) {
|
if (state.currentUser.domainMutes.includes(domain)) {
|
||||||
state.currentUser.domainMutes.push(domain)
|
state.currentUser.domainMutes.push(domain)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -385,12 +385,12 @@ const users = {
|
||||||
mutations,
|
mutations,
|
||||||
getters,
|
getters,
|
||||||
actions: {
|
actions: {
|
||||||
fetchUserIfMissing(store, id) {
|
async fetchUserIfMissing(store, id) {
|
||||||
const user = store.getters.findUser(id)
|
const user = store.getters.findUser(id)
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return store.dispatch('fetchUser', id)
|
return store.dispatch('fetchUser', id)
|
||||||
} else {
|
} else {
|
||||||
return Promise.resolve(user)
|
return user
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
updateUserAdminData(store, { userAdminData }) {
|
updateUserAdminData(store, { userAdminData }) {
|
||||||
|
|
@ -640,7 +640,7 @@ const users = {
|
||||||
},
|
},
|
||||||
addNewNotifications(store, { notifications }) {
|
addNewNotifications(store, { notifications }) {
|
||||||
const users = map(notifications, 'from_profile')
|
const users = map(notifications, 'from_profile')
|
||||||
const targetUsers = map(notifications, 'target').filter((_) => _)
|
const targetUsers = map(notifications, 'target').filter(Boolean)
|
||||||
const notificationIds = notifications.map((_) => _.id)
|
const notificationIds = notifications.map((_) => _.id)
|
||||||
store.commit('addNewUsers', users)
|
store.commit('addNewUsers', users)
|
||||||
store.commit('addNewUsers', targetUsers)
|
store.commit('addNewUsers', targetUsers)
|
||||||
|
|
|
||||||
|
|
@ -148,6 +148,14 @@
|
||||||
background-image:
|
background-image:
|
||||||
linear-gradient(to bottom, var(--background), var(--background)),
|
linear-gradient(to bottom, var(--background), var(--background)),
|
||||||
linear-gradient(to bottom, var(--__panel-background), var(--__panel-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);
|
--_shadow: var(--shadow);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,7 @@ export const maybeShowChatNotification = (chat) => {
|
||||||
body: chat.lastMessage.content,
|
body: chat.lastMessage.content,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (chat.lastMessage.attachment?.type === 'image') {
|
||||||
chat.lastMessage.attachment &&
|
|
||||||
chat.lastMessage.attachment.type === 'image'
|
|
||||||
) {
|
|
||||||
opts.image = chat.lastMessage.attachment.preview_url
|
opts.image = chat.lastMessage.attachment.preview_url
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import { contrastRatio, convert, invertLightness } from 'chromatism'
|
||||||
* @param {Number} [b] - Blue component
|
* @param {Number} [b] - Blue component
|
||||||
*/
|
*/
|
||||||
export const rgb2hex = (r, g, b) => {
|
export const rgb2hex = (r, g, b) => {
|
||||||
if (r === null || typeof r === 'undefined') {
|
if (r === null || r === undefined) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
// TODO: clean up this mess
|
// TODO: clean up this mess
|
||||||
|
|
@ -130,7 +130,7 @@ export const arithmeticBlend = (origin, value, operator) => {
|
||||||
* @returns {Object} sRGB of resulting color
|
* @returns {Object} sRGB of resulting color
|
||||||
*/
|
*/
|
||||||
export const alphaBlend = (fg, fga, bg) => {
|
export const alphaBlend = (fg, fga, bg) => {
|
||||||
if (fga === 1 || typeof fga === 'undefined') {
|
if (fga === 1 || fga === undefined) {
|
||||||
return fg
|
return fg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -210,16 +210,16 @@ export const rgba2css = function (rgba) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rgba !== null) {
|
if (rgba !== null) {
|
||||||
if (rgba.r !== undefined && !isNaN(rgba.r)) {
|
if (rgba.r !== undefined && !Number.isNaN(rgba.r)) {
|
||||||
base.r = 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
|
base.g = rgba.g
|
||||||
}
|
}
|
||||||
if (rgba.b !== undefined && !isNaN(rgba.b)) {
|
if (rgba.b !== undefined && !Number.isNaN(rgba.b)) {
|
||||||
base.b = 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
|
base.a = rgba.a
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -62,8 +62,8 @@ export const parseUser = (data) => {
|
||||||
})
|
})
|
||||||
output.fields_text = data.fields.map((field) => {
|
output.fields_text = data.fields.map((field) => {
|
||||||
return {
|
return {
|
||||||
name: unescape(field.name.replace(/<[^>]*>/g, '')),
|
name: unescape(field.name.replaceAll(/<[^>]*>/g, '')),
|
||||||
value: unescape(field.value.replace(/<[^>]*>/g, '')),
|
value: unescape(field.value.replaceAll(/<[^>]*>/g, '')),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -191,7 +191,7 @@ export const parseUser = (data) => {
|
||||||
|
|
||||||
// Convert punycode to unicode for UI
|
// Convert punycode to unicode for UI
|
||||||
output.screen_name_ui = output.screen_name
|
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 parts = output.screen_name.split('@')
|
||||||
const unicodeDomain = punycode.toUnicode(parts[1])
|
const unicodeDomain = punycode.toUnicode(parts[1])
|
||||||
if (unicodeDomain !== parts[1]) {
|
if (unicodeDomain !== parts[1]) {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { capitalize } from 'lodash'
|
||||||
function humanizeErrors(errors) {
|
function humanizeErrors(errors) {
|
||||||
return Object.entries(errors).reduce((errs, [k, val]) => {
|
return Object.entries(errors).reduce((errs, [k, val]) => {
|
||||||
const message = val.reduce((acc, message) => {
|
const message = val.reduce((acc, message) => {
|
||||||
const key = capitalize(k.replace(/_/g, ' '))
|
const key = capitalize(k.replaceAll('_', ' '))
|
||||||
return acc + [key, message].join(' ') + '. '
|
return acc + [key, message].join(' ') + '. '
|
||||||
}, '')
|
}, '')
|
||||||
return [...errs, message]
|
return [...errs, message]
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ export const fileTypeExt = (url) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const fileMatchesSomeType = (types, file) =>
|
export const fileMatchesSomeType = (types, file) =>
|
||||||
types.some((type) => fileType(file.mimetype) === type)
|
types.includes(fileType(file.mimetype))
|
||||||
|
|
||||||
const fileTypeService = {
|
const fileTypeService = {
|
||||||
fileType,
|
fileType,
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ const visibleTypes = (notificationVisibility) => {
|
||||||
notificationVisibility.emojiReactions && 'pleroma:emoji_reaction',
|
notificationVisibility.emojiReactions && 'pleroma:emoji_reaction',
|
||||||
notificationVisibility.reports && 'pleroma:report',
|
notificationVisibility.reports && 'pleroma:report',
|
||||||
notificationVisibility.polls && 'poll',
|
notificationVisibility.polls && 'poll',
|
||||||
].filter((_) => _)
|
].filter(Boolean)
|
||||||
}
|
}
|
||||||
|
|
||||||
const statusNotifications = new Set([
|
const statusNotifications = new Set([
|
||||||
|
|
@ -95,9 +95,7 @@ export const filteredNotificationsFromStore = (
|
||||||
types,
|
types,
|
||||||
) => {
|
) => {
|
||||||
// map is just to clone the array since sort mutates it and it causes some issues
|
// map is just to clone the array since sort mutates it and it causes some issues
|
||||||
const sortedNotifications = notificationsFromStore(store)
|
const sortedNotifications = notificationsFromStore(store).sort(sortById)
|
||||||
.map((_) => _)
|
|
||||||
.sort(sortById)
|
|
||||||
// TODO implement sorting elsewhere and make it optional
|
// TODO implement sorting elsewhere and make it optional
|
||||||
return sortedNotifications.filter((notification) =>
|
return sortedNotifications.filter((notification) =>
|
||||||
(types || visibleTypes(notificationVisibility)).includes(notification.type),
|
(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...
|
// Shows first attached non-nsfw image, if any. Should add configuration for this somehow...
|
||||||
if (
|
if (!status.nsfw && status?.attachments?.[0]?.mimetype.startsWith('image/')) {
|
||||||
status &&
|
|
||||||
status.attachments &&
|
|
||||||
status.attachments.length > 0 &&
|
|
||||||
!status.nsfw &&
|
|
||||||
status.attachments[0].mimetype.startsWith('image/')
|
|
||||||
) {
|
|
||||||
notifObj.image = status.attachments[0].url
|
notifObj.image = status.attachments[0].url
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,13 +25,13 @@ const createRuffleService = () => {
|
||||||
script.src = '/static/ruffle/ruffle.js'
|
script.src = '/static/ruffle/ruffle.js'
|
||||||
script.type = 'text/javascript'
|
script.type = 'text/javascript'
|
||||||
script.onerror = (e) => {
|
script.onerror = (e) => {
|
||||||
reject(e)
|
reject(new Error('Ruffle script errorred', e))
|
||||||
}
|
}
|
||||||
script.onabort = (e) => {
|
script.onabort = (e) => {
|
||||||
reject(e)
|
reject(new Error('Ruffle script aborted', e))
|
||||||
}
|
}
|
||||||
script.oncancel = (e) => {
|
script.oncancel = (e) => {
|
||||||
reject(e)
|
reject(new Error('Ruffle script cancelled', e))
|
||||||
}
|
}
|
||||||
script.onload = () => {
|
script.onload = () => {
|
||||||
ruffleInstance = window.RufflePlayer
|
ruffleInstance = window.RufflePlayer
|
||||||
|
|
|
||||||
|
|
@ -88,5 +88,5 @@ export const muteFilterHits = (muteFilters, status) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.filter((_) => _)
|
.filter(Boolean)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ export const createStyleSheet = (id, priority = 1000) => {
|
||||||
addRule(rule) {
|
addRule(rule) {
|
||||||
let newRule = rule
|
let newRule = rule
|
||||||
if (!CSS.supports?.('backdrop-filter', 'blur()')) {
|
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')) {
|
if (newRule.startsWith('::-webkit')) {
|
||||||
|
|
@ -44,7 +44,7 @@ export const createStyleSheet = (id, priority = 1000) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.rules.push(
|
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 EAGER_STYLE_ID = 'pleroma-eager-styles'
|
||||||
const LAZY_STYLE_ID = 'pleroma-lazy-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 generateTheme = (inputRuleset, callbacks, debug) => {
|
||||||
const {
|
const {
|
||||||
onNewRule = () => {
|
onNewRule = () => {
|
||||||
|
|
@ -151,7 +156,8 @@ export const tryLoadCache = async () => {
|
||||||
if (
|
if (
|
||||||
cache.engineChecksum === getEngineChecksum() &&
|
cache.engineChecksum === getEngineChecksum() &&
|
||||||
cache.checksum !== undefined &&
|
cache.checksum !== undefined &&
|
||||||
cache.checksum === useMergedConfigStore().mergedConfig.themeChecksum
|
cache.checksum === useMergedConfigStore().mergedConfig.themeChecksum &&
|
||||||
|
!hasInvalidCachedThemeRules(cache.data)
|
||||||
) {
|
) {
|
||||||
const eagerStyles = createStyleSheet(EAGER_STYLE_ID, 10)
|
const eagerStyles = createStyleSheet(EAGER_STYLE_ID, 10)
|
||||||
const lazyStyles = createStyleSheet(LAZY_STYLE_ID, 20)
|
const lazyStyles = createStyleSheet(LAZY_STYLE_ID, 20)
|
||||||
|
|
@ -318,7 +324,7 @@ export const getResourcesIndex = async (url, parser = noop) => {
|
||||||
const resourceTransform = (resources) => {
|
const resourceTransform = (resources) => {
|
||||||
return Object.entries(resources).map(([k, v]) => {
|
return Object.entries(resources).map(([k, v]) => {
|
||||||
if (typeof v === 'object') {
|
if (typeof v === 'object') {
|
||||||
return [k, () => Promise.resolve(v)]
|
return [k, () => v]
|
||||||
} else if (typeof v === 'string') {
|
} else if (typeof v === 'string') {
|
||||||
return [
|
return [
|
||||||
k,
|
k,
|
||||||
|
|
@ -362,11 +368,9 @@ export const getResourcesIndex = async (url, parser = noop) => {
|
||||||
|
|
||||||
const total = [...custom, ...builtin]
|
const total = [...custom, ...builtin]
|
||||||
if (total.length === 0) {
|
if (total.length === 0) {
|
||||||
return Promise.reject(
|
throw new Error(
|
||||||
new Error(
|
`Resource at ${url} and ${customUrl} completely unavailable. Panicking`,
|
||||||
`Resource at ${url} and ${customUrl} completely unavailable. Panicking`,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return Promise.resolve(Object.fromEntries(total))
|
return Object.fromEntries(total)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
/* global process */
|
/* global process */
|
||||||
function urlBase64ToUint8Array(base64String) {
|
function urlBase64ToUint8Array(base64String) {
|
||||||
const padding = '='.repeat((4 - (base64String.length % 4)) % 4)
|
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)
|
const rawData = window.atob(base64)
|
||||||
return Uint8Array.from([...rawData].map((char) => char.codePointAt(0)))
|
return Uint8Array.from([...rawData].map((char) => char.codePointAt(0)))
|
||||||
|
|
@ -26,10 +28,8 @@ function getOrCreateServiceWorker() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function subscribePush(registration, isEnabled, vapidPublicKey) {
|
function subscribePush(registration, isEnabled, vapidPublicKey) {
|
||||||
if (!isEnabled)
|
if (!isEnabled) throw new Error('Web Push is disabled in config')
|
||||||
return Promise.reject(new Error('Web Push is disabled in config'))
|
if (!vapidPublicKey) throw new Error('VAPID public key is not found')
|
||||||
if (!vapidPublicKey)
|
|
||||||
return Promise.reject(new Error('VAPID public key is not found'))
|
|
||||||
|
|
||||||
const subscribeOptions = {
|
const subscribeOptions = {
|
||||||
userVisibleOnly: false,
|
userVisibleOnly: false,
|
||||||
|
|
@ -38,10 +38,10 @@ function subscribePush(registration, isEnabled, vapidPublicKey) {
|
||||||
return registration.pushManager.subscribe(subscribeOptions)
|
return registration.pushManager.subscribe(subscribeOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
function unsubscribePush(registration) {
|
async function unsubscribePush(registration) {
|
||||||
return registration.pushManager.getSubscription().then((subscription) => {
|
return registration.pushManager.getSubscription().then((subscription) => {
|
||||||
if (subscription === null) {
|
if (subscription === null) {
|
||||||
return Promise.resolve('No subscription')
|
return 'No subscription'
|
||||||
}
|
}
|
||||||
return subscription.unsubscribe()
|
return subscription.unsubscribe()
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -165,7 +165,7 @@ export const getCssRules = (rules, debug) =>
|
||||||
header,
|
header,
|
||||||
directives,
|
directives,
|
||||||
rule.component === 'Text' &&
|
rule.component === 'Text' &&
|
||||||
rule.state.indexOf('faint') < 0 &&
|
!rule.state.includes('faint') &&
|
||||||
rule.directives.textNoCssColor !== 'yes'
|
rule.directives.textNoCssColor !== 'yes'
|
||||||
? ' color: var(--text);'
|
? ' color: var(--text);'
|
||||||
: '',
|
: '',
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ export const getAllPossibleCombinations = (array) => {
|
||||||
const nonSelf = array.filter((x) => !selfSet.has(x))
|
const nonSelf = array.filter((x) => !selfSet.has(x))
|
||||||
return nonSelf.map((x) => [...self, x])
|
return nonSelf.map((x) => [...self, x])
|
||||||
})
|
})
|
||||||
const flatCombos = newCombos.reduce((acc, x) => [...acc, ...x], [])
|
const flatCombos = newCombos.flat()
|
||||||
const uniqueComboStrings = new Set()
|
const uniqueComboStrings = new Set()
|
||||||
const uniqueCombos = flatCombos.map(sortBy).filter((x) => {
|
const uniqueCombos = flatCombos.map(sortBy).filter((x) => {
|
||||||
if (uniqueComboStrings.has(x.join())) {
|
if (uniqueComboStrings.has(x.join())) {
|
||||||
|
|
@ -36,7 +36,7 @@ export const getAllPossibleCombinations = (array) => {
|
||||||
})
|
})
|
||||||
combos.push(uniqueCombos)
|
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]
|
let arraySelector = Array.isArray(selector) ? selector : [selector]
|
||||||
if (ignoreOutOfTreeSelector || liteMode)
|
if (ignoreOutOfTreeSelector || liteMode)
|
||||||
arraySelector = [arraySelector[0]]
|
arraySelector = [arraySelector[0]]
|
||||||
arraySelector
|
|
||||||
.sort((a) => {
|
|
||||||
if (a.startsWith(':')) return 1
|
|
||||||
if (/^[a-z]/.exec(a)) return -1
|
|
||||||
else return 0
|
|
||||||
})
|
|
||||||
.join('')
|
|
||||||
return arraySelector
|
return arraySelector
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -191,14 +191,16 @@ export const convertTheme2To3 = (data) => {
|
||||||
newRules.push(rule)
|
newRules.push(rule)
|
||||||
|
|
||||||
if (rule.component === 'Button') {
|
if (rule.component === 'Button') {
|
||||||
newRules.push({ ...rule, component: 'ScrollbarElement' })
|
newRules.push(
|
||||||
newRules.push({ ...rule, component: 'Tab' })
|
{ ...rule, component: 'ScrollbarElement' },
|
||||||
newRules.push({
|
{ ...rule, component: 'Tab' },
|
||||||
...rule,
|
{
|
||||||
component: 'Tab',
|
...rule,
|
||||||
state: ['active'],
|
component: 'Tab',
|
||||||
directives: { opacity: 0 },
|
state: ['active'],
|
||||||
})
|
directives: { opacity: 0 },
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
if (rule.component === 'Panel') {
|
if (rule.component === 'Panel') {
|
||||||
newRules.push({ ...rule, component: 'Post' })
|
newRules.push({ ...rule, component: 'Post' })
|
||||||
|
|
@ -250,8 +252,10 @@ export const convertTheme2To3 = (data) => {
|
||||||
}
|
}
|
||||||
newRules.push(rule)
|
newRules.push(rule)
|
||||||
if (rule.component === 'Button') {
|
if (rule.component === 'Button') {
|
||||||
newRules.push({ ...rule, component: 'ScrollbarElement' })
|
newRules.push(
|
||||||
newRules.push({ ...rule, component: 'Tab' })
|
{ ...rule, component: 'ScrollbarElement' },
|
||||||
|
{ ...rule, component: 'Tab' },
|
||||||
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return newRules
|
return newRules
|
||||||
|
|
@ -349,16 +353,20 @@ export const convertTheme2To3 = (data) => {
|
||||||
newRules.push({ ...rule, parent: { component: 'Notification' } })
|
newRules.push({ ...rule, parent: { component: 'Notification' } })
|
||||||
}
|
}
|
||||||
if (key === 'buttonPressed') {
|
if (key === 'buttonPressed') {
|
||||||
newRules.push({ ...rule, state: ['toggled'] })
|
newRules.push(
|
||||||
newRules.push({ ...rule, state: ['toggled', 'focus'] })
|
{ ...rule, state: ['toggled'] },
|
||||||
newRules.push({ ...rule, state: ['pressed', 'focus'] })
|
{ ...rule, state: ['toggled', 'focus'] },
|
||||||
newRules.push({ ...rule, state: ['toggled', 'focus', 'hover'] })
|
{ ...rule, state: ['pressed', 'focus'] },
|
||||||
newRules.push({ ...rule, state: ['pressed', 'focus', 'hover'] })
|
{ ...rule, state: ['toggled', 'focus', 'hover'] },
|
||||||
|
{ ...rule, state: ['pressed', 'focus', 'hover'] },
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rule.component === 'Button') {
|
if (rule.component === 'Button') {
|
||||||
newRules.push({ ...rule, component: 'ScrollbarElement' })
|
newRules.push(
|
||||||
newRules.push({ ...rule, component: 'Tab' })
|
{ ...rule, component: 'ScrollbarElement' },
|
||||||
|
{ ...rule, component: 'Tab' },
|
||||||
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return newRules
|
return newRules
|
||||||
|
|
@ -512,15 +520,17 @@ export const convertTheme2To3 = (data) => {
|
||||||
{ ...newRule, component: 'Tab' },
|
{ ...newRule, component: 'Tab' },
|
||||||
{ ...newRule, component: 'ScrollbarElement' },
|
{ ...newRule, component: 'ScrollbarElement' },
|
||||||
]
|
]
|
||||||
if (newRule.state?.indexOf('toggled') >= 0) {
|
if (newRule.state?.includes('toggled')) {
|
||||||
rules.push({ ...newRule, state: [...newRule.state, 'focused'] })
|
rules.push(
|
||||||
rules.push({ ...newRule, state: [...newRule.state, 'hover'] })
|
{ ...newRule, state: [...newRule.state, 'focused'] },
|
||||||
rules.push({
|
{ ...newRule, state: [...newRule.state, 'hover'] },
|
||||||
...newRule,
|
{
|
||||||
state: [...newRule.state, 'hover', 'focused'],
|
...newRule,
|
||||||
})
|
state: [...newRule.state, 'hover', 'focused'],
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
if (newRule.state?.indexOf('hover') >= 0) {
|
if (newRule.state?.includes('hover')) {
|
||||||
rules.push({ ...newRule, state: [...newRule.state, 'focused'] })
|
rules.push({ ...newRule, state: [...newRule.state, 'focused'] })
|
||||||
}
|
}
|
||||||
return rules
|
return rules
|
||||||
|
|
@ -559,9 +569,9 @@ export const convertTheme2To3 = (data) => {
|
||||||
|
|
||||||
const flatExtRules = extendedRules
|
const flatExtRules = extendedRules
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.reduce((acc, x) => [...acc, ...x], [])
|
.flat()
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.reduce((acc, x) => [...acc, ...x], [])
|
.flat()
|
||||||
|
|
||||||
return [
|
return [
|
||||||
generateRoot(),
|
generateRoot(),
|
||||||
|
|
|
||||||
|
|
@ -244,10 +244,7 @@ export const OPACITIES = Object.entries(SLOT_INHERITANCE).reduce((acc, [k]) => {
|
||||||
...acc,
|
...acc,
|
||||||
[opacity]: {
|
[opacity]: {
|
||||||
defaultValue: DEFAULT_OPACITY[opacity] || 1,
|
defaultValue: DEFAULT_OPACITY[opacity] || 1,
|
||||||
affectedSlots: [
|
affectedSlots: [...(acc[opacity]?.affectedSlots || []), k],
|
||||||
...((acc[opacity] && acc[opacity].affectedSlots) || []),
|
|
||||||
k,
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -413,7 +410,7 @@ export const getColors = (sourceColors, sourceOpacity) =>
|
||||||
outputColor.a = Number(
|
outputColor.a = Number(
|
||||||
opacityOverriden
|
opacityOverriden
|
||||||
? sourceOpacity[opacitySlot]
|
? sourceOpacity[opacitySlot]
|
||||||
: (OPACITIES[opacitySlot] || {}).defaultValue,
|
: OPACITIES[opacitySlot]?.defaultValue,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -460,7 +457,7 @@ export const generatePreset = (input) => {
|
||||||
return composePreset(
|
return composePreset(
|
||||||
colors,
|
colors,
|
||||||
generateRadii(input),
|
generateRadii(input),
|
||||||
generateShadows(input, colors.theme.colors, colors.mod),
|
generateShadows(input, colors.theme.colors),
|
||||||
generateFonts(input),
|
generateFonts(input),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -262,7 +262,7 @@ export const init = ({
|
||||||
...r,
|
...r,
|
||||||
})),
|
})),
|
||||||
)
|
)
|
||||||
.reduce((acc, arr) => [...acc, ...arr], []),
|
.flat(),
|
||||||
...inputRuleset,
|
...inputRuleset,
|
||||||
].map((rule) => {
|
].map((rule) => {
|
||||||
normalizeCombination(rule)
|
normalizeCombination(rule)
|
||||||
|
|
@ -690,11 +690,11 @@ export const init = ({
|
||||||
.map((combination) => ['normal', ...combination])
|
.map((combination) => ['normal', ...combination])
|
||||||
.filter((combo) => {
|
.filter((combo) => {
|
||||||
// Optimization: filter out some hard-coded combinations that don't make sense
|
// Optimization: filter out some hard-coded combinations that don't make sense
|
||||||
if (combo.indexOf('disabled') >= 0) {
|
if (combo.includes('disabled')) {
|
||||||
return !(
|
return !(
|
||||||
combo.indexOf('hover') >= 0 ||
|
combo.includes('hover') ||
|
||||||
combo.indexOf('focused') >= 0 ||
|
combo.includes('focused') ||
|
||||||
combo.indexOf('pressed') >= 0
|
combo.includes('pressed')
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
|
@ -705,13 +705,13 @@ export const init = ({
|
||||||
.map((variant) => {
|
.map((variant) => {
|
||||||
return stateCombinations.map((state) => ({ variant, state }))
|
return stateCombinations.map((state) => ({ variant, state }))
|
||||||
})
|
})
|
||||||
.reduce((acc, x) => [...acc, ...x], [])
|
.flat()
|
||||||
|
|
||||||
stateVariantCombination.forEach((combination) => {
|
stateVariantCombination.forEach((combination) => {
|
||||||
combination.component = component.name
|
combination.component = component.name
|
||||||
combination.lazy = component.lazy || parent?.lazy
|
combination.lazy = component.lazy || parent?.lazy
|
||||||
combination.parent = parent
|
combination.parent = parent
|
||||||
if (!liteMode && combination.state.indexOf('hover') >= 0) {
|
if (!liteMode && combination.state.includes('hover')) {
|
||||||
combination.lazy = true
|
combination.lazy = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ const highlightStyle = (prefs) => {
|
||||||
|
|
||||||
const highlightClass = (user) => {
|
const highlightClass = (user) => {
|
||||||
return (
|
return (
|
||||||
'USER____' + user.screen_name?.replace(/\./g, '_').replace(/@/g, '_AT_')
|
'USER____' + user.screen_name?.replaceAll('.', '_').replaceAll('@', '_AT_')
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ export const useChatsStore = defineStore('chats', {
|
||||||
addNewChats(chats) {
|
addNewChats(chats) {
|
||||||
window.vuex.commit(
|
window.vuex.commit(
|
||||||
'addNewUsers',
|
'addNewUsers',
|
||||||
chats.map((k) => k.account).filter((k) => k),
|
chats.map((k) => k.account).filter(Boolean),
|
||||||
)
|
)
|
||||||
|
|
||||||
chats.forEach((updatedChat) => {
|
chats.forEach((updatedChat) => {
|
||||||
|
|
@ -69,8 +69,7 @@ export const useChatsStore = defineStore('chats', {
|
||||||
|
|
||||||
if (chat) {
|
if (chat) {
|
||||||
const isNewMessage =
|
const isNewMessage =
|
||||||
(chat.lastMessage && chat.lastMessage.id) !==
|
chat.lastMessage?.id !== updatedChat.lastMessage?.id
|
||||||
(updatedChat.lastMessage && updatedChat.lastMessage.id)
|
|
||||||
chat.lastMessage = updatedChat.lastMessage
|
chat.lastMessage = updatedChat.lastMessage
|
||||||
chat.unread = updatedChat.unread
|
chat.unread = updatedChat.unread
|
||||||
chat.updated_at = updatedChat.updated_at
|
chat.updated_at = updatedChat.updated_at
|
||||||
|
|
|
||||||
|
|
@ -120,23 +120,19 @@ export const useEmojiStore = defineStore('emoji', {
|
||||||
}, {})
|
}, {})
|
||||||
},
|
},
|
||||||
standardEmojiList(state) {
|
standardEmojiList(state) {
|
||||||
return (
|
return SORTED_EMOJI_GROUP_IDS.map((groupId) =>
|
||||||
SORTED_EMOJI_GROUP_IDS.map((groupId) =>
|
(this.emoji[groupId] || []).map((k) =>
|
||||||
(this.emoji[groupId] || []).map((k) =>
|
injectAnnotations(k, this.unicodeEmojiAnnotations),
|
||||||
injectAnnotations(k, this.unicodeEmojiAnnotations),
|
),
|
||||||
),
|
).flat()
|
||||||
).reduce((a, b) => a.concat(b), []) ?? []
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
standardEmojiGroupList(state) {
|
standardEmojiGroupList(state) {
|
||||||
return (
|
return SORTED_EMOJI_GROUP_IDS.map((groupId) => ({
|
||||||
SORTED_EMOJI_GROUP_IDS.map((groupId) => ({
|
id: groupId,
|
||||||
id: groupId,
|
emojis: (this.emoji[groupId] || []).map((k) =>
|
||||||
emojis: (this.emoji[groupId] || []).map((k) =>
|
injectAnnotations(k, this.unicodeEmojiAnnotations),
|
||||||
injectAnnotations(k, this.unicodeEmojiAnnotations),
|
),
|
||||||
),
|
}))
|
||||||
})) ?? []
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
|
|
@ -146,7 +142,7 @@ export const useEmojiStore = defineStore('emoji', {
|
||||||
async getStaticEmoji() {
|
async getStaticEmoji() {
|
||||||
try {
|
try {
|
||||||
// See build/emojis_plugin for more details
|
// 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) => {
|
const emoji = Object.keys(values).reduce((res, groupId) => {
|
||||||
res[groupId] = values[groupId].map((e) => ({
|
res[groupId] = values[groupId].map((e) => ({
|
||||||
|
|
@ -231,7 +227,7 @@ export const useEmojiStore = defineStore('emoji', {
|
||||||
.then((allPacks) => {
|
.then((allPacks) => {
|
||||||
// Sort by key
|
// Sort by key
|
||||||
return Object.keys(allPacks)
|
return Object.keys(allPacks)
|
||||||
.sort()
|
.sort((a, b) => a.localeCompare(b))
|
||||||
.reduce((acc, key) => {
|
.reduce((acc, key) => {
|
||||||
if (key.length === 0) return acc
|
if (key.length === 0) return acc
|
||||||
acc[key] = allPacks[key]
|
acc[key] = allPacks[key]
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue