Compare commits

..

No commits in common. "3d8047c01c67acd4f821253f1883c41a3b920e99" and "0fcda98b9fcab457f9eaa23003f30d21e1df66bd" have entirely different histories.

47 changed files with 345 additions and 618 deletions

View file

@ -9,7 +9,7 @@ using System.Text.RegularExpressions;
namespace Neighbourhood.omg.lol namespace Neighbourhood.omg.lol
{ {
public class RestService { public class RestService {
HttpClient _client; HttpClient _client;
JsonSerializerOptions _serializerOptions; JsonSerializerOptions _serializerOptions;
public const string BaseUrl = "https://api.omg.lol"; public const string BaseUrl = "https://api.omg.lol";
@ -25,18 +25,6 @@ namespace Neighbourhood.omg.lol
AddToken(token); AddToken(token);
} }
public T? Deserialize<T>(string str) {
T? responseObj = default(T);
try {
responseObj = JsonSerializer.Deserialize<T>(str, _serializerOptions);
}
catch (JsonException ex) {
Debug.WriteLine(@"\tERROR {0}", ex.Message);
Debug.WriteLine(str);
}
return responseObj;
}
public void AddToken(string? token = null) { public void AddToken(string? token = null) {
if (token == null) token = Task.Run(() => SecureStorage.GetAsync("accounttoken")).GetAwaiter().GetResult(); if (token == null) token = Task.Run(() => SecureStorage.GetAsync("accounttoken")).GetAwaiter().GetResult();
if (token != null) _client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token); if (token != null) _client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
@ -54,8 +42,8 @@ namespace Neighbourhood.omg.lol
string str = await response.Content.ReadAsStringAsync(); string str = await response.Content.ReadAsStringAsync();
try { try {
OmgLolResponse<T>? responseObj = await response.Content.ReadFromJsonAsync<OmgLolResponse<T>>(_serializerOptions, cancellationToken: cancellationToken); OmgLolResponse<T>? responseObj = await response.Content.ReadFromJsonAsync<OmgLolResponse<T>>(_serializerOptions, cancellationToken: cancellationToken);
if (responseObj?.Request == null || (responseObj?.Request?.Success ?? false)) { if (responseObj?.Request?.Success ?? false) {
responseData = responseObj!.Response; responseData = responseObj.Response;
} }
} }
catch (JsonException ex) { catch (JsonException ex) {
@ -90,81 +78,6 @@ namespace Neighbourhood.omg.lol
return responseData; return responseData;
} }
private async Task<TResponse?> PostBinary<TResponse, TData>(string uri, FileResult? fileResult = null, CancellationToken cancellationToken = default) where TResponse : IOmgLolResponseData {
TResponse? responseData = default(TResponse);
try {
if (fileResult != null) using (var fileStream = await fileResult.OpenReadAsync()) {
Uri url = new Uri(_client.BaseAddress?.AbsoluteUri + uri);
if (string.IsNullOrEmpty(url.Query)) uri += "?binary";
else if (!url.Query.Contains("binary")) uri += "&binary";
HttpContent fileStreamContent = new StreamContent(fileStream);
fileStreamContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(fileResult.ContentType ?? "application/octet-stream");
fileStreamContent.Headers.ContentLength = fileStream.Length;
HttpResponseMessage response = await _client.PostAsync(uri, fileStreamContent, cancellationToken: cancellationToken);
string str = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode) {
OmgLolResponse<TResponse>? responseObj = await response.Content.ReadFromJsonAsync<OmgLolResponse<TResponse>>(_serializerOptions, cancellationToken: cancellationToken);
if (responseObj?.Request?.Success ?? false) {
responseData = responseObj.Response;
}
}
}
}
catch (Exception ex) {
Debug.WriteLine(@"\tERROR {0}", ex.Message);
}
return responseData;
}
private async Task<TResponse?> PostMultipart<TResponse, TData>(string uri, TData? data = null, FileResult? fileResult = null, CancellationToken cancellationToken = default)
where TResponse : IOmgLolResponseData where TData : class
{
if(fileResult != null) {
using (var fileStream = await fileResult.OpenReadAsync())
return await PostMultipart<TResponse, TData>(uri, data: data, fileStream: fileStream, fileName: fileResult.FileName, contentType: fileResult.ContentType);
}
else return await PostMultipart<TResponse, TData>(uri, data, fileStream: null);
}
private async Task<TResponse?> PostMultipart<TResponse, TData>(string uri, TData? data = null, Stream? fileStream = null, string? fileName = null, string? contentType = null, CancellationToken cancellationToken = default)
where TResponse : IOmgLolResponseData where TData : class
{
TResponse? responseData = default;
try {
using (MultipartFormDataContent formData = new MultipartFormDataContent()) {
if(fileStream != null) {
HttpContent fileStreamContent = new StreamContent(fileStream);
fileStreamContent.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("form-data") {
Name = "\"file\"",
FileName = $"\"{fileName}\"" ?? "\"unknown\""
};
fileStreamContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(contentType ?? "application/octet-stream");
formData.Add(fileStreamContent);
}
if (data != null) {
HttpContent jsonContent = JsonContent.Create(data, options: _serializerOptions);
formData.Add(jsonContent);
}
HttpResponseMessage response = await _client.PostAsync(uri, formData, cancellationToken: cancellationToken);
string str = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode) {
OmgLolResponse<TResponse>? responseObj = await response.Content.ReadFromJsonAsync<OmgLolResponse<TResponse>>(_serializerOptions, cancellationToken: cancellationToken);
if (responseObj?.Request?.Success ?? false) {
responseData = responseObj.Response;
}
}
}
}
catch (Exception ex) {
Debug.WriteLine(@"\tERROR {0}", ex.Message);
}
return responseData;
}
private async Task<TResponse?> Put<TResponse, TData>(string uri, TData data, CancellationToken cancellationToken = default) where TResponse : IOmgLolResponseData { private async Task<TResponse?> Put<TResponse, TData>(string uri, TData data, CancellationToken cancellationToken = default) where TResponse : IOmgLolResponseData {
TResponse? responseData = default(TResponse); TResponse? responseData = default(TResponse);
try { try {
@ -318,14 +231,24 @@ namespace Neighbourhood.omg.lol
public async Task<BasicResponseData?> PostProfile(string address, PostProfile data) => public async Task<BasicResponseData?> PostProfile(string address, PostProfile data) =>
await Post<BasicResponseData, PostProfile>($"/address/{address}/web", data); await Post<BasicResponseData, PostProfile>($"/address/{address}/web", data);
public async Task<Dictionary<string, Theme>?> GetThemes() =>
(await Get<ThemeResponseData>($"/theme/list"))?.Themes;
public async Task<MarkupString?> GetThemePreview(string theme) => public async Task<List<MarkupString>> EphemeralScrape() {
(MarkupString)((await Get<ThemePreviewResponseData>($"/theme/{theme}/preview"))?.Html ?? string.Empty); List<string> notes = new List<string>();
Uri Uri = new Uri($"https://eph.emer.al/");
public async Task<BasicResponseData?> PostProfilePic(string address, FileResult image) => try {
await PostBinary<BasicResponseData, object>($"/address/{address}/pfp", fileResult: image); var response = await _client.GetAsync(Uri);
var str = await response.Content.ReadAsStringAsync();
string pattern = @"<p class=""post"">(.*?)<\/p>";
var matches = Regex.Matches(str, pattern, RegexOptions.IgnoreCase | RegexOptions.Singleline);
foreach (Match match in matches) {
notes.Add(match.Groups[1].Value);
}
}
catch (Exception ex) {
Debug.WriteLine(ex);
}
return notes.Select(s => (MarkupString)s).ToList();
}
public async Task<string?> OAuth(string code, string client_id, string client_secret, string redirect_uri) { public async Task<string?> OAuth(string code, string client_id, string client_secret, string redirect_uri) {
string? token = null; string? token = null;

View file

@ -26,8 +26,6 @@ namespace Neighbourhood.omg.lol {
public List<StatusOrPic>? Feed { get; set; } public List<StatusOrPic>? Feed { get; set; }
public Dictionary<string, Theme>? Themes { get; set; }
// Account data // Account data
private AccountResponseData? _accountInfo; private AccountResponseData? _accountInfo;
public AccountResponseData? AccountInfo { public AccountResponseData? AccountInfo {
@ -307,12 +305,5 @@ namespace Neighbourhood.omg.lol {
return Feed.OrderByDescending(s => s.CreatedTime); return Feed.OrderByDescending(s => s.CreatedTime);
} }
public async Task<Dictionary<string, Theme>?> GetThemes(bool forceRefresh = false) {
if (forceRefresh || this.Themes == null || this.Themes.Count == 0) {
this.Themes = await api.GetThemes();
}
return this.Themes;
}
} }
} }

View file

@ -1,117 +0,0 @@
@inject IJSRuntime JS
@inject State State
@inject RestService api
@inject NavigationManager navigationManager
<div class="overlay @(Active ? "active" : string.Empty)" data-ui="#@id"></div>
<dialog id="@id" class="@(Active ? "active" : string.Empty)" open="@Active">
<h5>Update your profile picture</h5>
<div class="padding center-align">
<img src="@(Base64Url ?? ExistingUrl)" class="small-height square" />
</div>
<div class="row">
<button @onclick="PicFromMedia"><i class="fa-solid fa-image"></i> Select a picture</button>
<button @onclick="PicFromPhoto"><i class="fa-solid fa-camera"></i> Take a photo</button>
</div>
<nav class="right-align no-space">
<button class="transparent link" data-ui="#@id" disabled="@loading">Cancel</button>
<button @onclick="PostPic" disabled="@loading">
@if (loading) {
<span>Uploading...</span>
}
else {
<i class="fa-solid fa-cloud-arrow-up"></i> <span>Upload</span>
}
</button>
</nav>
</dialog>
@code {
[Parameter]
public string? Address { get; set; }
public string ExistingUrl { get => $"https://profiles.cache.lol/{Address ?? ""}/picture"; }
// private IBrowserFile? File { get; set; }
[Parameter]
public string? Base64File { get; set; }
[Parameter]
public long? FileSize { get; set; }
[Parameter]
public string? FileContentType { get; set; }
[Parameter]
public string? id { get; set; }
[Parameter]
public bool Active { get; set; }
private bool loading = false;
private FileResult? File { get; set; }
private string? Base64Url {
get {
if (FileContentType == null || Base64File == null) return null;
return $"data:{FileContentType};base64,{Base64File}";
}
}
public async Task PostPic() {
loading = true;
await InvokeAsync(StateHasChanged);
//TODO: upload the profile pic
//PutPicResponseData? response = await api.PutPic(State.SelectedAddressName!, Base64File!);
if (Base64File != null && File != null)
{
// using var fileStream = await File.OpenReadAsync();
BasicResponseData? response = await api.PostProfilePic(Address!, File);
if (response != null)
{
await JS.InvokeVoidAsync("ui", "#" + id);
// clear input
File = null;
Base64File = null;
FileSize = null;
FileContentType = null;
await JS.InvokeVoidAsync("cacheBust", ExistingUrl);
}
loading = false;
await InvokeAsync(StateHasChanged);
}
}
private string formatSizeUnits(long? bytes) {
if (bytes == null) return "?? bytes";
string formatted = "0 bytes";
if (bytes >= 1073741824) { formatted = $"{(bytes / 1073741824):.##} GB"; }
else if (bytes >= 1048576) { formatted = $"{(bytes / 1048576):.##} MB"; }
else if (bytes >= 1024) { formatted = $"{(bytes / 1024):.##} KB"; }
else if (bytes > 1) { formatted = $"{bytes} bytes"; }
else if (bytes == 1) { formatted = $"{bytes} byte"; }
return formatted;
}
private async Task PicFromMedia(EventArgs e) {
File = await MediaPicker.Default.PickPhotoAsync();
await PopulateFileDetails();
}
private async Task PicFromPhoto(EventArgs e) {
File = await MediaPicker.Default.CapturePhotoAsync();
await PopulateFileDetails();
}
private async Task PopulateFileDetails() {
if (File == null) {
FileContentType = null;
FileSize = null;
Base64File = null;
}
else {
FileContentType = File.ContentType;
FileSize = await Utilities.FileSize(File);
Base64File = await Utilities.Base64FromFile(File);
}
}
}

View file

@ -10,8 +10,6 @@
public string? Url { get; set; } public string? Url { get; set; }
[Parameter] [Parameter]
public string? id { get; set; } public string? id { get; set; }
[Parameter]
public string? SrcString { get; set; }
public MarkupString? Html { get; set; } public MarkupString? Html { get; set; }
protected override async Task OnAfterRenderAsync(bool firstRender) { protected override async Task OnAfterRenderAsync(bool firstRender) {
@ -23,18 +21,11 @@
public async Task Reload() { public async Task Reload() {
if (Url != null){ if (Url != null){
Html = await api.GetHtml(Url); Html = await api.GetHtml(Url);
SrcString = Html?.ToString(); string? HtmlString = Html?.ToString();
} HtmlString = HtmlString?.Replace("</head>", "<base target='_blank'></head>");
if(SrcString != null) { HtmlString = HtmlString?.Replace("</body>", "<script src='https://cdn.jsdelivr.net/npm/@iframe-resizer/child'></script></body>");
SrcString = SrcString?.Replace("</head>", "<base target='_blank'></head>"); Html = (MarkupString)(HtmlString ?? string.Empty);
SrcString = SrcString?.Replace("</body>", "<script src='https://cdn.jsdelivr.net/npm/@iframe-resizer/child'></script></body>");
Html = (MarkupString)(SrcString ?? string.Empty);
} }
await InvokeAsync(StateHasChanged); await InvokeAsync(StateHasChanged);
await IframeResize();
}
public async Task IframeResize() {
await JS.InvokeVoidAsync("iframeResize", new { license = "GPLv3" });
} }
} }

View file

@ -9,6 +9,12 @@
<div class="row"> <div class="row">
<button @onclick="PicFromMedia"><i class="fa-solid fa-image"></i> Select a picture</button> <button @onclick="PicFromMedia"><i class="fa-solid fa-image"></i> Select a picture</button>
<button @onclick="PicFromPhoto"><i class="fa-solid fa-camera"></i> Take a photo</button> <button @onclick="PicFromPhoto"><i class="fa-solid fa-camera"></i> Take a photo</button>
@* <div class="field label prefix border">
<i class="fa-solid fa-image"></i>
<InputFile OnChange="@ChangeFile" accept="image/gif, image/heic, image/heif, image/jpeg, image/png, image/svg+xml, image/webp"></InputFile>
<input type="text">
<label>Select a picture</label>
</div> *@
</div> </div>
<div class="row"> <div class="row">
@if(Base64File != null && FileSize != null){ @if(Base64File != null && FileSize != null){

View file

@ -2,32 +2,14 @@
@inject NavigationManager Nav @inject NavigationManager Nav
@inject RestService api @inject RestService api
@inject State State @inject State State
@inject IJSRuntime JS
<div class="max markdown-editor"> <div class="max markdown-editor">
@if (markdownValue != null) <MarkdownEditor @ref="Editor"
{ @bind-Value="@markdownValue"
<MarkdownEditor @ref="Editor" Theme="material-darker"
@bind-Value="@markdownValue" MaxHeight="100%"
Theme="material-darker" />
MaxHeight="100%"
CustomButtonClicked="@OnCustomButtonClicked">
<Toolbar>
<MarkdownToolbarButton Action="MarkdownAction.Bold" Icon="fa-solid fa-bold" Title="Bold" />
<MarkdownToolbarButton Action="MarkdownAction.Italic" Icon="fa-solid fa-italic" Title="Italic" />
<MarkdownToolbarButton Action="MarkdownAction.Heading" Icon="fa-solid fa-heading" Title="Heading" />
<MarkdownToolbarButton Action="MarkdownAction.Code" Icon="fa-solid fa-code" Title="Code" Separator="true" />
<MarkdownToolbarButton Action="MarkdownAction.Quote" Icon="fa-solid fa-quote-left" Title="Quote" />
<MarkdownToolbarButton Action="MarkdownAction.UnorderedList" Icon="fa-solid fa-list-ul" Title="Unordered List" />
<MarkdownToolbarButton Action="MarkdownAction.OrderedList" Icon="fa-solid fa-list-ol" Title="Ordered List" />
<MarkdownToolbarButton Action="MarkdownAction.Link" Icon="fa-solid fa-link" Title="Link" Separator="true" />
<MarkdownToolbarButton Action="MarkdownAction.Image" Icon="fa-solid fa-image" Title="Image" />
<MarkdownToolbarButton Action="MarkdownAction.HorizontalRule" Icon="fa-solid fa-horizontal-rule" Title="Horizontal Rule" />
<MarkdownToolbarButton Action="MarkdownAction.Guide" Icon="fa-solid fa-circle-question" Title="Guide" Separator="true" />
<MarkdownToolbarButton Action="MarkdownAction.Custom" Icon="omg-icon omg-prami" Title="Editor Information" Name="Help" />
</Toolbar>
</MarkdownEditor>
}
</div> </div>
<nav> <nav>
@ -52,7 +34,7 @@
private bool listed; private bool listed;
private string? markdownValue; private string? markdownValue;
private bool loading = true; private bool loading = false;
protected override async Task OnInitializedAsync() { protected override async Task OnInitializedAsync() {
await base.OnInitializedAsync(); await base.OnInitializedAsync();
@ -61,13 +43,8 @@
{ {
listed = data.Listed == 1; listed = data.Listed == 1;
markdownValue = data.Content; markdownValue = data.Content;
loading = false;
await InvokeAsync(StateHasChanged);
await Editor!.SetValueAsync(markdownValue); await Editor!.SetValueAsync(markdownValue);
} }
loading = false;
await InvokeAsync(StateHasChanged); await InvokeAsync(StateHasChanged);
} }
@ -87,10 +64,4 @@
loading = false; loading = false;
} }
public async Task OnCustomButtonClicked(MarkdownButtonEventArgs eventArgs) {
if (eventArgs.Name == "Help") {
await JS.InvokeVoidAsync("open", "https://home.omg.lol/info/editor", "_blank");
}
}
} }

View file

@ -4,59 +4,46 @@
@inject State State @inject State State
@inject IJSRuntime JS @inject IJSRuntime JS
<div class="max markdown-editor"> <div class="max markdown-editor">
@if (markdownValue != null) <MarkdownEditor @ref="Editor"
{ @bind-Value="@markdownValue"
<MarkdownEditor @ref="Editor" Theme="material-darker"
@bind-Value="@markdownValue" MaxHeight="100%"
Theme="material-darker" CustomButtonClicked="@OnCustomButtonClicked">
MaxHeight="100%" <Toolbar>
CustomButtonClicked="@OnCustomButtonClicked"> <MarkdownToolbarButton Action="MarkdownAction.Bold" Icon="fa-solid fa-bold" Title="Bold" />
<Toolbar> <MarkdownToolbarButton Action="MarkdownAction.Italic" Icon="fa-solid fa-italic" Title="Italic" />
<MarkdownToolbarButton Action="MarkdownAction.Bold" Icon="fa-solid fa-bold" Title="Bold" /> <MarkdownToolbarButton Action="MarkdownAction.Heading" Icon="fa-solid fa-heading" Title="Heading" />
<MarkdownToolbarButton Action="MarkdownAction.Italic" Icon="fa-solid fa-italic" Title="Italic" /> <MarkdownToolbarButton Action="MarkdownAction.Code" Icon="fa-solid fa-code" Title="Code" Separator="true" />
<MarkdownToolbarButton Action="MarkdownAction.Heading" Icon="fa-solid fa-heading" Title="Heading" /> <MarkdownToolbarButton Action="MarkdownAction.Quote" Icon="fa-solid fa-quote-left" Title="Quote" />
<MarkdownToolbarButton Action="MarkdownAction.Code" Icon="fa-solid fa-code" Title="Code" Separator="true" /> <MarkdownToolbarButton Action="MarkdownAction.UnorderedList" Icon="fa-solid fa-list-ul" Title="Unordered List" />
<MarkdownToolbarButton Action="MarkdownAction.Quote" Icon="fa-solid fa-quote-left" Title="Quote" /> <MarkdownToolbarButton Action="MarkdownAction.OrderedList" Icon="fa-solid fa-list-ol" Title="Ordered List" />
<MarkdownToolbarButton Action="MarkdownAction.UnorderedList" Icon="fa-solid fa-list-ul" Title="Unordered List" /> <MarkdownToolbarButton Action="MarkdownAction.Link" Icon="fa-solid fa-link" Title="Link" Separator="true" />
<MarkdownToolbarButton Action="MarkdownAction.OrderedList" Icon="fa-solid fa-list-ol" Title="Ordered List" /> <MarkdownToolbarButton Action="MarkdownAction.Image" Icon="fa-solid fa-image" Title="Image" />
<MarkdownToolbarButton Action="MarkdownAction.Link" Icon="fa-solid fa-link" Title="Link" Separator="true" /> <MarkdownToolbarButton Action="MarkdownAction.HorizontalRule" Icon="fa-solid fa-horizontal-rule" Title="Horizontal Rule" />
<MarkdownToolbarButton Action="MarkdownAction.Image" Icon="fa-solid fa-image" Title="Image" /> <MarkdownToolbarButton Action="MarkdownAction.Guide" Icon="fa-solid fa-circle-question" Title="Guide" Separator="true" />
<MarkdownToolbarButton Action="MarkdownAction.HorizontalRule" Icon="fa-solid fa-horizontal-rule" Title="Horizontal Rule" /> <MarkdownToolbarButton Name="Help" Action="MarkdownAction.Custom"
<MarkdownToolbarButton Action="MarkdownAction.Guide" Icon="fa-solid fa-circle-question" Title="Guide" Separator="true" /> Icon="omg-icon omg-prami"
<MarkdownToolbarButton Action="MarkdownAction.Custom" Icon="omg-icon omg-prami" Title="Editor Information" Name="Help" /> Title="Editor Information" />
</Toolbar> </Toolbar>
</MarkdownEditor> </MarkdownEditor>
}
</div> </div>
@if (markdownValue != null)
{ <details id="advanced">
<details id="advanced"> <summary> Advanced </summary>
<summary> Advanced </summary> <small>Style you include here will be places in a &lt;style&gt; element in your pages &lt;head&gt;.</small>
<h5>Theme:</h5> <div class="field textarea label border max">
<div class="row bottom-margin"> <InputTextArea @bind-Value="css"></InputTextArea>
<ThemeDialog id="theme-modal" onthemechanged="ThemeChanged"></ThemeDialog> <label>Custom CSS</label>
<a data-ui="#theme-modal" class="row min" style="text-decoration:none;"> </div>
@if(selectedTheme != null) { <small>Anything you put here will be included in your pages &lt;head&gt; element.</small>
<ThemeCard theme="selectedTheme"></ThemeCard> <div class="field textarea label border max">
} <InputTextArea @bind-Value="head"></InputTextArea>
else { <label>Additional &lt;head&gt; Content</label>
<button>Choose a theme</button> </div>
} </details>
</a>
</div>
<small>Style you include here will be places in a &lt;style&gt; element in your pages &lt;head&gt;.</small>
<div class="field textarea label border max">
<InputTextArea @bind-Value="css"></InputTextArea>
<label>Custom CSS</label>
</div>
<small>Anything you put here will be included in your pages &lt;head&gt; element.</small>
<div class="field textarea label border max">
<InputTextArea @bind-Value="head"></InputTextArea>
<label>Additional &lt;head&gt; Content</label>
</div>
</details>
}
<nav> <nav>
<div class="max"></div> <div class="max"></div>
<button class="transparent link" onclick="history.back();" disabled="@loading">Cancel</button> <button class="transparent link" onclick="history.back();" disabled="@loading">Cancel</button>
@ -75,11 +62,8 @@
private string? markdownValue; private string? markdownValue;
private string? css; private string? css;
private string? head; private string? head;
private string? theme;
private Theme? selectedTheme;
private Dictionary<string, Theme>? themes;
private bool loading = true; private bool loading = false;
protected override async Task OnInitializedAsync() { protected override async Task OnInitializedAsync() {
await base.OnInitializedAsync(); await base.OnInitializedAsync();
@ -88,17 +72,9 @@
markdownValue = data.Content; markdownValue = data.Content;
css = data.Css; css = data.Css;
head = data.Head; head = data.Head;
theme = data.Theme;
themes = await State.GetThemes();
selectedTheme = themes?[theme];
loading = false;
await InvokeAsync(StateHasChanged);
await Editor!.SetValueAsync(markdownValue); await Editor!.SetValueAsync(markdownValue);
} }
loading = false;
await InvokeAsync(StateHasChanged); await InvokeAsync(StateHasChanged);
} }
@ -113,8 +89,7 @@
new PostProfile() { new PostProfile() {
Content = markdownValue ?? string.Empty, Content = markdownValue ?? string.Empty,
Css = string.IsNullOrEmpty(css) ? null : css, Css = string.IsNullOrEmpty(css) ? null : css,
Head = string.IsNullOrEmpty(head) ? null : head, Head = string.IsNullOrEmpty(head) ? null : head
Theme = string.IsNullOrEmpty(theme) ? null : theme
}); });
if (result != null) { if (result != null) {
await State.RefreshNow(); await State.RefreshNow();
@ -123,7 +98,6 @@
} }
loading = false; loading = false;
await InvokeAsync(StateHasChanged);
} }
public async Task OnCustomButtonClicked(MarkdownButtonEventArgs eventArgs) { public async Task OnCustomButtonClicked(MarkdownButtonEventArgs eventArgs) {
@ -131,10 +105,4 @@
await JS.InvokeVoidAsync("open", "https://home.omg.lol/info/editor", "_blank"); await JS.InvokeVoidAsync("open", "https://home.omg.lol/info/editor", "_blank");
} }
} }
public void ThemeChanged(Theme? _theme) {
theme = _theme?.Id;
selectedTheme = _theme;
InvokeAsync(StateHasChanged);
}
} }

