Merge pull request 'Synchronized Settings' (#3473) from setttingssync into develop

Reviewed-on: https://git.pleroma.social/pleroma/pleroma-fe/pulls/3473
This commit is contained in:
HJ 2026-04-23 18:56:01 +00:00
commit 9f3c0ec60b
150 changed files with 3942 additions and 1810 deletions

View file

@ -1,8 +1,8 @@
import { parseLinkHeader } from '@web3-storage/parse-link-header'
import escapeHtml from 'escape-html'
import fileTypeService from '../file_type/file_type.service.js'
import punycode from 'punycode.js'
import fileTypeService from '../file_type/file_type.service.js'
import { isStatusNotification } from '../notification_utils/notification_utils.js'
/** NOTICE! **
@ -117,6 +117,7 @@ export const parseUser = (data) => {
if (data.pleroma) {
if (data.pleroma.settings_store) {
output.storage = data.pleroma.settings_store['pleroma-fe']
output.user_highlight = data.pleroma.settings_store['user_highlight']
}
const relationship = data.pleroma.relationship
@ -371,8 +372,8 @@ export const parseStatus = (data) => {
const quoteData = quoteRaw ? parseStatus(quoteRaw) : undefined
output.quote = quoteData
output.quote_id =
data.quote?.id ?? data.quote_id ?? quoteData?.id ?? pleroma.quote_id
output.quote_url = data.quote?.url ?? quoteData?.url ?? pleroma.quote_url
data.quote?.id ?? data.quote_id ?? quoteData?.id ?? pleroma?.quote_id
output.quote_url = data.quote?.url ?? quoteData?.url ?? pleroma?.quote_url
output.in_reply_to_status_id = data.in_reply_to_id
output.in_reply_to_user_id = data.in_reply_to_account_id

View file

@ -23,7 +23,9 @@ export const getAttrs = (tag, filter) => {
.replace(/\/?$/, '')
.trim()
const attrs = Array.from(
innertag.matchAll(/([a-z]+[a-z0-9-]*)(?:=((?:"(?:\\.|[^"\\])*")|(?:'(?:\\.|[^'\\])*')))?/gi),
innertag.matchAll(
/([a-z]+[a-z0-9-]*)(?:=((?:"(?:\\.|[^"\\])*")|(?:'(?:\\.|[^'\\])*')))?/gi,
),
)
.map(([, key, value]) => [key, value])
.map(([k, v]) => {

View file

@ -20,10 +20,8 @@ export const getJsonOrError = async (response) => {
}
export const createApp = (instance) => {
console.log('NAP', instance)
const url = `${instance}/api/v1/apps`
const form = new window.FormData()
console.log(url)
form.append('client_name', 'PleromaFE')
form.append('website', 'https://pleroma.social')

View file

@ -1,7 +1,7 @@
import { showDesktopNotification } from '../desktop_notification_utils/desktop_notification_utils.js'
import { muteFilterHits } from '../status_parser/status_parser.js'
import { useAnnouncementsStore } from 'src/stores/announcements'
import { useAnnouncementsStore } from 'src/stores/announcements.js'
import { useI18nStore } from 'src/stores/i18n.js'
import FaviconService from 'src/services/favicon_service/favicon_service.js'
@ -16,12 +16,7 @@ let cachedBadgeUrl = null
export const notificationsFromStore = (store) => store.state.notifications.data
export const visibleTypes = (store) => {
// When called from within a module we need rootGetters to access wider scope
// however when called from a component (i.e. this.$store) we already have wider scope
const rootGetters = store.rootGetters || store.getters
const { notificationVisibility } = rootGetters.mergedConfig
const visibleTypes = (notificationVisibility) => {
return [
notificationVisibility.likes && 'like',
notificationVisibility.mentions && 'mention',
@ -70,18 +65,23 @@ const sortById = (a, b) => {
}
}
const isMutedNotification = (notification) => {
const isMutedNotification = (muteFilters, notification) => {
if (!notification.status) return false
if (notification.status.muted) return true
return muteFilterHits(notification.status).length > 0
return muteFilterHits(muteFilters, notification.status).length > 0
}
export const maybeShowNotification = (store, notification) => {
export const maybeShowNotification = (
store,
notificationVisibility,
muteFilters,
notification,
) => {
const rootState = store.rootState || store.state
if (notification.seen) return
if (!visibleTypes(store).includes(notification.type)) return
if (notification.type === 'mention' && isMutedNotification(notification))
if (!visibleTypes(notificationVisibility).includes(notification.type)) return
if (notification.type === 'mention' && isMutedNotification(muteFilters, notification))
return
const notificationObject = prepareNotificationObject(
@ -91,26 +91,33 @@ export const maybeShowNotification = (store, notification) => {
showDesktopNotification(rootState, notificationObject)
}
export const filteredNotificationsFromStore = (store, types) => {
export const filteredNotificationsFromStore = (
store,
notificationVisibility,
types,
) => {
// map is just to clone the array since sort mutates it and it causes some issues
const sortedNotifications = notificationsFromStore(store)
.map((_) => _)
.sort(sortById)
// TODO implement sorting elsewhere and make it optional
return sortedNotifications.filter((notification) =>
(types || visibleTypes(store)).includes(notification.type),
(types || visibleTypes(notificationVisibility)).includes(notification.type),
)
}
export const unseenNotificationsFromStore = (store) => {
const rootGetters = store.rootGetters || store.getters
const ignoreInactionableSeen = rootGetters.mergedConfig.ignoreInactionableSeen
return filteredNotificationsFromStore(store).filter(({ seen, type }) => {
if (!ignoreInactionableSeen) return !seen
if (seen) return false
return ACTIONABLE_NOTIFICATION_TYPES.has(type)
})
export const unseenNotificationsFromStore = (
store,
notificationVisibility,
ignoreInactionableSeen,
) => {
return filteredNotificationsFromStore(store, notificationVisibility).filter(
({ seen, type }) => {
if (!ignoreInactionableSeen) return !seen
if (seen) return false
return ACTIONABLE_NOTIFICATION_TYPES.has(type)
},
)
}
export const prepareNotificationObject = (notification, i18n) => {
@ -183,9 +190,8 @@ export const prepareNotificationObject = (notification, i18n) => {
return notifObj
}
export const countExtraNotifications = (store) => {
export const countExtraNotifications = (store, mergedConfig) => {
const rootGetters = store.rootGetters || store.getters
const mergedConfig = rootGetters.mergedConfig
if (!mergedConfig.showExtraNotifications) {
return 0

View file

@ -4,6 +4,7 @@ import { promiseInterval } from '../promise_interval/promise_interval.js'
import { useInstanceStore } from 'src/stores/instance.js'
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
import { useInterfaceStore } from 'src/stores/interface.js'
import { useMergedConfigStore } from 'src/stores/merged_config.js'
const update = ({ store, notifications, older }) => {
store.dispatch('addNewNotifications', { notifications, older })
@ -26,10 +27,9 @@ const mastoApiNotificationTypes = new Set([
const fetchAndUpdate = ({ store, credentials, older = false, since }) => {
const args = { credentials }
const { getters } = store
const rootState = store.rootState || store.state
const timelineData = rootState.notifications
const hideMutedPosts = getters.mergedConfig.hideMutedPosts
const hideMutedPosts = useMergedConfigStore().mergedConfig.hideMutedPosts
if (useInstanceCapabilitiesStore().pleromaChatMessagesAvailable) {
mastoApiNotificationTypes.add('pleroma:chat_mention')

View file

@ -1,10 +1,14 @@
import sum from 'hash-sum'
import localforage from 'localforage'
import { chunk, throttle } from 'lodash'
import { getCssRules } from '../theme_data/css_utils.js'
import { getEngineChecksum, init } from '../theme_data/theme_data_3.service.js'
import { defaultState } from 'src/modules/default_config_state.js'
import { useMergedConfigStore } from 'src/stores/merged_config.js'
import { useSyncConfigStore } from 'src/stores/sync_config.js'
import { ROOT_CONFIG } from 'src/modules/default_config_state.js'
// On platforms where this is not supported, it will return undefined
// Otherwise it will return an array
@ -77,7 +81,7 @@ export const adoptStyleSheets = throttle(() => {
const EAGER_STYLE_ID = 'pleroma-eager-styles'
const LAZY_STYLE_ID = 'pleroma-lazy-styles'
export const generateTheme = (inputRuleset, callbacks, debug) => {
const generateTheme = (inputRuleset, callbacks, debug) => {
const {
onNewRule = () => {
/* no-op */
@ -136,7 +140,11 @@ export const tryLoadCache = async () => {
const cache = await localforage.getItem('pleromafe-theme-cache')
if (!cache) return null
try {
if (cache.engineChecksum === getEngineChecksum()) {
if (
cache.engineChecksum === getEngineChecksum() &&
cache.checksum !== undefined &&
cache.checksum === useMergedConfigStore().mergedConfig.themeChecksum
) {
const eagerStyles = createStyleSheet(EAGER_STYLE_ID, 10)
const lazyStyles = createStyleSheet(LAZY_STYLE_ID, 20)
@ -149,7 +157,7 @@ export const tryLoadCache = async () => {
console.info(`Loaded theme from cache`)
return true
} else {
console.warn("Engine checksum doesn't match, cache not usable, clearing")
console.warn("Checksums don't match, cache not usable, clearing")
localStorage.removeItem('pleroma-fe-theme-cache')
}
} catch (e) {
@ -195,10 +203,17 @@ export const applyTheme = (
onLazyFinished() {
lazyStyles.ready = true
adoptStyleSheets()
const data = [eagerStyles.rules, lazyStyles.rules]
const checksum = sum(data)
const cache = {
checksum,
engineChecksum: getEngineChecksum(),
data: [eagerStyles.rules, lazyStyles.rules],
data,
}
useSyncConfigStore().setSimplePrefAndSave({
path: 'themeChecksum',
value: checksum,
})
onFinish(cache)
localforage.setItem('pleromafe-theme-cache', cache)
console.info('Theme cache stored')
@ -253,7 +268,7 @@ const extractStyleConfig = ({
return result
}
const defaultStyleConfig = extractStyleConfig(defaultState)
const defaultStyleConfig = extractStyleConfig(ROOT_CONFIG)
export const applyStyleConfig = (input) => {
const config = extractStyleConfig(input)

View file

@ -41,7 +41,7 @@ function subscribePush(registration, isEnabled, vapidPublicKey) {
function unsubscribePush(registration) {
return registration.pushManager.getSubscription().then((subscription) => {
if (subscription === null) {
return
return Promise.resolve('No subscription')
}
return subscription.unsubscribe()
})
@ -158,23 +158,23 @@ export function registerPushNotifications(
export function unregisterPushNotifications(token) {
if (isPushSupported()) {
Promise.all([
deleteSubscriptionFromBackEnd(token),
getOrCreateServiceWorker()
.then((registration) => {
return unsubscribePush(registration).then((result) => [
registration,
result,
])
})
.then(([, unsubResult]) => {
if (!unsubResult) {
console.warn("Push subscription cancellation wasn't successful")
}
}),
]).catch((e) =>
console.warn(`Failed to disable Web Push Notifications: ${e.message}`),
)
getOrCreateServiceWorker()
.then((registration) => {
return unsubscribePush(registration).then((result) => [
registration,
result,
])
})
.then(([, unsubResult]) => {
if (unsubResult === 'No subscription') return
if (!unsubResult) {
console.warn("Push subscription cancellation wasn't successful")
}
return deleteSubscriptionFromBackEnd(token)
})
.catch((e) => {
console.warn(`Failed to disable Web Push Notifications: ${e.message}`)
})
}
}

View file

@ -6,6 +6,7 @@ import { promiseInterval } from '../promise_interval/promise_interval.js'
import { useInstanceStore } from 'src/stores/instance.js'
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
import { useInterfaceStore } from 'src/stores/interface.js'
import { useMergedConfigStore } from 'src/stores/merged_config.js'
const update = ({
store,
@ -44,9 +45,9 @@ const fetchAndUpdate = ({
}) => {
const args = { timeline, credentials }
const rootState = store.rootState || store.state
const { getters } = store
const timelineData = rootState.statuses.timelines[camelCase(timeline)]
const { hideMutedPosts, replyVisibility } = getters.mergedConfig
const { hideMutedPosts, replyVisibility } =
useMergedConfigStore().mergedConfig
const loggedIn = !!rootState.users.currentUser
if (older) {

View file

@ -2,7 +2,7 @@ import { hex2rgb } from '../color_convert/color_convert.js'
const highlightStyle = (prefs) => {
if (prefs === undefined) return
const { color, type } = prefs
const { color = '#FFFFFF', type } = prefs
if (typeof color !== 'string') return
const rgb = hex2rgb(color)
if (rgb == null) return