Kaynağa Gözat

preference improve

Nattiden 1 yıl önce
ebeveyn
işleme
c89e1682c1

+ 2 - 1
Project/configs/config.yml

@@ -4,4 +4,5 @@ Public.domain: news.bugjump.net
 Server.Version.By: hash
 Refresher.Enable: true
 Refresher.Precision: d
-NewsHomepage.Debug: False
+NewsHomepage.Debug: False
+NewsHomepage.Protocol: https

+ 27 - 0
Project/modules/domain.py

@@ -0,0 +1,27 @@
+from homepagebuilder.interfaces import config, Logger
+from homepagebuilder.core.types import Context
+
+_PROTOCOL = config('NewsHomepage.Protocol', 'https', str)
+_DOMAIN = config('NewsHomepage.Domain', 'pcl.mcnews.thestack.top', str)
+_PORT = config('NewsHomepage.Port', '443', str)
+
+logger = Logger('Domain')
+
+def _get_disp_port():
+    if config('NewsHomepage.HidePort', True, bool):
+        return ''
+    if _PROTOCOL == 'http' and _PORT == '80':
+        return ''
+    if _PROTOCOL == 'https' and _PORT == '443':
+        return ''
+    return ':' + _PORT
+
+_DISP_PORT_STR = _get_disp_port()
+
+def get_doamin_url(context:Context):
+    display_port = _DISP_PORT_STR
+    if config('NewsHomepage.UseRuntimePort', False):
+        display_port = ':' + context.builder.get_data('server.port')
+    if not display_port:
+        logger.warning('无效的端口')
+    return f"{_PROTOCOL}://{_DOMAIN}{display_port}/"

+ 198 - 23
Project/modules/homepage_preference.py

@@ -1,35 +1,210 @@
-from homepagebuilder.interfaces import script, config
+from homepagebuilder.interfaces import script, config, require
 from homepagebuilder.core.types import Context
+from homepagebuilder.core.formatter import format_code
+from typing import Type, TypeVar, Dict, List, Iterable, Optional
+from enum import Enum
 
-def true_that(obj:str):
+T = TypeVar('T')
+
+def is_true(obj:str):
     if isinstance(obj, bool):
         return obj
     if isinstance(obj, str):
-        return obj.lower() == 'true'
-
-PREFERENCE_LIST=[
-    'skin',
-    'not_finished',
-    'mod',
-    'beta',
-    'domain'
-]
-DOMAIN = config('NewsHomepage.Domain', 'https://pcl.mcnews.thestack.top/', str)
-if not DOMAIN.endswith('/'):
-    DOMAIN += '/'
+        return obj.lower() == 'true' or obj == ''
+
+GET_DOMAIN_URL = require('domain').get_doamin_url
+
+class PrefenrenceEntry:
+    __name:str
+    __type:Type[T]
+    __default:T
+    
+    def __init__(self, name:str, type:Type[T], default:T):
+        self.__name = name
+        self.__type = type
+        self.__default = default
+        
+    
+    @property
+    def modify_method(self):
+        return self.__method
+    
+    @property
+    def name(self):
+        return self.__name
+    
+    @property
+    def default(self):
+        return self.__default
+
+    def get_element_names(self) -> List[str]:
+        return []
+    
+    def get_element_name(cls) -> str:
+        raise NotImplementedError()
+    
+    @classmethod
+    def binding(cls, name) -> str:
+         raise NotImplementedError()
+
+    def get_value_from_save_page(self, settings):
+        raise NotImplementedError()
+
+class PrefenrenceHiddenEntry(PrefenrenceEntry):
+    def get_element_names(self):
+        return []
+    
+    def get_element_name(cls, name:str) -> str:
+        raise NotImplementedError()
+    
+    @classmethod
+    def binding(cls, name):
+         raise NotImplementedError()
+     
+    def get_value_from_save_page(self, settings):
+        return settings.get(self.name, self.default)
+
+class PrefenrenceChoiceEntry(PrefenrenceEntry):
+    
+    def __init__(self, name, type, default, values:List[T]):
+        super().__init__(name, type, default)
+        self.__values = values
+    
+    def get_element_names(self):
+        return [self.get_element_name(value) for value in self.__values]
+    
+    def get_element_name(self, value):
+        return self.get_element_name_by_value(self.name, value)
+    
+    @classmethod
+    def get_element_name_by_value(cls, name, value):
+        return name + value
+
+    @classmethod
+    def binding(cls, name):
+        return f'<Binding Path="Checked" ElementName="{name}"/>'
+    
+    def get_value_from_save_page(self, settings):
+        for value in self.__values:
+            if is_true(settings.get(self.get_element_name(value))):
+                return value
+        return self.default
+
+class PrefenrenceBoolEntry(PrefenrenceEntry):
+    def get_element_names(self):
+        return [self.get_element_name()]
+    
+    def get_element_name(self):
+        return self.get_element_name_by_value(self.name)
+    
+    @classmethod
+    def binding(cls, name):
+        return f'<Binding Path="Checked" ElementName="{name}"/>'
+    
+    @classmethod
+    def get_element_name_by_value(cls, name):
+        return name
+    
+    def get_value_from_save_page(self, settings):
+        return is_true(settings.get(self.get_element_name(), self.default))
+
+class PreferenceManager:
+    __entries:Dict[str, PrefenrenceEntry] = {}
+    
+    @classmethod
+    def add_entries(cls, entries:Iterable[PrefenrenceEntry]):
+        for entry in entries:
+            cls.__entries[entry.name] = entry
+    
+    @classmethod
+    def has(cls, item):
+        if isinstance(item, str):
+            return item in cls.__entries
+        raise TypeError()
+    
+    @classmethod
+    def get(cls, key:str, default = None):
+        return cls.__entries.get(key, default)
+    
+    @classmethod
+    def is_using(cls, context:'Context', key, value) -> bool:
+        entry = cls.get(key)
+        if not entry:
+            return False
+        settings = context.setter.toProperties().get('args',{})
+        current_value = settings.get(key)
+        if current_value is not None:
+            return current_value == value
+        else:
+            return entry.default == value
+    
+    @classmethod
+    def pref_length(cls):
+        return len(cls.__entries)
+    
+    @classmethod
+    def get_entries(self):
+        return self.__entries.values()
+    
+PreferenceManager.add_entries([
+            PrefenrenceChoiceEntry('skin', str, 'basic',
+                             ['basic', 'newra']),
+            PrefenrenceChoiceEntry('edition', str, 'standard',
+                             ['standard', 'light']),
+            PrefenrenceBoolEntry('hidewip', bool, False),
+            PrefenrenceHiddenEntry('mod', bool, False),
+            PrefenrenceHiddenEntry('beta', bool, False)
+        ])
 
 @script('PreferenceUrl')
 def get_preferece_url(context:'Context', *_args, **_kwargs):
     settings = context.setter.toProperties().get('args',{})
-    url = DOMAIN
-    if true_that(settings.get('LightEdition', False)):
-        url += 'Light'
     args = []
-    args.append('mod')
-    if true_that(settings.get('SkinNewra', False)):
-        args.append('skin=newra')
-    if true_that(settings.get('HideTranslating', False)):
-        args.append('not_finished=false')
+    url = GET_DOMAIN_URL(context)
+    for entry in PreferenceManager.get_entries():
+        value = entry.get_value_from_save_page(settings=settings)
+        if value != entry.default:
+            if is_true(value):
+                args.append(entry.name)
+            else:
+                args.append(entry.name + '=' + value)
     if len(args) > 0:
         url += '?' + '&amp;'.join(args)
-    return url
+    return url
+
+@script('PreferenceSavePageUrl')
+def preferece_save_page_url(context:'Context', *_args, **_kwargs):
+    settings = context.setter.toProperties().get('args',{})
+    string_format = '{}' + GET_DOMAIN_URL(context) + 'SaveChanges.json?'
+    bindings = ''
+    i = 0
+    for entry in PreferenceManager.get_entries():
+        if isinstance(entry, PrefenrenceHiddenEntry):
+            value = settings.get(entry.name)
+            if value is not None:
+                string_format += f'{entry.name}={value}'
+            continue
+        for name in entry.get_element_names():
+            string_format += f'{name}={{{i}}}&amp;'
+            bindings += entry.binding(name)
+            i += 1
+    return f'<MultiBinding StringFormat="{string_format}">{bindings}</MultiBinding>'
+    
+
+@script('PreferenceRadioBox')
+def preferece_radio(pref_key, pref_value, column, row, content, context:'Context', **_kwargs):
+    data = {}
+    data['column'] = column
+    data['row'] = row
+    data['content'] = content
+    data['element_key'] = PrefenrenceChoiceEntry.get_element_name_by_value(pref_key, pref_value)
+    data['check'] = PreferenceManager.is_using(context, pref_key, pref_value)
+    return context.components['Preference/PreferenceRadioBox'].toxaml(data, context)
+
+@script('PreferenceCheckBox')
+def preferece_check(pref_key, content, context:'Context', **_kwargs):
+    data = {}
+    data['content'] = content
+    data['element_key'] = PrefenrenceBoolEntry.get_element_name_by_value(pref_key)
+    data['check'] = PreferenceManager.is_using(context, pref_key, True)
+    return context.components['Preference/PreferenceCheckBox'].toxaml(data, context)

+ 18 - 11
Project/modules/homepage_url.py

@@ -1,9 +1,10 @@
 from homepagebuilder.interfaces import require, script
 
 pref_module = require('homepage_preference')
-PREFERENCE_LIST = pref_module.PREFERENCE_LIST
-PREFERENCE_LIST_LENGTH = len(PREFERENCE_LIST)
-DOMAIN = pref_module.DOMAIN
+PREFERENCE_MANAGER = pref_module.PreferenceManager
+is_true = pref_module.is_true
+PREFERENCE_LIST_LENGTH = PREFERENCE_MANAGER.pref_length()
+GET_DOMAIN_URL = require('domain').get_doamin_url
 
 @script('Link')
 def link_script(page_name, context, *_args, **kwargs):
@@ -12,21 +13,27 @@ def link_script(page_name, context, *_args, **kwargs):
     if '.json' not in page_name:
         page_name += '.json'
     if settings_length == 0:
-        return DOMAIN + page_name
+        return GET_DOMAIN_URL(context) + page_name
     new_args = []
     # 判断优化: 取长度小的遍历
     if PREFERENCE_LIST_LENGTH > len(settings):
         for name, value in settings.items():
-            if name in PREFERENCE_LIST:
-                new_args.append(name + '=' + value)
+            if PREFERENCE_MANAGER.has(name):
+                if is_true(value):
+                    new_args.append(name)
+                else:
+                    new_args.append(name + '=' + value)
     else:
-        for name in PREFERENCE_LIST:
-            if value := settings.get(name, None) is not None:
-                new_args.append(name + '=' + value)
+        for entry in PREFERENCE_MANAGER.get_entries():
+            if value := settings.get(entry.name, None) is not None:
+                if is_true(value):
+                    new_args.append(entry.name)
+                else:
+                    new_args.append(entry.name + '=' + value)
     if len(new_args) == 0:
-        return DOMAIN + page_name
+        return GET_DOMAIN_URL(context) + page_name
     if '?' not in page_name:
         page_name += '?'
     else:
         page_name += '&amp;'
-    return DOMAIN + page_name + '&amp;'.join(new_args)
+    return GET_DOMAIN_URL(context) + page_name + '&amp;'.join(new_args)

+ 6 - 5
Project/modules/zh_translate.py

@@ -25,12 +25,12 @@ def fetch_transltions():
     logger.info("正在加载中文翻译")
     try:
         response = requests.get('https://raw.githubusercontent.com/Light-Beacon/Minecraft-ZH-Translation-Sheet/refs/heads/main/data/translations.json', 
-                        timeout=10)
-    except ConnectionError:
+                        timeout=5)
+    except requests.ConnectionError:
         logger.warning("无法加载中文翻译:连接错误")
         return {}
-    except Exception as ex:
-        logger.warning(f"无法加载中文翻译:{type(ex)}:{ex}")
+    except requests.ReadTimeout as ex:
+        logger.warning("无法加载中文翻译:访问超时")
         return {}
     response_content = response.content
     return json.loads(response_content)
@@ -43,5 +43,6 @@ def init(context:'Context', *args, **kwargs):
             logger.info("从缓存中加载中文翻译数据成功, 共计 %d 条翻译", len(TRANSLATIONS))
             return
     TRANSLATIONS = fetch_transltions()
-    logger.info("加载中文翻译数据成功, 共计 %d 条翻译", len(TRANSLATIONS))
+    if len(TRANSLATIONS) > 0:
+        logger.info("加载中文翻译数据成功, 共计 %d 条翻译", len(TRANSLATIONS))
     context.builder.set_data('translation_manifset_cache', TRANSLATIONS)

+ 12 - 68
Project/pages/Preference/Preference.xaml

@@ -45,13 +45,7 @@
     <StackPanel Margin="15,5">
         <TextBlock x:Name="OutputTextBlock" Visibility="Collapsed">
             <TextBlock.Text>
-                <MultiBinding StringFormat="{}${@Link|SaveChanges.json?SkinBasic={0}&amp;SkinNewra={1}&amp;StandardEdition={2}&amp;LightEdition={3}&amp;HideTranslating={4}}">
-                    <Binding Path="Checked" ElementName="BasicSkinRadioBox"/>
-                    <Binding Path="Checked" ElementName="NewraSkinRadioBox"/>
-                    <Binding Path="Checked" ElementName="StandardEditionRadioBox"/>
-                    <Binding Path="Checked" ElementName="LightEditionRadioBox"/>
-                    <Binding Path="Checked" ElementName="HideTranslatingCheckBox"/>
-                </MultiBinding>
+                ${@PreferenceSavePageUrl}
             </TextBlock.Text>
         </TextBlock>
         <local:MyButton IsEnabled="{Binding Path=Checked, ElementName=ConfirmCheckBox}"
