* Pixiview/Pixiview.projitems: * Pixiview/Utils/IllustData.cs: * Pixiview/Utils/HttpUtility.cs: * Pixiview/Utils/IllustLegacy.cs: * Pixiview/Illust/ViewIllustPage.xaml: * Pixiview/Illust/ViewIllustPage.xaml.cs: feature: view animate * Pixiview/Illust/FavoritesPage.xaml.cs: lazy load favorites page * Pixiview/UI/CardView.cs: * Pixiview/UI/AdaptedPage.cs: * Pixiview/Utils/Converters.cs: * Pixiview/UI/StyleDefinition.cs: * Pixiview/UI/Theme/ThemeBase.cs: * Pixiview/Illust/RankingPage.xaml: * Pixiview/Illust/RankingPage.xaml.cs: * Pixiview/Resources/Languages/zh-CN.xml: * Pixiview/Illust/IllustCollectionPage.cs: * Pixiview.iOS/Renderers/SegmentedControlRenderer.cs: feature: filter ranking * Pixiview.iOS/Info.plist: * Pixiview.iOS/Pixiview.iOS.csproj: * Pixiview.iOS.OpenExtension/Info.plist: * Pixiview.Android/Pixiview.Android.csproj: * Pixiview.Android/Properties/AndroidManifest.xml: version update
438 lines
14 KiB
C#
438 lines
14 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using System.Windows.Input;
|
|
using Pixiview.Resources;
|
|
using Pixiview.UI;
|
|
using Pixiview.UI.Theme;
|
|
using Pixiview.Utils;
|
|
using Xamarin.Essentials;
|
|
using Xamarin.Forms;
|
|
|
|
namespace Pixiview.Illust
|
|
{
|
|
[QueryProperty("IllustId", "id")]
|
|
public partial class ViewIllustPage : AdaptedPage
|
|
{
|
|
public static readonly BindableProperty FavoriteIconProperty = BindableProperty.Create(
|
|
nameof(FavoriteIcon), typeof(ImageSource), typeof(ViewIllustPage));
|
|
|
|
public static readonly BindableProperty IllustsProperty = BindableProperty.Create(
|
|
nameof(Illusts), typeof(IllustDetailItem[]), typeof(ViewIllustPage));
|
|
public static readonly BindableProperty PagePositionTextProperty = BindableProperty.Create(
|
|
nameof(PagePositionText), typeof(string), typeof(ViewIllustPage));
|
|
public static readonly BindableProperty IsPageVisibleProperty = BindableProperty.Create(
|
|
nameof(IsPageVisible), typeof(bool), typeof(ViewIllustPage));
|
|
public static readonly BindableProperty IllustItemProperty = BindableProperty.Create(
|
|
nameof(IllustItem), typeof(IllustItem), typeof(ViewIllustPage));
|
|
|
|
public ImageSource FavoriteIcon
|
|
{
|
|
get => (ImageSource)GetValue(FavoriteIconProperty);
|
|
set => SetValue(FavoriteIconProperty, value);
|
|
}
|
|
|
|
public IllustDetailItem[] Illusts
|
|
{
|
|
get => (IllustDetailItem[])GetValue(IllustsProperty);
|
|
set => SetValue(IllustsProperty, value);
|
|
}
|
|
public string PagePositionText
|
|
{
|
|
get => (string)GetValue(PagePositionTextProperty);
|
|
set => SetValue(PagePositionTextProperty, value);
|
|
}
|
|
public bool IsPageVisible
|
|
{
|
|
get => (bool)GetValue(IsPageVisibleProperty);
|
|
set => SetValue(IsPageVisibleProperty, value);
|
|
}
|
|
public IllustItem IllustItem
|
|
{
|
|
get => (IllustItem)GetValue(IllustItemProperty);
|
|
set => SetValue(IllustItemProperty, value);
|
|
}
|
|
|
|
public int CurrentPage { get; private set; }
|
|
|
|
private readonly bool saveFavorites;
|
|
private readonly ICommand longPressed;
|
|
private readonly ImageSource fontIconLove;
|
|
private readonly ImageSource fontIconNotLove;
|
|
private IllustUgoiraData ugoiraData;
|
|
private Ugoira ugoira;
|
|
|
|
public ViewIllustPage(IllustItem illust, bool save)
|
|
{
|
|
IllustItem = illust;
|
|
saveFavorites = save;
|
|
longPressed = new Command<IllustDetailItem>(Illust_LongPressed);
|
|
BindingContext = this;
|
|
|
|
fontIconLove = (ImageSource)Application.Current.Resources[ThemeBase.FontIconLove];
|
|
fontIconNotLove = (ImageSource)Application.Current.Resources[ThemeBase.FontIconNotLove];
|
|
var favorites = Stores.Favorites;
|
|
FavoriteIcon = favorites.Any(i => i.Id == illust.Id)
|
|
? fontIconLove
|
|
: fontIconNotLove;
|
|
|
|
InitializeComponent();
|
|
|
|
if (illust != null)
|
|
{
|
|
LoadIllust(illust);
|
|
}
|
|
}
|
|
|
|
public override void OnLoad()
|
|
{
|
|
OnOrientationChanged(CurrentOrientation);
|
|
}
|
|
|
|
protected override void OnAppearing()
|
|
{
|
|
base.OnAppearing();
|
|
Screen.SetHomeIndicatorAutoHidden(Shell.Current, true);
|
|
}
|
|
|
|
protected override void OnDisappearing()
|
|
{
|
|
base.OnDisappearing();
|
|
|
|
if (ugoira != null)
|
|
{
|
|
ugoira.TogglePlay(false);
|
|
IllustItem.IsPlaying = false;
|
|
}
|
|
|
|
if (saveFavorites)
|
|
{
|
|
Stores.SaveFavoritesIllusts();
|
|
}
|
|
Screen.SetHomeIndicatorAutoHidden(Shell.Current, false);
|
|
}
|
|
|
|
private void LoadIllust(IllustItem illust)
|
|
{
|
|
if (illust == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var items = new IllustDetailItem[illust.PageCount];
|
|
if (items.Length > 1)
|
|
{
|
|
IsPageVisible = true;
|
|
PagePositionText = $"1/{items.Length}";
|
|
}
|
|
else
|
|
{
|
|
IsPageVisible = false;
|
|
}
|
|
|
|
for (var i = 0; i < items.Length; i++)
|
|
{
|
|
items[i] = new IllustDetailItem
|
|
{
|
|
Id = illust.Id,
|
|
LongPressed = longPressed
|
|
};
|
|
if (i == 0)
|
|
{
|
|
items[i].Loading = true;
|
|
items[i].Image = illust.Image;
|
|
}
|
|
}
|
|
|
|
Illusts = items;
|
|
Task.Run(DoLoadImages);
|
|
}
|
|
|
|
private void DoLoadImages()
|
|
{
|
|
var illustItem = IllustItem;
|
|
var pages = Stores.LoadIllustPageData(illustItem.Id);
|
|
if (pages == null)
|
|
{
|
|
App.DebugError("illustPage.load", $"failed to load illust page data, id: {illustItem.Id}");
|
|
return;
|
|
}
|
|
var items = Illusts;
|
|
if (pages.body.Length > items.Length)
|
|
{
|
|
App.DebugPrint($"local page count ({items.Length}) is not equals the remote one ({pages.body.Length})");
|
|
var tmp = new IllustDetailItem[pages.body.Length];
|
|
items.CopyTo(items, 0);
|
|
for (var i = items.Length; i < tmp.Length; i++)
|
|
{
|
|
tmp[i] = new IllustDetailItem
|
|
{
|
|
Id = illustItem.Id,
|
|
LongPressed = longPressed
|
|
};
|
|
}
|
|
Illusts = items = tmp;
|
|
}
|
|
|
|
for (var i = 0; i < items.Length; i++)
|
|
{
|
|
var item = items[i];
|
|
var p = pages.body[i];
|
|
item.PreviewUrl = p.urls.regular;
|
|
item.OriginalUrl = p.urls.original;
|
|
|
|
if (i == 0 && illustItem.ImageUrl == null)
|
|
{
|
|
// maybe open from a link
|
|
var preload = Stores.LoadIllustPreloadData(illustItem.Id);
|
|
if (preload != null && preload.illust.TryGetValue(illustItem.Id, out var illust))
|
|
{
|
|
illust.CopyToItem(illustItem);
|
|
if (preload.user.TryGetValue(illust.userId, out var user))
|
|
{
|
|
illustItem.ProfileUrl = user.image;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
DoLoadImage(0, true);
|
|
var image = items[0].Image;
|
|
if (image != null)
|
|
{
|
|
illustItem.Image = image;
|
|
}
|
|
if (items.Length > 1)
|
|
{
|
|
DoLoadImage(1);
|
|
}
|
|
else if (illustItem.IllustType == IllustType.Anime)
|
|
{
|
|
// anime
|
|
ugoiraData = Stores.LoadIllustUgoiraData(illustItem.Id);
|
|
}
|
|
}
|
|
|
|
private void DoLoadImage(int index, bool force = false)
|
|
{
|
|
var items = Illusts;
|
|
if (index < 0 || index >= items.Length)
|
|
{
|
|
App.DebugPrint($"invalid index: {index}");
|
|
return;
|
|
}
|
|
|
|
var item = items[index];
|
|
if (!force)
|
|
{
|
|
if (item.Loading || (index > 0 && item.Image != null))
|
|
{
|
|
App.DebugPrint($"skipped, loading or already loaded, index: {index}, loading: {item.Loading}");
|
|
return;
|
|
}
|
|
}
|
|
item.Loading = true;
|
|
var image = Stores.LoadPreviewImage(item.PreviewUrl);
|
|
if (image != null)
|
|
{
|
|
item.Image = image;
|
|
}
|
|
item.Loading = false;
|
|
}
|
|
|
|
private void CarouselView_PositionChanged(object sender, PositionChangedEventArgs e)
|
|
{
|
|
var index = e.CurrentPosition;
|
|
CurrentPage = index;
|
|
var items = Illusts;
|
|
var length = items.Length;
|
|
PagePositionText = $"{index + 1}/{length}";
|
|
|
|
var item = items[index];
|
|
if (!item.Loading && item.Image == null)
|
|
{
|
|
Task.Run(() => DoLoadImage(index));
|
|
}
|
|
if (index < length - 1)
|
|
{
|
|
item = items[index + 1];
|
|
if (!item.Loading && item.Image == null)
|
|
{
|
|
Task.Run(() => DoLoadImage(index + 1));
|
|
}
|
|
}
|
|
}
|
|
|
|
private void Favorite_Clicked(object sender, EventArgs e)
|
|
{
|
|
var favorites = Stores.Favorites;
|
|
var illust = IllustItem;
|
|
var index = favorites.FindIndex(i => i.Id == illust.Id);
|
|
if (index < 0)
|
|
{
|
|
illust.IsFavorite = true;
|
|
favorites.Insert(0, illust);
|
|
FavoriteIcon = fontIconLove;
|
|
}
|
|
else
|
|
{
|
|
illust.IsFavorite = false;
|
|
favorites.RemoveAt(index);
|
|
FavoriteIcon = fontIconNotLove;
|
|
}
|
|
}
|
|
|
|
private void Image_Tapped(object sender, EventArgs e)
|
|
{
|
|
if (ugoiraData == null)
|
|
{
|
|
return;
|
|
}
|
|
var illustItem = IllustItem;
|
|
|
|
if (ugoira != null)
|
|
{
|
|
var playing = !ugoira.IsPlaying;
|
|
ugoira.TogglePlay(playing);
|
|
illustItem.IsPlaying = playing;
|
|
}
|
|
else if (((Image)sender).BindingContext is IllustDetailItem item)
|
|
{
|
|
if (illustItem.IsPlaying ||
|
|
illustItem.IllustType != IllustType.Anime)
|
|
{
|
|
return;
|
|
}
|
|
|
|
ugoira = new Ugoira(ugoiraData, item);
|
|
ugoira.FrameChanged += OnUgoiraFrameChanged;
|
|
illustItem.IsPlaying = true;
|
|
ugoira.TogglePlay(true);
|
|
}
|
|
}
|
|
|
|
private void OnUgoiraFrameChanged(object sender, UgoiraEventArgs e)
|
|
{
|
|
e.DetailItem.Image = e.Image;
|
|
}
|
|
|
|
private async void Illust_LongPressed(IllustDetailItem item)
|
|
{
|
|
List<string> extras = new List<string>();
|
|
var share = ResourceHelper.Share;
|
|
var preview = Stores.GetPreviewImagePath(item.PreviewUrl);
|
|
if (preview != null)
|
|
{
|
|
extras.Add(share);
|
|
}
|
|
var saveOriginal = ResourceHelper.SaveOriginal;
|
|
var userDetail = ResourceHelper.UserDetail;
|
|
extras.Add(userDetail);
|
|
|
|
var illustItem = IllustItem;
|
|
var result = await DisplayActionSheet(
|
|
illustItem.Title,
|
|
ResourceHelper.Cancel,
|
|
saveOriginal,
|
|
extras.ToArray());
|
|
if (result == saveOriginal)
|
|
{
|
|
SaveOriginalImage(item);
|
|
}
|
|
else if (result == share)
|
|
{
|
|
await Share.RequestAsync(new ShareFileRequest
|
|
{
|
|
Title = illustItem.Title,
|
|
File = new ShareFile(preview)
|
|
});
|
|
}
|
|
else if (result == userDetail)
|
|
{
|
|
var page = new UserIllustPage(illustItem);
|
|
await Navigation.PushAsync(page);
|
|
}
|
|
}
|
|
|
|
private async void SaveOriginalImage(IllustDetailItem item)
|
|
{
|
|
if (Stores.CheckIllustImage(item.OriginalUrl))
|
|
{
|
|
var flag = await DisplayAlert(ResourceHelper.Operation,
|
|
ResourceHelper.AlreadySavedQuestion,
|
|
ResourceHelper.Yes,
|
|
ResourceHelper.No);
|
|
if (!flag)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
var status = await Permissions.CheckStatusAsync<Permissions.Photos>();
|
|
if (status != PermissionStatus.Granted)
|
|
{
|
|
status = await Permissions.RequestAsync<Permissions.Photos>();
|
|
if (status != PermissionStatus.Granted)
|
|
{
|
|
App.DebugPrint("access denied to gallery.");
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (item == null || item.Downloading)
|
|
{
|
|
return;
|
|
}
|
|
item.Downloading = true;
|
|
_ = Task.Run(() => DoLoadOriginalImage(item));
|
|
}
|
|
|
|
private void DoLoadOriginalImage(IllustDetailItem item)
|
|
{
|
|
var image = Stores.LoadIllustImage(item.OriginalUrl);
|
|
if (image != null)
|
|
{
|
|
MainThread.BeginInvokeOnMainThread(async () =>
|
|
{
|
|
var result = await FileStore.SaveImageToGalleryAsync(image, item.OriginalUrl);
|
|
|
|
string message = result ?? ResourceHelper.SaveSuccess;
|
|
await DisplayAlert(ResourceHelper.Title, message, ResourceHelper.Ok);
|
|
});
|
|
}
|
|
item.Downloading = false;
|
|
}
|
|
}
|
|
|
|
public class IllustDetailItem : BindableObject
|
|
{
|
|
public static readonly BindableProperty ImageProperty = BindableProperty.Create(
|
|
nameof(Image), typeof(ImageSource), typeof(IllustDetailItem));
|
|
public static readonly BindableProperty LoadingProperty = BindableProperty.Create(
|
|
nameof(Loading), typeof(bool), typeof(IllustDetailItem));
|
|
public static readonly BindableProperty DownloadingProperty = BindableProperty.Create(
|
|
nameof(Downloading), typeof(bool), typeof(IllustDetailItem));
|
|
|
|
public ImageSource Image
|
|
{
|
|
get => (ImageSource)GetValue(ImageProperty);
|
|
set => SetValue(ImageProperty, value);
|
|
}
|
|
public bool Loading
|
|
{
|
|
get => (bool)GetValue(LoadingProperty);
|
|
set => SetValue(LoadingProperty, value);
|
|
}
|
|
public bool Downloading
|
|
{
|
|
get => (bool)GetValue(DownloadingProperty);
|
|
set => SetValue(DownloadingProperty, value);
|
|
}
|
|
public string Id { get; set; }
|
|
public ICommand LongPressed { get; set; }
|
|
public string PreviewUrl { get; set; }
|
|
public string OriginalUrl { get; set; }
|
|
}
|
|
}
|