diff --git a/Pixiview.iOS/AppDelegate.cs b/Pixiview.iOS/AppDelegate.cs index 47ab490..ce788b0 100644 --- a/Pixiview.iOS/AppDelegate.cs +++ b/Pixiview.iOS/AppDelegate.cs @@ -27,7 +27,7 @@ namespace Pixiview.iOS public override bool OpenUrl(UIApplication app, NSUrl url, NSDictionary options) { App.DebugPrint($"url: {url}."); - return App.OpenUrl(url.AbsoluteString); + return App.OpenUrl(url); } } } diff --git a/Pixiview.iOS/Info.plist b/Pixiview.iOS/Info.plist index 3861c37..770a341 100644 --- a/Pixiview.iOS/Info.plist +++ b/Pixiview.iOS/Info.plist @@ -65,5 +65,20 @@ Assets.xcassets/AppIcon.appiconset/Icon58 + CFBundleDocumentTypes + + + CFBundleTypeName + Favorites Json + LSItemContentTypes + + public.text + + CFBundleTypeIconFiles + + Assets.xcassets/AppIcon.appiconset/Icon180 + + + diff --git a/Pixiview/App.cs b/Pixiview/App.cs index f3f8b33..647835f 100644 --- a/Pixiview/App.cs +++ b/Pixiview/App.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; +using System.Linq; using System.Text.RegularExpressions; using Pixiview.Illust; using Pixiview.Resources; @@ -123,24 +125,92 @@ namespace Pixiview Debug.Fail(string.Format("[{0:HH:mm:ss.ffff}] - {1} - {2}", DateTime.Now, category, message)); } - public static bool OpenUrl(string url) + public static bool OpenUrl(Uri uri) { var current = Current.MainPage; - if (current != null) + if (current != null && uri != null) { - var m = Regex.Match(url, "/artworks/([0-9]+)", RegexOptions.IgnoreCase); - if (m.Success) + var url = uri.AbsolutePath; + if (uri.Scheme == "pixiview://") { - var illust = new IllustItem { Id = m.Groups[1].Value }; - var page = new ViewIllustPage(illust, true); - MainThread.BeginInvokeOnMainThread(() => current.Navigation.PushAsync(page)); + var m = Regex.Match(url, "/artworks/([0-9]+)", RegexOptions.IgnoreCase); + if (m.Success) + { + var illust = new IllustItem { Id = m.Groups[1].Value }; + var page = new ViewIllustPage(illust, true); + MainThread.BeginInvokeOnMainThread(() => current.Navigation.PushAsync(page)); + } + else + { + MainThread.BeginInvokeOnMainThread(() => current.DisplayAlert( + url, + ResourceHelper.InvalidUrl, + ResourceHelper.Ok)); + } } - else + else if (File.Exists(url)) { - MainThread.BeginInvokeOnMainThread(() => current.DisplayAlert( - url, - ResourceHelper.InvalidUrl, - ResourceHelper.Ok)); + IllustFavorite favObj; + try + { + favObj = Stores.LoadFavoritesIllusts(url); + } + catch (Exception ex) + { + DebugError("open.file", $"failed to parse file, name: {url}, error: {ex.Message}"); + return true; + } + var path = Stores.FavoritesPath; + if (File.Exists(path)) + { + MainThread.BeginInvokeOnMainThread(async () => + { + var opReplace = ResourceHelper.FavoritesReplace; + var opCombine = ResourceHelper.FavoritesCombine; + var result = await current.DisplayActionSheet( + ResourceHelper.FavoritesOperation, + ResourceHelper.Cancel, + opCombine, + opReplace); + if (result == opReplace) + { + // replace favorite file + File.Copy(url, path, true); + } + else if (result == opCombine) + { + // combine favorite file + var favNow = Stores.GetFavoriteObject(); + var list = favObj.Illusts; + var distinct = favNow.Illusts.Where(f => !list.Any(i => i.Id == f.Id)).ToList(); + list.AddRange(distinct); + + favNow.Illusts = list; + Stores.SaveFavoritesIllusts(); + } + + if (Shell.Current.CurrentState.Location.OriginalString.EndsWith(Routes.Favorites)) + { + var sc = (IShellSectionController)Shell.Current.CurrentItem.CurrentItem; + if (sc.PresentedPage is FavoritesPage fav) + { + fav.Reload(true); + } + } + }); + } + else + { + File.Copy(url, path); + if (Shell.Current.CurrentState.Location.OriginalString.EndsWith(Routes.Favorites)) + { + var sc = (IShellSectionController)Shell.Current.CurrentItem.CurrentItem; + if (sc.PresentedPage is FavoritesPage fav) + { + fav.Reload(true); + } + } + } } } return true; diff --git a/Pixiview/Illust/FavoritesPage.xaml b/Pixiview/Illust/FavoritesPage.xaml index 878a84e..8bfd2c1 100644 --- a/Pixiview/Illust/FavoritesPage.xaml +++ b/Pixiview/Illust/FavoritesPage.xaml @@ -7,8 +7,12 @@ x:Class="Pixiview.Illust.FavoritesPage" BackgroundColor="{DynamicResource WindowColor}" Title="{r:Text Favorites}"> + + + - + DoGetIllustList(IllustItem[] data, ICommand command) @@ -37,12 +39,28 @@ namespace Pixiview.Illust protected override IllustItem[] DoLoadIllustData(bool force) { - var favorites = Stores.FavoriteObject; + var favorites = Stores.GetFavoriteObject(force); if (favorites == null) { return null; } return favorites.Illusts.ToArray(); } + + public void Reload(bool force = false) + { + loaded = false; + StartLoad(force); + } + + private async void ShareFavorites_Clicked(object sender, EventArgs e) + { + var file = Stores.FavoritesPath; + await Share.RequestAsync(new ShareFileRequest + { + Title = ResourceHelper.Favorites, + File = new ShareFile(file) + }); + } } } diff --git a/Pixiview/Illust/IllustCollectionPage.cs b/Pixiview/Illust/IllustCollectionPage.cs index 43ba8f5..f8e9237 100644 --- a/Pixiview/Illust/IllustCollectionPage.cs +++ b/Pixiview/Illust/IllustCollectionPage.cs @@ -121,7 +121,15 @@ namespace Pixiview.Illust protected override void OnSizeAllocated(double width, double height) { base.OnSizeAllocated(width, height); - var columns = width > height ? 4 : 2; + int columns; + if (isPhone) + { + columns = width > height ? 4 : 2; + } + else + { + columns = width > height ? 6 : 4; + } if (Columns != columns) { Columns = columns; @@ -341,7 +349,7 @@ namespace Pixiview.Illust IsLoading = false; return; } - if (force) + if (force && IsFavoriteVisible) { NeedUpdate = true; } diff --git a/Pixiview/Illust/MainPage.xaml b/Pixiview/Illust/MainPage.xaml index 0005048..090e33b 100644 --- a/Pixiview/Illust/MainPage.xaml +++ b/Pixiview/Illust/MainPage.xaml @@ -12,7 +12,7 @@ IconImageSource="{DynamicResource FontIconRefresh}"/> - + - + - + - + 成功保存图片到照片库。 原图已保存,是否继续? 无法识别该 URL。 + 请选择收藏夹操作 + 替换 + 合并 \ No newline at end of file diff --git a/Pixiview/Resources/ResourceHelper.cs b/Pixiview/Resources/ResourceHelper.cs index d4936d6..c7f4f19 100644 --- a/Pixiview/Resources/ResourceHelper.cs +++ b/Pixiview/Resources/ResourceHelper.cs @@ -23,6 +23,10 @@ namespace Pixiview.Resources public static string SaveSuccess => GetResource(nameof(SaveSuccess)); public static string AlreadySavedQuestion => GetResource(nameof(AlreadySavedQuestion)); public static string InvalidUrl => GetResource(nameof(InvalidUrl)); + public static string Favorites => GetResource(nameof(Favorites)); + public static string FavoritesOperation => GetResource(nameof(FavoritesOperation)); + public static string FavoritesReplace => GetResource(nameof(FavoritesReplace)); + public static string FavoritesCombine => GetResource(nameof(FavoritesCombine)); static readonly Dictionary dict = new Dictionary(); diff --git a/Pixiview/UI/AdaptedPage.cs b/Pixiview/UI/AdaptedPage.cs index abae771..c99ae0d 100644 --- a/Pixiview/UI/AdaptedPage.cs +++ b/Pixiview/UI/AdaptedPage.cs @@ -22,7 +22,7 @@ namespace Pixiview.UI public event EventHandler Unload; public event EventHandler OrientationChanged; - protected readonly bool isPhone = DeviceInfo.Idiom == DeviceIdiom.Phone; + protected static readonly bool isPhone = DeviceInfo.Idiom == DeviceIdiom.Phone; public AdaptedPage() { @@ -48,13 +48,13 @@ namespace Pixiview.UI PageTopMargin = AppShell.TotalBarOffset; break; case Orientation.PortraitUpsideDown: - PageTopMargin = isPhone ? AppShell.NavigationBarOffset : AppShell.TotalBarOffset; - break; + //PageTopMargin = isPhone ? AppShell.NavigationBarOffset : AppShell.TotalBarOffset; + //break; case Orientation.Unknown: case Orientation.LandscapeLeft: case Orientation.LandscapeRight: default: - PageTopMargin = AppShell.NavigationBarOffset; + PageTopMargin = isPhone ? AppShell.NavigationBarOffset : AppShell.TotalBarOffset; break; } OrientationChanged?.Invoke(this, new OrientationEventArgs { CurrentOrientation = orientation }); diff --git a/Pixiview/UI/StyleDefinition.cs b/Pixiview/UI/StyleDefinition.cs index 393cded..2cef140 100644 --- a/Pixiview/UI/StyleDefinition.cs +++ b/Pixiview/UI/StyleDefinition.cs @@ -29,6 +29,7 @@ namespace Pixiview.UI public const string IconOption = "\uf013"; public const string IconDownload = "\uf019"; public const string IconFavorite = "\uf02e"; + public const string IconShare = "\uf35d"; static StyleDefinition() { diff --git a/Pixiview/UI/Theme/ThemeBase.cs b/Pixiview/UI/Theme/ThemeBase.cs index b344d0c..81721ac 100644 --- a/Pixiview/UI/Theme/ThemeBase.cs +++ b/Pixiview/UI/Theme/ThemeBase.cs @@ -13,6 +13,7 @@ namespace Pixiview.UI.Theme public const string FontIconOption = nameof(FontIconOption); public const string FontIconDownload = nameof(FontIconDownload); public const string FontIconFavorite = nameof(FontIconFavorite); + public const string FontIconShare = nameof(FontIconShare); public const string StatusBarStyle = nameof(StatusBarStyle); public const string WindowColor = nameof(WindowColor); @@ -68,6 +69,7 @@ namespace Pixiview.UI.Theme Add(FontIconOption, GetSolidIcon(StyleDefinition.IconOption, solidFontFamily)); Add(FontIconDownload, GetSolidIcon(StyleDefinition.IconDownload, solidFontFamily)); Add(FontIconFavorite, GetSolidIcon(StyleDefinition.IconFavorite, solidFontFamily)); + Add(FontIconShare, GetSolidIcon(StyleDefinition.IconShare, solidFontFamily)); } private FontImageSource GetSolidIcon(string icon, string family, Color color = default) diff --git a/Pixiview/Utils/Stores.cs b/Pixiview/Utils/Stores.cs index 5f584e9..f6de5b2 100644 --- a/Pixiview/Utils/Stores.cs +++ b/Pixiview/Utils/Stores.cs @@ -18,10 +18,10 @@ namespace Pixiview.Utils public static readonly string PersonalFolder = Environment.GetFolderPath(Environment.SpecialFolder.Personal); public static readonly string CacheFolder = Environment.GetFolderPath(Environment.SpecialFolder.InternetCache); + private const string favoriteFile = "favorites.json"; private const string imageFolder = "img-original"; private const string previewFolder = "img-master"; private const string illustFile = "illust.json"; - private const string favoriteFile = "favorites.json"; private const string pagesFolder = "pages"; private const string preloadsFolder = "preloads"; @@ -45,38 +45,40 @@ namespace Pixiview.Utils } } + public static List Favorites => GetFavoriteObject().Illusts; + public static string FavoritesPath => Path.Combine(PersonalFolder, favoriteFile); + private static IllustFavorite favoriteObject; - public static IllustFavorite FavoriteObject + + public static IllustFavorite GetFavoriteObject(bool force = false) { - get + lock (sync) { - lock (sync) + if (force || favoriteObject == null) { - if (favoriteObject == null) + var favorites = LoadFavoritesIllusts(); + if (favorites != null) { - var favorites = LoadFavoritesIllusts(); - if (favorites != null) - { - favoriteObject = favorites; - } - else - { - favoriteObject = new IllustFavorite - { - Illusts = new List() - }; - } + favoriteObject = favorites; + } + else + { + favoriteObject = new IllustFavorite + { + Illusts = new List() + }; } - return favoriteObject; } + return favoriteObject; } } - public static List Favorites => FavoriteObject.Illusts; - - private static IllustFavorite LoadFavoritesIllusts() + public static IllustFavorite LoadFavoritesIllusts(string file = null) { - var file = Path.Combine(PersonalFolder, favoriteFile); + if (file == null) + { + file = FavoritesPath; + } lock (sync) { return ReadObject(file); @@ -85,10 +87,10 @@ namespace Pixiview.Utils public static void SaveFavoritesIllusts() { - var file = Path.Combine(PersonalFolder, favoriteFile); + var file = FavoritesPath; lock (sync) { - var data = FavoriteObject; + var data = GetFavoriteObject(); data.LastFavoriteUtc = DateTime.UtcNow; WriteObject(file, data); } @@ -236,9 +238,9 @@ namespace Pixiview.Utils Configs.UrlIllustList, Configs.Referer, force: force); - if (result.error) + if (result == null || result.error) { - App.DebugPrint($"error when load illust data: {result.message} ({force})"); + App.DebugPrint($"error when load illust data: {result?.message} ({force})"); } return result; } @@ -276,9 +278,9 @@ namespace Pixiview.Utils string.Format(Configs.UrlIllustPage, id), string.Format(Configs.UrlIllust, id), force: force); - if (result.error) + if (result == null || result.error) { - App.DebugPrint($"error when load page data: {result.message} ({force})"); + App.DebugPrint($"error when load page data: {result?.message} ({force})"); } return result; } @@ -290,22 +292,22 @@ namespace Pixiview.Utils string.Format(Configs.UrlIllustUserAll, userId), string.Format(Configs.UrlIllustUser, userId), force: force); - if (list.error) + if (list == null || list.error) { - App.DebugPrint($"error when load user data: {list.message} ({force})"); + App.DebugPrint($"error when load user data: {list?.message} ({force})"); } // TODO - var ids = string.Join("&ids%5B%5D=", list.body.illusts.Keys.Take(20)); + var ids = string.Join("", list.body.illusts.Keys.Take(20).Select(id => $"ids%5B%5D={id}&")); var result = LoadObject( null, string.Format(Configs.UrlIllustUserArtworks, userId, ids, 1), string.Format(Configs.UrlIllustUser, userId), force: force); - if (result.error) + if (result == null || result.error) { - App.DebugPrint($"error when load user illust data: {result.message} ({force})"); + App.DebugPrint($"error when load user illust data: {result?.message} ({force})"); } return result; } @@ -451,20 +453,21 @@ namespace Pixiview.Utils } var client = new HttpClient(handler) { - BaseAddress = new Uri($"{uri.Scheme}://{uri.Host}") + BaseAddress = new Uri($"{uri.Scheme}://{uri.Host}"), + Timeout = TimeSpan.FromSeconds(30) }; return TryCount(() => { using (var request = new HttpRequestMessage(HttpMethod.Get, uri.PathAndQuery) { - Version = new Version(2, 0) + Version = new Version(2, 0), }) { var headers = request.Headers; headerAction(headers); headers.Add("accept-language", Configs.AcceptLanguage); headers.Add("accept-encoding", Configs.AcceptEncoding); - return client.SendAsync(request).Result; + return client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).Result; } }); } @@ -485,7 +488,7 @@ namespace Pixiview.Utils public const string SuffixPreload = " id=\"meta-preload-data\" content='"; public const int SuffixPreloadLength = 33; // SuffixPreload.Length public const string UrlIllustUserAll = "https://www.pixiv.net/ajax/user/{0}/profile/all?lang=zh"; - public const string UrlIllustUserArtworks = "https://www.pixiv.net/ajax/user/{0}/profile/illusts?ids%5B%5D={1}&work_category=illustManga&is_first_page={2}&lang=zh"; + public const string UrlIllustUserArtworks = "https://www.pixiv.net/ajax/user/{0}/profile/illusts?{1}work_category=illustManga&is_first_page={2}&lang=zh"; public const string UrlIllustUser = "https://www.pixiv.net/users/{0}/artworks"; public const string UrlIllustPage = "https://www.pixiv.net/ajax/illust/{0}/pages?lang=zh"; public const string UserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36";