feature: airdrop share favorites

This commit is contained in:
Tsanie Lily 2020-05-10 02:43:59 +08:00
parent bdee786f3a
commit 9859c56da6
18 changed files with 207 additions and 75 deletions

View File

@ -27,7 +27,7 @@ namespace Pixiview.iOS
public override bool OpenUrl(UIApplication app, NSUrl url, NSDictionary options)
{
App.DebugPrint($"url: {url}.");
return App.OpenUrl(url.AbsoluteString);
return App.OpenUrl(url);
}
}
}

View File

@ -65,5 +65,20 @@
<string>Assets.xcassets/AppIcon.appiconset/Icon58</string>
</dict>
</array>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeName</key>
<string>Favorites Json</string>
<key>LSItemContentTypes</key>
<array>
<string>public.text</string>
</array>
<key>CFBundleTypeIconFiles</key>
<array>
<string>Assets.xcassets/AppIcon.appiconset/Icon180</string>
</array>
</dict>
</array>
</dict>
</plist>

View File

@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using Pixiview.Illust;
using Pixiview.Resources;
@ -123,24 +125,92 @@ namespace Pixiview
Debug.Fail(string.Format("[{0:HH:mm:ss.ffff}] - {1} - {2}", DateTime.Now, category, message));
}
public static bool OpenUrl(string url)
public static bool OpenUrl(Uri uri)
{
var current = Current.MainPage;
if (current != null)
if (current != null && uri != null)
{
var m = Regex.Match(url, "/artworks/([0-9]+)", RegexOptions.IgnoreCase);
if (m.Success)
var url = uri.AbsolutePath;
if (uri.Scheme == "pixiview://")
{
var illust = new IllustItem { Id = m.Groups[1].Value };
var page = new ViewIllustPage(illust, true);
MainThread.BeginInvokeOnMainThread(() => current.Navigation.PushAsync(page));
var m = Regex.Match(url, "/artworks/([0-9]+)", RegexOptions.IgnoreCase);
if (m.Success)
{
var illust = new IllustItem { Id = m.Groups[1].Value };
var page = new ViewIllustPage(illust, true);
MainThread.BeginInvokeOnMainThread(() => current.Navigation.PushAsync(page));
}
else
{
MainThread.BeginInvokeOnMainThread(() => current.DisplayAlert(
url,
ResourceHelper.InvalidUrl,
ResourceHelper.Ok));
}
}
else
else if (File.Exists(url))
{
MainThread.BeginInvokeOnMainThread(() => current.DisplayAlert(
url,
ResourceHelper.InvalidUrl,
ResourceHelper.Ok));
IllustFavorite favObj;
try
{
favObj = Stores.LoadFavoritesIllusts(url);
}
catch (Exception ex)
{
DebugError("open.file", $"failed to parse file, name: {url}, error: {ex.Message}");
return true;
}
var path = Stores.FavoritesPath;
if (File.Exists(path))
{
MainThread.BeginInvokeOnMainThread(async () =>
{
var opReplace = ResourceHelper.FavoritesReplace;
var opCombine = ResourceHelper.FavoritesCombine;
var result = await current.DisplayActionSheet(
ResourceHelper.FavoritesOperation,
ResourceHelper.Cancel,
opCombine,
opReplace);
if (result == opReplace)
{
// replace favorite file
File.Copy(url, path, true);
}
else if (result == opCombine)
{
// combine favorite file
var favNow = Stores.GetFavoriteObject();
var list = favObj.Illusts;
var distinct = favNow.Illusts.Where(f => !list.Any(i => i.Id == f.Id)).ToList();
list.AddRange(distinct);
favNow.Illusts = list;
Stores.SaveFavoritesIllusts();
}
if (Shell.Current.CurrentState.Location.OriginalString.EndsWith(Routes.Favorites))
{
var sc = (IShellSectionController)Shell.Current.CurrentItem.CurrentItem;
if (sc.PresentedPage is FavoritesPage fav)
{
fav.Reload(true);
}
}
});
}
else
{
File.Copy(url, path);
if (Shell.Current.CurrentState.Location.OriginalString.EndsWith(Routes.Favorites))
{
var sc = (IShellSectionController)Shell.Current.CurrentItem.CurrentItem;
if (sc.PresentedPage is FavoritesPage fav)
{
fav.Reload(true);
}
}
}
}
}
return true;

View File

@ -7,8 +7,12 @@
x:Class="Pixiview.Illust.FavoritesPage"
BackgroundColor="{DynamicResource WindowColor}"
Title="{r:Text Favorites}">
<ContentPage.ToolbarItems>
<ToolbarItem Order="Primary" Clicked="ShareFavorites_Clicked"
IconImageSource="{DynamicResource FontIconShare}"/>
</ContentPage.ToolbarItems>
<Grid>
<ScrollView HorizontalOptions="Fill">
<ScrollView HorizontalOptions="Fill" HorizontalScrollBarVisibility="Never">
<u:FlowLayout ItemsSource="{Binding Illusts}"
HorizontalOptions="Fill" Column="{Binding Columns}"
Margin="16" RowSpacing="16" ColumnSpacing="16"

View File

@ -1,7 +1,10 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Input;
using Pixiview.Resources;
using Pixiview.Utils;
using Xamarin.Essentials;
namespace Pixiview.Illust
{
@ -22,8 +25,7 @@ namespace Pixiview.Illust
protected override void OnAppearing()
{
//base.OnAppearing();
loaded = false;
StartLoad();
Reload();
}
protected override IEnumerable<IllustItem> DoGetIllustList(IllustItem[] data, ICommand command)
@ -37,12 +39,28 @@ namespace Pixiview.Illust
protected override IllustItem[] DoLoadIllustData(bool force)
{
var favorites = Stores.FavoriteObject;
var favorites = Stores.GetFavoriteObject(force);
if (favorites == null)
{
return null;
}
return favorites.Illusts.ToArray();
}
public void Reload(bool force = false)
{
loaded = false;
StartLoad(force);
}
private async void ShareFavorites_Clicked(object sender, EventArgs e)
{
var file = Stores.FavoritesPath;
await Share.RequestAsync(new ShareFileRequest
{
Title = ResourceHelper.Favorites,
File = new ShareFile(file)
});
}
}
}

