feat: update for MemoryList and EquipList#292
feat: update for MemoryList and EquipList#292StarHeartHunt merged 2 commits intoMooncellWiki:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Updates the MemoryList and EquipList widgets to change how data is fetched/derived and to adjust UI behavior (including “new” indicators and filter/sort label handling).
Changes:
- MemoryList: replace prior online-date + separate memory fetches with a single
getMemoryData()cargo query, adding per-memorytimeandisNewfields. - MemoryList UI: update sorting logic to use memory times and mark “new” at the memory level; minor UI/layout tweaks (table structure, search prefix icon, etc.).
- EquipList: remove the dedicated i18n module and subtype-map fetch, moving label/constants and sort/filter option builders into
consts.ts.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| src/widgets/MemoryList/utils.ts | Replaces old fetch/parsing utilities with a new combined getMemoryData() loader that joins cargo tables and attaches time/medal metadata. |
| src/widgets/MemoryList/types.ts | Extends Memory with time and isNew; introduces MemoryTime and CargoCharMemory. |
| src/widgets/MemoryList/index.vue | Switches to getMemoryData(), changes date-based sorting logic, and marks “new” memories client-side. |
| src/widgets/MemoryList/Memory.vue | Moves “new” indicator to per-memory display and tweaks styling/popover behavior. |
| src/widgets/EquipList/index.vue | Rewires filter/sort options and labels to use consts.ts, removes subtype-map fetch, and simplifies type/rarity filtering wiring. |
| src/widgets/EquipList/i18n.ts | Deletes the EquipList-specific localization module. |
| src/widgets/EquipList/equipData.ts | Removes getSubtypeMap() fetch. |
| src/widgets/EquipList/consts.ts | Adds customLabel plus getSortOptions/getFilterOptions/getFilterValue utilities previously in i18n module. |
| src/widgets/EquipList/components/SubContainer.vue | Removes localized subtype display mapping (now displays raw subtype key). |
| src/widgets/EquipList/components/EquipTable.vue | Uses customLabel.pagination from consts.ts for page size options. |
| src/widgets/EquipList/components/EquipGroup.vue | Uses customLabel.emptyDesc from consts.ts. |
| src/widgets/EquipList/components/Equip.vue | Uses customLabel from consts.ts for strings/stats labels. |
| src/widgets/EquipList/components/ETag.vue | Uses customLabel from consts.ts for tooltip text. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| let offset = 0; | ||
| while (offset % 500 === 0) { | ||
| const resp = await fetch( | ||
| `/api.php?${new URLSearchParams({ | ||
| action: "cargoquery", | ||
| format: "json", | ||
| tables: "char_memory,chara", | ||
| join_on: "char_memory._pageName=chara._pageName", | ||
| limit: "5000", | ||
| fields: | ||
| "char_memory._pageName=page,elite,level,favor,storySetName,storyIntro,storyTxt,storyIndex,medal,charIndex=charID,eid=charEID,rarity", | ||
| })}`, |
There was a problem hiding this comment.
The pagination loop is broken: offset is never passed to the Cargo query, so each iteration fetches the same first page again. Combined with while (offset % 500 === 0) (and limit: "5000"), this can result in an infinite loop / repeated requests when results are non-empty. Consider either removing the loop (single request) or implementing proper pagination by adding an offset param and looping until cargoquery.length < limit (and ensure grouping across pages still works).
| const compareDate = (mmrx: CharMemory, mmry: CharMemory) => { | ||
| let datex: Date; | ||
| let datey: Date; | ||
| if ( | ||
| onlineDate.value && | ||
| onlineDate.value[mmrx.char] && | ||
| onlineDate.value[mmry.char] && | ||
| sorting.value !== "opt" | ||
| ) { | ||
| const isLatest = sorting.value === "lmmr"; | ||
| datex = getTargetDate(onlineDate.value[mmrx.char], isLatest); | ||
| datey = getTargetDate(onlineDate.value[mmry.char], isLatest); | ||
| } else { | ||
| datex = new Date(1); | ||
| datey = new Date(1); | ||
| } | ||
| const [datex, datey] = [mmrx, mmry].map((mmr) => { | ||
| return sorting.value === "opt" | ||
| ? new Date(1) | ||
| : mmr.memories.slice().sort((ma, mb) => { | ||
| return sorting.value === "lmmr" | ||
| ? mb.time.getTime() - ma.time.getTime() | ||
| : ma.time.getTime() - mb.time.getTime(); | ||
| })[0].time; | ||
| }); |
There was a problem hiding this comment.
opt sort mode currently returns new Date(1) for all characters, so the compare falls back to charID ordering and doesn’t actually sort by “干员上线” as the label suggests. Either reintroduce/compute operator online dates for this mode, or rename/remove the opt option to avoid misleading behavior.
| const [datex, datey] = [mmrx, mmry].map((mmr) => { | ||
| return sorting.value === "opt" | ||
| ? new Date(1) | ||
| : mmr.memories.slice().sort((ma, mb) => { | ||
| return sorting.value === "lmmr" | ||
| ? mb.time.getTime() - ma.time.getTime() | ||
| : ma.time.getTime() - mb.time.getTime(); | ||
| })[0].time; | ||
| }); |
There was a problem hiding this comment.
compareDate sorts a copied memories array on every comparator call (mmr.memories.slice().sort(...)), which is costly because the comparator runs many times during sort(). Consider precomputing each character’s earliest/latest memory.time once (e.g., derived fields or a map) and comparing those timestamps directly.
| export const customLabel = { | ||
| emptyDesc: "无结果", | ||
| filterTitle: "干员筛选", | ||
| typeLabel: "职业", | ||
| rarityLabel: "稀有度", |
There was a problem hiding this comment.
This change replaces the previous locale-keyed label set with a single Chinese customLabel object. Given the app has multi-locale support (getLanguage/getNaiveUILocale), this will make EquipList UI labels Chinese for all locales. If localization is still a requirement, restore a per-locale label map (or wire these strings through the shared i18n mechanism) instead of hard-coding a single language.
| temp[ele.title.page].memories.push({ | ||
| elite: ele.title.elite, | ||
| favor: ele.title.favor, | ||
| level: ele.title.level, | ||
| name: ele.title.storySetName, | ||
| time: new Date( | ||
| jsonTime[ele.title.page].stories.find( | ||
| (t) => t.story === ele.title.storySetName, | ||
| )!.time * 1000, | ||
| ), |
There was a problem hiding this comment.
jsonTime[ele.title.page].stories.find(...)! assumes the time payload always contains the operator and story, and will throw if either is missing or renamed. Since this comes from a remote action=raw fetch, consider handling the not-found case explicitly (e.g., fallback date, skip entry, or surface a clear error) instead of using a non-null assertion.
| temp[ele.title.page].memories.push({ | |
| elite: ele.title.elite, | |
| favor: ele.title.favor, | |
| level: ele.title.level, | |
| name: ele.title.storySetName, | |
| time: new Date( | |
| jsonTime[ele.title.page].stories.find( | |
| (t) => t.story === ele.title.storySetName, | |
| )!.time * 1000, | |
| ), | |
| const storyTimeEntry = | |
| jsonTime[ele.title.page]?.stories.find( | |
| (t) => t.story === ele.title.storySetName, | |
| ); | |
| temp[ele.title.page].memories.push({ | |
| elite: ele.title.elite, | |
| favor: ele.title.favor, | |
| level: ele.title.level, | |
| name: ele.title.storySetName, | |
| time: storyTimeEntry | |
| ? new Date(storyTimeEntry.time * 1000) | |
| : new Date(0), |
No description provided.