Merge remote-tracking branch 'origin/develop' into users-statuses-pinia
This commit is contained in:
commit
657f405ee0
27 changed files with 167 additions and 20 deletions
1
changelog.d/actions-extra-tap.fix
Normal file
1
changelog.d/actions-extra-tap.fix
Normal file
|
|
@ -0,0 +1 @@
|
|||
fixed tapping "Mute..." and "Change visiblity" (admin action) in extra status actions closing the dropdown
|
||||
1
changelog.d/fix-domain-mute.fix
Normal file
1
changelog.d/fix-domain-mute.fix
Normal file
|
|
@ -0,0 +1 @@
|
|||
domain mute fixedw
|
||||
1
changelog.d/followers-list.fix
Normal file
1
changelog.d/followers-list.fix
Normal file
|
|
@ -0,0 +1 @@
|
|||
Fix followers list showing followed users as non-followed
|
||||
1
changelog.d/moderation-tools.fix
Normal file
1
changelog.d/moderation-tools.fix
Normal file
|
|
@ -0,0 +1 @@
|
|||
fix moderation tools button possibly not appearing for admins
|
||||
1
changelog.d/moderation.fix
Normal file
1
changelog.d/moderation.fix
Normal file
|
|
@ -0,0 +1 @@
|
|||
Fixed moderation actions requiring confirmation closing user popover
|
||||
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/theme-index-html-fallback.fix
Normal file
1
changelog.d/theme-index-html-fallback.fix
Normal file
|
|
@ -0,0 +1 @@
|
|||
Fix theme lists failing to load when custom resource indexes are unavailable
|
||||
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/themes2-font.fix
Normal file
1
changelog.d/themes2-font.fix
Normal file
|
|
@ -0,0 +1 @@
|
|||
Fix Themes 2.0 applying incorrect fonts
|
||||
1
changelog.d/themes3.fix
Normal file
1
changelog.d/themes3.fix
Normal file
|
|
@ -0,0 +1 @@
|
|||
Fixed themes 3 not loading in appearance tab
|
||||
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
|
||||
|
|
@ -67,6 +67,7 @@ export const promisedRequest = async ({
|
|||
url,
|
||||
payload,
|
||||
formData,
|
||||
forceContentType,
|
||||
cache,
|
||||
credentials,
|
||||
headers = {},
|
||||
|
|
@ -75,7 +76,7 @@ export const promisedRequest = async ({
|
|||
method,
|
||||
credentials: 'same-origin',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Accept: forceContentType ?? 'application/json',
|
||||
...headers,
|
||||
},
|
||||
}
|
||||
|
|
@ -110,7 +111,7 @@ export const promisedRequest = async ({
|
|||
)
|
||||
if (contentLength === 0) return null
|
||||
|
||||
switch (contentType) {
|
||||
switch (forceContentType ?? contentType) {
|
||||
case 'text/plain':
|
||||
return await response.text()
|
||||
case 'application/json':
|
||||
|
|
|
|||
|
|
@ -117,9 +117,17 @@ export const fetchUserByName = ({ name, credentials }) =>
|
|||
|
||||
export const fetchFriends = ({ id, maxId, sinceId, limit = 20, credentials }) =>
|
||||
promisedRequest({
|
||||
url: MASTODON_FOLLOWING_URL(id, { maxId, sinceId, limit }),
|
||||
url: MASTODON_FOLLOWING_URL(id, {
|
||||
maxId,
|
||||
sinceId,
|
||||
limit,
|
||||
withRelationships: true,
|
||||
}),
|
||||
credentials,
|
||||
}).then(({ data, ...rest }) => ({ ...rest, data: data.map(parseUser) }))
|
||||
}).then(({ data, ...rest }) => ({
|
||||
...rest,
|
||||
data: data.map(parseUser),
|
||||
}))
|
||||
|
||||
export const fetchFollowers = ({
|
||||
id,
|
||||
|
|
@ -136,7 +144,10 @@ export const fetchFollowers = ({
|
|||
withRelationships: true,
|
||||
}),
|
||||
credentials,
|
||||
}).then(({ data, ...rest }) => ({ ...rest, data: data.map(parseUser) }))
|
||||
}).then(({ data, ...rest }) => ({
|
||||
...rest,
|
||||
data: data.map(parseUser),
|
||||
}))
|
||||
|
||||
export const fetchConversation = ({ id, credentials }) =>
|
||||
promisedRequest({
|
||||
|
|
|
|||
|
|
@ -410,7 +410,6 @@ export const exportFriends = ({ id, credentials }) => {
|
|||
id,
|
||||
maxId,
|
||||
credentials,
|
||||
withRelationships: true,
|
||||
})
|
||||
friends = [...friends, ...users]
|
||||
if (users.length === 0) {
|
||||
|
|
|
|||
|
|
@ -517,8 +517,7 @@ const ModerationTools = {
|
|||
setOpen(value) {
|
||||
this.open = value
|
||||
},
|
||||
maybeShowConfirm(close, { group, name, action, value }) {
|
||||
close()
|
||||
maybeShowConfirm({ group, name, action, value }) {
|
||||
this.confirmDialogName = name
|
||||
this.confirmDialogGroup = group
|
||||
this.confirmDialogAction = () => action()
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
@show="setOpen(true)"
|
||||
@close="setOpen(false)"
|
||||
>
|
||||
<template #content="{close}">
|
||||
<template #content>
|
||||
<div class="dropdown-menu">
|
||||
<template v-for="(entry, index) in entries">
|
||||
<div
|
||||
|
|
@ -26,7 +26,7 @@
|
|||
>
|
||||
<button
|
||||
class="main-button"
|
||||
@click="() => maybeShowConfirm(close, entry)"
|
||||
@click="() => maybeShowConfirm(entry)"
|
||||
>
|
||||
<span
|
||||
v-if="entry.checkbox"
|
||||
|
|
|
|||
|
|
@ -78,8 +78,8 @@
|
|||
v-model="expiryUnit"
|
||||
unstyled="true"
|
||||
class="expiry-unit"
|
||||
@change="expiryAmountChange"
|
||||
:aria-label="$t('polls.expiry_unit')"
|
||||
@change="expiryAmountChange"
|
||||
>
|
||||
<option
|
||||
v-for="unit in expiryUnits"
|
||||
|
|
|
|||
|
|
@ -580,8 +580,11 @@ const PostStatusForm = {
|
|||
}),
|
||||
},
|
||||
watch: {
|
||||
isDirty(newVal, oldVal) {
|
||||
this.statusChanged()
|
||||
newStatus: {
|
||||
deep: true,
|
||||
handler() {
|
||||
this.statusChanged()
|
||||
},
|
||||
},
|
||||
saveable(val) {
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event#usage_notes
|
||||
|
|
|
|||
|
|
@ -153,7 +153,6 @@
|
|||
</div>
|
||||
</template>
|
||||
<template #item="{item}">
|
||||
{{ item }}
|
||||
<DomainMuteCard :domain="item" />
|
||||
</template>
|
||||
<template #empty>
|
||||
|
|
|
|||
|
|
@ -169,7 +169,7 @@ export default {
|
|||
setTimeout(() => {
|
||||
this.animationState = false
|
||||
}, 500)
|
||||
close()
|
||||
if (!this.button.dropdown) close()
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -213,7 +213,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
|
||||
|
|
|
|||
|
|
@ -214,6 +214,7 @@ const api = {
|
|||
if (state.mastoUserSocketStatus !== WSConnectionStatus.ERROR) {
|
||||
dispatch('startFetchingTimeline', { timeline: 'friends' })
|
||||
dispatch('startFetchingNotifications')
|
||||
useChatsStore().startFetchingChats()
|
||||
useInterfaceStore().pushGlobalNotice({
|
||||
level: 'error',
|
||||
messageKey: 'timeline.socket_broke',
|
||||
|
|
@ -235,6 +236,7 @@ const api = {
|
|||
stopMastoUserSocket({ state, dispatch }) {
|
||||
dispatch('startFetchingTimeline', { timeline: 'friends' })
|
||||
dispatch('startFetchingNotifications')
|
||||
useChatsStore().startFetchingChats()
|
||||
state.mastoUserSocket.close()
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -307,7 +313,9 @@ export const applyStyleConfig = (input) => {
|
|||
adoptStyleSheets()
|
||||
}
|
||||
|
||||
export const getResourcesIndex = async (url, parser = (x) => x) => {
|
||||
const noop = (x) => x
|
||||
|
||||
export const getResourcesIndex = async (url, parser = noop) => {
|
||||
const cache = 'no-store'
|
||||
const customUrl = url.replace(/\.(\w+)$/, '.custom.$1')
|
||||
let builtin
|
||||
|
|
@ -324,6 +332,7 @@ export const getResourcesIndex = async (url, parser = (x) => x) => {
|
|||
promisedRequest({
|
||||
url: v,
|
||||
cache,
|
||||
forceContentType: parser === noop ? null : 'text/plain',
|
||||
})
|
||||
.then(({ data: text }) => parser(text))
|
||||
.catch((e) => {
|
||||
|
|
@ -339,7 +348,11 @@ export const getResourcesIndex = async (url, parser = (x) => x) => {
|
|||
}
|
||||
|
||||
try {
|
||||
const { data: builtinData } = await promisedRequest({ url, cache })
|
||||
const { data: builtinData } = await promisedRequest({
|
||||
url,
|
||||
cache,
|
||||
forceContentType: 'application/json',
|
||||
})
|
||||
builtin = resourceTransform(builtinData)
|
||||
} catch {
|
||||
builtin = []
|
||||
|
|
@ -350,6 +363,7 @@ export const getResourcesIndex = async (url, parser = (x) => x) => {
|
|||
const { data: customData } = await promisedRequest({
|
||||
url: customUrl,
|
||||
cache,
|
||||
forceContentType: 'application/json',
|
||||
})
|
||||
custom = resourceTransform(customData)
|
||||
} catch {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -75,6 +75,11 @@ export const useNotificationsStore = defineStore('notifications', {
|
|||
return true
|
||||
})
|
||||
|
||||
commit(
|
||||
'addNewUsers',
|
||||
validNotifications.map((notification) => notification.from_profile),
|
||||
)
|
||||
|
||||
const statusNotifications = validNotifications.filter(
|
||||
(notification) =>
|
||||
isStatusNotification(notification.type) && notification.status,
|
||||
|
|
|
|||
58
test/unit/specs/services/style_setter/style_setter.spec.js
Normal file
58
test/unit/specs/services/style_setter/style_setter.spec.js
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
import {
|
||||
getResourcesIndex,
|
||||
hasInvalidCachedThemeRules,
|
||||
} from 'src/services/style_setter/style_setter.js'
|
||||
|
||||
describe('resource index', () => {
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks()
|
||||
vi.unstubAllGlobals()
|
||||
})
|
||||
|
||||
it('ignores an HTML fallback returned for a missing custom index', async () => {
|
||||
vi.spyOn(console, 'warn').mockImplementation(() => undefined)
|
||||
vi.stubGlobal(
|
||||
'fetch',
|
||||
vi
|
||||
.fn()
|
||||
.mockResolvedValueOnce(
|
||||
new Response(JSON.stringify({ builtin: { version: 1 } }), {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
}),
|
||||
)
|
||||
.mockResolvedValueOnce(
|
||||
new Response('<!doctype html><title>Pleroma</title>', {
|
||||
headers: { 'Content-Type': 'text/html' },
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
||||
const resources = await getResourcesIndex('/static/styles.json')
|
||||
|
||||
expect(Object.keys(resources)).to.deep.equal(['builtin'])
|
||||
expect(resources.builtin()).to.deep.equal({ version: 1 })
|
||||
})
|
||||
})
|
||||
|
||||
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)
|
||||
})
|
||||
})
|
||||
42
test/unit/specs/services/theme_data/theme2_to_theme3.spec.js
Normal file
42
test/unit/specs/services/theme_data/theme2_to_theme3.spec.js
Normal file
|
|
@ -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' },
|
||||
})
|
||||
})
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue