mirror of
https://github.com/MaaAssistantArknights/MaaAssistantArknights.git
synced 2026-07-18 10:10:45 +08:00
* fix: 统一WpfGui工作目录 * chore: 漏了的cache目录 * chore: Auto update by pre-commit hooks [skip changelog] * fix: 多了一层相对目录 * feat: wpf 创建软连接 * feat: 添加 PathsHelper,提取通用地址 * feat: 提取通用目录 * fix: Combine * fix: 重构依赖后 MaaWpfGui 的 resource 路径丢失 (#14077) fix: mklink * chore: 删除弹窗 * chore: 自定义基建使用绝对路径 * perf: 简化命名 * fix: 遗漏的issue rp * fix: 基建 * perf: config.new * feat: 去除本地作业前缀 * chore: 怎么改名了 * rft: rename 添加一个Dir后缀, 直接using static * fix: remove不再使用的../../../resrouce * perf: 短点 --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: uye <99072975+ABA2396@users.noreply.github.com> Co-authored-by: Status102 <102887808+status102@users.noreply.github.com>
280 lines
8.8 KiB
C#
280 lines
8.8 KiB
C#
// <copyright file="AnnouncementViewModel.cs" company="MaaAssistantArknights">
|
|
// Part of the MaaWpfGui project, maintained by the MaaAssistantArknights team (Maa Team)
|
|
// Copyright (C) 2021-2025 MaaAssistantArknights Contributors
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License v3.0 only as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY
|
|
// </copyright>
|
|
|
|
using System;
|
|
using System.Collections.ObjectModel;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using System.Windows.Input;
|
|
using HandyControl.Controls;
|
|
using HandyControl.Tools.Command;
|
|
using MaaWpfGui.Configuration.Factory;
|
|
using MaaWpfGui.Constants;
|
|
using MaaWpfGui.Helper;
|
|
using Serilog;
|
|
using Stylet;
|
|
|
|
namespace MaaWpfGui.ViewModels.UI
|
|
{
|
|
/// <summary>
|
|
/// The view model of version update.
|
|
/// </summary>
|
|
// 通过 container.Get<AnnouncementViewModel>(); 实例化或获取实例
|
|
// ReSharper disable once ClassNeverInstantiated.Global
|
|
public class AnnouncementViewModel : Screen
|
|
{
|
|
private static readonly ILogger _logger = Log.ForContext<AnnouncementViewModel>();
|
|
|
|
private static readonly object _lock = new();
|
|
|
|
public string ImageSource { get; set; }
|
|
|
|
public class AnnouncementSection
|
|
{
|
|
public string Title { get; set; }
|
|
|
|
public string Content { get; set; }
|
|
}
|
|
|
|
public AnnouncementViewModel()
|
|
{
|
|
UpdateImageSource();
|
|
|
|
UpdateScrollStateCommand = new RelayCommand<ScrollViewer>(scrollViewer =>
|
|
{
|
|
if (scrollViewer == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// 计算是否滚动到底部
|
|
IsScrolledToBottom |= scrollViewer.VerticalOffset >= scrollViewer.ScrollableHeight - 10;
|
|
});
|
|
|
|
ScrollToTopCommand = new RelayCommand<ScrollViewer>(scrollViewer =>
|
|
{
|
|
if (scrollViewer == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
scrollViewer.ScrollToTop();
|
|
});
|
|
AnnouncementSections = [.. ParseAnnouncementInfo(AnnouncementInfo)];
|
|
}
|
|
|
|
private void UpdateImageSource()
|
|
{
|
|
ImageSource = SettingsViewModel.GuiSettings.Language switch
|
|
{
|
|
"zh-cn" or "zh-tw" => "/Res/Img/NoSkland.jpg",
|
|
_ => "/Res/Img/NoSkLandEn.jpg",
|
|
};
|
|
}
|
|
|
|
private static ObservableCollection<AnnouncementSection> ParseAnnouncementInfo(string markdown)
|
|
{
|
|
var sections = markdown.Split(["### "], StringSplitOptions.RemoveEmptyEntries)
|
|
.Select(section =>
|
|
{
|
|
var lines = section.Split('\n');
|
|
return new AnnouncementSection
|
|
{
|
|
Title = lines.FirstOrDefault(),
|
|
Content = "### " + string.Join("\n", lines).Trim([' ', '\n', '-']),
|
|
};
|
|
}).ToList();
|
|
|
|
sections.Insert(0, new()
|
|
{
|
|
Title = "ALL~ the Announcements",
|
|
Content = markdown,
|
|
});
|
|
|
|
return [.. sections];
|
|
}
|
|
|
|
public ICommand UpdateScrollStateCommand { get; }
|
|
|
|
public ICommand ScrollToTopCommand { get; }
|
|
|
|
private bool _isScrolledToBottom;
|
|
|
|
public bool IsScrolledToBottom
|
|
{
|
|
get => _isScrolledToBottom;
|
|
set => SetAndNotify(ref _isScrolledToBottom, value);
|
|
}
|
|
|
|
private ObservableCollection<AnnouncementSection> _announcementSections;
|
|
|
|
public ObservableCollection<AnnouncementSection> AnnouncementSections
|
|
{
|
|
get => _announcementSections;
|
|
set
|
|
{
|
|
SetAndNotify(ref _announcementSections, value);
|
|
SelectedAnnouncementSection = AnnouncementSections.FirstOrDefault();
|
|
}
|
|
}
|
|
|
|
private AnnouncementSection _selectedAnnouncementSection;
|
|
|
|
public AnnouncementSection SelectedAnnouncementSection
|
|
{
|
|
get => _selectedAnnouncementSection;
|
|
set => SetAndNotify(ref _selectedAnnouncementSection, value);
|
|
}
|
|
|
|
private static readonly string _announcementInFile = SettingsViewModel.GuiSettings.Language switch
|
|
{
|
|
"zh-cn" or "zh-tw" => Path.Combine(PathsHelper.CacheDir, "announcement.md"),
|
|
_ => Path.Combine(PathsHelper.CacheDir, "announcement_en.md"),
|
|
};
|
|
|
|
private static string AnnouncementInFile
|
|
{
|
|
get
|
|
{
|
|
if (!File.Exists(_announcementInFile))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
try
|
|
{
|
|
lock (_lock)
|
|
{
|
|
return File.ReadAllText(_announcementInFile);
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
_logger.Error(e, "Failed to read announcement from file");
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
set
|
|
{
|
|
try
|
|
{
|
|
lock (_lock)
|
|
{
|
|
File.WriteAllText(_announcementInFile, value);
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
_logger.Error(e, "Failed to write announcement to file");
|
|
}
|
|
}
|
|
}
|
|
|
|
private string _announcementInfo = AnnouncementInFile ?? string.Empty;
|
|
|
|
/// <summary>
|
|
/// Gets the announcement info.
|
|
/// </summary>
|
|
// ReSharper disable once MemberCanBePrivate.Global
|
|
public string AnnouncementInfo
|
|
{
|
|
get => _announcementInfo;
|
|
private set
|
|
{
|
|
SetAndNotify(ref _announcementInfo, value);
|
|
AnnouncementInFile = value;
|
|
AnnouncementSections = [.. ParseAnnouncementInfo(AnnouncementInfo)];
|
|
}
|
|
}
|
|
|
|
private bool _doNotRemindThisAnnouncementAgain = ConfigFactory.Root.AnnouncementInfo.DoNotShowAgain;
|
|
|
|
public bool DoNotRemindThisAnnouncementAgain
|
|
{
|
|
get => _doNotRemindThisAnnouncementAgain;
|
|
set
|
|
{
|
|
SetAndNotify(ref _doNotRemindThisAnnouncementAgain, value);
|
|
ConfigFactory.Root.AnnouncementInfo.DoNotShowAgain = value;
|
|
}
|
|
}
|
|
|
|
private bool _doNotShowAnnouncement = ConfigFactory.Root.AnnouncementInfo.DoNotShow;
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether to show the update.
|
|
/// </summary>
|
|
public bool DoNotShowAnnouncement
|
|
{
|
|
get => _doNotShowAnnouncement;
|
|
set
|
|
{
|
|
SetAndNotify(ref _doNotShowAnnouncement, value);
|
|
ConfigFactory.Root.AnnouncementInfo.DoNotShow = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 检查更新
|
|
/// </summary>
|
|
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
|
public async Task CheckAndDownloadAnnouncement()
|
|
{
|
|
string path = "announcements/wpf";
|
|
if (SettingsViewModel.GuiSettings.Language is not ("zh-cn" or "zh-tw"))
|
|
{
|
|
path += "_en";
|
|
}
|
|
|
|
string url = MaaUrls.MaaApi + path + ".md";
|
|
|
|
using var response = await ETagCache.FetchResponseWithEtag(url, string.IsNullOrEmpty(AnnouncementInfo));
|
|
|
|
if (response == null ||
|
|
response.StatusCode == System.Net.HttpStatusCode.NotModified ||
|
|
response.StatusCode != System.Net.HttpStatusCode.OK)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var body = await HttpResponseHelper.GetStringAsync(response);
|
|
if (!string.IsNullOrEmpty(body) && AnnouncementInfo != body)
|
|
{
|
|
const string Template =
|
|
"----------- OLD -----------\n" +
|
|
"{AnnouncementInfo}\n" +
|
|
"---------------------------\n\n" +
|
|
"=========== NEW ===========\n" +
|
|
"{Body}\n" +
|
|
"===========================";
|
|
_logger.Information(Template,
|
|
AnnouncementInfo,
|
|
body);
|
|
AnnouncementInfo = body;
|
|
DoNotRemindThisAnnouncementAgain = false;
|
|
}
|
|
|
|
ETagCache.Set(response, url);
|
|
ETagCache.Save();
|
|
}
|
|
|
|
public void Close()
|
|
{
|
|
RequestClose();
|
|
}
|
|
}
|
|
}
|