300 lines
11 KiB
C#
300 lines
11 KiB
C#
using Microsoft.AspNetCore.Components;
|
|
using System.ComponentModel;
|
|
using System.Globalization;
|
|
using System.Text.Json;
|
|
using Neighbourhood.omg.lol.Models;
|
|
using System.Runtime.CompilerServices;
|
|
|
|
namespace Neighbourhood.omg.lol {
|
|
public class State : INotifyPropertyChanged {
|
|
|
|
// Events
|
|
public event EventHandler<EventArgs>? IntentReceived;
|
|
public event PropertyChangedEventHandler? PropertyChanged;
|
|
|
|
// Create the OnPropertyChanged method to raise the event
|
|
// The calling member's name will be used as the parameter.
|
|
protected void OnPropertyChanged([CallerMemberName] string? name = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
|
|
protected void OnIntentRecieved() => IntentReceived?.Invoke(this, EventArgs.Empty);
|
|
|
|
// Main data lists
|
|
public List<Status>? Statuses { get; set; }
|
|
public List<Pic>? Pics { get; set; }
|
|
public List<NowData>? NowGarden { get; set; }
|
|
public List<MarkupString>? EphemeralMessages { get; set; }
|
|
public List<string>? AddressDirectory { get; set; }
|
|
|
|
public List<StatusOrPic>? Feed { get; set; }
|
|
|
|
// Account data
|
|
public AccountResponseData? AccountInfo { get; set; }
|
|
public AddressResponseList? AddressList { get; set; }
|
|
|
|
public bool IsAuthorized { get => AccountInfo != null; }
|
|
public string? Name { get => AccountInfo?.Name; }
|
|
public string? Email { get => AccountInfo?.Email; }
|
|
public IEnumerable<string>? AddressNames { get => AddressList?.Select(a => a.Address); }
|
|
|
|
// Selected Address
|
|
private AddressResponseData? _selectedAddress;
|
|
public AddressResponseData? SelectedAddress {
|
|
get {
|
|
if (_selectedAddress == null) {
|
|
string selectedAddressJson = Preferences.Default.Get("selectedaddress", string.Empty);
|
|
if (!string.IsNullOrEmpty(selectedAddressJson)) _selectedAddress = JsonSerializer.Deserialize<AddressResponseData>(selectedAddressJson);
|
|
}
|
|
return _selectedAddress;
|
|
}
|
|
set {
|
|
if (_selectedAddress != value) {
|
|
_selectedAddress = value;
|
|
if (_selectedAddress == null) Preferences.Default.Remove("selectedaddress");
|
|
else {
|
|
string selectedAddressJson = JsonSerializer.Serialize(_selectedAddress);
|
|
Preferences.Default.Set("selectedaddress", selectedAddressJson);
|
|
OnPropertyChanged();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public List<string>? Following { get; private set; }
|
|
public string? SelectedAddressName { get => SelectedAddress?.Address; }
|
|
|
|
// data for selected address
|
|
public List<Status>? CachedAddressStatuses { get; set; }
|
|
public List<Pic>? CachedAddressPics { get; set; }
|
|
public MarkupString? CachedAddressBio { get; set; }
|
|
private string? _cachedAddress;
|
|
public string? CachedAddress {
|
|
get => _cachedAddress;
|
|
set {
|
|
if (_cachedAddress != value) {
|
|
_cachedAddress = value;
|
|
CachedAddressStatuses = new List<Status>();
|
|
CachedAddressPics = new List<Pic>();
|
|
CachedAddressBio = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
// share intent stuff
|
|
private string? _shareString;
|
|
public string? ShareString {
|
|
get => _shareString;
|
|
set {
|
|
_shareString = value;
|
|
OnIntentRecieved();
|
|
}
|
|
}
|
|
public string? ShareStringSubject { get; set; }
|
|
|
|
private string? _sharePhoto;
|
|
public string? SharePhoto {
|
|
get => _sharePhoto;
|
|
set {
|
|
_sharePhoto = value;
|
|
OnIntentRecieved();
|
|
}
|
|
}
|
|
public long? SharePhotoSize { get; set; }
|
|
public string? SharePhotoContentType { get; set; }
|
|
public string? SharePhotoText { get; set; }
|
|
|
|
// refreshing
|
|
private bool _isRefreshing;
|
|
public bool IsRefreshing {
|
|
get => _isRefreshing;
|
|
private set {
|
|
_isRefreshing = value;
|
|
OnPropertyChanged();
|
|
}
|
|
}
|
|
|
|
public void SendRefresh() => IsRefreshing = true;
|
|
|
|
private static int _refresherCount = 0;
|
|
private static Mutex mutex = new Mutex();
|
|
|
|
public class RefreshToken : IDisposable {
|
|
public event EventHandler? Disposed;
|
|
public void Dispose() => Disposed?.Invoke(this, EventArgs.Empty);
|
|
}
|
|
public RefreshToken GetRefreshToken() {
|
|
mutex.WaitOne();
|
|
_refresherCount++;
|
|
mutex.ReleaseMutex();
|
|
RefreshToken token = new RefreshToken();
|
|
token.Disposed += RefreshToken_Disposed;
|
|
return token;
|
|
}
|
|
|
|
private void RefreshToken_Disposed(object? sender, EventArgs e) {
|
|
mutex.WaitOne();
|
|
_refresherCount--;
|
|
if (_refresherCount == 0) IsRefreshing = false;
|
|
mutex.ReleaseMutex();
|
|
}
|
|
|
|
private bool _canRefresh;
|
|
public bool CanRefresh {
|
|
get => _canRefresh;
|
|
set {
|
|
_canRefresh = value;
|
|
OnPropertyChanged();
|
|
}
|
|
}
|
|
|
|
// api service
|
|
private RestService api { get; set; }
|
|
|
|
public State(RestService restService) {
|
|
api = restService;
|
|
}
|
|
|
|
public async Task PopulateAccountDetails(string token) {
|
|
api.AddToken(token);
|
|
|
|
string accountJson = Preferences.Default.Get("accountdetails", string.Empty);
|
|
string addressJson = Preferences.Default.Get("accountaddresses", string.Empty);
|
|
string selectedAddressJson = Preferences.Default.Get("selectedaddress", string.Empty);
|
|
string followingJson = Preferences.Default.Get("following", string.Empty);
|
|
|
|
if (!string.IsNullOrEmpty(accountJson)) AccountInfo = JsonSerializer.Deserialize<AccountResponseData>(accountJson);
|
|
if (!string.IsNullOrEmpty(addressJson)) AddressList = JsonSerializer.Deserialize<AddressResponseList>(addressJson);
|
|
if (!string.IsNullOrEmpty(selectedAddressJson)) SelectedAddress = JsonSerializer.Deserialize<AddressResponseData>(selectedAddressJson);
|
|
if (!string.IsNullOrEmpty(followingJson)) Following = JsonSerializer.Deserialize<List<string>>(followingJson);
|
|
|
|
// if we haven't got account info, attempt to retrieve it.
|
|
if (AccountInfo == null) {
|
|
AccountInfo = await api.AccountInfo();
|
|
if (AccountInfo != null) {
|
|
// quick fix for users without names (such as the review account)
|
|
if(AccountInfo.Name == null) AccountInfo.Name = AccountInfo.Email.Split('@').FirstOrDefault() ?? "person";
|
|
Preferences.Default.Set("accountdetails", JsonSerializer.Serialize(AccountInfo));
|
|
}
|
|
}
|
|
|
|
// if we don't have the list of addresses, attempt to retrieve that.
|
|
if (AddressList == null) {
|
|
AddressList = await api.Addresses();
|
|
if (AddressList != null) {
|
|
Preferences.Default.Set("accountaddresses", JsonSerializer.Serialize(AddressList));
|
|
SelectedAddress = AddressList.FirstOrDefault();
|
|
Preferences.Default.Set("selectedaddress", JsonSerializer.Serialize(SelectedAddress));
|
|
}
|
|
}
|
|
}
|
|
|
|
public async Task RemoveAccountDetails() {
|
|
await Task.Run(() => {
|
|
Preferences.Default.Clear();
|
|
AccountInfo = null;
|
|
AddressList = null;
|
|
SelectedAddress = null;
|
|
Following = null;
|
|
api.RemoveToken();
|
|
});
|
|
}
|
|
|
|
public bool IsFollowing(string address) => Following?.Contains(address) ?? false;
|
|
public void Follow(string address) {
|
|
if (Following == null) Following = new List<string>();
|
|
Following.Add(address);
|
|
Preferences.Default.Set("following", JsonSerializer.Serialize(Following));
|
|
}
|
|
|
|
public void Unfollow(string address) {
|
|
if (Following == null) Following = new List<string>();
|
|
Following.Remove(address);
|
|
Preferences.Default.Set("following", JsonSerializer.Serialize(Following));
|
|
}
|
|
|
|
public async Task<MarkupString?> GetBio(string address, bool forceRefresh = false) {
|
|
CachedAddress = address;
|
|
if (forceRefresh || CachedAddressBio == null) {
|
|
CachedAddressBio = await api.StatuslogBio(address);
|
|
}
|
|
return CachedAddressBio;
|
|
}
|
|
|
|
public async Task<List<MarkupString>?> GetEphemeralMessages(bool forceRefresh = false) {
|
|
if (forceRefresh || this.EphemeralMessages == null || this.EphemeralMessages.Count == 0) {
|
|
this.EphemeralMessages = await api.Ephemeral();
|
|
}
|
|
return this.EphemeralMessages;
|
|
}
|
|
|
|
public async Task<List<string>?> GetDirectory(bool forceRefresh = false) {
|
|
if (forceRefresh || this.AddressDirectory == null || this.AddressDirectory.Count == 0) {
|
|
IdnMapping idn = new IdnMapping();
|
|
this.AddressDirectory = (await api.Directory())?.Select(s => {
|
|
if (s.StartsWith("xn--")) return idn.GetUnicode(s);
|
|
else return s;
|
|
}).ToList();
|
|
}
|
|
return this.AddressDirectory;
|
|
}
|
|
|
|
public async Task<List<Status>?> GetStatuses(bool forceRefresh = false) {
|
|
if (forceRefresh || this.Statuses == null || this.Statuses.Count == 0) {
|
|
this.Statuses = await api.StatuslogLatest();
|
|
}
|
|
return this.Statuses;
|
|
}
|
|
|
|
public async Task<List<Status>?> GetStatuses(string address, bool forceRefresh = false) {
|
|
this.CachedAddress = address;
|
|
if (forceRefresh || this.CachedAddressStatuses == null || this.CachedAddressStatuses.Count == 0) {
|
|
this.CachedAddressStatuses = await api.Statuslog(address);
|
|
}
|
|
return this.CachedAddressStatuses;
|
|
}
|
|
|
|
public async Task<List<NowData>?> GetNowGarden(bool forceRefresh = false) {
|
|
if (forceRefresh || this.NowGarden == null || this.NowGarden.Count == 0) {
|
|
this.NowGarden = await api.NowGarden();
|
|
}
|
|
return this.NowGarden;
|
|
}
|
|
|
|
public async Task<List<Pic>?> GetPics(bool forceRefresh = false) {
|
|
if (forceRefresh || this.Pics == null || this.Pics.Count == 0) {
|
|
this.Pics = await api.SomePics();
|
|
}
|
|
return this.Pics;
|
|
}
|
|
|
|
public async Task<List<Pic>?> GetPics(string address, bool forceRefresh = false) {
|
|
CachedAddress = address;
|
|
if (forceRefresh || this.CachedAddressPics == null || this.CachedAddressPics.Count == 0) {
|
|
CachedAddressPics = (await api.SomePics(address)) ?? new List<Pic>();
|
|
}
|
|
return CachedAddressPics;
|
|
}
|
|
|
|
public async Task RefreshStatuses() {
|
|
await GetStatuses(forceRefresh: true);
|
|
if(SelectedAddressName != null)
|
|
await GetStatuses(SelectedAddressName, forceRefresh: true);
|
|
}
|
|
public async Task RefreshPics() {
|
|
await GetPics(forceRefresh: true);
|
|
if (SelectedAddressName != null)
|
|
await GetPics(SelectedAddressName, forceRefresh: true );
|
|
}
|
|
public async Task RefreshNow() => await GetNowGarden(forceRefresh: true);
|
|
|
|
public async Task<IOrderedEnumerable<StatusOrPic>> GetFeed(bool forceRefresh = false) {
|
|
if(forceRefresh || Feed == null || Feed.Count == 0) {
|
|
Feed = new List<StatusOrPic>();
|
|
foreach(string address in Following ?? new List<string>()) {
|
|
Feed.AddRange((await GetStatuses(address, forceRefresh))?.Select(s => new StatusOrPic { Status = s }) ?? new List<StatusOrPic>());
|
|
Feed.AddRange((await GetPics(address, forceRefresh))?.Select(p => new StatusOrPic { Pic = p }) ?? new List<StatusOrPic>());
|
|
}
|
|
}
|
|
return Feed.OrderByDescending(s => s.CreatedTime);
|
|
}
|
|
|
|
}
|
|
}
|