View file

@ -9,13 +9,7 @@
<h3 class="page-heading"><i class="fa-solid fa-fw fa-at"></i>@Address</h3> <h3 class="page-heading"><i class="fa-solid fa-fw fa-at"></i>@Address</h3>
</div> </div>
<div class="row center-align"> <div class="row center-align">
<div class="min"> <img class="profile avatar" src="https://profiles.cache.lol/@Address/picture" alt="@Address" />
@if (IsMe) {
<EditProfilePicDialog id="profile-pic" Address="@Address"></EditProfilePicDialog>
<button data-ui="#profile-pic" class="small circle small-elevate absolute top right no-margin" style="z-index:1;"><i class="fa-solid fa-pencil"></i></button>
}
<img class="profile avatar" src="https://profiles.cache.lol/@Address/picture" alt="@Address" />
</div>
</div> </div>
@if (FeatureFlags.Following) { @if (FeatureFlags.Following) {
<div class="row center-align"> <div class="row center-align">
@ -82,7 +76,7 @@
<StatusList @ref="StatusList" StatusFunc="@(async(refresh) => await State.GetStatuses(Address, refresh))" Editable="@IsMe"></StatusList> <StatusList @ref="StatusList" StatusFunc="@(async(refresh) => await State.GetStatuses(Address, refresh))" Editable="@IsMe"></StatusList>
@if(IsMe) { @if(IsMe) {
<button class="fab circle extra large-elevate" data-ui="#post-modal"> <button class="fab circle extra large-elevate" data-ui="#post-modal">
<i class="fa-solid fa-message-plus square"></i> <i class="fa-solid fa-pen-to-square"></i>
</button> </button>
<NewStatusDialog id="post-modal"></NewStatusDialog> <NewStatusDialog id="post-modal"></NewStatusDialog>
} }
@ -137,7 +131,7 @@
private PicList? PicList { get; set; } private PicList? PicList { get; set; }
private bool IsMe { private bool IsMe {
get => State.AddressList?.Any(a => a.Address == Address) ?? false; get => Address == State.SelectedAddressName;
} }
private MarkupString? bio; private MarkupString? bio;

View file

@ -10,7 +10,7 @@
<AuthorizeView> <AuthorizeView>
<Authorized> <Authorized>
<button class="fab circle extra large-elevate" data-ui="#post-modal"> <button class="fab circle extra large-elevate" data-ui="#post-modal">
<i class="fa-solid fa-message-plus square"></i> <i class="fa-solid fa-pen-to-square"></i>
</button> </button>
<NewStatusDialog id="post-modal" Active="true" Content="@Text"></NewStatusDialog> <NewStatusDialog id="post-modal" Active="true" Content="@Text"></NewStatusDialog>
</Authorized> </Authorized>

View file

@ -11,7 +11,7 @@
<AuthorizeView> <AuthorizeView>
<Authorized> <Authorized>
<button class="fab circle extra large-elevate" data-ui="#post-modal"> <button class="fab circle extra large-elevate" data-ui="#post-modal">
<i class="fa-solid fa-message-plus square"></i> <i class="fa-solid fa-pen-to-square"></i>
</button> </button>
<NewStatusDialog id="post-modal"></NewStatusDialog> <NewStatusDialog id="post-modal"></NewStatusDialog>
</Authorized> </Authorized>

View file

@ -1,11 +0,0 @@
<article class="theme" style="@theme?.PreviewCssData?.BackgroundCss ; @theme?.PreviewCssData?.TextCss">
<h5 class="address">@theme?.Name</h5>
<p class="small theme-author" style="@theme?.PreviewCssData?.LinkCss">
<i class="fa-solid fa-palette" style="@theme?.PreviewCssData?.IconCss"></i> by @theme?.Author
</p>
</article>
@code {
[Parameter]
public Theme? theme { get; set; }
}

View file

@ -1,82 +0,0 @@
@inject IJSRuntime JS
@inject State State
@inject RestService api
@inject NavigationManager navigationManager
<div class="overlay @(Active ? "active" : string.Empty)" data-ui="#@id"></div>
<dialog id="@id" class="@(Active ? "active" : string.Empty)" open="@Active" style="overflow:auto;">
<h5>Choose a theme</h5>
<nav class="wrap max">
@if(themes != null) foreach(Theme theme in themes.Values) {
<a onclick="@(() => ClickTheme(theme))" class="min" style="text-decoration:none;">
<ThemeCard theme="@theme"></ThemeCard>
</a>
}
</nav>
<nav class="right-align no-space">
<button class="transparent link" data-ui="#@id">Cancel</button>
</nav>
</dialog>
<div class="overlay" data-ui="#@previewId"></div>
<dialog id="@previewId" style="overflow:auto;">
<h5 class="address">@activeTheme?.Name</h5>
<div class="max">
<p>@((MarkupString)(activeTheme?.Description ?? string.Empty)) A theme by <a href="@activeTheme?.AuthorUrl" target="_blank">@activeTheme?.Author</a>.</p>
@if(themePreview != null) {
<ExternalPageComponent id="profile_page" @ref="iframe" SrcString="@themePreview.ToString()"></ExternalPageComponent>
}
</div>
<nav class="right-align no-space">
<button class="transparent link" @onclick="CancelPreview">Back</button>
<button @onclick=UseTheme><i class="fa-solid fa-palette"></i> Use the @activeTheme?.Name theme</button>
</nav>
</dialog>
@code {
private Dictionary<string, Theme>? themes;
[Parameter]
public string? id { get; set; }
private string? previewId { get => $"{id}-preview"; }
[Parameter]
public bool Active { get; set; }
[Parameter]
public Action<Theme?>? onthemechanged { get; set; }
private Theme? activeTheme { get; set; }
private MarkupString? themePreview { get; set; }
private ExternalPageComponent iframe { get; set; }
protected override async Task OnInitializedAsync() {
await base.OnInitializedAsync();
activeTheme = null;
themes = await State.GetThemes();
await InvokeAsync(StateHasChanged);
}
public async Task ClickTheme(Theme theme) {
activeTheme = theme;
await InvokeAsync(StateHasChanged);
await JS.InvokeVoidAsync("ui", "#" + id);
await JS.InvokeVoidAsync("ui", "#" + previewId);
themePreview = await api.GetThemePreview(theme.Id);
await InvokeAsync(StateHasChanged);
@* iframe.SrcString = themePreview.ToString(); *@
await iframe.Reload();
}
public async Task CancelPreview() {
activeTheme = null;
await InvokeAsync(StateHasChanged);
await JS.InvokeVoidAsync("ui", "#" + previewId);
await JS.InvokeVoidAsync("ui", "#" + id);
}
public async Task UseTheme() {
// todo: update theme
onthemechanged?.Invoke(activeTheme);
activeTheme = null;
await InvokeAsync(StateHasChanged);
await JS.InvokeVoidAsync("ui", "#" + previewId);
}
}

View file

@ -1,4 +1,10 @@
namespace Neighbourhood.omg.lol.Models { using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Neighbourhood.omg.lol.Models {
public class AddressResponseData : IOmgLolResponseData { public class AddressResponseData : IOmgLolResponseData {
public string Address { get; set; } = string.Empty; public string Address { get; set; } = string.Empty;
public string Message { get; set; } = string.Empty; public string Message { get; set; } = string.Empty;

View file

@ -1,4 +1,10 @@
namespace Neighbourhood.omg.lol.Models { using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Neighbourhood.omg.lol.Models {
public class AddressResponseList : List<AddressResponseData>, IOmgLolResponseList<AddressResponseData> { public class AddressResponseList : List<AddressResponseData>, IOmgLolResponseList<AddressResponseData> {
} }
} }

View file

@ -1,4 +1,10 @@
namespace Neighbourhood.omg.lol.Models { using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Neighbourhood.omg.lol.Models {
public class BasicResponseData : IOmgLolResponseData { public class BasicResponseData : IOmgLolResponseData {
public string Message { get; set; } = string.Empty; public string Message { get; set; } = string.Empty;
} }

View file

@ -1,4 +1,10 @@
namespace Neighbourhood.omg.lol.Models { using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Neighbourhood.omg.lol.Models {
public class DirectoryResponseData : IOmgLolResponseData { public class DirectoryResponseData : IOmgLolResponseData {
public string Message { get; set; } = string.Empty; public string Message { get; set; } = string.Empty;
public string Url { get; set; } = string.Empty; public string Url { get; set; } = string.Empty;

View file

@ -1,4 +1,10 @@
namespace Neighbourhood.omg.lol.Models { using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Neighbourhood.omg.lol.Models {
public class EphemeralData { public class EphemeralData {
public string Content { get; set; } = string.Empty; public string Content { get; set; } = string.Empty;
} }

View file

@ -1,4 +1,10 @@
namespace Neighbourhood.omg.lol.Models { using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Neighbourhood.omg.lol.Models {
public class EphemeralResponseData : IOmgLolResponseData { public class EphemeralResponseData : IOmgLolResponseData {
public string Message { get; set; } = string.Empty; public string Message { get; set; } = string.Empty;
public List<string> Content { get; set; } = new List<string>(); public List<string> Content { get; set; } = new List<string>();

View file

@ -1,4 +1,10 @@
namespace Neighbourhood.omg.lol.Models { using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Neighbourhood.omg.lol.Models {
public interface IOmgLolResponseList<T> : IList<T>, IOmgLolResponseData where T : IOmgLolResponseData { public interface IOmgLolResponseList<T> : IList<T>, IOmgLolResponseData where T : IOmgLolResponseData {
} }
} }

View file

@ -1,4 +1,10 @@
namespace Neighbourhood.omg.lol.Models { using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Neighbourhood.omg.lol.Models {
public class NowContentData { public class NowContentData {
public string? Content { get; set; } public string? Content { get; set; }
public long? Updated { get; set; } public long? Updated { get; set; }

View file

@ -1,4 +1,10 @@
namespace Neighbourhood.omg.lol.Models { using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Neighbourhood.omg.lol.Models {
public class NowPageResponseData : IOmgLolResponseData { public class NowPageResponseData : IOmgLolResponseData {
public string Message { get; set; } = string.Empty; public string Message { get; set; } = string.Empty;
public NowContentData? Now { get; set; } public NowContentData? Now { get; set; }

View file

@ -1,4 +1,10 @@
namespace Neighbourhood.omg.lol.Models { using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Neighbourhood.omg.lol.Models {
public class NowResponseData : IOmgLolResponseData { public class NowResponseData : IOmgLolResponseData {
public string Message { get; set; } = string.Empty; public string Message { get; set; } = string.Empty;
public long Count { get; set; } public long Count { get; set; }

View file

@ -1,4 +1,10 @@
namespace Neighbourhood.omg.lol.Models { using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Neighbourhood.omg.lol.Models {
public class PatchStatus { public class PatchStatus {
public string Id { get; set; } = string.Empty; public string Id { get; set; } = string.Empty;
public string Content { get; set; } = string.Empty; public string Content { get; set; } = string.Empty;

View file

@ -1,4 +1,10 @@
namespace Neighbourhood.omg.lol.Models { using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Neighbourhood.omg.lol.Models {
public class PatchStatusResponseData : IOmgLolResponseData { public class PatchStatusResponseData : IOmgLolResponseData {
public string Message { get; set; } = string.Empty; public string Message { get; set; } = string.Empty;
public string Id { get; set; } = string.Empty; public string Id { get; set; } = string.Empty;

View file

@ -1,4 +1,10 @@
namespace Neighbourhood.omg.lol.Models { using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Neighbourhood.omg.lol.Models {
public class PostPic { public class PostPic {
public string? Description { get; set; } public string? Description { get; set; }
} }

View file

@ -1,8 +1,13 @@
namespace Neighbourhood.omg.lol.Models { using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Neighbourhood.omg.lol.Models {
public class PostProfile { public class PostProfile {
public string Content { get; set; } = string.Empty; public string Content { get; set; } = string.Empty;
public bool Publish { get; set; } = true; public bool Publish { get; set; } = true;
public string? Theme { get; set; }
public string? Css { get; set; } public string? Css { get; set; }
public string? Head { get; set; } public string? Head { get; set; }
} }

View file

@ -1,8 +0,0 @@
namespace Neighbourhood.omg.lol.Models {
public class PreviewCssData {
public string BackgroundCss { get; set; } = string.Empty;
public string TextCss { get; set; } = string.Empty;
public string LinkCss { get; set; } = string.Empty;
public string IconCss { get; set; } = string.Empty;
}
}

View file

@ -1,16 +1,21 @@
namespace Neighbourhood.omg.lol.Models { using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Neighbourhood.omg.lol.Models {
public class ProfileResponseData: IOmgLolResponseData { public class ProfileResponseData: IOmgLolResponseData {
public string Message { get; set; } = string.Empty; public string Message { get; set; } = string.Empty;
public string Content { get; set; } = string.Empty; public string Content { get; set; } = string.Empty;
public string Type { get; set; } = string.Empty; public string Type { get; set; } = string.Empty;
public string Theme { get; set; } = string.Empty; public string Theme { get; set; } = string.Empty;
public string? Css { get; set; } public string Css { get; set; } = string.Empty;
public string? Head { get; set; } public string Head { get; set; } = string.Empty;
public short Verified { get; set; } public string Verified { get; set; } = string.Empty;
public string Pfp { get; set; } = string.Empty; public string Pfp { get; set; } = string.Empty;
public string Metadata { get; set; } = string.Empty; public string Metadata { get; set; } = string.Empty;
public string Branding { get; set; } = string.Empty; public string Branding { get; set; } = string.Empty;
public string? Modified { get; set; }
} }
} }

View file

@ -1,4 +1,10 @@
namespace Neighbourhood.omg.lol.Models { using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Neighbourhood.omg.lol.Models {
public class PutPic { public class PutPic {
public string Pic { get; set; } = string.Empty; public string Pic { get; set; } = string.Empty;
} }

View file

@ -1,4 +1,10 @@
namespace Neighbourhood.omg.lol.Models { using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Neighbourhood.omg.lol.Models {
public class PutPicResponseData : IOmgLolResponseData { public class PutPicResponseData : IOmgLolResponseData {
public string Message { get; set; } = string.Empty; public string Message { get; set; } = string.Empty;
public string Id { get; set; } = string.Empty; public string Id { get; set; } = string.Empty;

View file

@ -1,4 +1,10 @@
namespace Neighbourhood.omg.lol.Models { using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Neighbourhood.omg.lol.Models {
public class SomePicsResponseData : IOmgLolResponseData { public class SomePicsResponseData : IOmgLolResponseData {
public string Message { get; set; } = string.Empty; public string Message { get; set; } = string.Empty;
public List<Pic>? Pics { get; set; } public List<Pic>? Pics { get; set; }

View file

@ -1,4 +1,10 @@
namespace Neighbourhood.omg.lol.Models { using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Neighbourhood.omg.lol.Models {
public class StatusBioResponseData : IOmgLolResponseData { public class StatusBioResponseData : IOmgLolResponseData {
public string Message { get; set; } = string.Empty; public string Message { get; set; } = string.Empty;
public string Bio { get; set; } = string.Empty; public string Bio { get; set; } = string.Empty;

View file

@ -1,4 +1,10 @@
namespace Neighbourhood.omg.lol.Models { using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Neighbourhood.omg.lol.Models {
public class StatusPost { public class StatusPost {
public string? Emoji { get; set; } public string? Emoji { get; set; }
public string? Content { get; set; } public string? Content { get; set; }

View file

@ -1,4 +1,10 @@
namespace Neighbourhood.omg.lol.Models { using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Neighbourhood.omg.lol.Models {
public class StatusPostResponseData : IOmgLolResponseData { public class StatusPostResponseData : IOmgLolResponseData {
public string? Message { get; set; } public string? Message { get; set; }
public string? Id { get; set; } public string? Id { get; set; }

View file

@ -1,4 +1,10 @@
namespace Neighbourhood.omg.lol.Models { using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Neighbourhood.omg.lol.Models {
public class StatusResponseData : IOmgLolResponseData { public class StatusResponseData : IOmgLolResponseData {
public string Message { get; set; } = string.Empty; public string Message { get; set; } = string.Empty;
public List<Status> Statuses { get; set; } = new List<Status>(); public List<Status> Statuses { get; set; } = new List<Status>();

View file

@ -1,6 +0,0 @@
namespace Neighbourhood.omg.lol.Models {
public class ThemePreviewResponseData : IOmgLolResponseData {
public string Message { get; set; } = string.Empty;
public string Html { get; set; } = string.Empty;
}
}

View file

@ -1,6 +0,0 @@
namespace Neighbourhood.omg.lol.Models {
public class ThemeResponseData : IOmgLolResponseData {
public string Message { get; set; } = string.Empty;
public Dictionary<string, Theme> Themes { get; set; } = new Dictionary<string, Theme>();
}
}

View file

@ -1,4 +1,10 @@
namespace Neighbourhood.omg.lol.Models { using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Neighbourhood.omg.lol.Models {
public class StatusOrPic { public class StatusOrPic {
public Status? Status { get; set; } public Status? Status { get; set; }
public Pic? Pic { get; set; } public Pic? Pic { get; set; }

View file

@ -1,19 +0,0 @@
namespace Neighbourhood.omg.lol.Models {
public class Theme {
public string Id { get; set; } = string.Empty;
public string Name { get; set; } = string.Empty;
public string Created { get; set; } = string.Empty;
public string Updated { get; set; } = string.Empty;
public string Author { get; set; } = string.Empty;
public string AuthorUrl { get; set; } = string.Empty;
public string Version { get; set; } = string.Empty;
public string License { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public string PreviewCss { get; set; } = string.Empty;
public string? SampleProfile { get; set; } = string.Empty;
public PreviewCssData? PreviewCssData {
get => new RestService().Deserialize<PreviewCssData>(this.PreviewCss);
}
}
}

View file

@ -1,4 +1,10 @@
namespace Neighbourhood.omg.lol.Models { using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Neighbourhood.omg.lol.Models {
public class TimeData { public class TimeData {
public long? UnixEpochTime { get; set; } public long? UnixEpochTime { get; set; }
public string? Iso8601Time { get; set; } public string? Iso8601Time { get; set; }

View file

@ -103,7 +103,7 @@
<!-- Images --> <!-- Images -->
<MauiImage Include="Resources\Images\*" /> <MauiImage Include="Resources\Images\*" />
<None Update="Resources\Images\dotnet_bot.svg" BaseSize="168,208" /> <MauiImage Update="Resources\Images\dotnet_bot.svg" BaseSize="168,208" />
<!-- Custom Fonts --> <!-- Custom Fonts -->
<MauiFont Include="Resources\Fonts\*" /> <MauiFont Include="Resources\Fonts\*" />
@ -117,11 +117,22 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<MauiAsset Remove="Resources\Raw\AboutAssets.txt" /> <MauiFont Remove="Resources\Fonts\fa-brands-400.ttf" />
</ItemGroup> <MauiFont Remove="Resources\Fonts\fa-brands-400.woff2" />
<MauiFont Remove="Resources\Fonts\fa-duotone-900.ttf" />
<ItemGroup> <MauiFont Remove="Resources\Fonts\fa-duotone-900.woff2" />
<MauiImage Remove="Resources\Images\dotnet_bot.svg" /> <MauiFont Remove="Resources\Fonts\fa-light-300.ttf" />
<MauiFont Remove="Resources\Fonts\fa-light-300.woff2" />
<MauiFont Remove="Resources\Fonts\fa-regular-400.ttf" />
<MauiFont Remove="Resources\Fonts\fa-regular-400.woff2" />
<MauiFont Remove="Resources\Fonts\fa-solid-900.ttf" />
<MauiFont Remove="Resources\Fonts\fa-solid-900.woff2" />
<MauiFont Remove="Resources\Fonts\fa-thin-100.ttf" />
<MauiFont Remove="Resources\Fonts\fa-thin-100.woff2" />
<MauiFont Remove="Resources\Fonts\fa-v4compatibility.ttf" />
<MauiFont Remove="Resources\Fonts\fa-v4compatibility.woff2" />
<MauiFont Remove="Resources\Fonts\omg.lol-icons.woff2" />
<MauiFont Remove="Resources\Fonts\seguiemj.ttf" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@ -143,6 +154,73 @@
<None Remove="Resources\Fonts\seguiemj.ttf" /> <None Remove="Resources\Fonts\seguiemj.ttf" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Content Include="Resources\Fonts\fa-brands-400.ttf">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="Resources\Fonts\fa-brands-400.woff2">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="Resources\Fonts\fa-duotone-900.ttf">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="Resources\Fonts\fa-duotone-900.woff2">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="Resources\Fonts\fa-light-300.ttf">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="Resources\Fonts\fa-light-300.woff2">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="Resources\Fonts\fa-regular-400.ttf">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="Resources\Fonts\fa-regular-400.woff2">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="Resources\Fonts\fa-solid-900.ttf">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="Resources\Fonts\fa-solid-900.woff2">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="Resources\Fonts\fa-thin-100.ttf">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="Resources\Fonts\fa-thin-100.woff2">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="Resources\Fonts\fa-v4compatibility.ttf">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="Resources\Fonts\fa-v4compatibility.woff2">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="Resources\Fonts\omg.lol-icons.woff2">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="Resources\Fonts\seguiemj.ttf">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
</ItemGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="appsettings.json" /> <EmbeddedResource Include="appsettings.json" />
</ItemGroup> </ItemGroup>
@ -162,73 +240,6 @@
<PackageReference Include="PSC.Blazor.Components.MarkdownEditor" Version="8.0.0" /> <PackageReference Include="PSC.Blazor.Components.MarkdownEditor" Version="8.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<MauiFont Update="Resources\Fonts\fa-brands-400.ttf">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</MauiFont>
<MauiFont Update="Resources\Fonts\fa-brands-400.woff2">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</MauiFont>
<MauiFont Update="Resources\Fonts\fa-duotone-900.ttf">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</MauiFont>
<MauiFont Update="Resources\Fonts\fa-duotone-900.woff2">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</MauiFont>
<MauiFont Update="Resources\Fonts\fa-light-300.ttf">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</MauiFont>
<MauiFont Update="Resources\Fonts\fa-light-300.woff2">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</MauiFont>
<MauiFont Update="Resources\Fonts\fa-regular-400.ttf">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</MauiFont>
<MauiFont Update="Resources\Fonts\fa-regular-400.woff2">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</MauiFont>
<MauiFont Update="Resources\Fonts\fa-solid-900.ttf">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</MauiFont>
<MauiFont Update="Resources\Fonts\fa-solid-900.woff2">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</MauiFont>
<MauiFont Update="Resources\Fonts\fa-thin-100.ttf">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</MauiFont>
<MauiFont Update="Resources\Fonts\fa-thin-100.woff2">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</MauiFont>
<MauiFont Update="Resources\Fonts\fa-v4compatibility.ttf">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</MauiFont>
<MauiFont Update="Resources\Fonts\fa-v4compatibility.woff2">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</MauiFont>
<MauiFont Update="Resources\Fonts\omg.lol-icons.woff2">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</MauiFont>
<MauiFont Update="Resources\Fonts\seguiemj.ttf">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</MauiFont>
</ItemGroup>
<ItemGroup> <ItemGroup>
<MauiXaml Update="XamlComponents\AppShell.xaml"> <MauiXaml Update="XamlComponents\AppShell.xaml">
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>

View file

@ -4,11 +4,11 @@
@import url(../vendor/fluent-emoji/SegoeUIEmoji.css); @import url(../vendor/fluent-emoji/SegoeUIEmoji.css);
/*@import url(../vendor/fluent-emoji/3d.css);*/ /*@import url(../vendor/fluent-emoji/3d.css);*/
@import url(../vendor/fluent-emoji/animated-optional.css); @import url(../vendor/fluent-emoji/animated-optional.css);
@import url(../vendor/TwemojiCountryFlags.css); @import url(../vendor/TossFaceEmojiFlags.css);
:root { :root {
--emoji-font: TwemojiCountryFlags, SegoeUIEmoji, 'Segoe UI Emoji', 'Noto Color Emoji', 'Apple Color Emoji', 'Segoe UI Symbol'; --emoji-font: TossFaceEmojiFlags, SegoeUIEmoji, 'Segoe UI Emoji', 'Noto Color Emoji', 'Apple Color Emoji', 'Segoe UI Symbol';
--font: 'Lato', 'Helvetica Neue', Helvetica, Arial, sans-serif, var(--emoji-font); --font: 'Lato', 'Helvetica Neue', Helvetica, Arial, sans-serif, var(--emoji-font);
--prami-svg: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500"><path fill="%23FF6BAE" stroke="none" d="M250 450C211.612 450 173.225 435.354 143.934 406.066L43.9346 306.066C-14.6446 247.487 -14.6446 152.513 43.9346 93.9341C100.533 37.3361 191.104 35.421 250 88.1907C308.898 35.4229 399.47 37.3379 456.066 93.9341C514.645 152.513 514.645 247.487 456.066 306.066L356.066 406.066C326.778 435.354 288.389 450 250 450" /><path fill="none" stroke="%23471036" stroke-width="19" stroke-linecap="round" d="M208.749 223.817C227.625 254.619 272.376 254.619 291.251 223.817" /><circle fill="%23471036" cx="291.3" cy="176.6" r="17.75" /><circle fill="%23471036" cx="208.6" cy="176.6" r="17.75" /><circle fill="%23E24097" cx="120.3" cy="212" r="59.2" /><circle fill="%23E24097" cx="379.7" cy="212" r="59.2" /></svg>'); --prami-svg: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500"><path fill="%23FF6BAE" stroke="none" d="M250 450C211.612 450 173.225 435.354 143.934 406.066L43.9346 306.066C-14.6446 247.487 -14.6446 152.513 43.9346 93.9341C100.533 37.3361 191.104 35.421 250 88.1907C308.898 35.4229 399.47 37.3379 456.066 93.9341C514.645 152.513 514.645 247.487 456.066 306.066L356.066 406.066C326.778 435.354 288.389 450 250 450" /><path fill="none" stroke="%23471036" stroke-width="19" stroke-linecap="round" d="M208.749 223.817C227.625 254.619 272.376 254.619 291.251 223.817" /><circle fill="%23471036" cx="291.3" cy="176.6" r="17.75" /><circle fill="%23471036" cx="208.6" cy="176.6" r="17.75" /><circle fill="%23E24097" cx="120.3" cy="212" r="59.2" /><circle fill="%23E24097" cx="379.7" cy="212" r="59.2" /></svg>');
--spacing: 1.5rem; --spacing: 1.5rem;
@ -319,7 +319,7 @@ article.ephemeral {
flex-direction:column; flex-direction:column;
flex-grow: 1; flex-grow: 1;
} }
iframe { #now.page > iframe {
width:100%; width:100%;
flex-grow: 1; flex-grow: 1;
border: none; border: none;
@ -501,9 +501,4 @@ article.now {
background-color: #212121; background-color: #212121;
color: #EEFFFF; color: #EEFFFF;
font-family: monospace; font-family: monospace;
}
article.theme {
height: 10rem;
width: 15rem;
} }

View file

@ -28,20 +28,4 @@ function toggleDetails(id) {
const element = document.getElementById(id) const element = document.getElementById(id)
if (element instanceof HTMLDetailsElement) if (element instanceof HTMLDetailsElement)
element.open = !element.open element.open = !element.open
}
function cacheBust(url) {
fetch(new Request(url), {
headers: new Headers({
"pragma": "no-cache",
"cache-control": "no-cache"
}),
mode: 'no-cors',
cache: 'no-cache',
})
.finally(() => {
let els = document.querySelectorAll(`[src="${url}"]`)
els.forEach(el => el.removeAttribute('src'))
els.forEach(el => el.src = url)
})
} }

5
wwwroot/vendor/TossFaceEmojiFlags.css vendored Normal file
View file

@ -0,0 +1,5 @@
@font-face {
font-family: TossFaceEmojiFlags;
src: url(TossFaceFontWeb.otf);
unicode-range: U+1F1E6-1F1FF;
}

BIN
wwwroot/vendor/TossFaceFontWeb.otf vendored Normal file

Binary file not shown.

View file

@ -1,5 +0,0 @@
@font-face {
font-family: TwemojiCountryFlags;
src: url(TwemojiCountryFlags.woff2);
unicode-range: U+1F1E6-1F1FF;
}

Binary file not shown.