View File

@ -121,7 +121,15 @@ namespace Pixiview.Illust
protected override void OnSizeAllocated(double width, double height)
{
base.OnSizeAllocated(width, height);
var columns = width > height ? 4 : 2;
int columns;
if (isPhone)
{
columns = width > height ? 4 : 2;
}
else
{
columns = width > height ? 6 : 4;
}
if (Columns != columns)
{
Columns = columns;
@ -341,7 +349,7 @@ namespace Pixiview.Illust
IsLoading = false;
return;
}
if (force)
if (force && IsFavoriteVisible)
{
NeedUpdate = true;
}

View File

@ -12,7 +12,7 @@
IconImageSource="{DynamicResource FontIconRefresh}"/>
</ContentPage.ToolbarItems>
<Grid>
<ScrollView HorizontalOptions="Fill">
<ScrollView HorizontalOptions="Fill" HorizontalScrollBarVisibility="Never">
<u:FlowLayout ItemsSource="{Binding Illusts}"
HorizontalOptions="Fill" Column="{Binding Columns}"
Margin="16" RowSpacing="16" ColumnSpacing="16"

View File

@ -11,7 +11,7 @@
IconImageSource="{DynamicResource FontIconRefresh}"/>
</ContentPage.ToolbarItems>
<Grid Padding="{Binding PageTopMargin}">
<ScrollView HorizontalOptions="Fill">
<ScrollView HorizontalOptions="Fill" HorizontalScrollBarVisibility="Never">
<u:FlowLayout ItemsSource="{Binding Illusts}"
HorizontalOptions="Fill" Column="{Binding Columns}"
Margin="16, 10, 16, 16" RowSpacing="16" ColumnSpacing="16"

View File

@ -34,15 +34,17 @@ namespace Pixiview.Illust
PageTopMargin = new Thickness(0, AppShell.TotalBarOffset.Top + 50, 0, 0);
break;
case Orientation.PortraitUpsideDown:
PageTopMargin = isPhone ?
new Thickness(0, AppShell.NavigationBarOffset.Top + 50, 0, 0) :
new Thickness(0, AppShell.TotalBarOffset.Top + 50, 0, 0);
break;
//PageTopMargin = isPhone ?
// new Thickness(0, AppShell.NavigationBarOffset.Top + 50, 0, 0) :
// new Thickness(0, AppShell.TotalBarOffset.Top + 50, 0, 0);
//break;
case Orientation.Unknown:
case Orientation.LandscapeLeft:
case Orientation.LandscapeRight:
default:
PageTopMargin = new Thickness(0, AppShell.NavigationBarOffset.Top + 50, 0, 0);
PageTopMargin = isPhone ?
new Thickness(0, AppShell.NavigationBarOffset.Top + 50, 0, 0) :
new Thickness(0, AppShell.TotalBarOffset.Top + 50, 0, 0);
break;
}
}

View File

@ -12,7 +12,7 @@
IconImageSource="{DynamicResource FontIconRefresh}"/>
</ContentPage.ToolbarItems>
<Grid Padding="{Binding PageTopMargin}">
<ScrollView HorizontalOptions="Fill">
<ScrollView HorizontalOptions="Fill" HorizontalScrollBarVisibility="Never">
<u:FlowLayout ItemsSource="{Binding Illusts}"
HorizontalOptions="Fill" Column="{Binding Columns}"
Margin="16, 6, 16, 16" RowSpacing="16" ColumnSpacing="16"

View File

@ -40,15 +40,17 @@ namespace Pixiview.Illust
PageTopMargin = new Thickness(0, AppShell.TotalBarOffset.Top + 40, 0, 0);
break;
case Orientation.PortraitUpsideDown:
PageTopMargin = isPhone ?
new Thickness(0, AppShell.NavigationBarOffset.Top + 40, 0, 0) :
new Thickness(0, AppShell.TotalBarOffset.Top + 40, 0, 0);
break;
//PageTopMargin = isPhone ?
// new Thickness(0, AppShell.NavigationBarOffset.Top + 40, 0, 0) :
// new Thickness(0, AppShell.TotalBarOffset.Top + 40, 0, 0);
//break;
case Orientation.Unknown:
case Orientation.LandscapeLeft:
case Orientation.LandscapeRight:
default:
PageTopMargin = new Thickness(0, AppShell.NavigationBarOffset.Top + 40, 0, 0);
PageTopMargin = isPhone ?
new Thickness(0, AppShell.NavigationBarOffset.Top + 40, 0, 0) :
new Thickness(0, AppShell.TotalBarOffset.Top + 40, 0, 0);
break;
}
}

View File

@ -18,7 +18,7 @@
IconImageSource="{DynamicResource FontIconRefresh}"/>
</ContentPage.ToolbarItems>
<Grid>
<ScrollView HorizontalOptions="Fill">
<ScrollView HorizontalOptions="Fill" HorizontalScrollBarVisibility="Never">
<u:FlowLayout ItemsSource="{Binding Illusts}"
HorizontalOptions="Fill" Column="{Binding Columns}"
Margin="16" RowSpacing="16" ColumnSpacing="16"

View File

@ -26,4 +26,7 @@
<SaveSuccess>成功保存图片到照片库。</SaveSuccess>
<AlreadySavedQuestion>原图已保存,是否继续?</AlreadySavedQuestion>
<InvalidUrl>无法识别该 URL。</InvalidUrl>
<FavoritesOperation>请选择收藏夹操作</FavoritesOperation>
<FavoritesReplace>替换</FavoritesReplace>
<FavoritesCombine>合并</FavoritesCombine>
</root>

View File

@ -23,6 +23,10 @@ namespace Pixiview.Resources
public static string SaveSuccess => GetResource(nameof(SaveSuccess));
public static string AlreadySavedQuestion => GetResource(nameof(AlreadySavedQuestion));
public static string InvalidUrl => GetResource(nameof(InvalidUrl));
public static string Favorites => GetResource(nameof(Favorites));
public static string FavoritesOperation => GetResource(nameof(FavoritesOperation));
public static string FavoritesReplace => GetResource(nameof(FavoritesReplace));
public static string FavoritesCombine => GetResource(nameof(FavoritesCombine));
static readonly Dictionary<string, LanguageResource> dict = new Dictionary<string, LanguageResource>();

View File

@ -22,7 +22,7 @@ namespace Pixiview.UI
public event EventHandler Unload;
public event EventHandler<OrientationEventArgs> OrientationChanged;
protected readonly bool isPhone = DeviceInfo.Idiom == DeviceIdiom.Phone;
protected static readonly bool isPhone = DeviceInfo.Idiom == DeviceIdiom.Phone;
public AdaptedPage()
{
@ -48,13 +48,13 @@ namespace Pixiview.UI
PageTopMargin = AppShell.TotalBarOffset;
break;
case Orientation.PortraitUpsideDown:
PageTopMargin = isPhone ? AppShell.NavigationBarOffset : AppShell.TotalBarOffset;
break;
//PageTopMargin = isPhone ? AppShell.NavigationBarOffset : AppShell.TotalBarOffset;
//break;
case Orientation.Unknown:
case Orientation.LandscapeLeft:
case Orientation.LandscapeRight:
default:
PageTopMargin = AppShell.NavigationBarOffset;
PageTopMargin = isPhone ? AppShell.NavigationBarOffset : AppShell.TotalBarOffset;
break;
}
OrientationChanged?.Invoke(this, new OrientationEventArgs { CurrentOrientation = orientation });

View File

@ -29,6 +29,7 @@ namespace Pixiview.UI
public const string IconOption = "\uf013";
public const string IconDownload = "\uf019";
public const string IconFavorite = "\uf02e";
public const string IconShare = "\uf35d";
static StyleDefinition()
{

View File

@ -13,6 +13,7 @@ namespace Pixiview.UI.Theme
public const string FontIconOption = nameof(FontIconOption);
public const string FontIconDownload = nameof(FontIconDownload);
public const string FontIconFavorite = nameof(FontIconFavorite);
public const string FontIconShare = nameof(FontIconShare);
public const string StatusBarStyle = nameof(StatusBarStyle);
public const string WindowColor = nameof(WindowColor);
@ -68,6 +69,7 @@ namespace Pixiview.UI.Theme
Add(FontIconOption, GetSolidIcon(StyleDefinition.IconOption, solidFontFamily));
Add(FontIconDownload, GetSolidIcon(StyleDefinition.IconDownload, solidFontFamily));
Add(FontIconFavorite, GetSolidIcon(StyleDefinition.IconFavorite, solidFontFamily));
Add(FontIconShare, GetSolidIcon(StyleDefinition.IconShare, solidFontFamily));
}
private FontImageSource GetSolidIcon(string icon, string family, Color color = default)

View File

