diff --git a/changelog.d/server-mute-render-modify.fix b/changelog.d/server-mute-render-modify.fix
new file mode 100644
index 000000000..cb94bb826
--- /dev/null
+++ b/changelog.d/server-mute-render-modify.fix
@@ -0,0 +1 @@
+Fixed server-side domain mutes rendering/api calls
diff --git a/changelog.d/theme2-font-cache.fix b/changelog.d/theme2-font-cache.fix
new file mode 100644
index 000000000..d8782b467
--- /dev/null
+++ b/changelog.d/theme2-font-cache.fix
@@ -0,0 +1 @@
+Fix Theme 2 fonts falling back to serif after upgrade
diff --git a/changelog.d/timeline_rendering_status_index_approx.fix b/changelog.d/timeline_rendering_status_index_approx.fix
new file mode 100644
index 000000000..717464c58
--- /dev/null
+++ b/changelog.d/timeline_rendering_status_index_approx.fix
@@ -0,0 +1 @@
+Fixed status index approxmiation in timeline rendering for dynamically changing viewport geometries
diff --git a/src/components/settings_modal/tabs/mutes_and_blocks_tab.vue b/src/components/settings_modal/tabs/mutes_and_blocks_tab.vue
index f89253133..a5395ae36 100644
--- a/src/components/settings_modal/tabs/mutes_and_blocks_tab.vue
+++ b/src/components/settings_modal/tabs/mutes_and_blocks_tab.vue
@@ -153,7 +153,6 @@
- {{ item }}
diff --git a/src/components/timeline/timeline.js b/src/components/timeline/timeline.js
index 17065a9ef..c3f4d381d 100644
--- a/src/components/timeline/timeline.js
+++ b/src/components/timeline/timeline.js
@@ -263,7 +263,10 @@ const Timeline = {
// Start from approximating the index of some visible status by using the
// the center of the screen on the timeline.
- let approxIndex = Math.floor(statuses.length * (centerOfScreen / height))
+ let approxIndex = Math.min(
+ Math.floor(statuses.length * (centerOfScreen / height)),
+ statuses.length - 1,
+ )
let err = statuses[approxIndex].getBoundingClientRect().y
// if we have a previous scroll index that can be used, test if it's
diff --git a/src/modules/users.js b/src/modules/users.js
index 9e24c1bdd..bfa4f0f41 100644
--- a/src/modules/users.js
+++ b/src/modules/users.js
@@ -37,8 +37,10 @@ import {
import {
blockUser as apiBlockUser,
editUserNote as apiEditUserNote,
+ muteDomain as apiMuteDomain,
muteUser as apiMuteUser,
unblockUser as apiUnblockUser,
+ unmuteDomain as apiUnmuteDomain,
unmuteUser as apiUnmuteUser,
fetchBlocks,
fetchDomainMutes,
@@ -170,14 +172,14 @@ const showReblogs = (store, userId) => {
}
const muteDomain = (store, domain) => {
- return muteDomain({
+ return apiMuteDomain({
domain,
credentials: useOAuthStore().token,
}).then(() => store.commit('addDomainMute', domain))
}
const unmuteDomain = (store, domain) => {
- return unmuteDomain({
+ return apiUnmuteDomain({
domain,
credentials: useOAuthStore().token,
}).then(() => store.commit('removeDomainMute', domain))
diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js
index 318978264..d42c5793f 100644
--- a/src/services/style_setter/style_setter.js
+++ b/src/services/style_setter/style_setter.js
@@ -92,6 +92,11 @@ export const adoptStyleSheets = throttle(() => {
const EAGER_STYLE_ID = 'pleroma-eager-styles'
const LAZY_STYLE_ID = 'pleroma-lazy-styles'
+export const hasInvalidCachedThemeRules = (data) =>
+ data
+ .flat()
+ .some((rule) => /--(?:mono)?font:\s*\[object Object\](?:;|$)/i.test(rule))
+
const generateTheme = (inputRuleset, callbacks, debug) => {
const {
onNewRule = () => {
@@ -151,7 +156,8 @@ export const tryLoadCache = async () => {
if (
cache.engineChecksum === getEngineChecksum() &&
cache.checksum !== undefined &&
- cache.checksum === useMergedConfigStore().mergedConfig.themeChecksum
+ cache.checksum === useMergedConfigStore().mergedConfig.themeChecksum &&
+ !hasInvalidCachedThemeRules(cache.data)
) {
const eagerStyles = createStyleSheet(EAGER_STYLE_ID, 10)
const lazyStyles = createStyleSheet(LAZY_STYLE_ID, 20)
diff --git a/src/services/theme_data/theme2_to_theme3.js b/src/services/theme_data/theme2_to_theme3.js
index 8c3ac6ad2..48afe4ad9 100644
--- a/src/services/theme_data/theme2_to_theme3.js
+++ b/src/services/theme_data/theme2_to_theme3.js
@@ -266,7 +266,7 @@ export const convertTheme2To3 = (data) => {
Object.keys(data.fonts || {}).forEach((key) => {
if (!fontsKeys.has(key)) return
if (!data.fonts[key]) return
- const originalFont = data.fonts[key]
+ const originalFont = data.fonts[key].family
const rule = { source: '2to3' }
switch (key) {
diff --git a/test/unit/specs/services/style_setter/style_setter.spec.js b/test/unit/specs/services/style_setter/style_setter.spec.js
new file mode 100644
index 000000000..c1bacdfa1
--- /dev/null
+++ b/test/unit/specs/services/style_setter/style_setter.spec.js
@@ -0,0 +1,24 @@
+import { hasInvalidCachedThemeRules } from 'src/services/style_setter/style_setter.js'
+
+describe('style setter cache', () => {
+ it('rejects cached rules containing serialized objects', () => {
+ expect(
+ hasInvalidCachedThemeRules([
+ ['html { --font: [object Object]; }'],
+ ['.post { --font: sans-serif; }'],
+ ]),
+ ).to.equal(true)
+ })
+
+ it('accepts cached rules containing valid font families', () => {
+ expect(
+ hasInvalidCachedThemeRules([
+ ['html { --font: sans-serif; }'],
+ [
+ '.post { --font: "Atkinson Hyperlegible"; }',
+ '.post::after { content: "[object Object]"; }',
+ ],
+ ]),
+ ).to.equal(false)
+ })
+})
diff --git a/test/unit/specs/services/theme_data/theme2_to_theme3.spec.js b/test/unit/specs/services/theme_data/theme2_to_theme3.spec.js
new file mode 100644
index 000000000..7165d88db
--- /dev/null
+++ b/test/unit/specs/services/theme_data/theme2_to_theme3.spec.js
@@ -0,0 +1,42 @@
+import {
+ basePaletteKeys,
+ convertTheme2To3,
+} from 'src/services/theme_data/theme2_to_theme3.js'
+
+describe('Theme 2 to Theme 3 conversion', () => {
+ it('converts font descriptors to CSS font families', () => {
+ const colors = Object.fromEntries(
+ [...basePaletteKeys].map((key) => [key, '#000000']),
+ )
+ const rules = convertTheme2To3({
+ colors,
+ fonts: {
+ interface: { family: 'sans-serif' },
+ input: { family: 'Open Sans' },
+ post: { family: 'Atkinson Hyperlegible' },
+ postCode: { family: 'monospace' },
+ },
+ })
+
+ expect(rules).to.deep.include({
+ source: '2to3',
+ component: 'Root',
+ directives: { '--font': 'generic | sans-serif' },
+ })
+ expect(rules).to.deep.include({
+ source: '2to3',
+ component: 'Root',
+ directives: { '--monoFont': 'generic | monospace' },
+ })
+ expect(rules).to.deep.include({
+ source: '2to3',
+ component: 'Input',
+ directives: { '--font': 'generic | Open Sans' },
+ })
+ expect(rules).to.deep.include({
+ source: '2to3',
+ component: 'RichContent',
+ directives: { '--font': 'generic | Atkinson Hyperlegible' },
+ })
+ })
+})