feature: sync favorites

feature: remote bookmark icon
fix: sometimes blank title
This commit is contained in:
2020-05-19 11:40:17 +08:00
parent 01d782660b
commit 32058fe210
14 changed files with 154 additions and 84 deletions

View File

@ -176,12 +176,37 @@ namespace Pixiview.Illust
else if (result == combine) else if (result == combine)
{ {
// combine // combine
var favNow = Stores.GetFavoriteObject(); var nows = Stores.GetFavoriteObject().Illusts;
var nows = favNow.Illusts; // sync bookmarks from remote
for (var i = nows.Count - 1; i >= 0; i--)
{
var b = nows[i];
var bookmarkId = b.BookmarkId;
if (!string.IsNullOrEmpty(bookmarkId))
{
var bookmark = list.FirstOrDefault(f => f.Id == b.Id);
if (bookmark == null)
{
// not exists in remote any more
App.DebugPrint($"remove bookmark ({bookmarkId}) - {b.Id}: {b.Title}");
nows.RemoveAt(i);
}
else if (bookmarkId != bookmark.BookmarkId)
{
// update bookmark id
App.DebugPrint($"change bookmark ({bookmarkId}) to ({bookmark.BookmarkId}) - {b.Id}: {b.Title}");
b.BookmarkId = bookmark.BookmarkId;
}
}
}
// add bookmarks that exists in remote only
list = list.Where(f => !nows.Any(i => i.Id == f.Id)).ToArray(); list = list.Where(f => !nows.Any(i => i.Id == f.Id)).ToArray();
for (var i = 0; i < list.Length; i++) for (var i = 0; i < list.Length; i++)
{ {
list[i].Image = StyleDefinition.DownloadBackground; var item = list[i];
App.DebugPrint($"add bookmark ({item.BookmarkId}) - {item.Id}: {item.Title}");
item.Image = StyleDefinition.DownloadBackground;
} }
nows.InsertRange(0, list); nows.InsertRange(0, list);
} }

View File

@ -174,7 +174,7 @@ namespace Pixiview.Illust
protected abstract IEnumerable<IllustItem> DoGetIllustList(T data); protected abstract IEnumerable<IllustItem> DoGetIllustList(T data);
protected virtual void OnIllustImageTapped(IllustItem illust) protected virtual void OnIllustImageTapped(IllustItem illust)
{ {
var page = new ViewIllustPage(illust, IsFavoriteVisible); var page = new ViewIllustPage(illust, true);
Navigation.PushAsync(page); Navigation.PushAsync(page);
} }
protected virtual IllustCollection GetIllustsLoadedCollection(IllustCollection collection, bool bottom) protected virtual IllustCollection GetIllustsLoadedCollection(IllustCollection collection, bool bottom)
@ -388,9 +388,10 @@ namespace Pixiview.Illust
VerticalOptions = LayoutOptions.Center, VerticalOptions = LayoutOptions.Center,
FontSize = StyleDefinition.FontSizeSmall, FontSize = StyleDefinition.FontSizeSmall,
TextColor = StyleDefinition.ColorRedBackground, TextColor = StyleDefinition.ColorRedBackground,
Text = StyleDefinition.IconLove IsVisible = false
} }
.Binding(IsVisibleProperty, IsFavoriteVisible ? nameof(IllustItem.IsFavorite) : null) .Binding(Label.TextProperty, ".", converter: new FavoriteIconConverter(IsFavoriteVisible))
.Binding(IsVisibleProperty, ".", converter: new FavoriteVisibleConverter())
.DynamicResource(Label.FontFamilyProperty, ThemeBase.IconSolidFontFamily); .DynamicResource(Label.FontFamilyProperty, ThemeBase.IconSolidFontFamily);
#endregion #endregion
@ -488,7 +489,7 @@ namespace Pixiview.Illust
HeightRequest = 30, HeightRequest = 30,
Aspect = Aspect.AspectFill Aspect = Aspect.AspectFill
} }
.Binding(Image.SourceProperty, nameof(IllustItem.ProfileImage)), .Binding(Image.SourceProperty, nameof(IIllustItem.ProfileImage)),
// user name // user name
new Label new Label
@ -498,7 +499,7 @@ namespace Pixiview.Illust
LineBreakMode = LineBreakMode.TailTruncation, LineBreakMode = LineBreakMode.TailTruncation,
FontSize = StyleDefinition.FontSizeMicro FontSize = StyleDefinition.FontSizeMicro
} }
.Binding(Label.TextProperty, nameof(IllustItem.UserName)) .Binding(Label.TextProperty, nameof(IIllustItem.UserName))
.DynamicResource(Label.TextColorProperty, ThemeBase.SubTextColor) .DynamicResource(Label.TextColorProperty, ThemeBase.SubTextColor)
.GridColumn(1), .GridColumn(1),
@ -558,17 +559,14 @@ namespace Pixiview.Illust
var data = DoGetIllustList(illustData).Where(i => i != null && (r18 || !i.IsRestrict)); var data = DoGetIllustList(illustData).Where(i => i != null && (r18 || !i.IsRestrict));
var collection = new IllustCollection(data); var collection = new IllustCollection(data);
if (IsFavoriteVisible) var favorites = Stores.Favorites;
foreach (var item in collection)
{ {
var favorites = Stores.Favorites; if (item.Image == null)
foreach (var item in collection)
{ {
if (item.Image == null) item.Image = StyleDefinition.DownloadBackground;
{
item.Image = StyleDefinition.DownloadBackground;
}
item.IsFavorite = favorites.Any(i => i.Id == item.Id);
} }
item.IsFavorite = IsFavoriteVisible && favorites.Any(i => i.Id == item.Id);
} }
DoIllustsLoaded(collection, bottom); DoIllustsLoaded(collection, bottom);
@ -767,15 +765,17 @@ namespace Pixiview.Illust
Anime = 2 Anime = 2
} }
public interface IIllustUser public interface IIllustItem
{ {
string UserId { get; } string UserId { get; }
string UserName { get; } string UserName { get; }
ImageSource ProfileImage { get; } ImageSource ProfileImage { get; }
bool IsFavorite { get; }
string BookmarkId { get; }
} }
[JsonObject(MemberSerialization.OptIn)] [JsonObject(MemberSerialization.OptIn)]
public class IllustItem : BindableObject, IIllustUser public class IllustItem : BindableObject, IIllustItem
{ {
private static IllustItem empty; private static IllustItem empty;
public static IllustItem Empty public static IllustItem Empty
@ -798,10 +798,13 @@ namespace Pixiview.Illust
nameof(ImageHeight), typeof(GridLength), typeof(IllustItem), GridLength.Auto); nameof(ImageHeight), typeof(GridLength), typeof(IllustItem), GridLength.Auto);
public static readonly BindableProperty IsFavoriteProperty = BindableProperty.Create( public static readonly BindableProperty IsFavoriteProperty = BindableProperty.Create(
nameof(IsFavorite), typeof(bool), typeof(IllustItem)); nameof(IsFavorite), typeof(bool), typeof(IllustItem));
public static readonly BindableProperty BookmarkIdProperty = BindableProperty.Create(
nameof(BookmarkId), typeof(string), typeof(IllustItem));
[JsonProperty] [JsonProperty]
public string Title { get; set; } public string Title { get; set; }
public string RankTitle { get; set; } public int Rank { get; set; }
public string RankTitle => Rank > 0 ? $"#{Rank} {Title}" : Title;
public ImageSource Image public ImageSource Image
{ {
get => (ImageSource)GetValue(ImageProperty); get => (ImageSource)GetValue(ImageProperty);
@ -827,7 +830,11 @@ namespace Pixiview.Illust
[JsonProperty] [JsonProperty]
public string Id { get; set; } public string Id { get; set; }
[JsonProperty] [JsonProperty]
public string BookmarkId { get; set; } public string BookmarkId
{
get => (string)GetValue(BookmarkIdProperty);
set => SetValue(BookmarkIdProperty, value);
}
[JsonProperty] [JsonProperty]
public DateTime FavoriteDateUtc { get; set; } public DateTime FavoriteDateUtc { get; set; }
[JsonProperty] [JsonProperty]

View File

@ -347,7 +347,7 @@ namespace Pixiview.Illust
} }
} }
public class IllustUserItem : BindableObject, IIllustUser public class IllustUserItem : BindableObject, IIllustItem
{ {
//public static readonly BindableProperty Image1Property = BindableProperty.Create( //public static readonly BindableProperty Image1Property = BindableProperty.Create(
// nameof(Image1), typeof(ImageSource), typeof(IllustUserItem)); // nameof(Image1), typeof(ImageSource), typeof(IllustUserItem));
@ -371,5 +371,8 @@ namespace Pixiview.Illust
public IllustItem Image2Item { get; set; } public IllustItem Image2Item { get; set; }
public IllustItem Image3Item { get; set; } public IllustItem Image3Item { get; set; }
public string ProfileUrl { get; set; } public string ProfileUrl { get; set; }
public bool IsFavorite { get; }
public string BookmarkId { get; }
} }
} }

View File

@ -20,13 +20,13 @@ namespace Pixiview.Illust
set => SetValue(UserIconProperty, value); set => SetValue(UserIconProperty, value);
} }
public IIllustUser UserItem { get; } public IIllustItem UserItem { get; }
private int startIndex; private int startIndex;
private int nextIndex; private int nextIndex;
private string[] illustIds; private string[] illustIds;
public UserIllustPage(IIllustUser item) public UserIllustPage(IIllustItem item)
{ {
UserItem = item; UserItem = item;
UserIcon = item.ProfileImage; UserIcon = item.ProfileImage;

View File

@ -528,6 +528,10 @@ namespace Pixiview.Illust
{ {
return; return;
} }
if (!add && string.IsNullOrEmpty(illust.BookmarkId))
{
return;
}
if (Configs.SyncFavType == SyncType.Prompt) if (Configs.SyncFavType == SyncType.Prompt)
{ {
var ok = await DisplayAlert( var ok = await DisplayAlert(
@ -551,11 +555,7 @@ namespace Pixiview.Illust
} }
else else
{ {
var bookmarkId = illust.BookmarkId; _ = Task.Run(() => Stores.DeleteBookmark(illust.BookmarkId));
if (!string.IsNullOrEmpty(bookmarkId))
{
_ = Task.Run(() => Stores.DeleteBookmark(bookmarkId));
}
} }
} }

View File

@ -44,7 +44,9 @@ namespace Pixiview.UI
protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint) protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
{ {
if (BindingContext is IllustItem illust) if (BindingContext is IllustItem illust &&
illust.Width > 0 &&
illust.ImageHeight.IsAuto)
{ {
illust.ImageHeight = widthConstraint * illust.Height / illust.Width; illust.ImageHeight = widthConstraint * illust.Height / illust.Width;
} }

View File

@ -245,7 +245,7 @@ namespace Pixiview.UI
} }
freezed = false; freezed = false;
UpdateChildrenLayout(); UpdateChildrenLayout();
InvalidateLayout(); //InvalidateLayout();
} }
} }

View File

@ -40,6 +40,7 @@ namespace Pixiview.UI
public const string IconLayer = "\uf302"; public const string IconLayer = "\uf302";
public const string IconRefresh = "\uf2f9"; public const string IconRefresh = "\uf2f9";
public const string IconLove = "\uf004"; public const string IconLove = "\uf004";
public const string IconCircleLove = "\uf4c7";
public const string IconOption = "\uf013"; public const string IconOption = "\uf013";
public const string IconFavorite = "\uf02e"; public const string IconFavorite = "\uf02e";
public const string IconShare = "\uf1e0"; public const string IconShare = "\uf1e0";

View File

@ -1,33 +1,18 @@
using System; using System;
using System.Globalization; using System.Globalization;
using Pixiview.Illust;
using Pixiview.UI; using Pixiview.UI;
using Xamarin.Forms; using Xamarin.Forms;
namespace Pixiview.Utils namespace Pixiview.Utils
{ {
public class PageConverter : IValueConverter public class FavoriteVisibleConverter : IValueConverter
{ {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{ {
return StyleDefinition.IconLayer + value; if (value is IIllustItem item)
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class OffsetConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is Thickness thickness && parameter != null)
{ {
if (double.TryParse(parameter.ToString(), out var offset)) return item.IsFavorite || !string.IsNullOrEmpty(item.BookmarkId);
{
return new Thickness(thickness.Left, thickness.Top + offset, thickness.Right, thickness.Bottom);
}
} }
return value; return value;
} }
@ -37,4 +22,31 @@ namespace Pixiview.Utils
throw new NotImplementedException(); throw new NotImplementedException();
} }
} }
public class FavoriteIconConverter : IValueConverter
{
private readonly bool isFavorite;
public FavoriteIconConverter(bool favorite)
{
isFavorite = favorite;
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is IIllustItem item)
{
var love = isFavorite ? StyleDefinition.IconLove : string.Empty;
return !string.IsNullOrEmpty(item.BookmarkId) ?
StyleDefinition.IconCircleLove :
love;
}
return string.Empty;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
} }

View File

@ -7,7 +7,8 @@ namespace Pixiview.Utils
{ {
public static class Extensions public static class Extensions
{ {
public static T Binding<T>(this T view, BindableProperty property, string name, BindingMode mode = BindingMode.Default) where T : BindableObject public static T Binding<T>(this T view, BindableProperty property, string name,
BindingMode mode = BindingMode.Default, IValueConverter converter = null) where T : BindableObject
{ {
if (name == null) if (name == null)
{ {
@ -15,7 +16,7 @@ namespace Pixiview.Utils
} }
else else
{ {
view.SetBinding(property, name, mode); view.SetBinding(property, name, mode, converter);
} }
return view; return view;
} }

View File

@ -67,7 +67,6 @@ namespace Pixiview.Utils
Id = illustId, Id = illustId,
BookmarkId = bookmarkData?.id, BookmarkId = bookmarkData?.id,
Title = illustTitle, Title = illustTitle,
RankTitle = illustTitle,
IllustType = (IllustType)illustType, IllustType = (IllustType)illustType,
Image = image, Image = image,
ImageUrl = urls?.x360 ?? url, ImageUrl = urls?.x360 ?? url,
@ -188,7 +187,6 @@ namespace Pixiview.Utils
{ {
item.BookmarkId = bookmarkData?.id; item.BookmarkId = bookmarkData?.id;
item.Title = illustTitle; item.Title = illustTitle;
item.RankTitle = illustTitle;
item.IllustType = (IllustType)illustType; item.IllustType = (IllustType)illustType;
item.ImageUrl = urls?.regular; item.ImageUrl = urls?.regular;
item.IsRestrict = xRestrict == 1; item.IsRestrict = xRestrict == 1;

View File

@ -98,7 +98,7 @@ namespace Pixiview.Utils
Id = illust_id.ToString(), Id = illust_id.ToString(),
BookmarkId = bookmark_id, BookmarkId = bookmark_id,
Title = title, Title = title,
RankTitle = $"#{rank} {title}", Rank = rank,
IllustType = (IllustType)type, IllustType = (IllustType)type,
ImageUrl = url, ImageUrl = url,
IsRestrict = restrict, IsRestrict = restrict,

View File

@ -659,6 +659,12 @@ namespace Pixiview.Utils
Changed = true; Changed = true;
} }
public new void InsertRange(int index, IEnumerable<IllustItem> collection)
{
base.InsertRange(index, collection);
Changed = true;
}
public new void RemoveAt(int index) public new void RemoveAt(int index)
{ {
base.RemoveAt(index); base.RemoveAt(index);

View File

@ -41,45 +41,46 @@ namespace Pixiview.Utils
detailItem = item; detailItem = item;
frames = new ImageSource[ugoira.frames.Length]; frames = new ImageSource[ugoira.frames.Length];
FrameCount = frames.Length; FrameCount = frames.Length;
timer = new Timer(OnTimerCallback, null, Timeout.Infinite, Timeout.Infinite);
Task.Run(LoadFrames); Task.Run(LoadFrames);
} }
public void Dispose() public void Dispose()
{
if (IsPlaying)
{
TogglePlay(false);
}
ClearTimer();
}
private void ClearTimer()
{ {
lock (sync) lock (sync)
{ {
if (IsPlaying) if (timer != null)
{ {
TogglePlay(false); timer.Dispose();
timer = null;
} }
} }
if (timer != null)
{
timer.Dispose();
timer = null;
}
} }
public void TogglePlay(bool flag) public void TogglePlay(bool flag)
{ {
lock (sync) if (IsPlaying == flag)
{ {
if (timer == null || IsPlaying == flag) return;
{ }
return; ClearTimer();
} if (flag)
if (flag) {
{ timer = new Timer(OnTimerCallback, null, 0, Timeout.Infinite);
IsPlaying = true; IsPlaying = true;
timer.Change(0, Timeout.Infinite); }
} else
else {
{ IsPlaying = false;
IsPlaying = false;
timer.Change(Timeout.Infinite, Timeout.Infinite);
}
} }
} }
@ -122,6 +123,13 @@ namespace Pixiview.Utils
var info = ugoira.frames[i]; var info = ugoira.frames[i];
while ((frame = frames[i]) == null) while ((frame = frames[i]) == null)
{ {
lock (sync)
{
if (timer == null)
{
return;
}
}
// not downloaded yet, waiting... // not downloaded yet, waiting...
Thread.Sleep(DELAY); Thread.Sleep(DELAY);
} }
@ -137,12 +145,9 @@ namespace Pixiview.Utils
i = 0; i = 0;
} }
index = i; index = i;
lock (sync) if (timer != null && IsPlaying)
{ {
if (timer != null && IsPlaying) timer.Change(info.delay, Timeout.Infinite);
{
timer.Change(info.delay, Timeout.Infinite);
}
} }
} }
@ -297,9 +302,12 @@ namespace Pixiview.Utils
var last = frame.Last; var last = frame.Last;
while (segs.AnyFor(first, last, s => !s.Done)) while (segs.AnyFor(first, last, s => !s.Done))
{ {
if (timer == null) lock (sync)
{ {
return; if (timer == null)
{
return;
}
} }
Thread.Sleep(DELAY); Thread.Sleep(DELAY);
} }
@ -456,6 +464,13 @@ namespace Pixiview.Utils
var cgImage = images[i].CGImage; var cgImage = images[i].CGImage;
var width = cgImage.Width; var width = cgImage.Width;
var height = cgImage.Height; var height = cgImage.Height;
//var scan = cgImage.BytesPerRow;
//var mod = scan % 16;
//if (mod > 0)
//{
// App.DebugPrint($"add more {mod} bytes to align line from {scan} to {scan + mod}.");
// scan += mod;
//}
using (CGBitmapContext bitmapContext = new CGBitmapContext( using (CGBitmapContext bitmapContext = new CGBitmapContext(
pxdata, width, height, pxdata, width, height,
cgImage.BitsPerComponent, cgImage.BitsPerComponent,