Merge branch 'chat-refactor' into shigusegubu-themes3
This commit is contained in:
commit
e4429c57cf
20 changed files with 147 additions and 135 deletions
|
|
@ -3,7 +3,11 @@ All notable changes to this project will be documented in this file.
|
|||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
|
||||
## 2.11
|
||||
## 2.11.1
|
||||
### Fixed
|
||||
- User note not working
|
||||
|
||||
## 2.11.0
|
||||
### Added
|
||||
- Initial MFM rendering support
|
||||
- Button to remove all drafts
|
||||
|
|
|
|||
4
changelog.d/2-11-2.fix
Normal file
4
changelog.d/2-11-2.fix
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
Fixed crashing on Firefox 153
|
||||
Fixed serif-fonts being used by default in some cases
|
||||
Fixed font select component showing and using objects instead of strings
|
||||
Fixed generic ('sans-serif', 'monospace' etc) fonts not working
|
||||
1
changelog.d/extra-notifications-style.fix
Normal file
1
changelog.d/extra-notifications-style.fix
Normal file
|
|
@ -0,0 +1 @@
|
|||
Fix style of extra notifications
|
||||
1
changelog.d/mfa-fixes.fix
Normal file
1
changelog.d/mfa-fixes.fix
Normal file
|
|
@ -0,0 +1 @@
|
|||
Fix MFA login and recovery code authentication.
|
||||
1
changelog.d/no-crash-on-bad-style.fix
Normal file
1
changelog.d/no-crash-on-bad-style.fix
Normal file
|
|
@ -0,0 +1 @@
|
|||
Do not crash even on css rule insertion failure
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "pleroma_fe",
|
||||
"version": "2.11.0",
|
||||
"version": "2.11.1",
|
||||
"description": "Pleroma frontend, the default frontend of Pleroma social network server",
|
||||
"author": "Pleroma contributors <https://git.pleroma.social/pleroma/pleroma-fe/src/CONTRIBUTORS.md>",
|
||||
"private": false,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
import AuthForm from 'src/components/auth_form/auth_form.js'
|
||||
import BookmarkTimeline from 'src/components/bookmark_timeline/bookmark_timeline.vue'
|
||||
import BubbleTimeline from 'src/components/bubble_timeline/bubble_timeline.vue'
|
||||
|
|
@ -68,9 +66,7 @@ export default (store) => {
|
|||
{
|
||||
name: 'conversation2',
|
||||
path: '/conversation/:statusId',
|
||||
component: defineAsyncComponent(
|
||||
() => import('src/components/chat_view/chat_view.vue'),
|
||||
),
|
||||
component: () => import('src/components/chat_view/chat_view.vue'),
|
||||
props: true,
|
||||
meta: { dontScroll: true },
|
||||
beforeEnter: validateAuthenticatedRoute,
|
||||
|
|
@ -91,23 +87,18 @@ export default (store) => {
|
|||
{
|
||||
name: 'external-user-profile',
|
||||
path: '/users/$:id',
|
||||
component: defineAsyncComponent(
|
||||
() => import('src/components/user_profile/user_profile.vue'),
|
||||
),
|
||||
component: () => import('src/components/user_profile/user_profile.vue'),
|
||||
},
|
||||
{
|
||||
name: 'user-profile-admin-view',
|
||||
path: '/users/$:id/admin_view',
|
||||
component: defineAsyncComponent(
|
||||
() => import('src/components/user_profile/user_profile_admin_view.vue'),
|
||||
),
|
||||
component: () =>
|
||||
import('src/components/user_profile/user_profile_admin_view.vue'),
|
||||
},
|
||||
{
|
||||
name: 'interactions',
|
||||
path: '/users/:username/interactions',
|
||||
component: defineAsyncComponent(
|
||||
() => import('src/components/interactions/interactions.vue'),
|
||||
),
|
||||
component: () => import('src/components/interactions/interactions.vue'),
|
||||
beforeEnter: validateAuthenticatedRoute,
|
||||
},
|
||||
{
|
||||
|
|
@ -119,39 +110,31 @@ export default (store) => {
|
|||
{
|
||||
name: 'registration',
|
||||
path: '/registration',
|
||||
component: defineAsyncComponent(
|
||||
() => import('src/components/registration/registration.vue'),
|
||||
),
|
||||
component: () => import('src/components/registration/registration.vue'),
|
||||
},
|
||||
{
|
||||
name: 'password-reset',
|
||||
path: '/password-reset',
|
||||
component: defineAsyncComponent(
|
||||
() => import('src/components/password_reset/password_reset.vue'),
|
||||
),
|
||||
component: () =>
|
||||
import('src/components/password_reset/password_reset.vue'),
|
||||
props: true,
|
||||
},
|
||||
{
|
||||
name: 'registration-token',
|
||||
path: '/registration/:token',
|
||||
component: defineAsyncComponent(
|
||||
() => import('src/components/registration/registration.vue'),
|
||||
),
|
||||
component: () => import('src/components/registration/registration.vue'),
|
||||
},
|
||||
{
|
||||
name: 'friend-requests',
|
||||
path: '/friend-requests',
|
||||
component: defineAsyncComponent(
|
||||
() => import('src/components/follow_requests/follow_requests.vue'),
|
||||
),
|
||||
component: () =>
|
||||
import('src/components/follow_requests/follow_requests.vue'),
|
||||
beforeEnter: validateAuthenticatedRoute,
|
||||
},
|
||||
{
|
||||
name: 'notifications',
|
||||
path: '/:username/notifications',
|
||||
component: defineAsyncComponent(
|
||||
() => import('src/components/notifications/notifications.vue'),
|
||||
),
|
||||
component: () => import('src/components/notifications/notifications.vue'),
|
||||
props: () => ({ disableTeleport: true }),
|
||||
beforeEnter: validateAuthenticatedRoute,
|
||||
},
|
||||
|
|
@ -163,98 +146,74 @@ export default (store) => {
|
|||
{
|
||||
name: 'shout-panel',
|
||||
path: '/shout-panel',
|
||||
component: defineAsyncComponent(
|
||||
() => import('src/components/shout_panel/shout_panel.vue'),
|
||||
),
|
||||
component: () => import('src/components/shout_panel/shout_panel.vue'),
|
||||
props: () => ({ floating: false }),
|
||||
},
|
||||
{
|
||||
name: 'oauth-callback',
|
||||
path: '/oauth-callback',
|
||||
component: defineAsyncComponent(
|
||||
() => import('src/components/oauth_callback/oauth_callback.vue'),
|
||||
),
|
||||
component: () =>
|
||||
import('src/components/oauth_callback/oauth_callback.vue'),
|
||||
props: (route) => ({ code: route.query.code }),
|
||||
},
|
||||
{
|
||||
name: 'search',
|
||||
path: '/search',
|
||||
component: defineAsyncComponent(
|
||||
() => import('src/components/search/search.vue'),
|
||||
),
|
||||
component: () => import('src/components/search/search.vue'),
|
||||
props: (route) => ({ query: route.query.query }),
|
||||
},
|
||||
{
|
||||
name: 'who-to-follow',
|
||||
path: '/who-to-follow',
|
||||
component: defineAsyncComponent(
|
||||
() => import('src/components/who_to_follow/who_to_follow.vue'),
|
||||
),
|
||||
component: () => import('src/components/who_to_follow/who_to_follow.vue'),
|
||||
beforeEnter: validateAuthenticatedRoute,
|
||||
},
|
||||
{
|
||||
name: 'about',
|
||||
path: '/about',
|
||||
component: defineAsyncComponent(
|
||||
() => import('src/components/about/about.vue'),
|
||||
),
|
||||
component: () => import('src/components/about/about.vue'),
|
||||
},
|
||||
{
|
||||
name: 'announcements',
|
||||
path: '/announcements',
|
||||
component: defineAsyncComponent(
|
||||
() =>
|
||||
import('src/components/announcements_page/announcements_page.vue'),
|
||||
),
|
||||
component: () =>
|
||||
import('src/components/announcements_page/announcements_page.vue'),
|
||||
},
|
||||
{
|
||||
name: 'drafts',
|
||||
path: '/drafts',
|
||||
component: defineAsyncComponent(
|
||||
() => import('src/components/drafts/drafts.vue'),
|
||||
),
|
||||
component: () => import('src/components/drafts/drafts.vue'),
|
||||
},
|
||||
{
|
||||
name: 'user-profile',
|
||||
path: '/users/:name',
|
||||
component: defineAsyncComponent(
|
||||
() => import('src/components/user_profile/user_profile.vue'),
|
||||
),
|
||||
component: () => import('src/components/user_profile/user_profile.vue'),
|
||||
},
|
||||
{
|
||||
name: 'legacy-user-profile',
|
||||
path: '/:name',
|
||||
component: defineAsyncComponent(
|
||||
() => import('src/components/user_profile/user_profile.vue'),
|
||||
),
|
||||
component: () => import('src/components/user_profile/user_profile.vue'),
|
||||
},
|
||||
{
|
||||
name: 'lists',
|
||||
path: '/lists',
|
||||
component: defineAsyncComponent(
|
||||
() => import('src/components/lists/lists.vue'),
|
||||
),
|
||||
component: () => import('src/components/lists/lists.vue'),
|
||||
},
|
||||
{
|
||||
name: 'lists-timeline',
|
||||
path: '/lists/:id',
|
||||
component: defineAsyncComponent(
|
||||
() => import('src/components/lists_timeline/lists_timeline.vue'),
|
||||
),
|
||||
component: () =>
|
||||
import('src/components/lists_timeline/lists_timeline.vue'),
|
||||
},
|
||||
{
|
||||
name: 'lists-edit',
|
||||
path: '/lists/:id/edit',
|
||||
component: defineAsyncComponent(
|
||||
() => import('src/components/lists_edit/lists_edit.vue'),
|
||||
),
|
||||
component: () => import('src/components/lists_edit/lists_edit.vue'),
|
||||
},
|
||||
{
|
||||
name: 'lists-new',
|
||||
path: '/lists/new',
|
||||
component: defineAsyncComponent(
|
||||
() => import('src/components/lists_edit/lists_edit.vue'),
|
||||
),
|
||||
component: () => import('src/components/lists_edit/lists_edit.vue'),
|
||||
},
|
||||
{
|
||||
name: 'edit-navigation',
|
||||
|
|
@ -266,19 +225,14 @@ export default (store) => {
|
|||
{
|
||||
name: 'bookmark-folders',
|
||||
path: '/bookmark_folders',
|
||||
component: defineAsyncComponent(
|
||||
() => import('src/components/bookmark_folders/bookmark_folders.vue'),
|
||||
),
|
||||
component: () =>
|
||||
import('src/components/bookmark_folders/bookmark_folders.vue'),
|
||||
},
|
||||
{
|
||||
name: 'bookmark-folder-new',
|
||||
path: '/bookmarks/new-folder',
|
||||
component: defineAsyncComponent(
|
||||
() =>
|
||||
import(
|
||||
'src/components/bookmark_folder_edit/bookmark_folder_edit.vue'
|
||||
),
|
||||
),
|
||||
component: () =>
|
||||
import('src/components/bookmark_folder_edit/bookmark_folder_edit.vue'),
|
||||
},
|
||||
{
|
||||
name: 'bookmark-folder',
|
||||
|
|
@ -288,12 +242,8 @@ export default (store) => {
|
|||
{
|
||||
name: 'bookmark-folder-edit',
|
||||
path: '/bookmarks/:id/edit',
|
||||
component: defineAsyncComponent(
|
||||
() =>
|
||||
import(
|
||||
'src/components/bookmark_folder_edit/bookmark_folder_edit.vue'
|
||||
),
|
||||
),
|
||||
component: () =>
|
||||
import('src/components/bookmark_folder_edit/bookmark_folder_edit.vue'),
|
||||
},
|
||||
]
|
||||
|
||||
|
|
@ -301,19 +251,16 @@ export default (store) => {
|
|||
routes = routes.concat([
|
||||
{
|
||||
name: 'chat',
|
||||
path: '/users/:username/chats/:recipient_id',
|
||||
component: defineAsyncComponent(
|
||||
() => import('src/components/chat_view/chat_view.vue'),
|
||||
),
|
||||
path: '/users/:username/chats/:chatUserId',
|
||||
component: () => import('src/components/chat_view/chat_view.vue'),
|
||||
meta: { dontScroll: false },
|
||||
props: true,
|
||||
beforeEnter: validateAuthenticatedRoute,
|
||||
},
|
||||
{
|
||||
name: 'chats',
|
||||
path: '/users/:username/chats',
|
||||
component: defineAsyncComponent(
|
||||
() => import('src/components/chat_list/chat_list.vue'),
|
||||
),
|
||||
component: () => import('src/components/chat_list/chat_list.vue'),
|
||||
meta: { dontScroll: false },
|
||||
beforeEnter: validateAuthenticatedRoute,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -76,9 +76,7 @@ const ChatMessage = {
|
|||
return !!this.message.user
|
||||
},
|
||||
authorId() {
|
||||
return this.isStatus
|
||||
? this.message.user.id
|
||||
: this.message.account_id
|
||||
return this.isStatus ? this.message.user.id : this.message.account_id
|
||||
},
|
||||
author() {
|
||||
return this.$store.getters.findUser(this.authorId)
|
||||
|
|
|
|||
|
|
@ -27,8 +27,8 @@ import {
|
|||
readChat,
|
||||
sendChatMessage,
|
||||
} from 'src/api/chats.js'
|
||||
import { WSConnectionStatus } from 'src/api/websocket.js'
|
||||
import { fetchConversation, fetchStatus } from 'src/api/public.js'
|
||||
import { WSConnectionStatus } from 'src/api/websocket.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { faChevronDown, faChevronLeft } from '@fortawesome/free-solid-svg-icons'
|
||||
|
|
@ -53,7 +53,14 @@ const Chat = {
|
|||
PostStatusForm,
|
||||
},
|
||||
props: {
|
||||
statusId: String,
|
||||
statusId: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
chatUserId: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
testMode: Boolean,
|
||||
},
|
||||
data() {
|
||||
|
|
@ -180,11 +187,20 @@ const Chat = {
|
|||
},
|
||||
$route: async function (newVal) {
|
||||
if (this.messagesIndex[newVal.params.statusId]) {
|
||||
const focused = document.getElementById(`chatmessage-${this.$route.params.statusId}`)
|
||||
const focused = document.getElementById(
|
||||
`chatmessage-${this.$route.params.statusId}`,
|
||||
)
|
||||
if (focused?.getBoundingClientRect == null) return
|
||||
const bottomBoundary = window.innerHeight - this.$refs.footer.clientHeight
|
||||
const topBoundary = this.$refs.header.clientHeight + document.getElementById('nav').clientHeight
|
||||
const margin = Number(window.getComputedStyle(this.$refs.messageList.$el).gap.replace('px',''))
|
||||
const bottomBoundary =
|
||||
window.innerHeight - this.$refs.footer.clientHeight
|
||||
const topBoundary =
|
||||
this.$refs.header.clientHeight +
|
||||
document.getElementById('nav').clientHeight
|
||||
const margin = Number(
|
||||
window
|
||||
.getComputedStyle(this.$refs.messageList.$el)
|
||||
.gap.replace('px', ''),
|
||||
)
|
||||
|
||||
const rect = focused.getBoundingClientRect()
|
||||
const scrollAmount = (() => {
|
||||
|
|
@ -293,7 +309,9 @@ const Chat = {
|
|||
if (this.isConversation) {
|
||||
const [
|
||||
{ data: status },
|
||||
{ data: { ancestors, descendants } }
|
||||
{
|
||||
data: { ancestors, descendants },
|
||||
},
|
||||
] = await Promise.all([
|
||||
fetchStatus({
|
||||
id: this.statusId,
|
||||
|
|
@ -302,7 +320,7 @@ const Chat = {
|
|||
fetchConversation({
|
||||
id: this.statusId,
|
||||
credentials: useOAuthStore().token,
|
||||
})
|
||||
}),
|
||||
])
|
||||
messages = [...ancestors, status, ...descendants]
|
||||
} else {
|
||||
|
|
@ -337,17 +355,21 @@ const Chat = {
|
|||
// full height of the scrollable container.
|
||||
// If this is the case, we want to fetch the messages until the scrollable container
|
||||
// is fully populated so that the user has the ability to scroll up and load the history.
|
||||
if (!isScrollable() && messages.length > 0) {
|
||||
//
|
||||
// Conversation fetching doesn't support pagination and spews out everything at once
|
||||
// so we both can't and don't need to fetch previous posts
|
||||
if (!this.isConversation && !isScrollable() && messages.length > 0) {
|
||||
this.fetchChat({
|
||||
maxId: this.minId,
|
||||
})
|
||||
}
|
||||
},
|
||||
async startFetching() {
|
||||
console.log(this.statusId, this.chatUserId)
|
||||
if (!this.isConversation) {
|
||||
try {
|
||||
const { data } = await getOrCreateChat({
|
||||
accountId: this.recipientId,
|
||||
accountId: this.chatUserId,
|
||||
credentials: useOAuthStore().token,
|
||||
})
|
||||
this.$store.commit('addNewUsers', [data.account])
|
||||
|
|
@ -378,7 +400,7 @@ const Chat = {
|
|||
const message = newMessages[i]
|
||||
|
||||
// Sanity check
|
||||
if (!this.isConversation && (message.chat_id !== this.chat.id)) {
|
||||
if (!this.isConversation && message.chat_id !== this.chat.id) {
|
||||
console.warn(
|
||||
`Chat message doesn't belong to current chat (id: ${this.chat.id})!!`,
|
||||
message,
|
||||
|
|
@ -516,7 +538,10 @@ const Chat = {
|
|||
// Event handlers
|
||||
onPosted(data) {
|
||||
this.explicitReplyStatus = null
|
||||
this.$router.push({ name: 'conversation2', params: { statusId: data.id } })
|
||||
this.$router.push({
|
||||
name: 'conversation2',
|
||||
params: { statusId: data.id },
|
||||
})
|
||||
},
|
||||
handleVisibilityChange() {
|
||||
this.$nextTick(() => {
|
||||
|
|
|
|||
|
|
@ -67,7 +67,10 @@
|
|||
</div>
|
||||
</span>
|
||||
</div>
|
||||
<div class="auto-reply-to-section">
|
||||
<div
|
||||
v-if="isConversation"
|
||||
class="auto-reply-to-section"
|
||||
>
|
||||
<div class="reply-to-text">
|
||||
{{ explicitReplyStatus ? $t('status.reply_to_selected') : $t('status.reply_to_last') }}
|
||||
<button
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="ExtraNotifications">
|
||||
<div class="ExtraNotifications panel-body">
|
||||
<div
|
||||
v-if="shouldShowChats"
|
||||
class="notification unseen"
|
||||
|
|
@ -87,6 +87,11 @@
|
|||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
|
||||
&.panel-body::before {
|
||||
content: '';
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.notification {
|
||||
width: 100%;
|
||||
border-bottom: 1px solid;
|
||||
|
|
|
|||
|
|
@ -63,8 +63,8 @@ const LoginForm = {
|
|||
})
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error.errorData?.error === 'mfa_required') {
|
||||
this.requireMFA({ settings: error })
|
||||
if (error.errorData === 'mfa_required') {
|
||||
this.requireMFA({ settings: error.error })
|
||||
} else if (error.identifier === 'password_reset_required') {
|
||||
this.$router.push({
|
||||
name: 'password-reset',
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ export default {
|
|||
}
|
||||
|
||||
verifyRecoveryCode(data)
|
||||
.then((result) => {
|
||||
.then(({ data: result }) => {
|
||||
this.login(result).then(() => {
|
||||
this.$router.push({ name: 'friends' })
|
||||
})
|
||||
|
|
|
|||
|
|
@ -690,6 +690,7 @@ const PostStatusForm = {
|
|||
.then((data) => {
|
||||
this.abandonDraft()
|
||||
this.clearStatus()
|
||||
this.updateIdempotencyKey()
|
||||
this.$emit('posted', data)
|
||||
})
|
||||
.catch((error) => {
|
||||
|
|
@ -1007,6 +1008,7 @@ const PostStatusForm = {
|
|||
this.saveable = false
|
||||
if (!this.shouldAutoSaveDraft) {
|
||||
this.clearStatus()
|
||||
this.updateIdempotencyKey()
|
||||
this.$emit('draft-done')
|
||||
}
|
||||
})
|
||||
|
|
@ -1016,6 +1018,7 @@ const PostStatusForm = {
|
|||
this.saveable = false
|
||||
if (!this.shouldAutoSaveDraft) {
|
||||
this.clearStatus()
|
||||
this.updateIdempotencyKey()
|
||||
this.$emit('draft-done')
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -82,13 +82,13 @@ const ScopeSelector = {
|
|||
changeVis(scope) {
|
||||
this.currentScope = scope
|
||||
this.$emit('change', scope)
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
originalScope(newVal) {
|
||||
this.currentScope = newVal
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default ScopeSelector
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@
|
|||
:model-value="mergedConfig.fontInterface"
|
||||
name="ui"
|
||||
:label="$t('settings.style.fonts.components_inline.interface')"
|
||||
:fallback="{ family: 'sans-serif' }"
|
||||
fallback="sans-serif"
|
||||
no-inherit="1"
|
||||
@update:model-value="v => updateFont('fontInterface', v)"
|
||||
/>
|
||||
|
|
@ -77,7 +77,7 @@
|
|||
<FontControl
|
||||
:model-value="mergedConfig.fontInput"
|
||||
name="input"
|
||||
:fallback="{ family: 'inherit' }"
|
||||
fallback="inherit"
|
||||
:label="$t('settings.style.fonts.components_inline.input')"
|
||||
@update:model-value="v => updateFont('fontInput', v)"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@
|
|||
<FontControl
|
||||
:model-value="mergedConfig.fontPosts"
|
||||
name="post"
|
||||
:fallback="{ family: 'inherit' }"
|
||||
fallback="inherit"
|
||||
:label="$t('settings.style.fonts.components.post')"
|
||||
@update:model-value="v => updateFont('fontPosts', v)"
|
||||
/>
|
||||
|
|
@ -67,7 +67,7 @@
|
|||
<FontControl
|
||||
:model-value="mergedConfig.fontMonospace"
|
||||
name="postCode"
|
||||
:fallback="{ family: 'monospace' }"
|
||||
fallback="monospace"
|
||||
:label="$t('settings.style.fonts.components.monospace')"
|
||||
@update:model-value="v => updateFont('fontMonospace', v)"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -17,21 +17,21 @@ import {
|
|||
faCheck,
|
||||
faChevronDown,
|
||||
faChevronRight,
|
||||
faComments,
|
||||
faExternalLinkAlt,
|
||||
faEye,
|
||||
faEyeSlash,
|
||||
faHistory,
|
||||
faMinus,
|
||||
faPencil,
|
||||
faPlus,
|
||||
faReply,
|
||||
faRetweet,
|
||||
faShareAlt,
|
||||
faStar,
|
||||
faThumbtack,
|
||||
faPencil,
|
||||
faTimes,
|
||||
faWrench,
|
||||
faComments,
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
library.add(
|
||||
|
|
|
|||
|
|
@ -32,16 +32,17 @@ export const createStyleSheet = (id, priority = 1000) => {
|
|||
newRule = newRule.replace(/backdrop-filter:[^;]+;/g, '') // Remove backdrop-filter
|
||||
}
|
||||
|
||||
// firefox doesn't like invalid selectors
|
||||
if (
|
||||
!CSS.supports?.('selector(::-webkit-scrollbar)') &&
|
||||
!CSS.supports?.('selector(::-webkit-scrollbar-button)') &&
|
||||
!CSS.supports?.('selector(::-webkit-resizer)') &&
|
||||
!CSS.supports?.('selector(::-webkit-scrollbar-thumb)') &&
|
||||
newRule.startsWith('::-webkit')
|
||||
) {
|
||||
return
|
||||
if (newRule.startsWith('::-webkit')) {
|
||||
if (typeof CSS.supports !== 'function') return
|
||||
// firefox doesn't like invalid selectors
|
||||
const fullWebkitScrollbarSupport =
|
||||
CSS.supports('selector(::-webkit-scrollbar)') &&
|
||||
CSS.supports('selector(::-webkit-scrollbar-button)') &&
|
||||
CSS.supports('selector(::-webkit-resizer)') &&
|
||||
CSS.supports('selector(::-webkit-scrollbar-thumb)')
|
||||
if (!fullWebkitScrollbarSupport) return
|
||||
}
|
||||
|
||||
this.rules.push(
|
||||
newRule.replace(/var\(--shadowFilter\)[^;]*;/g, ''), // Remove shadowFilter references
|
||||
)
|
||||
|
|
@ -59,7 +60,13 @@ export const adoptStyleSheets = throttle(() => {
|
|||
.sort((a, b) => a.priority - b.priority)
|
||||
.map((sheet) => {
|
||||
const css = new CSSStyleSheet()
|
||||
sheet.rules.forEach((r) => css.insertRule(r))
|
||||
sheet.rules.forEach((r) => {
|
||||
try {
|
||||
css.insertRule(r)
|
||||
} catch (e) {
|
||||
console.warn('Error inserting rule:', e, r)
|
||||
}
|
||||
})
|
||||
return css
|
||||
})
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,16 @@ import {
|
|||
} from 'src/services/theme_data/theme_data.service.js'
|
||||
import { convertTheme2To3 } from 'src/services/theme_data/theme2_to_theme3.js'
|
||||
|
||||
const GENERIC_FONT_NAMES = new Set([
|
||||
'serif',
|
||||
'sans-serif',
|
||||
'system-ui',
|
||||
'cursive',
|
||||
'fantasy',
|
||||
'math',
|
||||
'monospace',
|
||||
])
|
||||
|
||||
export const useInterfaceStore = defineStore('interface', {
|
||||
state: () => ({
|
||||
localFonts: null,
|
||||
|
|
@ -246,7 +256,7 @@ export const useInterfaceStore = defineStore('interface', {
|
|||
}
|
||||
},
|
||||
setFontsList(value) {
|
||||
this.localFonts = [...new Set(value.map((font) => font)).values()]
|
||||
this.localFonts = [...new Set(value.map(({ family }) => family)).values()]
|
||||
},
|
||||
queryLocalFonts() {
|
||||
if (this.localFonts !== null) return
|
||||
|
|
@ -706,11 +716,14 @@ export const useInterfaceStore = defineStore('interface', {
|
|||
Object.entries(fontMap).forEach(([font, component]) => {
|
||||
const family = mergedConfig[`font${font}`]
|
||||
const variable = font === 'Monospace' ? '--monoFont' : '--font'
|
||||
if (family) {
|
||||
if (typeof family === 'string') {
|
||||
const familyString = GENERIC_FONT_NAMES.has(family)
|
||||
? family
|
||||
: `"${family}"`
|
||||
hacks.push({
|
||||
component,
|
||||
directives: {
|
||||
[variable]: `generic | "${family}"`,
|
||||
[variable]: `generic | ${familyString}`,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue