diff --git a/Directory.Packages.props b/Directory.Packages.props index f74d74b..b5802d1 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -47,6 +47,7 @@ + diff --git a/LanMountainDesktop.AirAppHost/AirAppLaunchOptions.cs b/LanMountainDesktop.AirAppHost/AirAppLaunchOptions.cs index 7094b9d..d88f0d0 100644 --- a/LanMountainDesktop.AirAppHost/AirAppLaunchOptions.cs +++ b/LanMountainDesktop.AirAppHost/AirAppLaunchOptions.cs @@ -7,10 +7,12 @@ public sealed record AirAppLaunchOptions( string? SourcePlacementId, string? LauncherPipeName, string? InstanceKey, - string? DataRoot) + string? DataRoot, + string? TargetEntryId) { public const string WorldClockAppId = "world-clock"; public const string WhiteboardAppId = "whiteboard"; + public const string RssReaderAppId = "rss-reader"; public static AirAppLaunchOptions Parse(IReadOnlyList args) { @@ -60,7 +62,8 @@ public sealed record AirAppLaunchOptions( GetOptionalValue(values, "source-placement-id"), GetOptionalValue(values, "launcher-pipe"), GetOptionalValue(values, "instance-key"), - GetOptionalValue(values, "data-root")); + GetOptionalValue(values, "data-root"), + GetOptionalValue(values, "target-entry-id")); } private static string GetValue(IReadOnlyDictionary values, string key, string fallback) diff --git a/LanMountainDesktop.AirAppHost/AirAppWindow.axaml.cs b/LanMountainDesktop.AirAppHost/AirAppWindow.axaml.cs index 6e3af58..1bc6744 100644 --- a/LanMountainDesktop.AirAppHost/AirAppWindow.axaml.cs +++ b/LanMountainDesktop.AirAppHost/AirAppWindow.axaml.cs @@ -4,6 +4,7 @@ using Avalonia.Threading; using FluentAvalonia.UI.Windowing; using LanMountainDesktop.ComponentSystem; using LanMountainDesktop.Services; +using LanMountainDesktop.Services.Settings; using LanMountainDesktop.Shared.IPC; using LanMountainDesktop.Shared.IPC.Abstractions.Services; using LanMountainDesktop.Views.Components; @@ -25,11 +26,39 @@ public sealed partial class AirAppWindow : FAAppWindow public AirAppWindow(AirAppLaunchOptions options) { _options = options; - _descriptor = AirAppWindowDescriptor.Create(options); + _descriptor = LocalizeDescriptor(AirAppWindowDescriptor.Create(options), options); InitializeComponent(); ConfigureWindow(); } + private static AirAppWindowDescriptor LocalizeDescriptor( + AirAppWindowDescriptor descriptor, + AirAppLaunchOptions options) + { + if (!string.Equals(options.AppId, AirAppLaunchOptions.RssReaderAppId, StringComparison.OrdinalIgnoreCase)) + return descriptor; + + var localization = new LocalizationService(); + string languageCode; + try + { + languageCode = localization.NormalizeLanguageCode(new AppSettingsService().Load().LanguageCode); + } + catch + { + languageCode = "zh-CN"; + } + + var title = localization.GetString(languageCode, "component.rss_reader", "RSS Reader"); + var airApp = localization.GetString(languageCode, "rss.air_app", "Air APP"); + return descriptor with + { + WindowTitle = $"{title} - {airApp}", + TitleBarTitle = title, + TitleBarSubtitle = airApp + }; + } + private void ConfigureWindow() { ApplyWindowDescriptor(_descriptor); @@ -46,6 +75,12 @@ public sealed partial class AirAppWindow : FAAppWindow return; } + if (string.Equals(_options.AppId, AirAppLaunchOptions.RssReaderAppId, StringComparison.OrdinalIgnoreCase)) + { + ContentHost.Content = new RssReaderAirAppView(_options); + return; + } + ContentHost.Content = new TextBlock { Text = $"Unsupported Air APP: {_options.AppId}", @@ -263,6 +298,11 @@ public sealed partial class AirAppWindow : FAAppWindow return $"{AirAppLaunchOptions.WorldClockAppId}:clock-suite:global"; } + if (string.Equals(_options.AppId, AirAppLaunchOptions.RssReaderAppId, StringComparison.OrdinalIgnoreCase)) + { + return $"{AirAppLaunchOptions.RssReaderAppId}:global"; + } + var componentId = string.IsNullOrWhiteSpace(_options.SourceComponentId) ? "none" : _options.SourceComponentId.Trim(); diff --git a/LanMountainDesktop.AirAppHost/AirAppWindowDescriptor.cs b/LanMountainDesktop.AirAppHost/AirAppWindowDescriptor.cs index 4a5111d..294ff11 100644 --- a/LanMountainDesktop.AirAppHost/AirAppWindowDescriptor.cs +++ b/LanMountainDesktop.AirAppHost/AirAppWindowDescriptor.cs @@ -43,6 +43,20 @@ public sealed record AirAppWindowDescriptor( "Air APP"); } + if (string.Equals(options.AppId, AirAppLaunchOptions.RssReaderAppId, StringComparison.OrdinalIgnoreCase)) + { + return Standard( + "RSS Reader - Air APP", + "RSS Reader", + "Air APP", + width: 1100, + height: 720, + minWidth: 760, + minHeight: 520, + canResize: true, + showAsDialog: false); + } + return Standard( "Air APP", "Air APP", diff --git a/LanMountainDesktop.AirAppHost/LanMountainDesktop.AirAppHost.csproj b/LanMountainDesktop.AirAppHost/LanMountainDesktop.AirAppHost.csproj index 14a2984..cbc1008 100644 --- a/LanMountainDesktop.AirAppHost/LanMountainDesktop.AirAppHost.csproj +++ b/LanMountainDesktop.AirAppHost/LanMountainDesktop.AirAppHost.csproj @@ -24,6 +24,7 @@ + diff --git a/LanMountainDesktop.AirAppRuntime/AirAppInstanceKey.cs b/LanMountainDesktop.AirAppRuntime/AirAppInstanceKey.cs index af49efd..fa972b0 100644 --- a/LanMountainDesktop.AirAppRuntime/AirAppInstanceKey.cs +++ b/LanMountainDesktop.AirAppRuntime/AirAppInstanceKey.cs @@ -10,6 +10,11 @@ internal static class AirAppInstanceKey return $"{normalizedAppId}:clock-suite:global"; } + if (string.Equals(normalizedAppId, "rss-reader", StringComparison.OrdinalIgnoreCase)) + { + return $"{normalizedAppId}:global"; + } + var normalizedComponentId = Normalize(sourceComponentId, "none"); var normalizedPlacementId = Normalize(sourcePlacementId, "none"); return $"{normalizedAppId}:{normalizedComponentId}:{normalizedPlacementId}"; diff --git a/LanMountainDesktop.AirAppRuntime/AirAppLifecycleService.cs b/LanMountainDesktop.AirAppRuntime/AirAppLifecycleService.cs index 97b75c5..625ba37 100644 --- a/LanMountainDesktop.AirAppRuntime/AirAppLifecycleService.cs +++ b/LanMountainDesktop.AirAppRuntime/AirAppLifecycleService.cs @@ -42,7 +42,8 @@ internal sealed class AirAppLifecycleService : IAirAppLifecycleService sessionId, instanceKey, request.SourceComponentId, - request.SourcePlacementId); + request.SourcePlacementId, + request.TargetEntryId); if (process is null) { return Task.FromResult(BuildResult(false, "start_failed", "AirAppHost process was not created.", null)); diff --git a/LanMountainDesktop.AirAppRuntime/IAirAppProcessStarter.cs b/LanMountainDesktop.AirAppRuntime/IAirAppProcessStarter.cs index 7414a8b..bf21af0 100644 --- a/LanMountainDesktop.AirAppRuntime/IAirAppProcessStarter.cs +++ b/LanMountainDesktop.AirAppRuntime/IAirAppProcessStarter.cs @@ -5,7 +5,7 @@ namespace LanMountainDesktop.AirAppRuntime; internal interface IAirAppProcessStarter { - Process? Start(string appId, string sessionId, string instanceKey, string? sourceComponentId, string? sourcePlacementId); + Process? Start(string appId, string sessionId, string instanceKey, string? sourceComponentId, string? sourcePlacementId, string? targetEntryId = null); } internal sealed class AirAppProcessStarter : IAirAppProcessStarter @@ -32,7 +32,8 @@ internal sealed class AirAppProcessStarter : IAirAppProcessStarter string sessionId, string instanceKey, string? sourceComponentId, - string? sourcePlacementId) + string? sourcePlacementId, + string? targetEntryId = null) { var hostPath = _locator.Resolve(_packageRootProvider(), _hostPathProvider()); var startInfo = CreateStartInfo(hostPath); @@ -57,6 +58,11 @@ internal sealed class AirAppProcessStarter : IAirAppProcessStarter AddArgument(startInfo, "--source-placement-id", sourcePlacementId.Trim()); } + if (!string.IsNullOrWhiteSpace(targetEntryId)) + { + AddArgument(startInfo, "--target-entry-id", targetEntryId.Trim()); + } + AirAppRuntimeLogger.Info( $"Starting AirAppHost. AppId='{appId}'; InstanceKey='{instanceKey}'; HostPath='{hostPath}'; DataRoot='{dataRoot ?? string.Empty}'."); var process = Process.Start(startInfo); diff --git a/LanMountainDesktop.Shared.IPC/Abstractions/Services/IAirAppLifecycleService.cs b/LanMountainDesktop.Shared.IPC/Abstractions/Services/IAirAppLifecycleService.cs index 68c6474..cf192cd 100644 --- a/LanMountainDesktop.Shared.IPC/Abstractions/Services/IAirAppLifecycleService.cs +++ b/LanMountainDesktop.Shared.IPC/Abstractions/Services/IAirAppLifecycleService.cs @@ -22,7 +22,8 @@ public sealed record AirAppOpenRequest( string AppId, string? SourceComponentId, string? SourcePlacementId, - int RequesterProcessId); + int RequesterProcessId, + string? TargetEntryId = null); public sealed record AirAppRegistrationRequest( string InstanceKey, diff --git a/LanMountainDesktop.Tests/AirAppLauncherServiceTests.cs b/LanMountainDesktop.Tests/AirAppLauncherServiceTests.cs index 3cd3297..be1a653 100644 --- a/LanMountainDesktop.Tests/AirAppLauncherServiceTests.cs +++ b/LanMountainDesktop.Tests/AirAppLauncherServiceTests.cs @@ -95,6 +95,22 @@ public sealed class AirAppLauncherServiceTests Assert.Equal(analogKey, worldKey); } + [Fact] + public void RssReaderRequest_CarriesEntryAndUsesGlobalInstance() + { + var request = AirAppLauncherService.BuildOpenRequest( + AirAppLauncherService.RssReaderAppId, + BuiltInComponentIds.DesktopRssReader, + "rss-placement", + 88, + "entry-42"); + + Assert.Equal("rss-reader", request.AppId); + Assert.Equal("entry-42", request.TargetEntryId); + Assert.Equal("rss-reader:global", AirAppLauncherService.BuildSingleInstanceKey( + request.AppId, request.SourceComponentId, request.SourcePlacementId)); + } + [Fact] public void CreateRuntimeStartInfo_UsesAirAppRuntimeAndRequesterPid() { diff --git a/LanMountainDesktop.Tests/AirAppRuntimeLifecycleServiceTests.cs b/LanMountainDesktop.Tests/AirAppRuntimeLifecycleServiceTests.cs index 4a71521..4cc8183 100644 --- a/LanMountainDesktop.Tests/AirAppRuntimeLifecycleServiceTests.cs +++ b/LanMountainDesktop.Tests/AirAppRuntimeLifecycleServiceTests.cs @@ -183,7 +183,8 @@ public sealed class AirAppRuntimeLifecycleServiceTests string sessionId, string instanceKey, string? sourceComponentId, - string? sourcePlacementId) + string? sourcePlacementId, + string? targetEntryId = null) { StartCount++; return _process; diff --git a/LanMountainDesktop/ComponentSystem/BuiltInComponentIds.cs b/LanMountainDesktop/ComponentSystem/BuiltInComponentIds.cs index 5e4b12a..b7640e0 100644 --- a/LanMountainDesktop/ComponentSystem/BuiltInComponentIds.cs +++ b/LanMountainDesktop/ComponentSystem/BuiltInComponentIds.cs @@ -38,6 +38,7 @@ public static class BuiltInComponentIds public const string DesktopBilibiliHotSearch = "DesktopBilibiliHotSearch"; public const string DesktopBaiduHotSearch = "DesktopBaiduHotSearch"; public const string DesktopStcn24Forum = "DesktopStcn24Forum"; + public const string DesktopRssReader = "DesktopRssReader"; public const string DesktopExchangeRateCalculator = "DesktopExchangeRateCalculator"; public const string DesktopWhiteboard = "DesktopWhiteboard"; public const string DesktopBlackboardLandscape = "DesktopBlackboardLandscape"; diff --git a/LanMountainDesktop/ComponentSystem/ComponentRegistry.cs b/LanMountainDesktop/ComponentSystem/ComponentRegistry.cs index cd8c9a1..4cd4382 100644 --- a/LanMountainDesktop/ComponentSystem/ComponentRegistry.cs +++ b/LanMountainDesktop/ComponentSystem/ComponentRegistry.cs @@ -307,6 +307,18 @@ public sealed class ComponentRegistry MinHeightCells: 4, AllowStatusBarPlacement: false, AllowDesktopPlacement: true), + new DesktopComponentDefinition( + BuiltInComponentIds.DesktopRssReader, + "RSS Reader", + "Rss", + "Info", + MinWidthCells: 8, + MinHeightCells: 4, + AllowStatusBarPlacement: false, + AllowDesktopPlacement: true, + ResizeMode: DesktopComponentResizeMode.Free, + Description: "A synchronized RSS and Atom reading inbox.", + DescriptionLocalizationKey: "component.rss_reader.description"), new DesktopComponentDefinition( BuiltInComponentIds.DesktopExchangeRateCalculator, "Exchange Rate Converter", diff --git a/LanMountainDesktop/LanMountainDesktop.csproj b/LanMountainDesktop/LanMountainDesktop.csproj index 40d88de..6cb23c7 100644 --- a/LanMountainDesktop/LanMountainDesktop.csproj +++ b/LanMountainDesktop/LanMountainDesktop.csproj @@ -75,6 +75,7 @@ + diff --git a/LanMountainDesktop/Localization/en-US.json b/LanMountainDesktop/Localization/en-US.json index efc1482..0f4f1e1 100644 --- a/LanMountainDesktop/Localization/en-US.json +++ b/LanMountainDesktop/Localization/en-US.json @@ -1515,5 +1515,62 @@ "settings.general.back_to_windows_fluent_icon_desc": "Search and choose a built-in Fluent icon for the left icon slot.", "settings.general.back_to_windows_icon_text_header": "Text icon", "settings.general.back_to_windows_icon_text_desc": "Enter up to four characters to display as the left icon.", - "settings.general.back_to_windows_fluent_icon_search_placeholder": "Search icon" + "settings.general.back_to_windows_fluent_icon_search_placeholder": "Search icon", + "component.rss_reader": "RSS Reader", + "component.rss_reader.description": "A synchronized RSS and Atom reading inbox.", + "rss.empty": "Import OPML or add your first RSS source", + "rss.latest_articles": "Latest articles", + "rss.refresh": "Refresh", + "rss.refresh_failed": "Refresh failed", + "rss.refreshing": "Refreshing...", + "rss.updated": "Updated", + "rss.mark_all_read": "Mark all read", + "rss.open_reader": "Open reader", + "rss.unread_count": "{0} unread", + "rss.all_articles": "All articles", + "rss.unread": "Unread", + "rss.favorites": "Favorites", + "rss.favorite": "Favorite", + "rss.favorited": "Favorited", + "rss.open_original": "Open original", + "rss.select_article": "Select an article", + "rss.article_placeholder": "Your RSS articles will appear here.", + "rss.status_counts": "{0} articles · {1} sources", + "rss.add_source": "Add source", + "rss.edit": "Edit", + "rss.edit_source": "Edit RSS source", + "rss.delete": "Delete", + "rss.delete_source": "Delete RSS source", + "rss.delete_source_confirm": "Delete {0}?", + "rss.cancel": "Cancel", + "rss.save": "Save", + "rss.enabled": "Enabled", + "rss.keep_favorites": "Keep favorited articles", + "rss.name": "Name", + "rss.folder": "Folder", + "rss.source": "Source", + "rss.all_sources": "All sources", + "rss.unread_first": "Show unread entries first", + "rss.entry_count": "Number of entries", + "rss.instance_settings_description": "These display options apply only to this component instance.", + "rss.feed_url": "Feed URL", + "rss.optional_name": "Optional custom name", + "rss.optional_folder": "Optional folder", + "rss.probe_add": "Probe and add", + "rss.checking_feed": "Checking feed...", + "rss.import_opml": "Import OPML", + "rss.export_opml": "Export OPML", + "rss.import_result": "Imported {0}, skipped {1}, failed {2}.", + "rss.exported": "OPML exported.", + "rss.settings": "RSS settings", + "rss.refresh_interval": "Refresh interval", + "rss.refresh_interval_minutes": "Refresh interval (minutes)", + "rss.minutes": "{0} minutes", + "rss.manual_only": "Manual only", + "rss.load_remote_images": "Load remote images", + "rss.air_app": "Air APP", + "rss.just_now": "Just now", + "rss.minutes_ago": "{0} min ago", + "rss.hours_ago": "{0} hr ago", + "rss.short_date_format": "MMM d" } diff --git a/LanMountainDesktop/Localization/zh-CN.json b/LanMountainDesktop/Localization/zh-CN.json index 0e55a90..4b9ebdb 100644 --- a/LanMountainDesktop/Localization/zh-CN.json +++ b/LanMountainDesktop/Localization/zh-CN.json @@ -1470,5 +1470,62 @@ "settings.update.transfer_controls_description": "暂停正在运行的下载,从保存的状态恢复,或取消并清除待处理的更新文件。", "settings.update.transfer_controls_title": "传输控制", "settings.update.type_reinstall": "重新安装", - "settings.update.update_type_label": "更新类型" + "settings.update.update_type_label": "更新类型", + "component.rss_reader": "RSS 阅读器", + "component.rss_reader.description": "在组件与轻应用之间同步的 RSS 与 Atom 阅读收件箱。", + "rss.empty": "导入 OPML 或添加第一个 RSS 源", + "rss.latest_articles": "最新文章", + "rss.refresh": "刷新", + "rss.refresh_failed": "刷新失败", + "rss.refreshing": "正在刷新...", + "rss.updated": "已更新", + "rss.mark_all_read": "全部标为已读", + "rss.open_reader": "进入阅读器", + "rss.unread_count": "{0} 篇未读", + "rss.all_articles": "全部文章", + "rss.unread": "未读", + "rss.favorites": "收藏", + "rss.favorite": "收藏", + "rss.favorited": "已收藏", + "rss.open_original": "打开原文", + "rss.select_article": "选择一篇文章", + "rss.article_placeholder": "RSS 文章正文将在这里显示。", + "rss.status_counts": "{0} 篇文章 · {1} 个订阅源", + "rss.add_source": "添加订阅源", + "rss.edit": "编辑", + "rss.edit_source": "编辑 RSS 订阅源", + "rss.delete": "删除", + "rss.delete_source": "删除 RSS 订阅源", + "rss.delete_source_confirm": "确定删除“{0}”吗?", + "rss.cancel": "取消", + "rss.save": "保存", + "rss.enabled": "启用此订阅源", + "rss.keep_favorites": "保留已收藏文章", + "rss.name": "名称", + "rss.folder": "文件夹", + "rss.source": "订阅源", + "rss.all_sources": "全部订阅源", + "rss.unread_first": "未读文章优先显示", + "rss.entry_count": "显示文章数量", + "rss.instance_settings_description": "这些显示选项仅应用于当前组件实例。", + "rss.feed_url": "订阅地址", + "rss.optional_name": "可选的自定义名称", + "rss.optional_folder": "可选的文件夹", + "rss.probe_add": "检测并添加", + "rss.checking_feed": "正在检测订阅源...", + "rss.import_opml": "导入 OPML", + "rss.export_opml": "导出 OPML", + "rss.import_result": "成功导入 {0} 个,跳过 {1} 个,失败 {2} 个。", + "rss.exported": "OPML 已导出。", + "rss.settings": "RSS 设置", + "rss.refresh_interval": "刷新间隔", + "rss.refresh_interval_minutes": "刷新间隔(分钟)", + "rss.minutes": "{0} 分钟", + "rss.manual_only": "仅手动刷新", + "rss.load_remote_images": "加载远程图片", + "rss.air_app": "轻应用", + "rss.just_now": "刚刚", + "rss.minutes_ago": "{0} 分钟前", + "rss.hours_ago": "{0} 小时前", + "rss.short_date_format": "M 月 d 日" } diff --git a/LanMountainDesktop/Models/ComponentSettingsSnapshot.cs b/LanMountainDesktop/Models/ComponentSettingsSnapshot.cs index cb344ad..08871ab 100644 --- a/LanMountainDesktop/Models/ComponentSettingsSnapshot.cs +++ b/LanMountainDesktop/Models/ComponentSettingsSnapshot.cs @@ -71,6 +71,12 @@ public sealed class ComponentSettingsSnapshot public string Stcn24ForumSourceType { get; set; } = Stcn24ForumSourceTypes.LatestCreated; + public string RssReaderSourceId { get; set; } = string.Empty; + + public bool RssReaderUnreadFirst { get; set; } = true; + + public int RssReaderDisplayCount { get; set; } = 20; + public List? OfficeRecentDocumentsEnabledSources { get; set; } // 智教Hub组件配置 diff --git a/LanMountainDesktop/Services/AirAppLauncherService.cs b/LanMountainDesktop/Services/AirAppLauncherService.cs index b7f1567..ce3a687 100644 --- a/LanMountainDesktop/Services/AirAppLauncherService.cs +++ b/LanMountainDesktop/Services/AirAppLauncherService.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using LanMountainDesktop.ComponentSystem; using LanMountainDesktop.Shared.IPC; using LanMountainDesktop.Shared.IPC.Abstractions.Services; +using LanMountainDesktop.Services.RssReader; namespace LanMountainDesktop.Services; @@ -15,12 +16,15 @@ public interface IAirAppLauncherService void OpenWorldClock(string sourceComponentId, string? sourcePlacementId); void OpenWhiteboard(string componentId, string? sourcePlacementId); + + void OpenRssReader(string componentId, string? sourcePlacementId, string? targetEntryId = null); } internal sealed class AirAppLauncherService : IAirAppLauncherService { public const string WorldClockAppId = "world-clock"; public const string WhiteboardAppId = "whiteboard"; + public const string RssReaderAppId = "rss-reader"; private const int RuntimeIpcRetryCount = 4; @@ -48,17 +52,30 @@ internal sealed class AirAppLauncherService : IAirAppLauncherService _ = OpenAsync(WhiteboardAppId, componentId, sourcePlacementId); } + public void OpenRssReader(string componentId, string? sourcePlacementId, string? targetEntryId = null) + { + AppLogger.Info("AirAppLauncher", $"RSS Reader Air APP requested. EntryId='{targetEntryId ?? string.Empty}'."); + if (!string.IsNullOrWhiteSpace(targetEntryId)) + { + using var service = new RssReaderService(); + service.SetPendingEntryId(targetEntryId); + } + _ = OpenAsync(RssReaderAppId, componentId, sourcePlacementId, targetEntryId); + } + internal static AirAppOpenRequest BuildOpenRequest( string appId, string? sourceComponentId, string? sourcePlacementId, - int requesterProcessId) + int requesterProcessId, + string? targetEntryId = null) { return new AirAppOpenRequest( appId.Trim(), string.IsNullOrWhiteSpace(sourceComponentId) ? null : sourceComponentId.Trim(), string.IsNullOrWhiteSpace(sourcePlacementId) ? null : sourcePlacementId.Trim(), - requesterProcessId); + requesterProcessId, + string.IsNullOrWhiteSpace(targetEntryId) ? null : targetEntryId.Trim()); } internal static string BuildSingleInstanceKey(string appId, string? sourceComponentId, string? sourcePlacementId) @@ -68,15 +85,19 @@ internal sealed class AirAppLauncherService : IAirAppLauncherService { return $"{normalizedAppId}:clock-suite:global"; } + if (string.Equals(normalizedAppId, RssReaderAppId, StringComparison.OrdinalIgnoreCase)) + { + return $"{normalizedAppId}:global"; + } var normalizedComponentId = string.IsNullOrWhiteSpace(sourceComponentId) ? "none" : sourceComponentId.Trim(); var normalizedPlacementId = string.IsNullOrWhiteSpace(sourcePlacementId) ? "none" : sourcePlacementId.Trim(); return $"{normalizedAppId}:{normalizedComponentId}:{normalizedPlacementId}"; } - private static async Task OpenAsync(string appId, string sourceComponentId, string? sourcePlacementId) + private static async Task OpenAsync(string appId, string sourceComponentId, string? sourcePlacementId, string? targetEntryId = null) { - var request = BuildOpenRequest(appId, sourceComponentId, sourcePlacementId, Environment.ProcessId); + var request = BuildOpenRequest(appId, sourceComponentId, sourcePlacementId, Environment.ProcessId, targetEntryId); try { var result = await SendOpenRequestAsync(request).ConfigureAwait(false); diff --git a/LanMountainDesktop/Services/DesktopComponentEditorRegistryFactory.cs b/LanMountainDesktop/Services/DesktopComponentEditorRegistryFactory.cs index f2fd5b6..9ee1ed1 100644 --- a/LanMountainDesktop/Services/DesktopComponentEditorRegistryFactory.cs +++ b/LanMountainDesktop/Services/DesktopComponentEditorRegistryFactory.cs @@ -263,6 +263,11 @@ public static class DesktopComponentEditorRegistryFactory nameof(ComponentSettingsSnapshot.Stcn24ForumSourceType) ] })), + [BuiltInComponentIds.DesktopRssReader] = new( + BuiltInComponentIds.DesktopRssReader, + context => new RssReaderComponentEditor(context), + preferredWidth: 480d, + preferredHeight: 480d), [BuiltInComponentIds.DesktopZhiJiaoHub] = new( BuiltInComponentIds.DesktopZhiJiaoHub, context => new ZhiJiaoHubComponentEditor(context), diff --git a/LanMountainDesktop/Views/Components/DesktopComponentRuntimeRegistry.cs b/LanMountainDesktop/Views/Components/DesktopComponentRuntimeRegistry.cs index aca32c1..3653f28 100644 --- a/LanMountainDesktop/Views/Components/DesktopComponentRuntimeRegistry.cs +++ b/LanMountainDesktop/Views/Components/DesktopComponentRuntimeRegistry.cs @@ -452,6 +452,12 @@ public sealed class DesktopComponentRuntimeRegistry BuiltInComponentIds.DesktopStcn24Forum, "component.stcn24_forum", () => new Stcn24ForumWidget()), + new DesktopComponentRuntimeRegistration( + BuiltInComponentIds.DesktopRssReader, + "component.rss_reader", + (Func)(context => + new RssReaderWidget(context.ComponentSettingsAccessor, context.PlacementId)), + cornerRadiusResolver: (Func?)null), new DesktopComponentRuntimeRegistration( BuiltInComponentIds.DesktopExchangeRateCalculator, "component.exchange_rate_converter",