@@ -84,42 +78,16 @@
                 <ColumnDefinition Width="*"/>
                 <ColumnDefinition Width="*"/>
             </Grid.ColumnDefinitions>
-
-            <Border Grid.Column="0" Height="50" Margin="10" BorderThickness="0.5" CornerRadius="6">
-                <Border.BorderBrush>
-                    <Binding Path="Children[1].Stroke" ElementName="StandardEditionRadioBox"/>
-                </Border.BorderBrush>
-            </Border>
-            <Border Grid.Column="0" Height="50" Margin="10" CornerRadius="6" Opacity="0.2">
-                <Border.BorderBrush>
-                    <Binding Path="Children[0].Foreground" ElementName="StandardEditionRadioBox"/>
-                </Border.BorderBrush>
-                <Border.Background>
-                    <Binding Path="Children[2].Fill" ElementName="StandardEditionRadioBox"/>
-                </Border.Background>
-            </Border>
-            <local:MyRadioBox Grid.Column="0" Height="50" Margin="20,10,10,10" x:Name="StandardEditionRadioBox" Checked="True">
+            ${@PreferenceRadioBox|edition|standard|0|0|
                 <Run>完整</Run>
                 <LineBreak/>
                 <Run FontSize="10">原汁原味的新闻主页体验</Run>
-            </local:MyRadioBox>
-
-            <Border Grid.Column="1" Height="50" Margin="10" BorderThickness="0.5" CornerRadius="6">
-                <Border.BorderBrush>
-                    <Binding Path="Children[1].Stroke" ElementName="LightEditionRadioBox"/>
-                </Border.BorderBrush>
-            </Border>
-            <Border Grid.Column="1" Height="50" Margin="10" CornerRadius="6" Opacity="0.2">
-                <Border.Background>
-                    <Binding Path="Children[2].Fill" ElementName="LightEditionRadioBox"/>
-                </Border.Background>
-            </Border>
-            <local:MyRadioBox Grid.Column="1" Height="50" Margin="20,10,10,10" x:Name="LightEditionRadioBox">
+            }
+            ${@PreferenceRadioBox|edition|light|1|0|
                 <Run>精简</Run>
                 <LineBreak/>
                 <Run FontSize="10">只保留最新版本的链接,性能更高</Run>
-            </local:MyRadioBox>
-
+            }
         </Grid>
     </StackPanel>
 </local:MyCard>
@@ -135,37 +103,13 @@
                 <RowDefinition/>
                 <RowDefinition/>
             </Grid.RowDefinitions>
-            <Border Grid.Column="0" Grid.Row="1" Height="50" Margin="10" BorderThickness="0.5" CornerRadius="6">
-                <Border.BorderBrush>
-                    <Binding Path="Children[1].Stroke" ElementName="BasicSkinRadioBox"/>
-                </Border.BorderBrush>
-            </Border>
-            <Border Grid.Column="0" Grid.Row="1" Height="50" Margin="10" CornerRadius="6" Opacity="0.2">
-                <Border.Background>
-                    <Binding Path="Children[2].Fill" ElementName="BasicSkinRadioBox"/>
-                </Border.Background>
-            </Border>
-            <local:MyRadioBox Grid.Column="0" Grid.Row="1" Height="50" Margin="20,10,10,10" x:Name="BasicSkinRadioBox" Checked="True">
-                <Run>经典</Run>
-            </local:MyRadioBox>
-            <local:MyImage Grid.Column="0" Grid.Row="0" Source="https://s21.ax1x.com/2025/08/03/pVNbeg0.png" Margin="10,5"/>
-
-            <Border Grid.Column="1" Grid.Row="1" Height="50" Margin="10" BorderThickness="0.5" CornerRadius="6">
-                <Border.BorderBrush>
-                    <Binding Path="Children[1].Stroke" ElementName="NewraSkinRadioBox"/>
-                </Border.BorderBrush>
-            </Border>
-            <Border Grid.Column="1" Grid.Row="1" Height="50" Margin="10" CornerRadius="6" Opacity="0.2">
-                <Border.BorderBrush>
-                    <Binding Path="Children[0].Foreground" ElementName="NewraSkinRadioBox"/>
-                </Border.BorderBrush>
-                <Border.Background>
-                    <Binding Path="Children[2].Fill" ElementName="NewraSkinRadioBox"/>
-                </Border.Background>
-            </Border>
-            <local:MyRadioBox Grid.Column="1" Grid.Row="1" Height="50" Margin="20,10,10,10" x:Name="NewraSkinRadioBox" >
+            ${@PreferenceRadioBox|skin|basic|0|1|
+                <Run>基础</Run>
+            }
+            ${@PreferenceRadioBox|skin|newra|1|1|
                 <Run>通透</Run>
-            </local:MyRadioBox>
+            }
+            <local:MyImage Grid.Column="0" Grid.Row="0" Source="https://s21.ax1x.com/2025/08/03/pVNbeg0.png" Margin="10,5"/>
             <local:MyImage Grid.Column="1" Grid.Row="0" Source="https://s21.ax1x.com/2025/08/03/pVNb359.png" Margin="10,5"/>
         </Grid>
     </StackPanel>
