@@ -14,6 +14,13 @@ import {
1414 getValidHashTab ,
1515 replaceTabRoute ,
1616} from "@/utils/hashRouteTabs.mjs" ;
17+ import {
18+ PIN_UPDATES_ON_TOP_STORAGE_KEY ,
19+ PLUGIN_LIST_VIEW_MODE_STORAGE_KEY ,
20+ SHOW_RESERVED_PLUGINS_STORAGE_KEY ,
21+ readBooleanPreference ,
22+ writeBooleanPreference ,
23+ } from "./extensionPreferenceStorage.mjs" ;
1724import { ref , computed , onMounted , onUnmounted , reactive , watch } from "vue" ;
1825import { useRoute , useRouter } from "vue-router" ;
1926import { useDisplay } from "vuetify" ;
@@ -124,11 +131,7 @@ export const useExtensionPage = () => {
124131
125132 // 从 localStorage 恢复显示系统插件的状态,默认为 false(隐藏)
126133 const getInitialShowReserved = ( ) => {
127- if ( typeof window !== "undefined" && window . localStorage ) {
128- const saved = localStorage . getItem ( "showReservedPlugins" ) ;
129- return saved === "true" ;
130- }
131- return false ;
134+ return readBooleanPreference ( SHOW_RESERVED_PLUGINS_STORAGE_KEY , false ) ;
132135 } ;
133136 const showReserved = ref ( getInitialShowReserved ( ) ) ;
134137 const snack_message = ref ( "" ) ;
@@ -178,16 +181,20 @@ export const useExtensionPage = () => {
178181 // 新增变量支持列表视图
179182 // 从 localStorage 恢复显示模式,默认为 false(卡片视图)
180183 const getInitialListViewMode = ( ) => {
181- if ( typeof window !== "undefined" && window . localStorage ) {
182- return localStorage . getItem ( "pluginListViewMode" ) === "true" ;
183- }
184- return false ;
184+ return readBooleanPreference ( PLUGIN_LIST_VIEW_MODE_STORAGE_KEY , false ) ;
185185 } ;
186186 const isListView = ref ( getInitialListViewMode ( ) ) ;
187187 const pluginSearch = ref ( "" ) ;
188188 const installedStatusFilter = ref ( "all" ) ;
189189 const installedSortBy = ref ( "default" ) ;
190190 const installedSortOrder = ref ( "desc" ) ;
191+ const getInitialPinUpdatesOnTop = ( ) => {
192+ return readBooleanPreference ( PIN_UPDATES_ON_TOP_STORAGE_KEY , true ) ;
193+ } ;
194+ const pinUpdatesOnTop = ref ( getInitialPinUpdatesOnTop ( ) ) ;
195+ watch ( pinUpdatesOnTop , ( val ) => {
196+ writeBooleanPreference ( PIN_UPDATES_ON_TOP_STORAGE_KEY , val ) ;
197+ } ) ;
191198 const loading_ = ref ( false ) ;
192199
193200 // 分页相关
@@ -426,6 +433,17 @@ export const useExtensionPage = () => {
426433 return Number . isFinite ( parsed ) ? parsed : null ;
427434 } ;
428435
436+ const compareInstalledFallback = ( left , right ) => {
437+ const nameCompare = compareInstalledPluginNames ( left . plugin , right . plugin ) ;
438+ return nameCompare !== 0 ? nameCompare : left . index - right . index ;
439+ } ;
440+
441+ const compareInstalledUpdatePinning = ( left , right ) => {
442+ const leftHasUpdate = left . plugin ?. has_update ? 1 : 0 ;
443+ const rightHasUpdate = right . plugin ?. has_update ? 1 : 0 ;
444+ return rightHasUpdate - leftHasUpdate ;
445+ } ;
446+
429447 const sortInstalledPlugins = ( plugins ) => {
430448 return plugins
431449 . map ( ( plugin , index ) => ( {
@@ -434,19 +452,24 @@ export const useExtensionPage = () => {
434452 installedAtTimestamp : getInstalledAtTimestamp ( plugin ) ,
435453 } ) )
436454 . sort ( ( left , right ) => {
437- const fallbackNameCompare = compareInstalledPluginNames (
438- left . plugin ,
439- right . plugin ,
440- ) ;
441- const fallbackResult =
442- fallbackNameCompare !== 0 ? fallbackNameCompare : left . index - right . index ;
455+ if (
456+ pinUpdatesOnTop . value &&
457+ installedSortBy . value !== "update_status"
458+ ) {
459+ // Pinning updates is a primary grouping; the selected sort order still
460+ // applies within the "has update" and "no update" groups below.
461+ const pinCompare = compareInstalledUpdatePinning ( left , right ) ;
462+ if ( pinCompare !== 0 ) {
463+ return pinCompare ;
464+ }
465+ }
443466
444467 if ( installedSortBy . value === "install_time" ) {
445468 const leftTimestamp = left . installedAtTimestamp ;
446469 const rightTimestamp = right . installedAtTimestamp ;
447470
448471 if ( leftTimestamp == null && rightTimestamp == null ) {
449- return fallbackResult ;
472+ return compareInstalledFallback ( left , right ) ;
450473 }
451474 if ( leftTimestamp == null ) {
452475 return 1 ;
@@ -459,7 +482,9 @@ export const useExtensionPage = () => {
459482 installedSortOrder . value === "desc"
460483 ? rightTimestamp - leftTimestamp
461484 : leftTimestamp - rightTimestamp ;
462- return timeDiff !== 0 ? timeDiff : fallbackResult ;
485+ return timeDiff !== 0
486+ ? timeDiff
487+ : compareInstalledFallback ( left , right ) ;
463488 }
464489
465490 if ( installedSortBy . value === "name" ) {
@@ -469,7 +494,7 @@ export const useExtensionPage = () => {
469494 ? - nameCompare
470495 : nameCompare ;
471496 }
472- return left . index - right . index ;
497+ return compareInstalledFallback ( left , right ) ;
473498 }
474499
475500 if ( installedSortBy . value === "author" ) {
@@ -482,20 +507,20 @@ export const useExtensionPage = () => {
482507 ? - authorCompare
483508 : authorCompare ;
484509 }
485- return fallbackResult ;
510+ return compareInstalledFallback ( left , right ) ;
486511 }
487512
488513 if ( installedSortBy . value === "update_status" ) {
489- const leftHasUpdate = left . plugin ?. has_update ? 1 : 0 ;
490- const rightHasUpdate = right . plugin ?. has_update ? 1 : 0 ;
491514 const updateDiff =
492515 installedSortOrder . value === "desc"
493- ? rightHasUpdate - leftHasUpdate
494- : leftHasUpdate - rightHasUpdate ;
495- return updateDiff !== 0 ? updateDiff : fallbackResult ;
516+ ? compareInstalledUpdatePinning ( left , right )
517+ : compareInstalledUpdatePinning ( right , left ) ;
518+ return updateDiff !== 0
519+ ? updateDiff
520+ : compareInstalledFallback ( left , right ) ;
496521 }
497522
498- return fallbackResult ;
523+ return compareInstalledFallback ( left , right ) ;
499524 } )
500525 . map ( ( item ) => item . plugin ) ;
501526 } ;
@@ -636,9 +661,7 @@ export const useExtensionPage = () => {
636661 const toggleShowReserved = ( ) => {
637662 showReserved . value = ! showReserved . value ;
638663 // 保存到 localStorage
639- if ( typeof window !== "undefined" && window . localStorage ) {
640- localStorage . setItem ( "showReservedPlugins" , showReserved . value . toString ( ) ) ;
641- }
664+ writeBooleanPreference ( SHOW_RESERVED_PLUGINS_STORAGE_KEY , showReserved . value ) ;
642665 } ;
643666
644667 const toast = ( message , success ) => {
@@ -1603,9 +1626,7 @@ export const useExtensionPage = () => {
16031626
16041627 // 监听显示模式变化并保存到 localStorage
16051628 watch ( isListView , ( newVal ) => {
1606- if ( typeof window !== "undefined" && window . localStorage ) {
1607- localStorage . setItem ( "pluginListViewMode" , String ( newVal ) ) ;
1608- }
1629+ writeBooleanPreference ( PLUGIN_LIST_VIEW_MODE_STORAGE_KEY , newVal ) ;
16091630 } ) ;
16101631
16111632 watch (
@@ -1695,6 +1716,7 @@ export const useExtensionPage = () => {
16951716 installedStatusFilter,
16961717 installedSortBy,
16971718 installedSortOrder,
1719+ pinUpdatesOnTop,
16981720 loading_,
16991721 currentPage,
17001722 marketCategoryFilter,
0 commit comments