feature: sync online favorites
This commit is contained in:
@ -191,7 +191,7 @@ namespace Pixiview
|
|||||||
var distinct = favObj.Illusts.Where(f => !list.Any(i => i.Id == f.Id)).ToList();
|
var distinct = favObj.Illusts.Where(f => !list.Any(i => i.Id == f.Id)).ToList();
|
||||||
list.InsertRange(0, distinct);
|
list.InsertRange(0, distinct);
|
||||||
|
|
||||||
favNow.Illusts = list;
|
//favNow.Illusts = list;
|
||||||
Stores.SaveFavoritesIllusts();
|
Stores.SaveFavoritesIllusts();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Pixiview.Resources;
|
using Pixiview.Resources;
|
||||||
|
using Pixiview.UI;
|
||||||
using Pixiview.Utils;
|
using Pixiview.Utils;
|
||||||
using Xamarin.Essentials;
|
using Xamarin.Essentials;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
@ -81,9 +84,105 @@ namespace Pixiview.Illust
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await ScrollToTopAsync(scrollView);
|
await ScrollToTopAsync(scrollView);
|
||||||
|
IsLoading = true;
|
||||||
|
|
||||||
|
var offset = 16 - IndicatorMarginTop;
|
||||||
|
activityLoading.Margin = new Thickness(0, loadingOffset - offset, 0, offset);
|
||||||
|
activityLoading.Animate("margin", top =>
|
||||||
|
{
|
||||||
|
activityLoading.Margin = new Thickness(0, top, 0, offset);
|
||||||
|
},
|
||||||
|
loadingOffset - offset, 16 - offset, easing: Easing.CubicOut, finished: (v, r) =>
|
||||||
|
{
|
||||||
|
Task.Run(() =>
|
||||||
|
{
|
||||||
|
var list = Stores.LoadOnlineFavorites();
|
||||||
|
if (list != null && list.Length > 0)
|
||||||
|
{
|
||||||
|
MainThread.BeginInvokeOnMainThread(() => ConfirmNext(list));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
flag = false;
|
flag = false;
|
||||||
lastUpdated = default;
|
lastUpdated = default;
|
||||||
StartLoad(true);
|
MainThread.BeginInvokeOnMainThread(() => StartLoad(true));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void ConfirmNext(IllustItem[] list)
|
||||||
|
{
|
||||||
|
var cancel = ResourceHelper.Cancel;
|
||||||
|
var combine = ResourceHelper.FavoritesCombine;
|
||||||
|
var replace = ResourceHelper.FavoritesReplace;
|
||||||
|
var result = await DisplayActionSheet(
|
||||||
|
ResourceHelper.FavoritesOperation,
|
||||||
|
cancel,
|
||||||
|
combine,
|
||||||
|
replace);
|
||||||
|
|
||||||
|
if (result == cancel)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (result == replace)
|
||||||
|
{
|
||||||
|
Stores.GetFavoriteObject().Illusts = new FavoriteList(list);
|
||||||
|
}
|
||||||
|
else if (result == combine)
|
||||||
|
{
|
||||||
|
// combine
|
||||||
|
var favNow = Stores.GetFavoriteObject();
|
||||||
|
var nows = favNow.Illusts;
|
||||||
|
list = list.Where(f => !nows.Any(i => i.Id == f.Id)).ToArray();
|
||||||
|
for (var i = 0; i < list.Length; i++)
|
||||||
|
{
|
||||||
|
list[i].Image = StyleDefinition.DownloadBackground;
|
||||||
|
}
|
||||||
|
nows.InsertRange(0, list);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ParallelTask.Start(0, list.Length, Configs.MaxPageThreads, i =>
|
||||||
|
{
|
||||||
|
var illustItem = list[i];
|
||||||
|
var data = Stores.LoadIllustPreloadData(illustItem.Id);
|
||||||
|
if (data != null && data.illust.TryGetValue(illustItem.Id, out var illust))
|
||||||
|
{
|
||||||
|
illust.CopyToItem(illustItem);
|
||||||
|
if (data.user.TryGetValue(illust.userId, out var user))
|
||||||
|
{
|
||||||
|
illustItem.ProfileUrl = user.image;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
App.DebugError("load.favorite", $"cannot fetch preload data, {illustItem.Id}, disposed.");
|
||||||
|
return false;
|
||||||
|
}, () =>
|
||||||
|
{
|
||||||
|
Stores.SaveFavoritesIllusts();
|
||||||
|
|
||||||
|
MainThread.BeginInvokeOnMainThread(() =>
|
||||||
|
{
|
||||||
|
var offset = 16 - IndicatorMarginTop;
|
||||||
|
activityLoading.Animate("margin", top =>
|
||||||
|
{
|
||||||
|
activityLoading.Margin = new Thickness(0, top, 0, offset);
|
||||||
|
},
|
||||||
|
16 - offset, loadingOffset - offset, easing: Easing.CubicIn, finished: (v, r) =>
|
||||||
|
{
|
||||||
|
IsLoading = false;
|
||||||
|
|
||||||
|
flag = false;
|
||||||
|
lastUpdated = default;
|
||||||
|
StartLoad();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void ShareFavorites_Clicked(object sender, EventArgs e)
|
private async void ShareFavorites_Clicked(object sender, EventArgs e)
|
||||||
|
@ -514,6 +514,11 @@ namespace Pixiview.Illust
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
var item = favorites[index];
|
||||||
|
if (illust.BookmarkId == null && item.BookmarkId != null)
|
||||||
|
{
|
||||||
|
illust.BookmarkId = item.BookmarkId;
|
||||||
|
}
|
||||||
illust.IsFavorite = false;
|
illust.IsFavorite = false;
|
||||||
favorites.RemoveAt(index);
|
favorites.RemoveAt(index);
|
||||||
FavoriteIcon = fontIconNotLove;
|
FavoriteIcon = fontIconNotLove;
|
||||||
|
@ -95,13 +95,13 @@ namespace Pixiview.Utils
|
|||||||
|
|
||||||
public class ParallelTask : IDisposable
|
public class ParallelTask : IDisposable
|
||||||
{
|
{
|
||||||
public static ParallelTask Start(int from, int toExclusive, int maxCount, Predicate<int> action)
|
public static ParallelTask Start(int from, int toExclusive, int maxCount, Predicate<int> action, Action complete = null)
|
||||||
{
|
{
|
||||||
if (toExclusive <= from)
|
if (toExclusive <= from)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
var task = new ParallelTask(from, toExclusive, maxCount, action);
|
var task = new ParallelTask(from, toExclusive, maxCount, action, complete);
|
||||||
Task.Run(task.Start);
|
Task.Run(task.Start);
|
||||||
return task;
|
return task;
|
||||||
}
|
}
|
||||||
@ -114,8 +114,9 @@ namespace Pixiview.Utils
|
|||||||
private readonly int from;
|
private readonly int from;
|
||||||
private readonly int to;
|
private readonly int to;
|
||||||
private readonly Predicate<int> action;
|
private readonly Predicate<int> action;
|
||||||
|
private readonly Action complete;
|
||||||
|
|
||||||
private ParallelTask(int from, int to, int maxCount, Predicate<int> action)
|
private ParallelTask(int from, int to, int maxCount, Predicate<int> action, Action complete)
|
||||||
{
|
{
|
||||||
if (maxCount <= 0)
|
if (maxCount <= 0)
|
||||||
{
|
{
|
||||||
@ -129,6 +130,7 @@ namespace Pixiview.Utils
|
|||||||
this.from = from;
|
this.from = from;
|
||||||
this.to = to;
|
this.to = to;
|
||||||
this.action = action;
|
this.action = action;
|
||||||
|
this.complete = complete;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Start()
|
private void Start()
|
||||||
@ -182,6 +184,7 @@ namespace Pixiview.Utils
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
App.DebugPrint($"parallel task done");
|
App.DebugPrint($"parallel task done");
|
||||||
|
complete?.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
@ -17,7 +17,8 @@ namespace Pixiview.Utils
|
|||||||
HttpContent post = null,
|
HttpContent post = null,
|
||||||
Action<HttpRequestHeaders> header = null,
|
Action<HttpRequestHeaders> header = null,
|
||||||
Func<T, string> namehandler = null,
|
Func<T, string> namehandler = null,
|
||||||
Func<string, string> action = null)
|
Func<string, string> action = null,
|
||||||
|
Func<string, T> @return = null)
|
||||||
{
|
{
|
||||||
string content = null;
|
string content = null;
|
||||||
if (post == null && !force && file != null && File.Exists(file))
|
if (post == null && !force && file != null && File.Exists(file))
|
||||||
@ -89,6 +90,11 @@ namespace Pixiview.Utils
|
|||||||
{
|
{
|
||||||
content = action(content);
|
content = action(content);
|
||||||
}
|
}
|
||||||
|
if (@return != null)
|
||||||
|
{
|
||||||
|
error = null;
|
||||||
|
return @return(content);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
@ -5,6 +5,7 @@ using System.Linq;
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Pixiview.Illust;
|
using Pixiview.Illust;
|
||||||
using Xamarin.Essentials;
|
using Xamarin.Essentials;
|
||||||
@ -478,6 +479,94 @@ namespace Pixiview.Utils
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static readonly Regex regexIllust = new Regex(
|
||||||
|
@"book_id\[\]"" value=""([0-9]+)"".*data-src=""([^""]+)"".*data-id=""([0-9]+)"".*" +
|
||||||
|
@"data-tags=""([^""]+)"".*data-user-id=""([0-9]+)"".*" +
|
||||||
|
@"class=""title"" title=""([^""]+)"".*data-user_name=""([^""]+)"".*" +
|
||||||
|
@"_bookmark-icon-inline""></i>([0-9]+)</a>",
|
||||||
|
RegexOptions.Compiled);
|
||||||
|
|
||||||
|
public static IllustItem[] LoadOnlineFavorites()
|
||||||
|
{
|
||||||
|
var list = new List<IllustItem>();
|
||||||
|
int p = 1;
|
||||||
|
while (p > 0)
|
||||||
|
{
|
||||||
|
var url = Configs.UrlFavoriteList;
|
||||||
|
if (p > 1)
|
||||||
|
{
|
||||||
|
url += $"&p={p}";
|
||||||
|
}
|
||||||
|
var array = HttpUtility.LoadObject(
|
||||||
|
null,
|
||||||
|
url,
|
||||||
|
null,
|
||||||
|
out _,
|
||||||
|
action: content =>
|
||||||
|
{
|
||||||
|
// page
|
||||||
|
p = -1;
|
||||||
|
var index = content.IndexOf("<span class=\"next\"><a href=\"?rest=show&p=");
|
||||||
|
if (index > 0)
|
||||||
|
{
|
||||||
|
var page = content.Substring(index + 45, content.IndexOf('\"', index + 45) - index - 45);
|
||||||
|
if (int.TryParse(page, out var next))
|
||||||
|
{
|
||||||
|
p = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// list
|
||||||
|
index = content.IndexOf("<li class=\"image-item\">");
|
||||||
|
if (index < 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
content = content.Substring(index + 23);
|
||||||
|
index = content.IndexOf("</li></ul><div class=\"clear\"></div>");
|
||||||
|
if (index < 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return content.Substring(0, index);
|
||||||
|
},
|
||||||
|
@return: content =>
|
||||||
|
{
|
||||||
|
if (content == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var lines = content.Split("</li><li class=\"image-item\">");
|
||||||
|
var illusts = new IllustItem[lines.Length];
|
||||||
|
for (var i = 0; i < illusts.Length; i++)
|
||||||
|
{
|
||||||
|
var line = lines[i];
|
||||||
|
var m = regexIllust.Match(line);
|
||||||
|
if (m.Success)
|
||||||
|
{
|
||||||
|
illusts[i] = new IllustItem
|
||||||
|
{
|
||||||
|
IsFavorite = true,
|
||||||
|
BookmarkId = m.Groups[1].Value,
|
||||||
|
ImageUrl = m.Groups[2].Value,
|
||||||
|
Id = m.Groups[3].Value,
|
||||||
|
Tags = m.Groups[4].Value.Split(' '),
|
||||||
|
UserId = m.Groups[5].Value,
|
||||||
|
Title = m.Groups[6].Value,
|
||||||
|
UserName = m.Groups[7].Value,
|
||||||
|
RatingCount = int.Parse(m.Groups[8].Value)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return illusts;
|
||||||
|
});
|
||||||
|
if (array != null && array.Length > 0)
|
||||||
|
{
|
||||||
|
list.AddRange(array);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
public static ImageSource LoadIllustImage(string url)
|
public static ImageSource LoadIllustImage(string url)
|
||||||
{
|
{
|
||||||
return LoadImage(url, PersonalFolder, imageFolder);
|
return LoadImage(url, PersonalFolder, imageFolder);
|
||||||
@ -561,6 +650,9 @@ namespace Pixiview.Utils
|
|||||||
{
|
{
|
||||||
public bool Changed { get; private set; }
|
public bool Changed { get; private set; }
|
||||||
|
|
||||||
|
public FavoriteList() : base() { }
|
||||||
|
public FavoriteList(IEnumerable<IllustItem> collection) : base(collection) { }
|
||||||
|
|
||||||
public new void Insert(int index, IllustItem item)
|
public new void Insert(int index, IllustItem item)
|
||||||
{
|
{
|
||||||
base.Insert(index, item);
|
base.Insert(index, item);
|
||||||
@ -603,7 +695,7 @@ namespace Pixiview.Utils
|
|||||||
public const string QueryDateKey = "query_date";
|
public const string QueryDateKey = "query_date";
|
||||||
|
|
||||||
public const int MaxPageThreads = 3;
|
public const int MaxPageThreads = 3;
|
||||||
public const int MaxThreads = 6;
|
public const int MaxThreads = 4;
|
||||||
public const string Referer = "https://www.pixiv.net/";
|
public const string Referer = "https://www.pixiv.net/";
|
||||||
public const string RefererIllust = "https://www.pixiv.net/artworks/{0}";
|
public const string RefererIllust = "https://www.pixiv.net/artworks/{0}";
|
||||||
public const string RefererIllustRanking = "https://www.pixiv.net/ranking.php?{0}";
|
public const string RefererIllustRanking = "https://www.pixiv.net/ranking.php?{0}";
|
||||||
@ -700,6 +792,7 @@ namespace Pixiview.Utils
|
|||||||
public static string UrlIllustUgoira => Prefix + "ajax/illust/{0}/ugoira_meta?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 UrlIllustRecommendsInit => Prefix + "ajax/illust/{0}/recommend/init?limit=18&lang=zh";
|
||||||
public static string UrlIllustRecommendsList => Prefix + "ajax/illust/recommend/illusts?{0}lang=zh";
|
public static string UrlIllustRecommendsList => Prefix + "ajax/illust/recommend/illusts?{0}lang=zh";
|
||||||
|
public static string UrlFavoriteList => Prefix + "bookmark.php?rest=show";
|
||||||
|
|
||||||
public static string BookmarkAdd => Prefix + "ajax/illusts/bookmarks/add";
|
public static string BookmarkAdd => Prefix + "ajax/illusts/bookmarks/add";
|
||||||
public static string BookmarkRpc => Prefix + "rpc/index.php";
|
public static string BookmarkRpc => Prefix + "rpc/index.php";
|
||||||
|
Reference in New Issue
Block a user