ENH - improve Weazel search and article media

Load the newest public articles before a search, show the shared empty state for real zero-result queries, and guard debounced requests across tab changes. Add accessible multi-image navigation and move composer category and status choices to the central SkyDropdown.
This commit is contained in:
Dominik
2026-08-16 20:34:44 +02:00
parent 3b7f0b8858
commit e062ed083f
6 changed files with 499 additions and 71 deletions
+3
View File
@@ -594,6 +594,9 @@ const defaultLocales: LocaleTree = {
search: 'Search Weazel News',
clearSearch: 'Clear article search',
articleImages: 'Article photos',
previousPhoto: 'Show the previous article photo',
nextPhoto: 'Show the next article photo',
showPhoto: 'Show article photo {current} of {total}',
coverPreview: 'Article cover preview',
removeCover: 'Remove the selected cover image',
removePhoto: 'Remove this article photo',
+31
View File
@@ -115,6 +115,37 @@ describe('Weazel News store', () => {
expect(store.publicHasMore).toBe(false)
})
it('normalizes the public response to newest first without changing images', async () => {
const older = {
...article,
id: 'older-article',
publishedAt: (article.publishedAt ?? 0) - 1_000,
}
const newer = {
...article,
id: 'newer-article',
images: [...article.images].reverse(),
publishedAt: (article.publishedAt ?? 0) + 1_000,
}
mockNuiCall.mockResolvedValueOnce({
data: { hasMore: false, items: [older, newer] },
success: true,
})
const store = useWeazelNewsStore()
expect(await store.loadPublic()).toBe(true)
expect(mockNuiCall).toHaveBeenCalledWith('weazel-news:list', {
category: null,
offset: 0,
search: '',
})
expect(store.publicItems.map((item) => item.id)).toEqual([
'newer-article',
'older-article',
])
expect(store.publicItems[0]?.images).toEqual(newer.images)
})
it('replaces stale search results with a successful empty response', async () => {
mockNuiCall.mockResolvedValueOnce({
data: { hasMore: false, items: [] },
+19 -10
View File
@@ -38,6 +38,16 @@ function mergeArticles(
return [...merged.values()]
}
function sortNewestFirst(
items: WeazelNewsArticleSummary[],
): WeazelNewsArticleSummary[] {
return [...items].sort(
(left, right) =>
(right.publishedAt ?? right.updatedAt ?? right.createdAt) -
(left.publishedAt ?? left.updatedAt ?? left.createdAt),
)
}
export const useWeazelNewsStore = defineStore('weazel-news', {
state: () => ({
context: null as WeazelNewsContext | null,
@@ -106,9 +116,11 @@ export const useWeazelNewsStore = defineStore('weazel-news', {
this.error = this.publicError
return false
}
this.publicItems = append
? mergeArticles(this.publicItems, response.data.items)
: response.data.items
this.publicItems = sortNewestFirst(
append
? mergeArticles(this.publicItems, response.data.items)
: response.data.items,
)
this.publicHasMore = response.data.hasMore
this.publicError = ''
this.error = ''
@@ -202,10 +214,10 @@ export const useWeazelNewsStore = defineStore('weazel-news', {
...this.managedItems.filter((item) => item.id !== article.id),
]
if (article.status === 'published') {
this.publicItems = [
this.publicItems = sortNewestFirst([
article,
...this.publicItems.filter((item) => item.id !== article.id),
]
])
}
this.error = ''
return article
@@ -240,13 +252,10 @@ export const useWeazelNewsStore = defineStore('weazel-news', {
this.managedItems = replaceArticle(this.managedItems, canonical)
this.publicItems =
canonical.status === 'published'
? [
? sortNewestFirst([
canonical,
...this.publicItems.filter((item) => item.id !== canonical.id),
].sort(
(left, right) =>
(right.publishedAt ?? 0) - (left.publishedAt ?? 0),
)
])
: this.publicItems.filter((item) => item.id !== canonical.id)
this.error = ''
return canonical