favorite & view page & etc.
This commit is contained in:
		| @@ -107,9 +107,11 @@ namespace Gallery | |||||||
|  |  | ||||||
|             GallerySources = new List<IGallerySource>() |             GallerySources = new List<IGallerySource>() | ||||||
|             { |             { | ||||||
|  |                 new Sources.FavoriteGallerySource(), | ||||||
|  |  | ||||||
|                 new Sources.Yandere.GallerySource(),    // https://yande.re |                 new Sources.Yandere.GallerySource(),    // https://yande.re | ||||||
|                 new Sources.Danbooru.GallerySource(),   // https://danbooru.donmai.us |                 new Sources.Danbooru.GallerySource(),   // https://danbooru.donmai.us | ||||||
|                 new Sources.Gelbooru.GallerySource()    // https://gelbooru.com |                 new Sources.Gelbooru.GallerySource(),   // https://gelbooru.com | ||||||
|             }; |             }; | ||||||
|  |  | ||||||
|             MainPage = new AppShell(); |             MainPage = new AppShell(); | ||||||
|   | |||||||
| @@ -1,4 +1,5 @@ | |||||||
| using Gallery.Resources.UI; | using Gallery.Resources; | ||||||
|  | using Gallery.Resources.UI; | ||||||
| using Gallery.Util; | using Gallery.Util; | ||||||
| using Gallery.Util.Interface; | using Gallery.Util.Interface; | ||||||
| using Gallery.Views; | using Gallery.Views; | ||||||
| @@ -36,7 +37,7 @@ namespace Gallery | |||||||
|                 } |                 } | ||||||
|                 var tab = new Tab |                 var tab = new Tab | ||||||
|                 { |                 { | ||||||
|                     Title = source.Name, |                     Title = Helper.GetResource(source.Name), | ||||||
|                     Route = source.Route, |                     Route = source.Route, | ||||||
|                     Items = |                     Items = | ||||||
|                     { |                     { | ||||||
|   | |||||||
| @@ -45,6 +45,8 @@ | |||||||
|     <Compile Include="$(MSBuildThisFileDirectory)Sources\Yandere\YandereItem.cs" /> |     <Compile Include="$(MSBuildThisFileDirectory)Sources\Yandere\YandereItem.cs" /> | ||||||
|     <Compile Include="$(MSBuildThisFileDirectory)Sources\Danbooru\GallerySource.cs" /> |     <Compile Include="$(MSBuildThisFileDirectory)Sources\Danbooru\GallerySource.cs" /> | ||||||
|     <Compile Include="$(MSBuildThisFileDirectory)Sources\Gelbooru\GallerySource.cs" /> |     <Compile Include="$(MSBuildThisFileDirectory)Sources\Gelbooru\GallerySource.cs" /> | ||||||
|  |     <Compile Include="$(MSBuildThisFileDirectory)Util\Consts.cs" /> | ||||||
|  |     <Compile Include="$(MSBuildThisFileDirectory)Sources\FavoriteGallerySource.cs" /> | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|     <Folder Include="$(MSBuildThisFileDirectory)Services\" /> |     <Folder Include="$(MSBuildThisFileDirectory)Services\" /> | ||||||
|   | |||||||
| @@ -11,4 +11,5 @@ | |||||||
|     <Detail>详细</Detail> |     <Detail>详细</Detail> | ||||||
|     <ProxyHost>代理主机</ProxyHost> |     <ProxyHost>代理主机</ProxyHost> | ||||||
|     <ProxyPort>代理端口</ProxyPort> |     <ProxyPort>代理端口</ProxyPort> | ||||||
|  |     <Favorite>收藏夹</Favorite> | ||||||
| </root> | </root> | ||||||
| @@ -32,6 +32,7 @@ namespace Gallery.Resources.Theme | |||||||
|             Add(TextColor, Color.White); |             Add(TextColor, Color.White); | ||||||
|             Add(SubTextColor, Color.LightGray); |             Add(SubTextColor, Color.LightGray); | ||||||
|             Add(CardBackgroundColor, Color.FromRgb(0x33, 0x33, 0x33)); |             Add(CardBackgroundColor, Color.FromRgb(0x33, 0x33, 0x33)); | ||||||
|  |             Add(MaskColor, Color.FromRgba(0, 0, 0, 0x64)); | ||||||
|             Add(NavigationColor, Color.FromRgb(0x11, 0x11, 0x11)); |             Add(NavigationColor, Color.FromRgb(0x11, 0x11, 0x11)); | ||||||
|             Add(NavigationSelectedColor, Color.FromRgb(0x22, 0x22, 0x22)); |             Add(NavigationSelectedColor, Color.FromRgb(0x22, 0x22, 0x22)); | ||||||
|             Add(OptionBackColor, Color.Black); |             Add(OptionBackColor, Color.Black); | ||||||
|   | |||||||
| @@ -32,6 +32,7 @@ namespace Gallery.Resources.Theme | |||||||
|             Add(TextColor, Color.Black); |             Add(TextColor, Color.Black); | ||||||
|             Add(SubTextColor, Color.DimGray); |             Add(SubTextColor, Color.DimGray); | ||||||
|             Add(CardBackgroundColor, Color.FromRgb(0xf3, 0xf3, 0xf3)); |             Add(CardBackgroundColor, Color.FromRgb(0xf3, 0xf3, 0xf3)); | ||||||
|  |             Add(MaskColor, Color.FromRgba(0, 0, 0, 0x64)); | ||||||
|             Add(NavigationColor, Color.FromRgb(0xf0, 0xf0, 0xf0)); |             Add(NavigationColor, Color.FromRgb(0xf0, 0xf0, 0xf0)); | ||||||
|             Add(NavigationSelectedColor, Color.LightGray); |             Add(NavigationSelectedColor, Color.LightGray); | ||||||
|             Add(OptionBackColor, Color.FromRgb(0xf0, 0xf0, 0xf0)); |             Add(OptionBackColor, Color.FromRgb(0xf0, 0xf0, 0xf0)); | ||||||
|   | |||||||
| @@ -11,6 +11,7 @@ namespace Gallery.Resources.Theme | |||||||
|         public const string TextColor = nameof(TextColor); |         public const string TextColor = nameof(TextColor); | ||||||
|         public const string SubTextColor = nameof(SubTextColor); |         public const string SubTextColor = nameof(SubTextColor); | ||||||
|         public const string CardBackgroundColor = nameof(CardBackgroundColor); |         public const string CardBackgroundColor = nameof(CardBackgroundColor); | ||||||
|  |         public const string MaskColor = nameof(MaskColor); | ||||||
|         public const string NavigationColor = nameof(NavigationColor); |         public const string NavigationColor = nameof(NavigationColor); | ||||||
|         public const string NavigationSelectedColor = nameof(NavigationSelectedColor); |         public const string NavigationSelectedColor = nameof(NavigationSelectedColor); | ||||||
|         public const string OptionBackColor = nameof(OptionBackColor); |         public const string OptionBackColor = nameof(OptionBackColor); | ||||||
| @@ -24,6 +25,9 @@ namespace Gallery.Resources.Theme | |||||||
|         public const string IconClose = nameof(IconClose); |         public const string IconClose = nameof(IconClose); | ||||||
|         public const string FontIconOption = nameof(FontIconOption); |         public const string FontIconOption = nameof(FontIconOption); | ||||||
|         public const string FontIconRefresh = nameof(FontIconRefresh); |         public const string FontIconRefresh = nameof(FontIconRefresh); | ||||||
|  |         public const string FontIconLove = nameof(FontIconLove); | ||||||
|  |         public const string FontIconNotLove = nameof(FontIconNotLove); | ||||||
|  |         public const string FontIconShare = nameof(FontIconShare); | ||||||
|  |  | ||||||
|         protected void InitResources() |         protected void InitResources() | ||||||
|         { |         { | ||||||
| @@ -34,6 +38,9 @@ namespace Gallery.Resources.Theme | |||||||
|  |  | ||||||
|             Add(FontIconOption, GetFontIcon(Definition.IconOption, Definition.IconSolidFamily)); |             Add(FontIconOption, GetFontIcon(Definition.IconOption, Definition.IconSolidFamily)); | ||||||
|             Add(FontIconRefresh, GetFontIcon(Definition.IconRefresh, Definition.IconSolidFamily)); |             Add(FontIconRefresh, GetFontIcon(Definition.IconRefresh, Definition.IconSolidFamily)); | ||||||
|  |             Add(FontIconLove, GetFontIcon(Definition.IconLove, Definition.IconSolidFamily, color: Definition.ColorRedBackground)); | ||||||
|  |             Add(FontIconNotLove, GetFontIcon(Definition.IconLove, Definition.IconRegularFamily, color: Definition.ColorRedBackground)); | ||||||
|  |             Add(FontIconShare, GetFontIcon(Definition.IconShare, Definition.IconSolidFamily)); | ||||||
|  |  | ||||||
|             Add(IconClose, Definition.IconClose); |             Add(IconClose, Definition.IconClose); | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -36,6 +36,7 @@ namespace Gallery.Resources.UI | |||||||
|         public const string IconLove = "\uf004"; |         public const string IconLove = "\uf004"; | ||||||
|         public const string IconCircleLove = "\uf4c7"; |         public const string IconCircleLove = "\uf4c7"; | ||||||
|         public const string IconClose = "\uf057"; |         public const string IconClose = "\uf057"; | ||||||
|  |         public const string IconShare = "\uf1e0"; | ||||||
|  |  | ||||||
|         static Definition() |         static Definition() | ||||||
|         { |         { | ||||||
|   | |||||||
| @@ -10,7 +10,7 @@ using Xamarin.Forms; | |||||||
|  |  | ||||||
| namespace Gallery.Resources.UI | namespace Gallery.Resources.UI | ||||||
| { | { | ||||||
|     public abstract class GalleryCollectionPage : GalleryScrollableCollectionPage<GalleryItem[]> |     public abstract class GalleryCollectionPage : GalleryScrollableCollectionPage<IEnumerable<GalleryItem>> | ||||||
|     { |     { | ||||||
|         public GalleryCollectionPage(IGallerySource source) : base(source) { } |         public GalleryCollectionPage(IGallerySource source) : base(source) { } | ||||||
|     } |     } | ||||||
| @@ -93,11 +93,11 @@ namespace Gallery.Resources.UI | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         protected readonly Command<GalleryItem> commandGalleryItemTapped; |  | ||||||
|         protected double topOffset; |         protected double topOffset; | ||||||
|         protected string lastError; |         protected string lastError; | ||||||
|  |  | ||||||
|         private readonly object sync = new(); |         private readonly object sync = new(); | ||||||
|  |         private readonly Command<GalleryItem> commandGalleryItemTapped; | ||||||
|         private readonly Stack<ParallelTask> tasks = new(); |         private readonly Stack<ParallelTask> tasks = new(); | ||||||
|         private T galleryData; |         private T galleryData; | ||||||
|  |  | ||||||
| @@ -107,17 +107,8 @@ namespace Gallery.Resources.UI | |||||||
|             commandGalleryItemTapped = new Command<GalleryItem>(OnGalleryItemTapped); |             commandGalleryItemTapped = new Command<GalleryItem>(OnGalleryItemTapped); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         private void OnGalleryItemTapped(GalleryItem item) |         protected virtual void OnGalleryItemTapped(GalleryItem item) | ||||||
|         { |         { | ||||||
|             if (item == null) |  | ||||||
|             { |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
|             //Start(async () => |  | ||||||
|             //{ |  | ||||||
|             //    var page = new GalleryItemPage(item); |  | ||||||
|             //    await Navigation.PushAsync(page); |  | ||||||
|             //}); |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public override void OnUnload() |         public override void OnUnload() | ||||||
| @@ -145,6 +136,14 @@ namespace Gallery.Resources.UI | |||||||
|             { |             { | ||||||
|                 StartLoading(); |                 StartLoading(); | ||||||
|             } |             } | ||||||
|  |             else if (GalleryCollection != null) | ||||||
|  |             { | ||||||
|  |                 var favorites = Store.FavoriteList; | ||||||
|  |                 foreach (var item in GalleryCollection) | ||||||
|  |                 { | ||||||
|  |                     item.IsFavorite = favorites.Any(i => i.SourceEquals(item)); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
| #if __IOS__ | #if __IOS__ | ||||||
| @@ -217,6 +216,17 @@ namespace Gallery.Resources.UI | |||||||
|         { |         { | ||||||
|             if (force || Expired) |             if (force || Expired) | ||||||
|             { |             { | ||||||
|  |                 if (!isBottom) | ||||||
|  |                 { | ||||||
|  |                     lock (sync) | ||||||
|  |                     { | ||||||
|  |                         // destory all the tasks | ||||||
|  |                         while (tasks.TryPop(out var t)) | ||||||
|  |                         { | ||||||
|  |                             t?.Dispose(); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|                 var indicator = LoadingIndicator; |                 var indicator = LoadingIndicator; | ||||||
|                 if (indicator == null || isBottom) |                 if (indicator == null || isBottom) | ||||||
|                 { |                 { | ||||||
| @@ -257,6 +267,7 @@ namespace Gallery.Resources.UI | |||||||
|                         _ = DoloadGallerySource(force, isBottom); |                         _ = DoloadGallerySource(force, isBottom); | ||||||
|                     }); |                     }); | ||||||
|                 } |                 } | ||||||
|  |                 BeforeLoading(); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -276,6 +287,7 @@ namespace Gallery.Resources.UI | |||||||
| #endif | #endif | ||||||
|                 { |                 { | ||||||
|                     Gallery = collection; |                     Gallery = collection; | ||||||
|  |                     AfterLoaded(); | ||||||
|                     return false; |                     return false; | ||||||
|                 }); |                 }); | ||||||
|             } |             } | ||||||
| @@ -301,12 +313,16 @@ namespace Gallery.Resources.UI | |||||||
| #endif | #endif | ||||||
|                     { |                     { | ||||||
|                         Gallery = collection; |                         Gallery = collection; | ||||||
|  |                         AfterLoaded(); | ||||||
|                         return false; |                         return false; | ||||||
|                     }); |                     }); | ||||||
|                 }); |                 }); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         protected virtual void BeforeLoading() { } | ||||||
|  |         protected virtual void AfterLoaded() { } | ||||||
|  |  | ||||||
|         protected async Task ScrollToTopAsync(ScrollView scrollView) |         protected async Task ScrollToTopAsync(ScrollView scrollView) | ||||||
|         { |         { | ||||||
|             if (scrollView.ScrollY > -topOffset) |             if (scrollView.ScrollY > -topOffset) | ||||||
| @@ -425,16 +441,18 @@ namespace Gallery.Resources.UI | |||||||
|  |  | ||||||
|             var data = DoGetGalleryList(galleryData, out int tag).Where(i => i != null); |             var data = DoGetGalleryList(galleryData, out int tag).Where(i => i != null); | ||||||
|             var collection = new GalleryCollection(data); |             var collection = new GalleryCollection(data); | ||||||
|  |             var favorites = Store.FavoriteList; | ||||||
|             foreach (var item in collection) |             foreach (var item in collection) | ||||||
|             { |             { | ||||||
|                 if (item.PreviewImage == null) |                 if (item.PreviewImage == null) | ||||||
|                 { |                 { | ||||||
|                     var image = await Store.LoadPreviewImage(item.PreviewUrl, false); |                     var image = await Store.LoadPreviewImage(item, false); | ||||||
|                     if (image != null) |                     if (image != null) | ||||||
|                     { |                     { | ||||||
|                         item.PreviewImage = image; |                         item.PreviewImage = image; | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|  |                 item.IsFavorite = favorites.Any(i => i.SourceEquals(item)); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             DoGalleryLoaded(collection, bottom); |             DoGalleryLoaded(collection, bottom); | ||||||
| @@ -473,7 +491,7 @@ namespace Gallery.Resources.UI | |||||||
|                     if (model.StartsWith("iPhone") || model.StartsWith("iPad")) |                     if (model.StartsWith("iPhone") || model.StartsWith("iPad")) | ||||||
|                     { |                     { | ||||||
| #endif | #endif | ||||||
|                     var image = Store.LoadPreviewImage(item.PreviewUrl, true, force: true).Result; |                     var image = Store.LoadPreviewImage(item, true, force: true).Result; | ||||||
|                     if (image != null) |                     if (image != null) | ||||||
|                     { |                     { | ||||||
|                         item.PreviewImage = image; |                         item.PreviewImage = image; | ||||||
| @@ -539,7 +557,7 @@ namespace Gallery.Resources.UI | |||||||
|             { |             { | ||||||
|                 lastRefreshY = double.MinValue; |                 lastRefreshY = double.MinValue; | ||||||
|             } |             } | ||||||
|             base.StartLoading(force, isBottom); |             base.StartLoading(force: force, isBottom: isBottom); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         protected override GalleryCollection FilterGalleryCollection(GalleryCollection collection, bool bottom) |         protected override GalleryCollection FilterGalleryCollection(GalleryCollection collection, bool bottom) | ||||||
| @@ -578,7 +596,7 @@ namespace Gallery.Resources.UI | |||||||
| #if DEBUG | #if DEBUG | ||||||
|                         Log.Print("start to load next page"); |                         Log.Print("start to load next page"); | ||||||
| #endif | #endif | ||||||
|                         StartLoading(true, true); |                         StartLoading(force: true, isBottom: true); | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|   | |||||||
| @@ -1,4 +1,5 @@ | |||||||
| using System; | using System; | ||||||
|  | using System.Collections.Generic; | ||||||
| using System.Text.RegularExpressions; | using System.Text.RegularExpressions; | ||||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||||
| using Gallery.Util; | using Gallery.Util; | ||||||
| @@ -8,14 +9,14 @@ using Xamarin.Forms; | |||||||
|  |  | ||||||
| namespace Gallery.Sources.Danbooru | namespace Gallery.Sources.Danbooru | ||||||
| { | { | ||||||
|     public class GallerySource : IGallerySource |     public class GallerySource : GallerySourceBase | ||||||
|     { |     { | ||||||
|         public string Name => "Danbooru"; |         public override string Name => "Danbooru"; | ||||||
|         public string Route => "danbooru"; |         public override string Route => "danbooru"; | ||||||
|         public string FlyoutIconKey => "Danbooru"; |         public override string FlyoutIconKey => "Danbooru"; | ||||||
|         public string HomePage => "https://danbooru.donmai.us"; |         public override string HomePage => "https://danbooru.donmai.us"; | ||||||
|  |  | ||||||
|         public async Task<GalleryItem[]> GetRecentItemsAsync(int page) |         public override async Task<IEnumerable<GalleryItem>> GetRecentItemsAsync(int page) | ||||||
|         { |         { | ||||||
|             var url = $"https://danbooru.donmai.us/posts?page={page}"; |             var url = $"https://danbooru.donmai.us/posts?page={page}"; | ||||||
|             var (result, error) = await NetHelper.RequestObject(url, @return: content => ResolveGalleryItems(content)); |             var (result, error) = await NetHelper.RequestObject(url, @return: content => ResolveGalleryItems(content)); | ||||||
| @@ -47,7 +48,8 @@ namespace Gallery.Sources.Danbooru | |||||||
|                     Width = int.Parse(g[3].Value), |                     Width = int.Parse(g[3].Value), | ||||||
|                     Height = int.Parse(g[4].Value), |                     Height = int.Parse(g[4].Value), | ||||||
|                     UserId = g[6].Value, |                     UserId = g[6].Value, | ||||||
|                     Source = g[7].Value, |                     Source = Route, | ||||||
|  |                     SourceUrl = g[7].Value, | ||||||
|                     RawUrl = g[8].Value, |                     RawUrl = g[8].Value, | ||||||
|                     PreviewUrl = g[9].Value |                     PreviewUrl = g[9].Value | ||||||
|                 }; |                 }; | ||||||
| @@ -55,12 +57,12 @@ namespace Gallery.Sources.Danbooru | |||||||
|             return items; |             return items; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public void SetCookie() |         public override void SetCookie() | ||||||
|         { |         { | ||||||
|             throw new NotImplementedException(); |             throw new NotImplementedException(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public void InitDynamicResources(string family, ResourceDictionary light, ResourceDictionary dark) |         public override void InitDynamicResources(string family, ResourceDictionary light, ResourceDictionary dark) | ||||||
|         { |         { | ||||||
|             var icon = new FontImageSource |             var icon = new FontImageSource | ||||||
|             { |             { | ||||||
|   | |||||||
							
								
								
									
										41
									
								
								Gallery.Share/Sources/FavoriteGallerySource.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								Gallery.Share/Sources/FavoriteGallerySource.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | |||||||
|  | using System; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using System.Threading.Tasks; | ||||||
|  | using Gallery.Util; | ||||||
|  | using Gallery.Util.Interface; | ||||||
|  | using Gallery.Util.Model; | ||||||
|  | using Xamarin.Forms; | ||||||
|  |  | ||||||
|  | namespace Gallery.Sources | ||||||
|  | { | ||||||
|  |     public class FavoriteGallerySource : GallerySourceBase | ||||||
|  |     { | ||||||
|  |         public override string Name => "Favorite"; | ||||||
|  |         public override string Route => Routes.Favorite; | ||||||
|  |         public override string FlyoutIconKey => "Favorite"; | ||||||
|  |         public override string HomePage => null; | ||||||
|  |         public override bool IsScrollable => false; | ||||||
|  |  | ||||||
|  |         public override Task<IEnumerable<GalleryItem>> GetRecentItemsAsync(int page) | ||||||
|  |         { | ||||||
|  |             return Task.FromResult((IEnumerable<GalleryItem>)Store.FavoriteList); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public override void SetCookie() | ||||||
|  |         { | ||||||
|  |             throw new NotImplementedException(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public override void InitDynamicResources(string family, ResourceDictionary light, ResourceDictionary dark) | ||||||
|  |         { | ||||||
|  |             var icon = new FontImageSource | ||||||
|  |             { | ||||||
|  |                 FontFamily = family, | ||||||
|  |                 Glyph = "\uf02e", | ||||||
|  |                 Size = 18.0 | ||||||
|  |             }; | ||||||
|  |             light.Add(FlyoutIconKey, icon); | ||||||
|  |             dark.Add(FlyoutIconKey, icon); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,4 +1,5 @@ | |||||||
| using System; | using System; | ||||||
|  | using System.Collections.Generic; | ||||||
| using System.Text.RegularExpressions; | using System.Text.RegularExpressions; | ||||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||||
| using Gallery.Util; | using Gallery.Util; | ||||||
| @@ -8,14 +9,14 @@ using Xamarin.Forms; | |||||||
|  |  | ||||||
| namespace Gallery.Sources.Gelbooru | namespace Gallery.Sources.Gelbooru | ||||||
| { | { | ||||||
|     public class GallerySource : IGallerySource |     public class GallerySource : GallerySourceBase | ||||||
|     { |     { | ||||||
|         public string Name => "Gelbooru"; |         public override string Name => "Gelbooru"; | ||||||
|         public string Route => "gelbooru"; |         public override string Route => "gelbooru"; | ||||||
|         public string FlyoutIconKey => "Gelbooru"; |         public override string FlyoutIconKey => "Gelbooru"; | ||||||
|         public string HomePage => "https://gelbooru.com"; |         public override string HomePage => "https://gelbooru.com"; | ||||||
|  |  | ||||||
|         public async Task<GalleryItem[]> GetRecentItemsAsync(int page) |         public override async Task<IEnumerable<GalleryItem>> GetRecentItemsAsync(int page) | ||||||
|         { |         { | ||||||
|             var offset = (page - 1) * 42; |             var offset = (page - 1) * 42; | ||||||
|             var url = $"https://gelbooru.com/index.php?page=post&s=list&tags=all&pid={offset}"; |             var url = $"https://gelbooru.com/index.php?page=post&s=list&tags=all&pid={offset}"; | ||||||
| @@ -42,21 +43,54 @@ namespace Gallery.Sources.Gelbooru | |||||||
|                 var g = matches[i].Groups; |                 var g = matches[i].Groups; | ||||||
|                 items[i] = new GalleryItem(int.Parse(g[2].Value)) |                 items[i] = new GalleryItem(int.Parse(g[2].Value)) | ||||||
|                 { |                 { | ||||||
|                     RawUrl = g[3].Value, |                     RawUrl = g[3].Value.Replace("&", "&"), | ||||||
|                     PreviewUrl = g[5].Value, |                     PreviewUrl = g[5].Value, | ||||||
|                     Tags = g[6].Value.Split(' '), |                     Tags = g[6].Value.Split(' '), | ||||||
|  |                     Source = Route, | ||||||
|                     IsRawPage = true |                     IsRawPage = true | ||||||
|                 }; |                 }; | ||||||
|             } |             } | ||||||
|             return items; |             return items; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public void SetCookie() |         public override async Task<string> ResolveImageUrl(GalleryItem item) | ||||||
|  |         { | ||||||
|  |             var (result, error) = await NetHelper.RequestObject(item.RawUrl, @return: content => | ||||||
|  |             { | ||||||
|  |                 var regex = new Regex( | ||||||
|  |                     @"<section class=""image-container.+?" +    // data-id=""(\d+?)"".+?data-tags=""([^""]+?)"".+? | ||||||
|  |                     @"data-width=""(\d+?)"" data-height=""(\d+?)"".+?data-uploader-id=""([^""]*?)"" " + | ||||||
|  |                     @"data-normalized-source=""([^""]+?)""(.|\n)*?<img .*?src=""([^""]+?)""", | ||||||
|  |                     RegexOptions.Multiline); | ||||||
|  |                 var m = regex.Match(content); | ||||||
|  |                 if (m.Success) | ||||||
|  |                 { | ||||||
|  |                     var g = m.Groups; | ||||||
|  |                     item.Width = int.Parse(g[1].Value); | ||||||
|  |                     item.Height = int.Parse(g[2].Value); | ||||||
|  |                     item.UserId = g[3].Value; | ||||||
|  |                     item.SourceUrl = g[4].Value; | ||||||
|  |                     item.RawUrl = g[6].Value; | ||||||
|  |                     return item.RawUrl; | ||||||
|  |                 } | ||||||
|  |                 return null; | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             if (result == null || !string.IsNullOrEmpty(error)) | ||||||
|  |             { | ||||||
|  |                 Log.Error("gelbooru.imageUrl.resolve", $"failed to load data, error: {error}"); | ||||||
|  |                 return null; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return result; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public override void SetCookie() | ||||||
|         { |         { | ||||||
|             throw new NotImplementedException(); |             throw new NotImplementedException(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public void InitDynamicResources(string family, ResourceDictionary light, ResourceDictionary dark) |         public override void InitDynamicResources(string family, ResourceDictionary light, ResourceDictionary dark) | ||||||
|         { |         { | ||||||
|             var icon = new FontImageSource |             var icon = new FontImageSource | ||||||
|             { |             { | ||||||
|   | |||||||
| @@ -1,4 +1,5 @@ | |||||||
| using System; | using System; | ||||||
|  | using System.Collections.Generic; | ||||||
| using System.Text.RegularExpressions; | using System.Text.RegularExpressions; | ||||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||||
| using Gallery.Util; | using Gallery.Util; | ||||||
| @@ -8,14 +9,14 @@ using Xamarin.Forms; | |||||||
|  |  | ||||||
| namespace Gallery.Sources.Yandere | namespace Gallery.Sources.Yandere | ||||||
| { | { | ||||||
|     public class GallerySource : IGallerySource |     public class GallerySource : GallerySourceBase | ||||||
|     { |     { | ||||||
|         public string Name => "Yande.re"; |         public override string Name => "Yande.re"; | ||||||
|         public string Route => "yandere"; |         public override string Route => "yandere"; | ||||||
|         public string FlyoutIconKey => "Yandere"; |         public override string FlyoutIconKey => "Yandere"; | ||||||
|         public string HomePage => "https://yande.re"; |         public override string HomePage => "https://yande.re"; | ||||||
|  |  | ||||||
|         public async Task<GalleryItem[]> GetRecentItemsAsync(int page) |         public override async Task<IEnumerable<GalleryItem>> GetRecentItemsAsync(int page) | ||||||
|         { |         { | ||||||
|             var url = $"https://yande.re/post?page={page}"; |             var url = $"https://yande.re/post?page={page}"; | ||||||
|             var (result, error) = await NetHelper.RequestObject<YandereItem[]>(url, contentHandler: ContentHandler); |             var (result, error) = await NetHelper.RequestObject<YandereItem[]>(url, contentHandler: ContentHandler); | ||||||
| @@ -37,7 +38,8 @@ namespace Gallery.Sources.Yandere | |||||||
|                     UpdatedTime = y.updated_at.ToLocalTime(), |                     UpdatedTime = y.updated_at.ToLocalTime(), | ||||||
|                     UserId = y.creator_id.ToString(), |                     UserId = y.creator_id.ToString(), | ||||||
|                     UserName = y.author, |                     UserName = y.author, | ||||||
|                     Source = y.source, |                     Source = Route, | ||||||
|  |                     SourceUrl = y.source, | ||||||
|                     PreviewUrl = y.preview_url, |                     PreviewUrl = y.preview_url, | ||||||
|                     RawUrl = y.file_url, |                     RawUrl = y.file_url, | ||||||
|                     Width = y.width, |                     Width = y.width, | ||||||
| @@ -60,12 +62,12 @@ namespace Gallery.Sources.Yandere | |||||||
|             return $"[{string.Join(',', array)}]"; |             return $"[{string.Join(',', array)}]"; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public void SetCookie() |         public override void SetCookie() | ||||||
|         { |         { | ||||||
|             throw new NotImplementedException(); |             throw new NotImplementedException(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public void InitDynamicResources(string family, ResourceDictionary light, ResourceDictionary dark) |         public override void InitDynamicResources(string family, ResourceDictionary light, ResourceDictionary dark) | ||||||
|         { |         { | ||||||
|             var icon = new FontImageSource |             var icon = new FontImageSource | ||||||
|             { |             { | ||||||
|   | |||||||
							
								
								
									
										30
									
								
								Gallery.Share/Util/Consts.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								Gallery.Share/Util/Consts.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | |||||||
|  | using System; | ||||||
|  | using System.Net; | ||||||
|  |  | ||||||
|  | namespace Gallery.Util | ||||||
|  | { | ||||||
|  |     public static class Config | ||||||
|  |     { | ||||||
|  |         public static readonly TimeSpan Timeout = TimeSpan.FromSeconds(30); | ||||||
|  |  | ||||||
|  |         public const string DownloadThreadsKey = "download_threads"; | ||||||
|  |         public const string IsProxiedKey = "is_proxied"; | ||||||
|  |         public const string ProxyHostKey = "proxy_host"; | ||||||
|  |         public const string ProxyPortKey = "proxy_port"; | ||||||
|  |  | ||||||
|  |         public const int MaxThreads = 4; | ||||||
|  |         public const string UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36"; | ||||||
|  |         public const string AcceptLanguage = "zh-cn"; | ||||||
|  |         public const string AcceptImage = "image/png,image/*,*/*;q=0.8"; | ||||||
|  |  | ||||||
|  |         public static int DownloadThreads; | ||||||
|  |         public static WebProxy Proxy; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static class Routes | ||||||
|  |     { | ||||||
|  |         public const string Gallery = "gallery"; | ||||||
|  |         public const string Favorite = "favorite"; | ||||||
|  |         public const string Option = "option"; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,4 +1,5 @@ | |||||||
| using System; | using System; | ||||||
|  | using Gallery.Util.Model; | ||||||
|  |  | ||||||
| namespace Gallery.Util | namespace Gallery.Util | ||||||
| { | { | ||||||
| @@ -63,5 +64,14 @@ namespace Gallery.Util | |||||||
|             var ticks = datetime.Ticks; |             var ticks = datetime.Ticks; | ||||||
|             return (ticks - 621355968000000000L) / 10000000; |             return (ticks - 621355968000000000L) / 10000000; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         public static bool SourceEquals(this GalleryItem a, GalleryItem b) | ||||||
|  |         { | ||||||
|  |             if (a == null || b == null) | ||||||
|  |             { | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |             return a.Id == b.Id && a.Source == b.Source; | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,4 +1,5 @@ | |||||||
| using System.Threading.Tasks; | using System.Collections.Generic; | ||||||
|  | using System.Threading.Tasks; | ||||||
| using Gallery.Util.Model; | using Gallery.Util.Model; | ||||||
| using Xamarin.Forms; | using Xamarin.Forms; | ||||||
|  |  | ||||||
| @@ -7,17 +8,28 @@ namespace Gallery.Util.Interface | |||||||
|     public interface IGallerySource |     public interface IGallerySource | ||||||
|     { |     { | ||||||
|         string Name { get; } |         string Name { get; } | ||||||
|  |  | ||||||
|         string Route { get; } |         string Route { get; } | ||||||
|  |  | ||||||
|         string FlyoutIconKey { get; } |         string FlyoutIconKey { get; } | ||||||
|  |  | ||||||
|         string HomePage { get; } |         string HomePage { get; } | ||||||
|  |         bool IsScrollable { get; } | ||||||
|  |  | ||||||
|         void SetCookie(); |         void SetCookie(); | ||||||
|  |  | ||||||
|         void InitDynamicResources(string family, ResourceDictionary light, ResourceDictionary dark); |         void InitDynamicResources(string family, ResourceDictionary light, ResourceDictionary dark); | ||||||
|  |         Task<IEnumerable<GalleryItem>> GetRecentItemsAsync(int page); | ||||||
|  |         Task<string> ResolveImageUrl(GalleryItem item); | ||||||
|  |     } | ||||||
|  |  | ||||||
|         Task<GalleryItem[]> GetRecentItemsAsync(int page); |     public abstract class GallerySourceBase : IGallerySource | ||||||
|  |     { | ||||||
|  |         public abstract string Name { get; } | ||||||
|  |         public abstract string Route { get; } | ||||||
|  |         public abstract string FlyoutIconKey { get; } | ||||||
|  |         public abstract string HomePage { get; } | ||||||
|  |         public virtual bool IsScrollable => true; | ||||||
|  |  | ||||||
|  |         public abstract void SetCookie(); | ||||||
|  |         public abstract void InitDynamicResources(string family, ResourceDictionary light, ResourceDictionary dark); | ||||||
|  |         public abstract Task<IEnumerable<GalleryItem>> GetRecentItemsAsync(int page); | ||||||
|  |         public virtual Task<string> ResolveImageUrl(GalleryItem item) => Task.FromResult(item.RawUrl); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -80,6 +80,8 @@ namespace Gallery.Util.Model | |||||||
|         [JsonProperty] |         [JsonProperty] | ||||||
|         public string Source { get; set; } |         public string Source { get; set; } | ||||||
|         [JsonProperty] |         [JsonProperty] | ||||||
|  |         public string SourceUrl { get; set; } | ||||||
|  |         [JsonProperty] | ||||||
|         public string PreviewUrl { get; set; } |         public string PreviewUrl { get; set; } | ||||||
|         [JsonProperty] |         [JsonProperty] | ||||||
|         public string RawUrl { get; set; } |         public string RawUrl { get; set; } | ||||||
| @@ -124,7 +126,7 @@ namespace Gallery.Util.Model | |||||||
|  |  | ||||||
|         public override string ToString() |         public override string ToString() | ||||||
|         { |         { | ||||||
|             var source = string.IsNullOrEmpty(Source) ? RawUrl : Source; |             var source = string.IsNullOrEmpty(SourceUrl) ? RawUrl : SourceUrl; | ||||||
|             return $"{Id}, {source}"; |             return $"{Id}, {source}"; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -125,7 +125,7 @@ namespace Gallery.Util | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public static async Task<string> DownloadImageAsync(string url, string id, string working, string folder) |         public static async Task<string> DownloadImageAsync(string url, string id, string working, string folder, Action<(int loc, int size)> action = null) | ||||||
|         { |         { | ||||||
|             try |             try | ||||||
|             { |             { | ||||||
| @@ -149,7 +149,7 @@ namespace Gallery.Util | |||||||
|                 { |                 { | ||||||
|                     Timeout = Config.Timeout |                     Timeout = Config.Timeout | ||||||
|                 }; |                 }; | ||||||
|                 long size; |                 int size; | ||||||
|                 DateTimeOffset lastModified; |                 DateTimeOffset lastModified; | ||||||
|                 using (var request = new HttpRequestMessage(HttpMethod.Head, url)) |                 using (var request = new HttpRequestMessage(HttpMethod.Head, url)) | ||||||
|                 { |                 { | ||||||
| @@ -158,7 +158,7 @@ namespace Gallery.Util | |||||||
|                     headers.Add("Accept-Language", Config.AcceptLanguage); |                     headers.Add("Accept-Language", Config.AcceptLanguage); | ||||||
|                     headers.Add("User-Agent", Config.UserAgent); |                     headers.Add("User-Agent", Config.UserAgent); | ||||||
|                     using var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); |                     using var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); | ||||||
|                     size = response.Content.Headers.ContentLength.Value; |                     size = (int)response.Content.Headers.ContentLength.Value; | ||||||
|                     lastModified = response.Content.Headers.LastModified.Value; |                     lastModified = response.Content.Headers.LastModified.Value; | ||||||
| #if DEBUG | #if DEBUG | ||||||
|                     Log.Print($"content length: {size:n0} bytes, last modified: {lastModified}"); |                     Log.Print($"content length: {size:n0} bytes, last modified: {lastModified}"); | ||||||
| @@ -167,10 +167,10 @@ namespace Gallery.Util | |||||||
|  |  | ||||||
|                 // segments |                 // segments | ||||||
|                 const int SIZE = 150000; |                 const int SIZE = 150000; | ||||||
|                 var list = new List<(long from, long to)>(); |                 var list = new List<(int from, int to)>(); | ||||||
|                 for (long i = 0; i < size; i += SIZE) |                 for (var i = 0; i < size; i += SIZE) | ||||||
|                 { |                 { | ||||||
|                     long to; |                     int to; | ||||||
|                     if (i + SIZE >= size) |                     if (i + SIZE >= size) | ||||||
|                     { |                     { | ||||||
|                         to = size - 1; |                         to = size - 1; | ||||||
| @@ -198,11 +198,12 @@ namespace Gallery.Util | |||||||
|                         headers.Range = new RangeHeaderValue(from, to); |                         headers.Range = new RangeHeaderValue(from, to); | ||||||
|                         headers.Add("User-Agent", Config.UserAgent); |                         headers.Add("User-Agent", Config.UserAgent); | ||||||
|                         using var response = client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).Result; |                         using var response = client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).Result; | ||||||
|                         using var ms = new MemoryStream(data, (int)from, (int)(to - from + 1)); |                         using var ms = new MemoryStream(data, from, to - from + 1); | ||||||
|                         response.Content.CopyToAsync(ms).Wait(); |                         response.Content.CopyToAsync(ms).Wait(); | ||||||
| #if DEBUG | #if DEBUG | ||||||
|                         Log.Print($"downloaded range: from({from:n0}) to ({to:n0})"); |                         Log.Print($"downloaded range: from({from:n0}) to ({to:n0})"); | ||||||
| #endif | #endif | ||||||
|  |                         action?.Invoke((to, size)); | ||||||
|                     } |                     } | ||||||
|                     return true; |                     return true; | ||||||
|                 }, |                 }, | ||||||
|   | |||||||
| @@ -1,7 +1,10 @@ | |||||||
| using System; | using System; | ||||||
|  | using System.Collections.Generic; | ||||||
| using System.IO; | using System.IO; | ||||||
| using System.Net; | using System.Text; | ||||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||||
|  | using Gallery.Util.Model; | ||||||
|  | using Newtonsoft.Json; | ||||||
| using Xamarin.Essentials; | using Xamarin.Essentials; | ||||||
| using Xamarin.Forms; | using Xamarin.Forms; | ||||||
|  |  | ||||||
| @@ -12,17 +15,120 @@ namespace Gallery.Util | |||||||
|         public static readonly string PersonalFolder = Environment.GetFolderPath(Environment.SpecialFolder.Personal); |         public static readonly string PersonalFolder = Environment.GetFolderPath(Environment.SpecialFolder.Personal); | ||||||
|         public static readonly string CacheFolder = FileSystem.CacheDirectory; |         public static readonly string CacheFolder = FileSystem.CacheDirectory; | ||||||
|  |  | ||||||
|  |         private const string favoriteFile = "favorites.json"; | ||||||
|         private const string imageFolder = "img-original"; |         private const string imageFolder = "img-original"; | ||||||
|         private const string previewFolder = "img-preview"; |         private const string previewFolder = "img-preview"; | ||||||
|  |  | ||||||
|         public static async Task<ImageSource> LoadRawImage(string url) |         public static FavoriteList FavoriteList => GetFavoriteList(); | ||||||
|  |         public static string FavoriteListPath => Path.Combine(PersonalFolder, favoriteFile); | ||||||
|  |         private static FavoriteList favoriteList; | ||||||
|  |  | ||||||
|  |         public static FavoriteList GetFavoriteList(bool force = false) | ||||||
|         { |         { | ||||||
|             return await LoadImageAsync(url, null, PersonalFolder, imageFolder, force: true); |             if (force || favoriteList == null) | ||||||
|  |             { | ||||||
|  |                 var favorites = LoadFavoriteList(); | ||||||
|  |                 if (favorites != null) | ||||||
|  |                 { | ||||||
|  |                     favoriteList = favorites; | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                 { | ||||||
|  |                     favoriteList = new FavoriteList(); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             return favoriteList; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public static async Task<ImageSource> LoadPreviewImage(string url, bool downloading, bool force = false) |         private static FavoriteList LoadFavoriteList(string file = null) | ||||||
|         { |         { | ||||||
|             return await LoadImage(url, CacheFolder, previewFolder, downloading, force: force); |             if (file == null) | ||||||
|  |             { | ||||||
|  |                 file = FavoriteListPath; | ||||||
|  |             } | ||||||
|  |             return LoadObject<FavoriteList>(file); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public static void SaveFavoriteList() | ||||||
|  |         { | ||||||
|  |             var file = FavoriteListPath; | ||||||
|  |             WriteObject(file, FavoriteList); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private static T LoadObject<T>(string file) | ||||||
|  |         { | ||||||
|  |             string content = null; | ||||||
|  |             if (File.Exists(file)) | ||||||
|  |             { | ||||||
|  |                 try | ||||||
|  |                 { | ||||||
|  |                     content = File.ReadAllText(file); | ||||||
|  |                 } | ||||||
|  |                 catch (Exception ex) | ||||||
|  |                 { | ||||||
|  |                     Log.Error("file.read", $"failed to read file: {file}, error: {ex.Message}"); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 return default; | ||||||
|  |             } | ||||||
|  |             try | ||||||
|  |             { | ||||||
|  |                 return JsonConvert.DeserializeObject<T>(content); | ||||||
|  |             } | ||||||
|  |             catch (Exception ex) | ||||||
|  |             { | ||||||
|  |                 Log.Error("file.deserialize", $"failed to parse JSON object, error: {ex.Message}"); | ||||||
|  |                 return default; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private static void WriteObject(string file, object obj) | ||||||
|  |         { | ||||||
|  |             var folder = Path.GetDirectoryName(file); | ||||||
|  |             if (!Directory.Exists(folder)) | ||||||
|  |             { | ||||||
|  |                 Directory.CreateDirectory(folder); | ||||||
|  |             } | ||||||
|  |             string content; | ||||||
|  |             try | ||||||
|  |             { | ||||||
|  |                 content = JsonConvert.SerializeObject(obj, Formatting.None); | ||||||
|  |             } | ||||||
|  |             catch (Exception ex) | ||||||
|  |             { | ||||||
|  |                 Log.Error("object.serialize", $"failed to serialize object, error: {ex.Message}"); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |             try | ||||||
|  |             { | ||||||
|  |                 File.WriteAllText(file, content, Encoding.UTF8); | ||||||
|  |             } | ||||||
|  |             catch (Exception ex) | ||||||
|  |             { | ||||||
|  |                 Log.Error("file.write", $"failed to write file: {file}, error: {ex.Message}"); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public static async Task<ImageSource> LoadRawImage(GalleryItem item, bool force = false, Action<(int loc, int size)> action = null) | ||||||
|  |         { | ||||||
|  |             return await LoadImageAsync(item.RawUrl, null, PersonalFolder, Path.Combine(imageFolder, item.Source), force, action); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public static string GetRawImagePath(GalleryItem item) | ||||||
|  |         { | ||||||
|  |             var file = Path.Combine(PersonalFolder, imageFolder, item.Source, Path.GetFileName(item.RawUrl)); | ||||||
|  |             if (File.Exists(file)) | ||||||
|  |             { | ||||||
|  |                 return file; | ||||||
|  |             } | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public static async Task<ImageSource> LoadPreviewImage(GalleryItem item, bool downloading, bool force = false) | ||||||
|  |         { | ||||||
|  |             return await LoadImage(item.PreviewUrl, CacheFolder, Path.Combine(previewFolder, item.Source), downloading, force: force); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         private static async Task<ImageSource> LoadImage(string url, string working, string folder, bool downloading, bool force = false) |         private static async Task<ImageSource> LoadImage(string url, string working, string folder, bool downloading, bool force = false) | ||||||
| @@ -48,7 +154,7 @@ namespace Gallery.Util | |||||||
|             return image; |             return image; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         private static async Task<ImageSource> LoadImageAsync(string url, string id, string working, string folder, bool force = false) |         private static async Task<ImageSource> LoadImageAsync(string url, string id, string working, string folder, bool force, Action<(int loc, int size)> action) | ||||||
|         { |         { | ||||||
|             var file = Path.Combine(working, folder, Path.GetFileName(url)); |             var file = Path.Combine(working, folder, Path.GetFileName(url)); | ||||||
|             ImageSource image; |             ImageSource image; | ||||||
| @@ -62,7 +168,7 @@ namespace Gallery.Util | |||||||
|             } |             } | ||||||
|             if (image == null) |             if (image == null) | ||||||
|             { |             { | ||||||
|                 file = await NetHelper.DownloadImageAsync(url, id, working, folder); |                 file = await NetHelper.DownloadImageAsync(url, id, working, folder, action); | ||||||
|                 if (file != null) |                 if (file != null) | ||||||
|                 { |                 { | ||||||
|                     image = ImageSource.FromFile(file); |                     image = ImageSource.FromFile(file); | ||||||
| @@ -72,27 +178,33 @@ namespace Gallery.Util | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public static class Config |     public class FavoriteList : List<GalleryItem> | ||||||
|     { |     { | ||||||
|         public static readonly TimeSpan Timeout = TimeSpan.FromSeconds(30); |         public bool Changed { get; private set; } | ||||||
|  |  | ||||||
|         public const string DownloadThreadsKey = "download_threads"; |         public FavoriteList() : base() { } | ||||||
|         public const string IsProxiedKey = "is_proxied"; |         public FavoriteList(IEnumerable<GalleryItem> collection) : base(collection) { } | ||||||
|         public const string ProxyHostKey = "proxy_host"; |  | ||||||
|         public const string ProxyPortKey = "proxy_port"; |  | ||||||
|  |  | ||||||
|         public const int MaxThreads = 2; |         public new void Insert(int index, GalleryItem item) | ||||||
|         public const string UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36"; |         { | ||||||
|         public const string AcceptLanguage = "zh-cn"; |             base.Insert(index, item); | ||||||
|         public const string AcceptImage = "image/png,image/*,*/*;q=0.8"; |             Changed = true; | ||||||
|  |         } | ||||||
|         public static int DownloadThreads; |         public new void InsertRange(int index, IEnumerable<GalleryItem> collection) | ||||||
|         public static WebProxy Proxy; |         { | ||||||
|  |             base.InsertRange(index, collection); | ||||||
|  |             Changed = true; | ||||||
|  |         } | ||||||
|  |         public new void RemoveAt(int index) | ||||||
|  |         { | ||||||
|  |             base.RemoveAt(index); | ||||||
|  |             Changed = true; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|     public static class Routes |         public FavoriteList Reload() | ||||||
|         { |         { | ||||||
|         public const string Gallery = "gallery"; |             Changed = false; | ||||||
|         public const string Option = "option"; |             return this; | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -2,11 +2,44 @@ | |||||||
| <ui:AdaptedPage | <ui:AdaptedPage | ||||||
|     xmlns="http://xamarin.com/schemas/2014/forms" |     xmlns="http://xamarin.com/schemas/2014/forms" | ||||||
|     xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" |     xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" | ||||||
|  |     xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core" | ||||||
|     xmlns:ui="clr-namespace:Gallery.Resources.UI" |     xmlns:ui="clr-namespace:Gallery.Resources.UI" | ||||||
|     x:Class="Gallery.Views.GalleryItemPage" |     x:Class="Gallery.Views.GalleryItemPage" | ||||||
|     x:Name="galleryItemPage" |     x:Name="galleryItemPage" | ||||||
|  |     ios:Page.UseSafeArea="False" | ||||||
|  |     Shell.TabBarIsVisible="False" | ||||||
|     BindingContext="{x:Reference galleryItemPage}"> |     BindingContext="{x:Reference galleryItemPage}"> | ||||||
|     <ContentPage.Content> |  | ||||||
|  |  | ||||||
|  |     <ContentPage.ToolbarItems> | ||||||
|  |         <ToolbarItem Command="{Binding ToolbarCommand}" CommandParameter="favorite" | ||||||
|  |                      Order="Primary" IconImageSource="{Binding FavoriteIcon}"/> | ||||||
|  |         <ToolbarItem Command="{Binding ToolbarCommand}" CommandParameter="refresh" | ||||||
|  |                      Order="Primary" IconImageSource="{DynamicResource FontIconRefresh}"/> | ||||||
|  |         <ToolbarItem Command="{Binding ToolbarCommand}" CommandParameter="share" | ||||||
|  |                      Order="Primary" IconImageSource="{DynamicResource FontIconShare}"/> | ||||||
|  |     </ContentPage.ToolbarItems> | ||||||
|  |     <ContentPage.Content> | ||||||
|  |         <Grid Padding="{Binding TopMargin}"> | ||||||
|  |             <Image HorizontalOptions="Fill" | ||||||
|  |                    VerticalOptions="Fill" | ||||||
|  |                    Aspect="AspectFit" | ||||||
|  |                    Source="{Binding GalleryItem.PreviewImage}"/> | ||||||
|  |  | ||||||
|  |             <Frame HasShadow="False" Margin="0" Padding="20" CornerRadius="8" | ||||||
|  |                    HorizontalOptions="Center" VerticalOptions="Center" | ||||||
|  |                    IsVisible="{Binding IsBusy}" | ||||||
|  |                    BackgroundColor="{DynamicResource MaskColor}"> | ||||||
|  |                 <ActivityIndicator IsVisible="{Binding IsBusy}" | ||||||
|  |                                    IsRunning="{Binding IsBusy}" | ||||||
|  |                                    Color="{DynamicResource WindowColor}"/> | ||||||
|  |             </Frame> | ||||||
|  |  | ||||||
|  |             <ProgressBar x:Name="progress" IsVisible="{Binding ProgressVisible}" | ||||||
|  |                          VerticalOptions="Start"> | ||||||
|  |                 <ProgressBar.Margin> | ||||||
|  |                     <OnPlatform x:TypeArguments="Thickness" Android="0, -6, 0, 0"/> | ||||||
|  |                 </ProgressBar.Margin> | ||||||
|  |             </ProgressBar> | ||||||
|  |         </Grid> | ||||||
|     </ContentPage.Content> |     </ContentPage.Content> | ||||||
| </ui:AdaptedPage> | </ui:AdaptedPage> | ||||||
|   | |||||||
| @@ -1,15 +1,213 @@ | |||||||
| using System; | using System.Linq; | ||||||
| using System.Collections.Generic; | using System.Threading.Tasks; | ||||||
|  | using Gallery.Resources.Theme; | ||||||
| using Gallery.Resources.UI; | using Gallery.Resources.UI; | ||||||
|  | using Gallery.Services; | ||||||
|  | using Gallery.Util; | ||||||
|  | using Gallery.Util.Model; | ||||||
|  | using Xamarin.Essentials; | ||||||
| using Xamarin.Forms; | using Xamarin.Forms; | ||||||
|  |  | ||||||
| namespace Gallery.Views | namespace Gallery.Views | ||||||
| { | { | ||||||
|  |     [QueryProperty("GalleryId", "id")] | ||||||
|     public partial class GalleryItemPage : AdaptedPage |     public partial class GalleryItemPage : AdaptedPage | ||||||
|     { |     { | ||||||
|         public GalleryItemPage() |         public static readonly BindableProperty FavoriteIconProperty = BindableProperty.Create(nameof(FavoriteIcon), typeof(ImageSource), typeof(GalleryItemPage)); | ||||||
|  |         public static readonly BindableProperty GalleryItemProperty = BindableProperty.Create(nameof(GalleryItem), typeof(GalleryItem), typeof(GalleryItemPage)); | ||||||
|  |         public static readonly BindableProperty ProgressVisibleProperty = BindableProperty.Create(nameof(ProgressVisible), typeof(bool), typeof(GalleryItemPage)); | ||||||
|  |         public static readonly BindableProperty ToolbarCommandProperty = BindableProperty.Create(nameof(ToolbarCommand), typeof(Command<string>), typeof(GalleryItemPage)); | ||||||
|  |  | ||||||
|  |         public ImageSource FavoriteIcon | ||||||
|         { |         { | ||||||
|  |             get => (ImageSource)GetValue(FavoriteIconProperty); | ||||||
|  |             set => SetValue(FavoriteIconProperty, value); | ||||||
|  |         } | ||||||
|  |         public GalleryItem GalleryItem | ||||||
|  |         { | ||||||
|  |             get => (GalleryItem)GetValue(GalleryItemProperty); | ||||||
|  |         } | ||||||
|  |         public bool ProgressVisible | ||||||
|  |         { | ||||||
|  |             get => (bool)GetValue(ProgressVisibleProperty); | ||||||
|  |             set => SetValue(ProgressVisibleProperty, value); | ||||||
|  |         } | ||||||
|  |         public Command<string> ToolbarCommand | ||||||
|  |         { | ||||||
|  |             get => (Command<string>)GetValue(ToolbarCommandProperty); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private readonly ImageSource fontIconLove; | ||||||
|  |         private readonly ImageSource fontIconNotLove; | ||||||
|  |         private bool favoriteChanged; | ||||||
|  |         private bool isLoaded; | ||||||
|  |  | ||||||
|  |         public GalleryItemPage(GalleryItem item) | ||||||
|  |         { | ||||||
|  |             SetValue(GalleryItemProperty, item); | ||||||
|  |             SetValue(ToolbarCommandProperty, new Command<string>(HandleCommand, cmd => !IsBusy)); | ||||||
|  |  | ||||||
|  |             fontIconLove = (ImageSource)Application.Current.Resources[Theme.FontIconLove]; | ||||||
|  |             fontIconNotLove = (ImageSource)Application.Current.Resources[Theme.FontIconNotLove]; | ||||||
|  |  | ||||||
|  |             if (item != null) | ||||||
|  |             { | ||||||
|  |                 InitInformation(item); | ||||||
|  |             } | ||||||
|  |  | ||||||
|             InitializeComponent(); |             InitializeComponent(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         protected override void OnAppearing() | ||||||
|  |         { | ||||||
|  |             base.OnAppearing(); | ||||||
|  |             Screen.SetHomeIndicatorAutoHidden(Shell.Current, true); | ||||||
|  |  | ||||||
|  |             if (!isLoaded) | ||||||
|  |             { | ||||||
|  |                 isLoaded = true; | ||||||
|  |                 Task.Run(() => LoadImage()); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         protected override void OnDisappearing() | ||||||
|  |         { | ||||||
|  |             Screen.SetHomeIndicatorAutoHidden(Shell.Current, false); | ||||||
|  |             base.OnDisappearing(); | ||||||
|  |  | ||||||
|  |             if (favoriteChanged) | ||||||
|  |             { | ||||||
|  |                 Store.SaveFavoriteList(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private void InitInformation(GalleryItem item) | ||||||
|  |         { | ||||||
|  |             Title = item.TagDescription; | ||||||
|  |             FavoriteIcon = Store.FavoriteList.Any(i => i.SourceEquals(item)) ? | ||||||
|  |                 fontIconLove : | ||||||
|  |                 fontIconNotLove; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private async void LoadImage(bool force = false) | ||||||
|  |         { | ||||||
|  |             IsBusy = true; | ||||||
|  |             MainThread.BeginInvokeOnMainThread(async () => | ||||||
|  |             { | ||||||
|  |                 ToolbarCommand.ChangeCanExecute(); | ||||||
|  |                 if (force) | ||||||
|  |                 { | ||||||
|  |                     ProgressVisible = true; | ||||||
|  |                     progress.Progress = 0; | ||||||
|  |                     await progress.FadeTo(1, easing: Easing.CubicIn); | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |             var item = GalleryItem; | ||||||
|  |             if (item.IsRawPage) | ||||||
|  |             { | ||||||
|  |                 var source = App.GallerySources.FirstOrDefault(s => s.Route == item.Source); | ||||||
|  |                 if (source != null) | ||||||
|  |                 { | ||||||
|  |                     var url = await source.ResolveImageUrl(item); | ||||||
|  |                     if (!string.IsNullOrEmpty(url)) | ||||||
|  |                     { | ||||||
|  |                         item.IsRawPage = false; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             var image = await Store.LoadRawImage(item, force: force, o => | ||||||
|  |             { | ||||||
|  |                 var val = o.loc / (double)o.size; | ||||||
|  |                 if (val > progress.Progress) | ||||||
|  |                 { | ||||||
|  |                     MainThread.BeginInvokeOnMainThread(async () => | ||||||
|  |                     { | ||||||
|  |                         if (!ProgressVisible) | ||||||
|  |                         { | ||||||
|  |                             ProgressVisible = true; | ||||||
|  |                         } | ||||||
|  |                         progress.CancelAnimations(); | ||||||
|  |                         await progress.ProgressTo(val, 250, Easing.CubicIn); | ||||||
|  |                     }); | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |             if (image != null) | ||||||
|  |             { | ||||||
|  |                 item.PreviewImage = image; | ||||||
|  |             } | ||||||
|  |             IsBusy = false; | ||||||
|  |             MainThread.BeginInvokeOnMainThread(async () => | ||||||
|  |             { | ||||||
|  |                 ToolbarCommand.ChangeCanExecute(); | ||||||
|  |                 progress.CancelAnimations(); | ||||||
|  |                 await progress.ProgressTo(1, 250, Easing.CubicIn); | ||||||
|  |                 await progress.FadeTo(0, easing: Easing.CubicIn); | ||||||
|  |                 ProgressVisible = false; | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private void HandleCommand(string cmd) | ||||||
|  |         { | ||||||
|  |             switch (cmd) | ||||||
|  |             { | ||||||
|  |                 case "favorite": Favorite_Clicked(); break; | ||||||
|  |                 case "refresh": Refresh_Clicked(); break; | ||||||
|  |                 case "share": Share_Clicked(); break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private void Favorite_Clicked() | ||||||
|  |         { | ||||||
|  |             if (IsBusy) | ||||||
|  |             { | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |             Start(() => | ||||||
|  |             { | ||||||
|  |                 var favorites = Store.FavoriteList; | ||||||
|  |                 var item = GalleryItem; | ||||||
|  |                 var index = favorites.FindIndex(i => i.SourceEquals(item)); | ||||||
|  |                 if (index < 0) | ||||||
|  |                 { | ||||||
|  |                     item.IsFavorite = true; | ||||||
|  |                     favorites.Insert(0, item); | ||||||
|  |                     FavoriteIcon = fontIconLove; | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                 { | ||||||
|  |                     favorites.RemoveAt(index); | ||||||
|  |                     item.IsFavorite = false; | ||||||
|  |                     FavoriteIcon = fontIconNotLove; | ||||||
|  |                 } | ||||||
|  |                 favoriteChanged = true; | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private void Refresh_Clicked() | ||||||
|  |         { | ||||||
|  |             if (IsBusy) | ||||||
|  |             { | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |             Start(async () => await Task.Run(() => LoadImage(true))); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private void Share_Clicked() | ||||||
|  |         { | ||||||
|  |             if (IsBusy) | ||||||
|  |             { | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |             Start(async () => | ||||||
|  |             { | ||||||
|  |                 var item = GalleryItem; | ||||||
|  |                 var path = Store.GetRawImagePath(item); | ||||||
|  |                 await Share.RequestAsync(new ShareFileRequest | ||||||
|  |                 { | ||||||
|  |                     Title = item.TagDescription, | ||||||
|  |                     File = new ShareFile(path) | ||||||
|  |                 }); | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -8,6 +8,11 @@ | |||||||
|     BackgroundColor="{DynamicResource WindowColor}" |     BackgroundColor="{DynamicResource WindowColor}" | ||||||
|     BindingContext="{x:Reference yanderePage}" |     BindingContext="{x:Reference yanderePage}" | ||||||
|     Title="{Binding Source.Name}"> |     Title="{Binding Source.Name}"> | ||||||
|  |  | ||||||
|  |     <ContentPage.ToolbarItems> | ||||||
|  |         <ToolbarItem Order="Primary" Command="{Binding ToolbarCommand}" | ||||||
|  |                      IconImageSource="{DynamicResource FontIconRefresh}"/> | ||||||
|  |     </ContentPage.ToolbarItems> | ||||||
|     <ContentPage.Content> |     <ContentPage.Content> | ||||||
|         <Grid> |         <Grid> | ||||||
|             <ScrollView x:Name="scrollView" Scrolled="ScrollView_Scrolled" |             <ScrollView x:Name="scrollView" Scrolled="ScrollView_Scrolled" | ||||||
|   | |||||||
| @@ -4,17 +4,25 @@ using Gallery.Resources.UI; | |||||||
| using Gallery.Util; | using Gallery.Util; | ||||||
| using Gallery.Util.Interface; | using Gallery.Util.Interface; | ||||||
| using Gallery.Util.Model; | using Gallery.Util.Model; | ||||||
|  | using Xamarin.Essentials; | ||||||
| using Xamarin.Forms; | using Xamarin.Forms; | ||||||
|  |  | ||||||
| namespace Gallery.Views | namespace Gallery.Views | ||||||
| { | { | ||||||
|     public partial class GalleryPage : GalleryCollectionPage |     public partial class GalleryPage : GalleryCollectionPage | ||||||
|     { |     { | ||||||
|  |         public static readonly BindableProperty ToolbarCommandProperty = BindableProperty.Create(nameof(ToolbarCommand), typeof(Command), typeof(GalleryPage)); | ||||||
|  |         public Command ToolbarCommand | ||||||
|  |         { | ||||||
|  |             get => (Command)GetValue(ToolbarCommandProperty); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         private int currentPage; |         private int currentPage; | ||||||
|  |  | ||||||
|         public GalleryPage(IGallerySource source) : base(source) |         public GalleryPage(IGallerySource source) : base(source) | ||||||
|         { |         { | ||||||
|             Resources.Add("cardView", GetCardViewTemplate()); |             Resources.Add("cardView", GetCardViewTemplate()); | ||||||
|  |             SetValue(ToolbarCommandProperty, new Command(DoRefresh, () => !IsLoading && !IsBottomLoading)); | ||||||
|             InitializeComponent(); |             InitializeComponent(); | ||||||
|  |  | ||||||
|             currentPage = 1; |             currentPage = 1; | ||||||
| @@ -28,21 +36,62 @@ namespace Gallery.Views | |||||||
|             { |             { | ||||||
|                 currentPage = 1; |                 currentPage = 1; | ||||||
|             } |             } | ||||||
|  |             if (Store.FavoriteList.Changed && Source is Sources.FavoriteGallerySource) | ||||||
|  |             { | ||||||
|  |                 Store.FavoriteList.Reload(); | ||||||
|  |                 LastUpdated = default; | ||||||
|  |             } | ||||||
|             base.OnAppearing(); |             base.OnAppearing(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         protected override async Task<GalleryItem[]> DoloadGalleryData(bool force) |         protected override void BeforeLoading() | ||||||
|  |         { | ||||||
|  |             MainThread.BeginInvokeOnMainThread(ToolbarCommand.ChangeCanExecute); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         protected override void AfterLoaded() | ||||||
|  |         { | ||||||
|  |             MainThread.BeginInvokeOnMainThread(ToolbarCommand.ChangeCanExecute); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         protected override async Task<IEnumerable<GalleryItem>> DoloadGalleryData(bool force) | ||||||
|         { |         { | ||||||
|             var result = await Source.GetRecentItemsAsync(currentPage); |             var result = await Source.GetRecentItemsAsync(currentPage); | ||||||
|             return result; |             return result; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         protected override IEnumerable<GalleryItem> DoGetGalleryList(GalleryItem[] data, out int tag) |         protected override IEnumerable<GalleryItem> DoGetGalleryList(IEnumerable<GalleryItem> data, out int tag) | ||||||
|         { |         { | ||||||
|             tag = currentPage; |             tag = currentPage; | ||||||
|             return data; |             return data; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         protected override void OnGalleryItemTapped(GalleryItem item) | ||||||
|  |         { | ||||||
|  |             if (item == null) | ||||||
|  |             { | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |             Start(async () => | ||||||
|  |             { | ||||||
|  |                 var page = new GalleryItemPage(item); | ||||||
|  |                 await Navigation.PushAsync(page); | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private void DoRefresh() | ||||||
|  |         { | ||||||
|  |             if (IsLoading) | ||||||
|  |             { | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |             Start(async () => | ||||||
|  |             { | ||||||
|  |                 await ScrollToTopAsync(scrollView); | ||||||
|  |                 StartLoading(force: true); | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         private void FlowLayout_MaxHeightChanged(object sender, HeightEventArgs e) |         private void FlowLayout_MaxHeightChanged(object sender, HeightEventArgs e) | ||||||
|         { |         { | ||||||
|             SetOffset(e.ContentHeight - scrollView.Bounds.Height - SCROLL_OFFSET); |             SetOffset(e.ContentHeight - scrollView.Bounds.Height - SCROLL_OFFSET); | ||||||
| @@ -50,6 +99,10 @@ namespace Gallery.Views | |||||||
|  |  | ||||||
|         protected override bool CheckRefresh() |         protected override bool CheckRefresh() | ||||||
|         { |         { | ||||||
|  |             if (!Source.IsScrollable) | ||||||
|  |             { | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|             currentPage++; |             currentPage++; | ||||||
| #if DEBUG | #if DEBUG | ||||||
|             Log.Print($"loading page: {currentPage}"); |             Log.Print($"loading page: {currentPage}"); | ||||||
|   | |||||||
							
								
								
									
										26
									
								
								Gallery.iOS/Assets.xcassets/IconBookmark.imageset/Contents.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								Gallery.iOS/Assets.xcassets/IconBookmark.imageset/Contents.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | |||||||
|  | { | ||||||
|  |   "images": [ | ||||||
|  |     { | ||||||
|  |       "idiom": "universal" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "filename": "bookmark-solid.png", | ||||||
|  |       "scale": "1x", | ||||||
|  |       "idiom": "universal" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "filename": "bookmark-solid@2x.png", | ||||||
|  |       "scale": "2x", | ||||||
|  |       "idiom": "universal" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "filename": "bookmark-solid@3x.png", | ||||||
|  |       "scale": "3x", | ||||||
|  |       "idiom": "universal" | ||||||
|  |     } | ||||||
|  |   ], | ||||||
|  |   "info": { | ||||||
|  |     "version": 1, | ||||||
|  |     "author": "xcode" | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										
											BIN
										
									
								
								Gallery.iOS/Assets.xcassets/IconBookmark.imageset/bookmark-solid.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Gallery.iOS/Assets.xcassets/IconBookmark.imageset/bookmark-solid.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 244 B | 
							
								
								
									
										
											BIN
										
									
								
								Gallery.iOS/Assets.xcassets/IconBookmark.imageset/bookmark-solid@2x.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Gallery.iOS/Assets.xcassets/IconBookmark.imageset/bookmark-solid@2x.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 339 B | 
							
								
								
									
										
											BIN
										
									
								
								Gallery.iOS/Assets.xcassets/IconBookmark.imageset/bookmark-solid@3x.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Gallery.iOS/Assets.xcassets/IconBookmark.imageset/bookmark-solid@3x.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 458 B | 
							
								
								
									
										26
									
								
								Gallery.iOS/Assets.xcassets/IconBookmarkRegular.imageset/Contents.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								Gallery.iOS/Assets.xcassets/IconBookmarkRegular.imageset/Contents.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | |||||||
|  | { | ||||||
|  |   "images": [ | ||||||
|  |     { | ||||||
|  |       "idiom": "universal" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "filename": "bookmark-regular.png", | ||||||
|  |       "scale": "1x", | ||||||
|  |       "idiom": "universal" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "filename": "bookmark-regular@2x.png", | ||||||
|  |       "scale": "2x", | ||||||
|  |       "idiom": "universal" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "filename": "bookmark-regular@3x.png", | ||||||
|  |       "scale": "3x", | ||||||
|  |       "idiom": "universal" | ||||||
|  |     } | ||||||
|  |   ], | ||||||
|  |   "info": { | ||||||
|  |     "version": 1, | ||||||
|  |     "author": "xcode" | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										
											BIN
										
									
								
								Gallery.iOS/Assets.xcassets/IconBookmarkRegular.imageset/bookmark-regular.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Gallery.iOS/Assets.xcassets/IconBookmarkRegular.imageset/bookmark-regular.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 287 B | 
							
								
								
									
										
											BIN
										
									
								
								Gallery.iOS/Assets.xcassets/IconBookmarkRegular.imageset/bookmark-regular@2x.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Gallery.iOS/Assets.xcassets/IconBookmarkRegular.imageset/bookmark-regular@2x.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 432 B | 
							
								
								
									
										
											BIN
										
									
								
								Gallery.iOS/Assets.xcassets/IconBookmarkRegular.imageset/bookmark-regular@3x.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Gallery.iOS/Assets.xcassets/IconBookmarkRegular.imageset/bookmark-regular@3x.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 595 B | 
| @@ -135,6 +135,14 @@ | |||||||
|     <BundleResource Include="Resources\logo_light.png" /> |     <BundleResource Include="Resources\logo_light.png" /> | ||||||
|     <BundleResource Include="Resources\logo_light%402x.png" /> |     <BundleResource Include="Resources\logo_light%402x.png" /> | ||||||
|     <BundleResource Include="Resources\logo_light%403x.png" /> |     <BundleResource Include="Resources\logo_light%403x.png" /> | ||||||
|  |     <ImageAsset Include="Assets.xcassets\IconBookmark.imageset\Contents.json" /> | ||||||
|  |     <ImageAsset Include="Assets.xcassets\IconBookmarkRegular.imageset\Contents.json" /> | ||||||
|  |     <ImageAsset Include="Assets.xcassets\IconBookmark.imageset\bookmark-solid.png" /> | ||||||
|  |     <ImageAsset Include="Assets.xcassets\IconBookmark.imageset\bookmark-solid%402x.png" /> | ||||||
|  |     <ImageAsset Include="Assets.xcassets\IconBookmark.imageset\bookmark-solid%403x.png" /> | ||||||
|  |     <ImageAsset Include="Assets.xcassets\IconBookmarkRegular.imageset\bookmark-regular.png" /> | ||||||
|  |     <ImageAsset Include="Assets.xcassets\IconBookmarkRegular.imageset\bookmark-regular%402x.png" /> | ||||||
|  |     <ImageAsset Include="Assets.xcassets\IconBookmarkRegular.imageset\bookmark-regular%403x.png" /> | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|     <Reference Include="System" /> |     <Reference Include="System" /> | ||||||
| @@ -158,6 +166,8 @@ | |||||||
|     <Folder Include="Assets.xcassets\IconSourceRegular.imageset\" /> |     <Folder Include="Assets.xcassets\IconSourceRegular.imageset\" /> | ||||||
|     <Folder Include="Assets.xcassets\LaunchLogo.imageset\" /> |     <Folder Include="Assets.xcassets\LaunchLogo.imageset\" /> | ||||||
|     <Folder Include="Assets.xcassets\LauncherLogo.imageset\" /> |     <Folder Include="Assets.xcassets\LauncherLogo.imageset\" /> | ||||||
|  |     <Folder Include="Assets.xcassets\IconBookmark.imageset\" /> | ||||||
|  |     <Folder Include="Assets.xcassets\IconBookmarkRegular.imageset\" /> | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
|   <Import Project="..\Gallery.UI\Gallery.UI.projitems" Label="Shared" Condition="Exists('..\Gallery.UI\Gallery.UI.projitems')" /> |   <Import Project="..\Gallery.UI\Gallery.UI.projitems" Label="Shared" Condition="Exists('..\Gallery.UI\Gallery.UI.projitems')" /> | ||||||
|   <Import Project="..\Gallery.Share\Gallery.Share.projitems" Label="Shared" Condition="Exists('..\Gallery.Share\Gallery.Share.projitems')" /> |   <Import Project="..\Gallery.Share\Gallery.Share.projitems" Label="Shared" Condition="Exists('..\Gallery.Share\Gallery.Share.projitems')" /> | ||||||
|   | |||||||
| @@ -151,15 +151,18 @@ namespace Gallery.iOS.Renderers.AppShellSection | |||||||
|         public void UpdateLayout(UITabBarController controller) |         public void UpdateLayout(UITabBarController controller) | ||||||
|         { |         { | ||||||
|             var tabBar = controller.TabBar; |             var tabBar = controller.TabBar; | ||||||
|             if (tabBar != null && tabBar.Items != null && tabBar.Items.Length == 3) |             if (tabBar != null && tabBar.Items != null && tabBar.Items.Length >= 4) | ||||||
|             { |             { | ||||||
|                 var tabBarItem = tabBar.Items[0]; |                 var tabBarItem = tabBar.Items[0]; | ||||||
|  |                 tabBarItem.Image = UIImage.FromBundle("IconBookmarkRegular"); | ||||||
|  |                 tabBarItem.SelectedImage = UIImage.FromBundle("IconBookmark"); | ||||||
|  |                 tabBarItem = tabBar.Items[1]; | ||||||
|                 tabBarItem.Image = UIImage.FromBundle("IconYandereRegular"); |                 tabBarItem.Image = UIImage.FromBundle("IconYandereRegular"); | ||||||
|                 tabBarItem.SelectedImage = UIImage.FromBundle("IconYandere"); |                 tabBarItem.SelectedImage = UIImage.FromBundle("IconYandere"); | ||||||
|                 tabBarItem = tabBar.Items[1]; |                 tabBarItem = tabBar.Items[2]; | ||||||
|                 tabBarItem.Image = UIImage.FromBundle("IconSourceRegular"); |                 tabBarItem.Image = UIImage.FromBundle("IconSourceRegular"); | ||||||
|                 tabBarItem.SelectedImage = UIImage.FromBundle("IconSource"); |                 tabBarItem.SelectedImage = UIImage.FromBundle("IconSource"); | ||||||
|                 tabBarItem = tabBar.Items[2]; |                 tabBarItem = tabBar.Items[3]; | ||||||
|                 tabBarItem.Image = UIImage.FromBundle("IconSourceRegular"); |                 tabBarItem.Image = UIImage.FromBundle("IconSourceRegular"); | ||||||
|                 tabBarItem.SelectedImage = UIImage.FromBundle("IconSource"); |                 tabBarItem.SelectedImage = UIImage.FromBundle("IconSource"); | ||||||
|             } |             } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user