@@ -173,7 +117,7 @@
 
 <local:MyCard Margin="0,0,0,15" CanSwap="False" Title="杂项">
     <StackPanel Margin="25,40,23,15"> 
-        <local:MyCheckBox Text="隐藏“正在翻译中”动画" x:Name="HideTranslatingCheckBox"/>
+        ${@PreferenceCheckBox|hidewip|隐藏“正在翻译中”动画}
     </StackPanel>
 </local:MyCard>
 

+ 1 - 0
Project/structures/components/Preference/PreferenceCheckBox.xaml

@@ -0,0 +1 @@
+<local:MyCheckBox Text="${content}" x:Name="${element_key}" Checked="${check}"/>

+ 13 - 0
Project/structures/components/Preference/PreferenceRadioBox.xaml

@@ -0,0 +1,13 @@
+<Border Grid.Row="${row}" Grid.Column="${column}" Height="50" Margin="10" BorderThickness="0.5" CornerRadius="6">
+    <Border.BorderBrush>
+        <Binding Path="Children[1].Stroke" ElementName="${element_key}"/>
+    </Border.BorderBrush>
+</Border>
+<Border Grid.Row="${row}" Grid.Column="${column}" Height="50" Margin="10" CornerRadius="6" Opacity="0.2">
+    <Border.Background>
+        <Binding Path="Children[2].Fill" ElementName="${element_key}"/>
+    </Border.Background>
+</Border>
+<local:MyRadioBox Grid.Row="${row}" Grid.Column="${column}" Checked="${check}" Height="50" Margin="20,10,10,10" x:Name="${element_key}">
+    ${content}
+</local:MyRadioBox>

+ 1 - 1
Project/structures/templates/Classic/VersionBase.yml

@@ -3,7 +3,7 @@ components:
  - ${@IF|${hint}|Head/Hint}
  - ${@IF|${version-image-link}|VersionHeaderImage}
  - FlowDocumentViewer
- - ${@IF|${not_finished}|Loading}
+ - ${@IF|${args.hidewip}|${@IF|${not_finished}|Loading}}
  - ${@IF|!${disable-websites}|Classic/VersionWebsites}
  - ${@IF|!${disable-toolbar}|Classic/VersionToolBar}
 base: Card

+ 1 - 1
Project/structures/templates/Newra/VersionBase.yml

@@ -3,7 +3,7 @@ components:
  - ${@IF|${hint}|Head/Hint}
  - ${@IF|!${disable-versionimage}|${@IF|${version-image-link}|Newra/VersionHeaderImage}}
  - FlowDocumentViewer
- - ${@IF|${not_finished}|Loading}
+ - ${@IF|${args.hidewip}|${@IF|${not_finished}|Loading}}
  - ${@IF|!${disable-translator}|TranslatorBar}
  - ${@IF|!${disable-lastupdate}|LastUpdateBox}
  - ${@IF|!${disable-toolbar}|VersionToolBar}

+ 1 - 1
Project/structures/templates/Simplex/VersionBase.yml

@@ -3,7 +3,7 @@ components:
  - ${@IF|${hint}|Head/Hint}
  - ${@IF|!${disable-versionimage}|${@IF|${version-image-link}|VersionHeaderImage}}
  - FlowDocumentViewer
- - ${@IF|${not_finished}|Loading}
+ - ${@IF|${args.hidewip}|${@IF|${not_finished}|Loading}}
  - ${@IF|!${disable-translator}|TranslatorBar}
  - ${@IF|!${disable-lastupdate}|LastUpdateBox}
  - ${@IF|!${disable-toolbar}|VersionToolBar}