@ -18,10 +18,10 @@ namespace Pixiview.Utils
public static readonly string PersonalFolder = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
public static readonly string CacheFolder = Environment.GetFolderPath(Environment.SpecialFolder.InternetCache);
private const string favoriteFile = "favorites.json";
private const string imageFolder = "img-original";
private const string previewFolder = "img-master";
private const string illustFile = "illust.json";
private const string favoriteFile = "favorites.json";
private const string pagesFolder = "pages";
private const string preloadsFolder = "preloads";
@ -45,38 +45,40 @@ namespace Pixiview.Utils
}
}
public static List<IllustItem> Favorites => GetFavoriteObject().Illusts;
public static string FavoritesPath => Path.Combine(PersonalFolder, favoriteFile);
private static IllustFavorite favoriteObject;
public static IllustFavorite FavoriteObject
public static IllustFavorite GetFavoriteObject(bool force = false)
{
get
lock (sync)
{
lock (sync)
if (force || favoriteObject == null)
{
if (favoriteObject == null)
var favorites = LoadFavoritesIllusts();
if (favorites != null)
{
var favorites = LoadFavoritesIllusts();
if (favorites != null)
{
favoriteObject = favorites;
}
else
{
favoriteObject = new IllustFavorite
{
Illusts = new List<IllustItem>()
};
}
favoriteObject = favorites;
}
else
{
favoriteObject = new IllustFavorite
{
Illusts = new List<IllustItem>()
};
}
return favoriteObject;
}
return favoriteObject;
}
}
public static List<IllustItem> Favorites => FavoriteObject.Illusts;
private static IllustFavorite LoadFavoritesIllusts()
public static IllustFavorite LoadFavoritesIllusts(string file = null)
{
var file = Path.Combine(PersonalFolder, favoriteFile);
if (file == null)
{
file = FavoritesPath;
}
lock (sync)
{
return ReadObject<IllustFavorite>(file);
@ -85,10 +87,10 @@ namespace Pixiview.Utils
public static void SaveFavoritesIllusts()
{
var file = Path.Combine(PersonalFolder, favoriteFile);
var file = FavoritesPath;
lock (sync)
{
var data = FavoriteObject;
var data = GetFavoriteObject();
data.LastFavoriteUtc = DateTime.UtcNow;
WriteObject(file, data);
}
@ -236,9 +238,9 @@ namespace Pixiview.Utils
Configs.UrlIllustList,
Configs.Referer,
force: force);
if (result.error)
if (result == null || result.error)
{
App.DebugPrint($"error when load illust data: {result.message} ({force})");
App.DebugPrint($"error when load illust data: {result?.message} ({force})");
}
return result;
}
@ -276,9 +278,9 @@ namespace Pixiview.Utils
string.Format(Configs.UrlIllustPage, id),
string.Format(Configs.UrlIllust, id),
force: force);
if (result.error)
if (result == null || result.error)
{
App.DebugPrint($"error when load page data: {result.message} ({force})");
App.DebugPrint($"error when load page data: {result?.message} ({force})");
}
return result;
}
@ -290,22 +292,22 @@ namespace Pixiview.Utils
string.Format(Configs.UrlIllustUserAll, userId),
string.Format(Configs.UrlIllustUser, userId),
force: force);
if (list.error)
if (list == null || list.error)
{
App.DebugPrint($"error when load user data: {list.message} ({force})");
App.DebugPrint($"error when load user data: {list?.message} ({force})");
}
// TODO
var ids = string.Join("&ids%5B%5D=", list.body.illusts.Keys.Take(20));
var ids = string.Join("", list.body.illusts.Keys.Take(20).Select(id => $"ids%5B%5D={id}&"));
var result = LoadObject<IllustUserData>(
null,
string.Format(Configs.UrlIllustUserArtworks, userId, ids, 1),
string.Format(Configs.UrlIllustUser, userId),
force: force);
if (result.error)
if (result == null || result.error)
{
App.DebugPrint($"error when load user illust data: {result.message} ({force})");
App.DebugPrint($"error when load user illust data: {result?.message} ({force})");
}
return result;
}
@ -451,20 +453,21 @@ namespace Pixiview.Utils
}
var client = new HttpClient(handler)
{
BaseAddress = new Uri($"{uri.Scheme}://{uri.Host}")
BaseAddress = new Uri($"{uri.Scheme}://{uri.Host}"),
Timeout = TimeSpan.FromSeconds(30)
};
return TryCount(() =>
{
using (var request = new HttpRequestMessage(HttpMethod.Get, uri.PathAndQuery)
{
Version = new Version(2, 0)
Version = new Version(2, 0),
})
{
var headers = request.Headers;
headerAction(headers);
headers.Add("accept-language", Configs.AcceptLanguage);
headers.Add("accept-encoding", Configs.AcceptEncoding);
return client.SendAsync(request).Result;
return client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).Result;
}
});
}
@ -485,7 +488,7 @@ namespace Pixiview.Utils
public const string SuffixPreload = " id=\"meta-preload-data\" content='";
public const int SuffixPreloadLength = 33; // SuffixPreload.Length
public const string UrlIllustUserAll = "https://www.pixiv.net/ajax/user/{0}/profile/all?lang=zh";
public const string UrlIllustUserArtworks = "https://www.pixiv.net/ajax/user/{0}/profile/illusts?ids%5B%5D={1}&work_category=illustManga&is_first_page={2}&lang=zh";
public const string UrlIllustUserArtworks = "https://www.pixiv.net/ajax/user/{0}/profile/illusts?{1}work_category=illustManga&is_first_page={2}&lang=zh";
public const string UrlIllustUser = "https://www.pixiv.net/users/{0}/artworks";
public const string UrlIllustPage = "https://www.pixiv.net/ajax/illust/{0}/pages?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";