534 lines
19 KiB
C#
534 lines
19 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using System.Windows.Input;
|
|
using Newtonsoft.Json;
|
|
using Pixiview.Resources;
|
|
using Pixiview.UI;
|
|
using Pixiview.UI.Theme;
|
|
using Pixiview.Utils;
|
|
using Xamarin.Essentials;
|
|
using Xamarin.Forms;
|
|
|
|
namespace Pixiview.Illust
|
|
{
|
|
public abstract class FavoriteIllustCollectionPage : IllustCollectionPage<IllustItem[]>
|
|
{
|
|
public override void OnUnload() { }
|
|
}
|
|
public abstract class IllustDataCollectionPage : IllustCollectionPage<IllustData> { }
|
|
public abstract class IllustUserDataCollectionPage : IllustCollectionPage<IllustUserData> { }
|
|
|
|
public interface IIllustCollectionPage
|
|
{
|
|
IllustCollection IllustCollection { get; set; }
|
|
}
|
|
public abstract class IllustCollectionPage<T> : AdaptedPage, IIllustCollectionPage
|
|
{
|
|
#region - Properties -
|
|
|
|
public static readonly BindableProperty IllustsProperty = BindableProperty.Create(
|
|
nameof(Illusts), typeof(IllustCollection), typeof(IllustCollectionPage<T>));
|
|
public static readonly BindableProperty ColumnsProperty = BindableProperty.Create(
|
|
nameof(Columns), typeof(int), typeof(IllustCollectionPage<T>), 2);
|
|
public static readonly BindableProperty IsLoadingProperty = BindableProperty.Create(
|
|
nameof(IsLoading), typeof(bool), typeof(IllustCollectionPage<T>), true);
|
|
|
|
public IllustCollection Illusts
|
|
{
|
|
get => (IllustCollection)GetValue(IllustsProperty);
|
|
set => SetValue(IllustsProperty, value);
|
|
}
|
|
public int Columns
|
|
{
|
|
get => (int)GetValue(ColumnsProperty);
|
|
set => SetValue(ColumnsProperty, value);
|
|
}
|
|
public bool IsLoading
|
|
{
|
|
get => (bool)GetValue(IsLoadingProperty);
|
|
set => SetValue(IsLoadingProperty, value);
|
|
}
|
|
|
|
#endregion
|
|
|
|
protected static bool NeedUpdate { get; private set; } = true;
|
|
|
|
public IllustCollection IllustCollection { get; set; }
|
|
|
|
protected virtual bool IsFavoriteVisible => true;
|
|
protected Thickness totalBarOffset;
|
|
protected Thickness navigationBarOffset;
|
|
protected bool loaded;
|
|
|
|
private T illustData;
|
|
private readonly ParallelOptions parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = Configs.MaxThreads };
|
|
private readonly Command<IllustItem> commandIllustImageTapped;
|
|
|
|
public IllustCollectionPage()
|
|
{
|
|
commandIllustImageTapped = new Command<IllustItem>(IllustImageTapped);
|
|
BindingContext = this;
|
|
}
|
|
|
|
private void IllustImageTapped(IllustItem illust)
|
|
{
|
|
Start(() => OnIllustImageTapped(illust));
|
|
}
|
|
|
|
#region - Overrides -
|
|
|
|
public override void OnUnload()
|
|
{
|
|
Illusts = IllustCollection.Empty;
|
|
loaded = false;
|
|
}
|
|
|
|
protected override void OnAppearing()
|
|
{
|
|
base.OnAppearing();
|
|
Connectivity.ConnectivityChanged += Connectivity_ConnectivityChanged;
|
|
|
|
if (NeedUpdate || !loaded)
|
|
{
|
|
NeedUpdate = false;
|
|
loaded = false;
|
|
StartLoad();
|
|
}
|
|
else if (IsFavoriteVisible)
|
|
{
|
|
var favorites = Stores.Favorites;
|
|
foreach (var item in IllustCollection)
|
|
{
|
|
item.IsFavorite = favorites.Any(i => i.Id == item.Id);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected override void OnDisappearing()
|
|
{
|
|
Connectivity.ConnectivityChanged -= Connectivity_ConnectivityChanged;
|
|
base.OnDisappearing();
|
|
}
|
|
|
|
private void Connectivity_ConnectivityChanged(object sender, ConnectivityChangedEventArgs e)
|
|
{
|
|
if (e.NetworkAccess == NetworkAccess.Internet)
|
|
{
|
|
if (!loaded)
|
|
{
|
|
StartLoad(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
public override void OnOrientationChanged(Orientation orientation)
|
|
{
|
|
int columns;
|
|
switch (orientation)
|
|
{
|
|
case Orientation.Portrait:
|
|
columns = 2;
|
|
break;
|
|
case Orientation.PortraitUpsideDown:
|
|
columns = isPhone ? 4 : 2;
|
|
break;
|
|
case Orientation.Unknown:
|
|
case Orientation.LandscapeLeft:
|
|
case Orientation.LandscapeRight:
|
|
default:
|
|
columns = 4;
|
|
break;
|
|
}
|
|
if (Columns != columns)
|
|
{
|
|
App.DebugPrint($"change columns to {columns}");
|
|
Columns = columns;
|
|
}
|
|
base.OnOrientationChanged(orientation);
|
|
}
|
|
|
|
#endregion
|
|
|
|
protected abstract T DoLoadIllustData(bool force);
|
|
protected abstract IEnumerable<IllustItem> DoGetIllustList(T data, ICommand command);
|
|
protected virtual void OnIllustImageTapped(IllustItem illust)
|
|
{
|
|
var page = new ViewIllustPage(illust, this);
|
|
Navigation.PushAsync(page);
|
|
}
|
|
|
|
protected void StartLoad(bool force = false)
|
|
{
|
|
if (force || !loaded)
|
|
{
|
|
loaded = true;
|
|
IsLoading = true;
|
|
Task.Run(() => DoLoadIllusts(force));
|
|
}
|
|
}
|
|
|
|
protected DataTemplate GetCardViewTemplate(bool hideUser = false)
|
|
{
|
|
return new DataTemplate(() =>
|
|
{
|
|
#region - components -
|
|
|
|
// image
|
|
var image = new RoundImage
|
|
{
|
|
BackgroundColor = Color.LightGray,
|
|
CornerRadius = 10,
|
|
CornerMasks = CornerMask.Top,
|
|
HorizontalOptions = LayoutOptions.Fill,
|
|
Aspect = Aspect.AspectFill,
|
|
GestureRecognizers =
|
|
{
|
|
new TapGestureRecognizer()
|
|
.Binding(TapGestureRecognizer.CommandProperty, nameof(IllustItem.IllustTapped))
|
|
.Binding(TapGestureRecognizer.CommandParameterProperty, ".")
|
|
}
|
|
}
|
|
.Binding(Image.SourceProperty, nameof(IllustItem.Image));
|
|
|
|
// label: r-18
|
|
var r18 = new RoundLabel
|
|
{
|
|
Text = ResourceHelper.R18,
|
|
BackgroundColor = StyleDefinition.ColorRedBackground,
|
|
Margin = new Thickness(6, 6, 0, 0),
|
|
Padding = new Thickness(6, 2),
|
|
CornerRadius = 4,
|
|
HorizontalOptions = LayoutOptions.Start,
|
|
VerticalOptions = LayoutOptions.Start,
|
|
FontSize = StyleDefinition.FontSizeMicro,
|
|
TextColor = Color.White
|
|
}
|
|
.Binding(IsVisibleProperty, nameof(IllustItem.IsRestrict));
|
|
|
|
// label: pages
|
|
var pages = new RoundLabel
|
|
{
|
|
BackgroundColor = StyleDefinition.ColorDeepShadow,
|
|
Margin = new Thickness(0, 6, 6, 0),
|
|
Padding = new Thickness(6, 4),
|
|
CornerRadius = 6,
|
|
HorizontalOptions = LayoutOptions.End,
|
|
VerticalOptions = LayoutOptions.Start,
|
|
FontSize = StyleDefinition.FontSizeMicro,
|
|
TextColor = Color.White
|
|
}
|
|
.Binding(Label.TextProperty, nameof(IllustItem.PageCountText))
|
|
.Binding(IsVisibleProperty, nameof(IllustItem.IsPageVisible))
|
|
.DynamicResource(Label.FontFamilyProperty, ThemeBase.IconSolidFontFamily);
|
|
|
|
// label: title
|
|
var title = new Label
|
|
{
|
|
Padding = new Thickness(8, 2),
|
|
HorizontalOptions = LayoutOptions.FillAndExpand,
|
|
LineBreakMode = LineBreakMode.TailTruncation,
|
|
FontSize = StyleDefinition.FontSizeSmall
|
|
}
|
|
.Binding(Label.TextProperty, nameof(IllustItem.Title))
|
|
.DynamicResource(Label.TextColorProperty, ThemeBase.TextColor);
|
|
|
|
// label: favorite
|
|
var favorite = new Label
|
|
{
|
|
WidthRequest = 26,
|
|
HorizontalOptions = LayoutOptions.End,
|
|
HorizontalTextAlignment = TextAlignment.End,
|
|
VerticalOptions = LayoutOptions.Center,
|
|
FontSize = StyleDefinition.FontSizeSmall,
|
|
TextColor = StyleDefinition.ColorRedBackground,
|
|
Text = StyleDefinition.IconLove
|
|
}
|
|
.Binding(IsVisibleProperty, IsFavoriteVisible ? nameof(IllustItem.IsFavorite) : null)
|
|
.DynamicResource(Label.FontFamilyProperty, ThemeBase.IconSolidFontFamily);
|
|
|
|
#endregion
|
|
|
|
if (hideUser)
|
|
{
|
|
return new CardView
|
|
{
|
|
Padding = 0,
|
|
Margin = 0,
|
|
CornerRadius = 10,
|
|
ShadowColor = StyleDefinition.ColorLightShadow,
|
|
ShadowOffset = new Size(2, 2),
|
|
Content = new Grid
|
|
{
|
|
HorizontalOptions = LayoutOptions.Fill,
|
|
RowDefinitions =
|
|
{
|
|
new RowDefinition().Binding(RowDefinition.HeightProperty, nameof(IllustItem.ImageHeight)),
|
|
new RowDefinition { Height = GridLength.Auto }
|
|
},
|
|
Children =
|
|
{
|
|
image,
|
|
r18,
|
|
pages,
|
|
title,
|
|
|
|
// stacklayout: user
|
|
new StackLayout
|
|
{
|
|
Orientation = StackOrientation.Horizontal,
|
|
Padding = new Thickness(0, 0, 8, 0),
|
|
Children = { title, favorite }
|
|
}
|
|
.GridRow(1)
|
|
}
|
|
}
|
|
}
|
|
.DynamicResource(BackgroundColorProperty, ThemeBase.CardBackgroundColor);
|
|
}
|
|
|
|
return new CardView
|
|
{
|
|
Padding = 0,
|
|
Margin = 0,
|
|
CornerRadius = 10,
|
|
ShadowColor = StyleDefinition.ColorLightShadow,
|
|
ShadowOffset = new Size(2, 2),
|
|
Content = new Grid
|
|
{
|
|
HorizontalOptions = LayoutOptions.Fill,
|
|
RowDefinitions =
|
|
{
|
|
new RowDefinition().Binding(RowDefinition.HeightProperty, nameof(IllustItem.ImageHeight)),
|
|
new RowDefinition { Height = GridLength.Auto },
|
|
new RowDefinition { Height = GridLength.Auto }
|
|
},
|
|
Children =
|
|
{
|
|
image,
|
|
r18,
|
|
pages,
|
|
title.GridRow(1),
|
|
|
|
// stacklayout: user
|
|
new StackLayout
|
|
{
|
|
Orientation = StackOrientation.Horizontal,
|
|
Padding = new Thickness(8, 0, 8, 8),
|
|
Children =
|
|
{
|
|
// user icon
|
|
new CircleImage
|
|
{
|
|
WidthRequest = 30,
|
|
HeightRequest = 30,
|
|
Aspect = Aspect.AspectFill
|
|
}
|
|
.Binding(Image.SourceProperty, nameof(IllustItem.ProfileImage)),
|
|
|
|
// user name
|
|
new Label
|
|
{
|
|
HorizontalOptions = LayoutOptions.FillAndExpand,
|
|
VerticalOptions = LayoutOptions.Center,
|
|
LineBreakMode = LineBreakMode.TailTruncation,
|
|
FontSize = StyleDefinition.FontSizeMicro
|
|
}
|
|
.Binding(Label.TextProperty, nameof(IllustItem.UserName))
|
|
.DynamicResource(Label.TextColorProperty, ThemeBase.SubTextColor),
|
|
|
|
// label: favorite
|
|
favorite
|
|
}
|
|
}
|
|
.GridRow(2)
|
|
}
|
|
}
|
|
}
|
|
.DynamicResource(BackgroundColorProperty, ThemeBase.CardBackgroundColor);
|
|
});
|
|
}
|
|
|
|
#region - Illust Tasks -
|
|
|
|
protected void DoLoadIllusts(bool force = false)
|
|
{
|
|
illustData = DoLoadIllustData(force);
|
|
if (illustData == null)
|
|
{
|
|
//App.DebugError("illusts.load", "failed to load illusts data.");
|
|
IsLoading = false;
|
|
return;
|
|
}
|
|
if (force)
|
|
{
|
|
NeedUpdate = true;
|
|
}
|
|
|
|
var data = DoGetIllustList(illustData, commandIllustImageTapped).Where(i => i != null);
|
|
|
|
var collection = new IllustCollection(data);
|
|
if (IsFavoriteVisible)
|
|
{
|
|
var favorites = Stores.Favorites;
|
|
foreach (var item in collection)
|
|
{
|
|
item.IsFavorite = favorites.Any(i => i.Id == item.Id);
|
|
}
|
|
}
|
|
IllustCollection = collection;
|
|
Illusts = collection;
|
|
IsLoading = false;
|
|
|
|
DoLoadImages(collection);
|
|
}
|
|
|
|
void DoLoadImages(IllustCollection collection)
|
|
{
|
|
Parallel.ForEach(collection, parallelOptions, illust =>
|
|
{
|
|
if (!collection.Running)
|
|
{
|
|
return;
|
|
}
|
|
if (illust.ImageUrl != null)
|
|
{
|
|
var url = Configs.GetThumbnailUrl(illust.ImageUrl);
|
|
var image = Stores.LoadPreviewImage(url, false);
|
|
if (image == null)
|
|
{
|
|
image = Stores.LoadThumbnailImage(url);
|
|
}
|
|
if (image != null)
|
|
{
|
|
illust.Image = image;
|
|
}
|
|
}
|
|
|
|
if (illust.ProfileUrl != null)
|
|
{
|
|
var userImage = Stores.LoadUserProfileImage(illust.ProfileUrl);
|
|
if (userImage != null)
|
|
{
|
|
illust.ProfileImage = userImage;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
public class IllustCollection : List<IllustItem>
|
|
{
|
|
private static IllustCollection empty;
|
|
|
|
public static IllustCollection Empty
|
|
{
|
|
get
|
|
{
|
|
if (empty == null)
|
|
{
|
|
empty = new IllustCollection();
|
|
}
|
|
return empty;
|
|
}
|
|
}
|
|
|
|
public IllustCollection() : base()
|
|
{
|
|
running = true;
|
|
}
|
|
public IllustCollection(IEnumerable<IllustItem> illusts) : base(illusts)
|
|
{
|
|
running = true;
|
|
}
|
|
|
|
private readonly object sync = new object();
|
|
private volatile bool running;
|
|
public bool Running
|
|
{
|
|
get => running;
|
|
set
|
|
{
|
|
lock (sync)
|
|
{
|
|
running = value;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public class IllustFavorite
|
|
{
|
|
public DateTime LastFavoriteUtc { get; set; }
|
|
public List<IllustItem> Illusts { get; set; }
|
|
}
|
|
|
|
[JsonObject(MemberSerialization.OptIn)]
|
|
public class IllustItem : BindableObject
|
|
{
|
|
public static readonly BindableProperty ImageProperty = BindableProperty.Create(
|
|
nameof(Image), typeof(ImageSource), typeof(IllustItem));
|
|
public static readonly BindableProperty ProfileImageProperty = BindableProperty.Create(
|
|
nameof(ProfileImage), typeof(ImageSource), typeof(IllustItem));
|
|
public static readonly BindableProperty ImageHeightProperty = BindableProperty.Create(
|
|
nameof(ImageHeight), typeof(GridLength), typeof(IllustItem), GridLength.Auto);
|
|
public static readonly BindableProperty IsFavoriteProperty = BindableProperty.Create(
|
|
nameof(IsFavorite), typeof(bool), typeof(IllustItem));
|
|
|
|
public ImageSource Image
|
|
{
|
|
get => (ImageSource)GetValue(ImageProperty);
|
|
set => SetValue(ImageProperty, value);
|
|
}
|
|
public ImageSource ProfileImage
|
|
{
|
|
get => (ImageSource)GetValue(ProfileImageProperty);
|
|
set => SetValue(ProfileImageProperty, value);
|
|
}
|
|
public GridLength ImageHeight
|
|
{
|
|
get => (GridLength)GetValue(ImageHeightProperty);
|
|
set => SetValue(ImageHeightProperty, value);
|
|
}
|
|
public bool IsFavorite
|
|
{
|
|
get => (bool)GetValue(IsFavoriteProperty);
|
|
set => SetValue(IsFavoriteProperty, value);
|
|
}
|
|
public ICommand IllustTapped { get; set; }
|
|
|
|
[JsonProperty]
|
|
public string Id { get; set; }
|
|
[JsonProperty]
|
|
public string ImageUrl { get; set; }
|
|
[JsonProperty]
|
|
public string Title { get; set; }
|
|
[JsonProperty]
|
|
public bool IsRestrict { get; set; }
|
|
[JsonProperty]
|
|
public string ProfileUrl { get; set; }
|
|
[JsonProperty]
|
|
public string UserId { get; set; }
|
|
[JsonProperty]
|
|
public string UserName { get; set; }
|
|
[JsonProperty]
|
|
public int Width { get; set; }
|
|
[JsonProperty]
|
|
public int Height { get; set; }
|
|
[JsonProperty]
|
|
public int PageCount { get; set; }
|
|
[JsonProperty]
|
|
public double ImageHeightValue
|
|
{
|
|
get => ImageHeight.IsAuto ? -1 : ImageHeight.Value;
|
|
set => ImageHeight = value > 0 ? value : GridLength.Auto;
|
|
}
|
|
|
|
public string PageCountText => $"{StyleDefinition.IconLayer} {PageCount}";
|
|
public bool IsPageVisible => PageCount > 1;
|
|
}
|
|
}
|