feature: view related illusts

This commit is contained in:
Tsanie Lily 2020-05-14 22:22:16 +08:00
parent 29fd8e9667
commit 3a80c0826b
13 changed files with 278 additions and 70 deletions

View File

@ -42,7 +42,7 @@
<AndroidManagedSymbols>true</AndroidManagedSymbols>
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
<AndroidCreatePackagePerAbi>true</AndroidCreatePackagePerAbi>
<AndroidSupportedAbis>arm64-v8a</AndroidSupportedAbis>
<AndroidSupportedAbis>arm64-v8a;armeabi-v7a</AndroidSupportedAbis>
</PropertyGroup>
<ItemGroup>
<Reference Include="Mono.Android" />

View File

@ -14,8 +14,9 @@ namespace Pixiview.Illust
{
public abstract class FavoriteIllustCollectionPage : IllustCollectionPage<IllustItem[]> { }
public abstract class IllustDataCollectionPage : IllustCollectionPage<IllustData> { }
public abstract class IllustRankingDataCollectionPage : IllustCollectionPage<IllustRankingData> { }
public abstract class IllustUserDataCollectionPage : IllustCollectionPage<IllustUserData> { }
public abstract class IllustRankingDataCollectionPage : IllustScrollableCollectionPage<IllustRankingData> { }
public abstract class IllustRecommendsCollectionPage : IllustScrollableCollectionPage<IllustRecommendsData> { }
public interface IIllustCollectionPage
{
@ -499,6 +500,68 @@ namespace Pixiview.Illust
#endregion
}
public abstract class IllustScrollableCollectionPage<T> : IllustCollectionPage<T>
{
protected const int SCROLL_OFFSET = 33;
protected static readonly object sync = new object();
private double lastScrollY = double.MinValue;
private double offset;
protected bool ScrollingDown(double y)
{
return y > lastScrollY;
}
protected void SetOffset(double off)
{
offset = off;
}
protected abstract bool CheckRefresh();
protected override void DoIllustsLoaded(IllustCollection collection)
{
var now = IllustCollection;
if (now == null)
{
IllustCollection = collection;
Illusts = collection;
}
else
{
now = new IllustCollection(now.Concat(collection));
IllustCollection = now;
Illusts = now;
}
}
protected void OnScrolled(double y)
{
lastScrollY = y;
if (y > 0 && offset > 0 && y - topOffset > offset)
{
bool refresh = false;
lock (sync)
{
if (IsLoading)
{
return;
}
if (CheckRefresh())
{
refresh = true;
}
}
if (refresh)
{
App.DebugPrint("start to load next page");
StartLoad(true);
}
}
}
}
public enum ScrollDirection
{
Stop,

View File

@ -37,6 +37,10 @@ namespace Pixiview.Illust
protected override IEnumerable<IllustItem> DoGetIllustList(IllustData data)
{
if (data.body == null)
{
return null;
}
return data.body.page.follow.Select(i =>
data.body.thumbnails.illust.FirstOrDefault(l => l.illustId == i.ToString())?.ConvertToItem());
}

View File

@ -50,7 +50,7 @@
HorizontalOptions="Fill" HorizontalScrollBarVisibility="Never">
<u:FlowLayout ItemsSource="{Binding Illusts}" MaxHeightChanged="FlowLayout_MaxHeightChanged"
HorizontalOptions="Fill" Column="{Binding Columns}"
Margin="16, 16, 16, 16" RowSpacing="16" ColumnSpacing="16"
Margin="16" RowSpacing="16" ColumnSpacing="16"
ItemTemplate="{StaticResource cardView}"/>
</ScrollView>
<u:BlurryPanel x:Name="panelFilter" VerticalOptions="Start" Opacity="0"

View File

@ -13,7 +13,6 @@ namespace Pixiview.Illust
public partial class RankingPage : IllustRankingDataCollectionPage
{
private static readonly string[] segmentDates = { "daily", "weekly", "monthly", "male" };
private static readonly object sync = new object();
public static readonly BindableProperty SegmentDateProperty = BindableProperty.Create(
nameof(SegmentDate), typeof(int), typeof(RankingPage), propertyChanged: OnSegmentDatePropertyChanged);
@ -77,11 +76,9 @@ namespace Pixiview.Illust
}
public Command<string> ToolbarCommand { get; private set; }
private double lastScrollY = double.MinValue;
private bool previousEnabled;
private bool dateEnabled;
private bool nextEnabled;
private double offset;
private bool isFilterVisible;
private string lastQueryKey;
@ -111,8 +108,6 @@ namespace Pixiview.Illust
currentPage = 1;
datePicker.MinimumDate = new DateTime(2007, 9, 13);
MaximumDate = DateTime.Now;
App.DebugPrint("page loaded.");
}
protected override void OnDisappearing()
@ -128,30 +123,6 @@ namespace Pixiview.Illust
}
}
private void FlowLayout_MaxHeightChanged(object sender, HeightEventArgs e)
{
if (e.ContentHeight > 0)
{
offset = e.ContentHeight - scrollView.Bounds.Height - 33;
}
}
protected override void DoIllustsLoaded(IllustCollection collection)
{
var now = IllustCollection;
if (now == null)
{
IllustCollection = collection;
Illusts = collection;
}
else
{
now = new IllustCollection(now.Concat(collection));
IllustCollection = now;
Illusts = now;
}
}
protected override IEnumerable<IllustItem> DoGetIllustList(IllustRankingData data)
{
return data.contents.Select(i => i.ConvertToItem());
@ -162,10 +133,6 @@ namespace Pixiview.Illust
var data = Stores.LoadIllustRankingData(lastQueryKey, queryDate, currentPage, force);
if (data != null)
{
//if (data.contents.Length * data.page < data.rank_total)
//{
// nextPage = currentPage + 1;
//}
if (int.TryParse(data.next, out int next))
{
nextPage = next;
@ -330,10 +297,29 @@ namespace Pixiview.Illust
ToggleFilterPanel(!isFilterVisible);
}
private void FlowLayout_MaxHeightChanged(object sender, HeightEventArgs e)
{
if (e.ContentHeight > 0)
{
SetOffset(e.ContentHeight - scrollView.Bounds.Height - SCROLL_OFFSET);
}
}
protected override bool CheckRefresh()
{
if (nextPage == currentPage + 1)
{
currentPage = nextPage;
App.DebugPrint($"loading page {nextPage}");
return true;
}
return false;
}
private void ScrollView_Scrolled(object sender, ScrolledEventArgs e)
{
var y = e.ScrollY;
if (y > lastScrollY)
if (ScrollingDown(y))
{
// down
if (isFilterVisible)
@ -341,31 +327,7 @@ namespace Pixiview.Illust
ToggleFilterPanel(false);
}
}
lastScrollY = y;
if (y > 0 && offset > 0 && y - topOffset > offset)
{
bool refresh = false;
lock (sync)
{
if (IsLoading)
{
return;
}
App.DebugPrint("bottom arrived.");
if (nextPage == currentPage + 1)
{
currentPage = nextPage;
refresh = true;
App.DebugPrint($"loading page {nextPage}");
}
}
if (refresh)
{
App.DebugPrint($"start to load page {nextPage}");
StartLoad(true);
}
}
OnScrolled(y);
}
private void Filter_Clicked(object sender, EventArgs e)

View File

@ -183,6 +183,10 @@ namespace Pixiview.Illust
protected override IEnumerable<IllustItem> DoGetIllustList(IllustData data)
{
if (data.body == null)
{
return null;
}
return data.body.page.recommend.Select(id =>
data.body.thumbnails.illust.FirstOrDefault(l => l.illustId == id)?.ConvertToItem());
}

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<i:IllustRecommendsCollectionPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:i="clr-namespace:Pixiview.Illust"
xmlns:u="clr-namespace:Pixiview.UI"
xmlns:r="clr-namespace:Pixiview.Resources"
x:Class="Pixiview.Illust.RelatedIllustsPage"
Title="{r:Text RelatedIllusts}"
BackgroundColor="{DynamicResource WindowColor}">
<Grid>
<ScrollView x:Name="scrollView" Scrolled="ScrollView_Scrolled"
HorizontalOptions="Fill" HorizontalScrollBarVisibility="Never">
<u:FlowLayout ItemsSource="{Binding Illusts}" MaxHeightChanged="FlowLayout_MaxHeightChanged"
HorizontalOptions="Fill" Column="{Binding Columns}"
Margin="16" RowSpacing="16" ColumnSpacing="16"
ItemTemplate="{StaticResource cardView}"/>
</ScrollView>
<Frame HasShadow="False" Margin="0" Padding="20" CornerRadius="8"
IsVisible="{Binding IsLoading}"
HorizontalOptions="Center" VerticalOptions="Center"
BackgroundColor="{DynamicResource MaskColor}">
<ActivityIndicator IsRunning="True" IsVisible="True"
Color="{DynamicResource WindowColor}"/>
</Frame>
</Grid>
</i:IllustRecommendsCollectionPage>

View File

@ -0,0 +1,92 @@
using System.Collections.Generic;
using System.Linq;
using Pixiview.UI;
using Pixiview.Utils;
using Xamarin.Forms;
namespace Pixiview.Illust
{
public partial class RelatedIllustsPage : IllustRecommendsCollectionPage
{
private readonly IllustItem illustItem;
private int startIndex;
private int nextIndex;
private string[] illustIds;
public RelatedIllustsPage(IllustItem item)
{
illustItem = item;
Resources.Add("cardView", GetCardViewTemplate());
InitializeComponent();
startIndex = -1;
nextIndex = 0;
}
protected override IEnumerable<IllustItem> DoGetIllustList(IllustRecommendsData data)
{
if (data.body == null)
{
return null;
}
return data.body.illusts.Select(i => i.ConvertToItem());
}
protected override IllustRecommendsData DoLoadIllustData(bool force)
{
IllustRecommendsData data;
if (startIndex < 0)
{
// init
data = Stores.LoadIllustRecommendsInitData(illustItem.Id, force);
if (data == null || data.body == null)
{
return null;
}
illustIds = data.body.nextIds;
}
else
{
if (illustIds == null || startIndex >= illustIds.Length)
{
return null;
}
var ids = illustIds.Skip(startIndex).Take(18).ToArray();
nextIndex = startIndex + ids.Length;
if (ids.Length == 0 || nextIndex >= illustIds.Length)
{
// done
App.DebugPrint($"download completed: {startIndex}");
return null;
}
data = Stores.LoadIllustRecommendsListData(illustItem.Id, ids);
}
return data;
}
private void FlowLayout_MaxHeightChanged(object sender, HeightEventArgs e)
{
if (e.ContentHeight > 0)
{
SetOffset(e.ContentHeight - scrollView.Bounds.Height - SCROLL_OFFSET);
}
}
protected override bool CheckRefresh()
{
if (nextIndex > startIndex)
{
startIndex = nextIndex;
return true;
}
return false;
}
private void ScrollView_Scrolled(object sender, ScrolledEventArgs e)
{
var y = e.ScrollY;
OnScrolled(y);
}
}
}

View File

@ -30,6 +30,10 @@ namespace Pixiview.Illust
protected override IEnumerable<IllustItem> DoGetIllustList(IllustUserData data)
{
if (data.body == null)
{
return null;
}
return data.body.works.Select(i => i.Value?.ConvertToItem());
}

View File

@ -509,9 +509,11 @@ namespace Pixiview.Illust
{
extras.Add(share);
}
var saveOriginal = ResourceHelper.SaveOriginal;
var userDetail = ResourceHelper.UserDetail;
extras.Add(userDetail);
var related = ResourceHelper.RelatedIllusts;
extras.Add(related);
var saveOriginal = ResourceHelper.SaveOriginal;
var illustItem = IllustItem;
var result = await DisplayActionSheet(
@ -536,6 +538,11 @@ namespace Pixiview.Illust
var page = new UserIllustPage(illustItem);
await Navigation.PushAsync(page);
}
else if (result == related)
{
var page = new RelatedIllustsPage(illustItem);
await Navigation.PushAsync(page);
}
}
private async void SaveOriginalImage(IllustDetailItem item)

View File

@ -42,6 +42,10 @@
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Resources\Languages\zh-CN.xml" />
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Illust\RelatedIllustsPage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)App.cs" />
@ -93,6 +97,9 @@
<Compile Include="$(MSBuildThisFileDirectory)UI\BlurryPanel.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Utils\IllustLegacy.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Utils\HttpUtility.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Illust\RelatedIllustsPage.xaml.cs">
<DependentUpon>RelatedIllustsPage.xaml</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<Folder Include="$(MSBuildThisFileDirectory)Illust\" />

View File

@ -208,16 +208,16 @@ namespace Pixiview.Utils
public string userId;
public IllustTranslate translation;
public string userName;
public class IllustTranslate
{
public string en;
}
}
}
}
}
public class IllustTranslate
{
public string en;
}
public class IllustPageData : IllustResponse<IllustPageBody[]> { }
public class IllustPageBody
{
@ -234,6 +234,13 @@ namespace Pixiview.Utils
}
}
public class IllustRecommendsData : IllustResponse<IllustRecommendsBody> { }
public class IllustRecommendsBody
{
public Illust[] illusts;
public string[] nextIds;
}
public class IllustUserListData : IllustResponse<IllustUserListBody> { }
public class IllustUserListBody
{

View File

@ -30,6 +30,7 @@ namespace Pixiview.Utils
private const string preloadsFolder = "preloads";
private const string thumbFolder = "img-thumb";
private const string userFolder = "user-profile";
private const string recommendsFolder = "recommends";
private static readonly object sync = new object();
@ -212,7 +213,7 @@ namespace Pixiview.Utils
public static IllustRankingData LoadIllustRankingData(string mode, string date, int page, bool force = false)
{
var file = Path.Combine(CacheFolder, $"{mode}_{date}_{page}.json");
var file = Path.Combine(CacheFolder, mode, $"{date}_{page}.json");
string query = $"mode={mode}";
if (mode != "male" && mode != "male_r18")
{
@ -234,7 +235,7 @@ namespace Pixiview.Utils
referer,
namehandler: rst =>
{
return Path.Combine(CacheFolder, $"{mode}_{rst.date}_{page}.json");
return Path.Combine(CacheFolder, mode, $"{rst.date}_{page}.json");
},
header: headers =>
{
@ -248,6 +249,35 @@ namespace Pixiview.Utils
return result;
}
public static IllustRecommendsData LoadIllustRecommendsInitData(string id, bool force = false)
{
var file = Path.Combine(CacheFolder, recommendsFolder, $"{id}.json");
var result = HttpUtility.LoadObject<IllustRecommendsData>(
file,
string.Format(Configs.UrlIllustRecommendsInit, id),
string.Format(Configs.RefererIllust, id),
force: force);
if (result == null || result.error)
{
App.DebugPrint($"error when load recommends init data: {result?.message}, force({force})");
}
return result;
}
public static IllustRecommendsData LoadIllustRecommendsListData(string id, string[] ids)
{
var ps = string.Concat(ids.Select(i => $"illust_ids%5B%5D={i}&"));
var result = HttpUtility.LoadObject<IllustRecommendsData>(
null,
string.Format(Configs.UrlIllustRecommendsList, ps),
string.Format(Configs.RefererIllust, id));
if (result == null || result.error)
{
App.DebugPrint($"error when load recommends list data: {result?.message}");
}
return result;
}
public static IllustPreloadBody LoadIllustPreloadData(string id, bool force = false)
{
var file = Path.Combine(CacheFolder, preloadsFolder, $"{id}.json");
@ -431,6 +461,8 @@ namespace Pixiview.Utils
public static string UrlIllustUserArtworks => Prefix + "ajax/user/{0}/profile/illusts?{1}work_category=illustManga&is_first_page={2}&lang=zh";
public static string UrlIllustPage => Prefix + "ajax/illust/{0}/pages?lang=zh";
public static string UrlIllustUgoira => Prefix + "ajax/illust/{0}/ugoira_meta?lang=zh";
public static string UrlIllustRecommendsInit => Prefix + "ajax/illust/{0}/recommend/init?limit=18&lang=zh";
public static string UrlIllustRecommendsList => Prefix + "ajax/illust/recommend/illusts?{0}lang=zh";
public const string UserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36";
public const string AcceptImage = "image/png,image/svg+xml,image/*;q=0.8,video/*;q=0.8,*/*;q=0.5";
public const string AcceptJson = "application/json";