[sln] Code cleanup

This commit is contained in:
Laura Hausmann 2024-09-13 21:21:52 +02:00
parent 28b57e35a3
commit afe62b0aab
No known key found for this signature in database
GPG key ID: D044E84C5BE01605
95 changed files with 580 additions and 523 deletions

View file

@ -16,7 +16,11 @@ namespace Iceshrimp.Backend.Controllers.Federation;
[Route("/nodeinfo")] [Route("/nodeinfo")]
[EnableCors("well-known")] [EnableCors("well-known")]
[Produces(MediaTypeNames.Application.Json)] [Produces(MediaTypeNames.Application.Json)]
public class NodeInfoController(IOptions<Config.InstanceSection> instanceConfig, IOptions<Config.StorageSection> storageConfig, DatabaseContext db) : ControllerBase public class NodeInfoController(
IOptions<Config.InstanceSection> instanceConfig,
IOptions<Config.StorageSection> storageConfig,
DatabaseContext db
) : ControllerBase
{ {
[HttpGet("2.1")] [HttpGet("2.1")]
[HttpGet("2.0")] [HttpGet("2.0")]
@ -92,30 +96,30 @@ public class NodeInfoController(IOptions<Config.InstanceSection> instanceConfig,
EnableGithubIntegration = false, EnableGithubIntegration = false,
EnableDiscordIntegration = false, EnableDiscordIntegration = false,
EnableEmail = false, EnableEmail = false,
PublicTimelineVisibility = new() { PublicTimelineVisibility = new NodeInfoResponse.PleromaPublicTimelineVisibility
{
Bubble = false, Bubble = false,
Federated = false, Federated = false,
Local = false, Local = false
}, },
UploadLimits = new() { // @formatter:off
UploadLimits = new NodeInfoResponse.PleromaUploadLimits
{
General = maxUploadSize, General = maxUploadSize,
Avatar = maxUploadSize, Avatar = maxUploadSize,
Background = maxUploadSize, Background = maxUploadSize,
Banner = maxUploadSize, Banner = maxUploadSize
}, },
Suggestions = new() { // @formatter:on
Enabled = false Suggestions = new NodeInfoResponse.PleromaSuggestions { Enabled = false },
}, Federation = new NodeInfoResponse.PleromaFederation { Enabled = true }
Federation = new() {
Enabled = true
}
}, },
OpenRegistrations = false OpenRegistrations = false
}; };
} }
[HttpGet("2.0.json")] [HttpGet("2.0.json")]
public IActionResult GetNodeInfoAkkoFE() public IActionResult GetNodeInfoAkkoma()
{ {
return Redirect("/nodeinfo/2.0"); return Redirect("/nodeinfo/2.0");
} }

View file

@ -96,7 +96,7 @@ public class ConversationsController(
var user = HttpContext.GetUserOrFail(); var user = HttpContext.GetUserOrFail();
var conversation = await db.Conversations(user) var conversation = await db.Conversations(user)
.IncludeCommonProperties() .IncludeCommonProperties()
.Where(p => (p.ThreadIdOrId) == id) .Where(p => p.ThreadIdOrId == id)
.Select(p => new Conversation .Select(p => new Conversation
{ {
Id = p.ThreadIdOrId, Id = p.ThreadIdOrId,

View file

@ -3,6 +3,7 @@ using System.Net.Mime;
using Iceshrimp.Backend.Controllers.Mastodon.Attributes; using Iceshrimp.Backend.Controllers.Mastodon.Attributes;
using Iceshrimp.Backend.Controllers.Mastodon.Schemas; using Iceshrimp.Backend.Controllers.Mastodon.Schemas;
using Iceshrimp.Backend.Controllers.Mastodon.Schemas.Entities; using Iceshrimp.Backend.Controllers.Mastodon.Schemas.Entities;
using Iceshrimp.Backend.Controllers.Pleroma.Schemas.Entities;
using Iceshrimp.Backend.Controllers.Shared.Attributes; using Iceshrimp.Backend.Controllers.Shared.Attributes;
using Iceshrimp.Backend.Core.Configuration; using Iceshrimp.Backend.Core.Configuration;
using Iceshrimp.Backend.Core.Database; using Iceshrimp.Backend.Core.Database;
@ -33,15 +34,14 @@ public class InstanceController(DatabaseContext db, MetaService meta) : Controll
var (instanceName, instanceDescription, adminContact) = var (instanceName, instanceDescription, adminContact) =
await meta.GetMany(MetaEntity.InstanceName, MetaEntity.InstanceDescription, MetaEntity.AdminContactEmail); await meta.GetMany(MetaEntity.InstanceName, MetaEntity.InstanceDescription, MetaEntity.AdminContactEmail);
var vapidKey = await meta.Get(MetaEntity.VapidPublicKey); // can't merge with above call since they're all nullable and this is not.
// can't merge with above call since they're all nullable and this is not.
var vapidKey = await meta.Get(MetaEntity.VapidPublicKey);
return new InstanceInfoV1Response(config.Value, instanceName, instanceDescription, adminContact) return new InstanceInfoV1Response(config.Value, instanceName, instanceDescription, adminContact)
{ {
Stats = new InstanceStats(userCount, noteCount, instanceCount), Stats = new InstanceStats(userCount, noteCount, instanceCount),
Pleroma = new() { Pleroma = new PleromaInstanceExtensions { VapidPublicKey = vapidKey, Metadata = new InstanceMetadata() }
VapidPublicKey = vapidKey,
Metadata = new(),
}
}; };
} }

View file

@ -76,7 +76,7 @@ public class NotificationController(DatabaseContext db, NotificationRenderer not
.FirstOrDefaultAsync() ?? .FirstOrDefaultAsync() ??
throw GracefulException.RecordNotFound(); throw GracefulException.RecordNotFound();
var res = await notificationRenderer.RenderAsync(notification.EnforceRenoteReplyVisibility(p => p.Note), user, isPleroma); return await notificationRenderer.RenderAsync(notification.EnforceRenoteReplyVisibility(p => p.Note),
return res; user, isPleroma);
} }
} }

View file

@ -1,4 +1,5 @@
using Iceshrimp.Backend.Controllers.Mastodon.Schemas.Entities; using Iceshrimp.Backend.Controllers.Mastodon.Schemas.Entities;
using Iceshrimp.Backend.Controllers.Pleroma.Schemas.Entities;
using Iceshrimp.Backend.Core.Configuration; using Iceshrimp.Backend.Core.Configuration;
using Iceshrimp.Backend.Core.Database; using Iceshrimp.Backend.Core.Database;
using Iceshrimp.Backend.Core.Database.Tables; using Iceshrimp.Backend.Core.Database.Tables;
@ -162,10 +163,7 @@ public class NoteRenderer(
Poll = poll, Poll = poll,
Reactions = reactions, Reactions = reactions,
Filtered = filterResult, Filtered = filterResult,
Pleroma = new() { Pleroma = new PleromaStatusExtensions { Reactions = reactions, ConversationId = note.ThreadIdOrId }
Reactions = reactions,
ConversationId = note.ThreadIdOrId
}
}; };
return res; return res;
@ -321,7 +319,11 @@ public class NoteRenderer(
db.NoteReactions.Any(i => i.NoteId == p.First().NoteId && db.NoteReactions.Any(i => i.NoteId == p.First().NoteId &&
i.Reaction == p.First().Reaction && i.Reaction == p.First().Reaction &&
i.User == user), i.User == user),
AccountIds = db.NoteReactions.Where(i => i.NoteId == p.First().NoteId && p.Select(r => r.Id).Contains(i.Id)).Select(i => i.UserId).ToList() AccountIds = db.NoteReactions
.Where(i => i.NoteId == p.First().NoteId &&
p.Select(r => r.Id).Contains(i.Id))
.Select(i => i.UserId)
.ToList()
}) })
.ToListAsync(); .ToListAsync();

View file

@ -1,4 +1,5 @@
using Iceshrimp.Backend.Controllers.Mastodon.Schemas.Entities; using Iceshrimp.Backend.Controllers.Mastodon.Schemas.Entities;
using Iceshrimp.Backend.Controllers.Pleroma.Schemas.Entities;
using Iceshrimp.Backend.Core.Database; using Iceshrimp.Backend.Core.Database;
using Iceshrimp.Backend.Core.Database.Tables; using Iceshrimp.Backend.Core.Database.Tables;
using Iceshrimp.Backend.Core.Extensions; using Iceshrimp.Backend.Core.Extensions;
@ -29,7 +30,8 @@ public class NotificationRenderer(DatabaseContext db, NoteRenderer noteRenderer,
await userRenderer.RenderAsync(dbNotifier); await userRenderer.RenderAsync(dbNotifier);
string? emojiUrl = null; string? emojiUrl = null;
if (notification.Reaction != null) { if (notification.Reaction != null)
{
// explicitly check to skip another database call if url is actually null // explicitly check to skip another database call if url is actually null
if (emojiUrls != null) if (emojiUrls != null)
{ {
@ -38,7 +40,10 @@ public class NotificationRenderer(DatabaseContext db, NoteRenderer noteRenderer,
else if (EmojiService.IsCustomEmoji(notification.Reaction)) else if (EmojiService.IsCustomEmoji(notification.Reaction))
{ {
var parts = notification.Reaction.Trim(':').Split('@'); var parts = notification.Reaction.Trim(':').Split('@');
emojiUrl = await db.Emojis.Where(e => e.Name == parts[0] && e.Host == (parts.Length > 1 ? parts[1] : null)).Select(e => e.PublicUrl).FirstOrDefaultAsync(); emojiUrl = await db.Emojis
.Where(e => e.Name == parts[0] && e.Host == (parts.Length > 1 ? parts[1] : null))
.Select(e => e.PublicUrl)
.FirstOrDefaultAsync();
} }
} }
@ -51,9 +56,7 @@ public class NotificationRenderer(DatabaseContext db, NoteRenderer noteRenderer,
CreatedAt = notification.CreatedAt.ToStringIso8601Like(), CreatedAt = notification.CreatedAt.ToStringIso8601Like(),
Emoji = notification.Reaction, Emoji = notification.Reaction,
EmojiUrl = emojiUrl, EmojiUrl = emojiUrl,
Pleroma = new() { Pleroma = new PleromaNotificationExtensions { IsSeen = notification.IsRead }
IsSeen = notification.IsRead
}
}; };
return res; return res;
@ -85,20 +88,28 @@ public class NotificationRenderer(DatabaseContext db, NoteRenderer noteRenderer,
.DistinctBy(p => p.Id), .DistinctBy(p => p.Id),
user, Filter.FilterContext.Notifications, accounts); user, Filter.FilterContext.Notifications, accounts);
var parts = notifications.Where(p => p.Reaction != null && EmojiService.IsCustomEmoji(p.Reaction)).Select(p => { var parts = notificationList.Where(p => p.Reaction != null && EmojiService.IsCustomEmoji(p.Reaction))
.Select(p =>
{
var parts = p.Reaction!.Trim(':').Split('@'); var parts = p.Reaction!.Trim(':').Split('@');
return new { Name = parts[0], Host = parts.Length > 1 ? parts[1] : null }; return new { Name = parts[0], Host = parts.Length > 1 ? parts[1] : null };
}); });
// https://github.com/dotnet/efcore/issues/31492 // https://github.com/dotnet/efcore/issues/31492
//TODO: is there a better way of expressing this using LINQ?
IQueryable<Emoji> urlQ = db.Emojis; IQueryable<Emoji> urlQ = db.Emojis;
foreach (var part in parts) foreach (var part in parts)
urlQ = urlQ.Concat(db.Emojis.Where(e => e.Name == part.Name && e.Host == part.Host)); urlQ = urlQ.Concat(db.Emojis.Where(e => e.Name == part.Name && e.Host == part.Host));
var emojiUrls = (await urlQ
.Select(e => new { Name = $":{e.Name}{(e.Host != null ? "@" + e.Host : "")}:", Url = e.PublicUrl }) //TODO: can we somehow optimize this to do the dedupe database side?
.ToArrayAsync()) var emojiUrls = await urlQ.Select(e => new
.DistinctBy(e => e.Name) {
.ToDictionary(e => e.Name, e => e.Url); Name = $":{e.Name}{(e.Host != null ? "@" + e.Host : "")}:",
Url = e.PublicUrl
})
.ToArrayAsync()
.ContinueWithResult(res => res.DistinctBy(e => e.Name)
.ToDictionary(e => e.Name, e => e.Url));
return await notificationList return await notificationList
.Select(p => RenderAsync(p, user, isPleroma, accounts, notes, emojiUrls)) .Select(p => RenderAsync(p, user, isPleroma, accounts, notes, emojiUrls))

View file

@ -1,8 +1,8 @@
using Iceshrimp.Backend.Controllers.Pleroma.Schemas.Entities;
using Iceshrimp.Backend.Core.Database; using Iceshrimp.Backend.Core.Database;
using Iceshrimp.Backend.Core.Middleware; using Iceshrimp.Backend.Core.Middleware;
using J = System.Text.Json.Serialization.JsonPropertyNameAttribute; using J = System.Text.Json.Serialization.JsonPropertyNameAttribute;
using static Iceshrimp.Backend.Core.Database.Tables.Notification; using static Iceshrimp.Backend.Core.Database.Tables.Notification;
using Iceshrimp.Backend.Controllers.Pleroma.Schemas.Entities;
namespace Iceshrimp.Backend.Controllers.Mastodon.Schemas.Entities; namespace Iceshrimp.Backend.Controllers.Mastodon.Schemas.Entities;

View file

@ -45,9 +45,7 @@ public abstract class StatusSchemas
[B(Name = "poll")] [J("poll")] public PollData? Poll { get; set; } [B(Name = "poll")] [J("poll")] public PollData? Poll { get; set; }
[B(Name = "preview")] [B(Name = "preview")] [J("preview")] public bool Preview { get; set; } = false;
[J("preview")]
public bool Preview { get; set; } = false;
public class PollData public class PollData
{ {

View file

@ -21,11 +21,13 @@ public class EmojiController(DatabaseContext db) : ControllerBase
[ProducesResults(HttpStatusCode.OK)] [ProducesResults(HttpStatusCode.OK)]
public async Task<Dictionary<string, PleromaEmojiEntity>> GetCustomEmojis() public async Task<Dictionary<string, PleromaEmojiEntity>> GetCustomEmojis()
{ {
var emoji = await db.Emojis.Where(p => p.Host == null) var emoji = await db.Emojis
.Select(p => KeyValuePair.Create(p.Name, new PleromaEmojiEntity .Where(p => p.Host == null)
.Select(p => KeyValuePair.Create(p.Name,
new PleromaEmojiEntity
{ {
ImageUrl = p.PublicUrl, ImageUrl = p.PublicUrl,
Tags = new string[] { p.Category == null ? "" : p.Category } Tags = new[] { p.Category ?? "" }
})) }))
.ToArrayAsync(); .ToArrayAsync();

View file

@ -4,10 +4,8 @@ using Iceshrimp.Backend.Controllers.Mastodon.Attributes;
using Iceshrimp.Backend.Controllers.Mastodon.Renderers; using Iceshrimp.Backend.Controllers.Mastodon.Renderers;
using Iceshrimp.Backend.Controllers.Mastodon.Schemas.Entities; using Iceshrimp.Backend.Controllers.Mastodon.Schemas.Entities;
using Iceshrimp.Backend.Controllers.Pleroma.Schemas; using Iceshrimp.Backend.Controllers.Pleroma.Schemas;
using Iceshrimp.Backend.Controllers.Pleroma.Schemas.Entities;
using Iceshrimp.Backend.Controllers.Shared.Attributes; using Iceshrimp.Backend.Controllers.Shared.Attributes;
using Iceshrimp.Backend.Core.Database; using Iceshrimp.Backend.Core.Database;
using Iceshrimp.Backend.Core.Database.Tables;
using Iceshrimp.Backend.Core.Extensions; using Iceshrimp.Backend.Core.Extensions;
using Iceshrimp.Backend.Core.Middleware; using Iceshrimp.Backend.Core.Middleware;
using Microsoft.AspNetCore.Cors; using Microsoft.AspNetCore.Cors;
@ -27,17 +25,17 @@ public class NotificationController(DatabaseContext db, NotificationRenderer not
[HttpPost("/api/v1/pleroma/notifications/read")] [HttpPost("/api/v1/pleroma/notifications/read")]
[Authorize("read:notifications")] [Authorize("read:notifications")]
[ProducesResults(HttpStatusCode.OK)] [ProducesResults(HttpStatusCode.OK)]
public async Task<List<NotificationEntity>> MarkNotificationsAsRead([FromHybrid] PleromaNotificationSchemas.ReadNotificationsRequest request) public async Task<List<NotificationEntity>> MarkNotificationsAsRead(
[FromHybrid] PleromaNotificationSchemas.ReadNotificationsRequest request
)
{ {
var user = HttpContext.GetUserOrFail(); var user = HttpContext.GetUserOrFail();
if (request.Id != null && request.MaxId != null) if (request is { Id: not null, MaxId: not null })
throw GracefulException.BadRequest("id and max_id are mutually exclusive."); throw GracefulException.BadRequest("id and max_id are mutually exclusive.");
var q = db.Notifications var q = db.Notifications
.IncludeCommonProperties() .IncludeCommonProperties()
.Include(p => p.Notifier)
.Include(p => p.Note)
.Where(p => p.Notifiee == user) .Where(p => p.Notifiee == user)
.Where(p => p.Notifier != null) .Where(p => p.Notifier != null)
.Where(p => !p.IsRead) .Where(p => !p.IsRead)

View file

@ -11,7 +11,22 @@ public class PleromaInstanceExtensions
public class InstanceMetadata public class InstanceMetadata
{ {
[J("post_formats")] public string[] PostFormats => ["text/plain", "text/x.misskeymarkdown"]; [J("post_formats")] public string[] PostFormats => ["text/plain", "text/x.misskeymarkdown"];
[J("features")] public string[] Features => ["pleroma_api", "akkoma_api", "mastodon_api", "mastodon_api_streaming", "polls", "quote_posting", "editing", "pleroma_emoji_reactions", "exposable_reactions", "custom_emoji_reactions"];
[J("features")]
public string[] Features =>
[
"pleroma_api",
"akkoma_api",
"mastodon_api",
"mastodon_api_streaming",
"polls",
"quote_posting",
"editing",
"pleroma_emoji_reactions",
"exposable_reactions",
"custom_emoji_reactions"
];
[J("fields_limits")] public FieldsLimits FieldsLimits => new(); [J("fields_limits")] public FieldsLimits FieldsLimits => new();
} }

View file

@ -2,13 +2,12 @@ using J = System.Text.Json.Serialization.JsonPropertyNameAttribute;
namespace Iceshrimp.Backend.Controllers.Pleroma.Schemas; namespace Iceshrimp.Backend.Controllers.Pleroma.Schemas;
public class FrontendConfigurationsResponse() public class FrontendConfigurationsResponse
{ {
[J("pleroma_fe")] public PleromaFeConfiguration PleromaFe => new(); [J("pleroma_fe")] public PleromaFeConfiguration PleromaFe => new();
} }
public class PleromaFeConfiguration
public class PleromaFeConfiguration()
{ {
[J("loginMethod")] public string LoginMethod => "token"; [J("loginMethod")] public string LoginMethod => "token";
[J("useStreamingApi")] public bool UseStreamingApi => true; [J("useStreamingApi")] public bool UseStreamingApi => true;

View file

@ -2,12 +2,10 @@ using System.Net;
using System.Net.Mime; using System.Net.Mime;
using Iceshrimp.Backend.Controllers.Mastodon.Attributes; using Iceshrimp.Backend.Controllers.Mastodon.Attributes;
using Iceshrimp.Backend.Controllers.Mastodon.Renderers; using Iceshrimp.Backend.Controllers.Mastodon.Renderers;
using Iceshrimp.Backend.Controllers.Mastodon.Schemas;
using Iceshrimp.Backend.Controllers.Mastodon.Schemas.Entities; using Iceshrimp.Backend.Controllers.Mastodon.Schemas.Entities;
using Iceshrimp.Backend.Controllers.Shared.Attributes; using Iceshrimp.Backend.Controllers.Shared.Attributes;
using Iceshrimp.Backend.Core.Configuration; using Iceshrimp.Backend.Core.Configuration;
using Iceshrimp.Backend.Core.Database; using Iceshrimp.Backend.Core.Database;
using Iceshrimp.Backend.Core.Database.Tables;
using Iceshrimp.Backend.Core.Extensions; using Iceshrimp.Backend.Core.Extensions;
using Iceshrimp.Backend.Core.Middleware; using Iceshrimp.Backend.Core.Middleware;
using Iceshrimp.Backend.Core.Services; using Iceshrimp.Backend.Core.Services;

View file

@ -31,7 +31,6 @@ public class IdenticonController : ControllerBase
var gradient = new LinearGradientBrush(new Point(0, 0), new Point(Size, Size), GradientRepetitionMode.None, var gradient = new LinearGradientBrush(new Point(0, 0), new Point(Size, Size), GradientRepetitionMode.None,
new ColorStop(0, color.start), new ColorStop(1, color.end)); new ColorStop(0, color.start), new ColorStop(1, color.end));
image.Mutate(p => p.Fill(gradient)); image.Mutate(p => p.Fill(gradient));
var paint = new SolidBrush(Color.White); var paint = new SolidBrush(Color.White);

View file

@ -12,7 +12,7 @@ public static class FilterRenderer
Expiry = filter.Expiry, Expiry = filter.Expiry,
Keywords = filter.Keywords, Keywords = filter.Keywords,
Action = (FilterResponse.FilterAction)filter.Action, Action = (FilterResponse.FilterAction)filter.Action,
Contexts = filter.Contexts.Cast<FilterResponse.FilterContext>().ToList(), Contexts = filter.Contexts.Cast<FilterResponse.FilterContext>().ToList()
}; };
public static IEnumerable<FilterResponse> RenderMany(IEnumerable<Filter> filters) => filters.Select(RenderOne); public static IEnumerable<FilterResponse> RenderMany(IEnumerable<Filter> filters) => filters.Select(RenderOne);

View file

@ -30,7 +30,7 @@ public class SettingsController(DatabaseContext db) : ControllerBase
AlwaysMarkSensitive = settings.AlwaysMarkSensitive, AlwaysMarkSensitive = settings.AlwaysMarkSensitive,
AutoAcceptFollowed = settings.AutoAcceptFollowed, AutoAcceptFollowed = settings.AutoAcceptFollowed,
DefaultNoteVisibility = (NoteVisibility)settings.DefaultNoteVisibility, DefaultNoteVisibility = (NoteVisibility)settings.DefaultNoteVisibility,
DefaultRenoteVisibility = (NoteVisibility)settings.DefaultNoteVisibility, DefaultRenoteVisibility = (NoteVisibility)settings.DefaultNoteVisibility
}; };
} }

View file

@ -537,7 +537,7 @@ public static class QueryableExtensions
) )
{ {
if (request.ExcludeReplies) if (request.ExcludeReplies)
query = query.Where(p => p.Reply == null && p.ReplyUri == null || p.ReplyUserId == p.UserId); query = query.Where(p => (p.Reply == null && p.ReplyUri == null) || p.ReplyUserId == p.UserId);
if (request.ExcludeRenotes) if (request.ExcludeRenotes)
query = query.Where(p => p.Renote == null && p.RenoteUri == null); query = query.Where(p => p.Renote == null && p.RenoteUri == null);
if (request.Tagged != null) if (request.Tagged != null)

View file

@ -112,8 +112,24 @@ public class NodeInfoResponse
[J("enableEmail")] public bool? EnableEmail { get; set; } [J("enableEmail")] public bool? EnableEmail { get; set; }
[J("post_formats")] public string[] PostFormats => ["text/plain", "text/x.misskeymarkdown"]; [J("post_formats")] public string[] PostFormats => ["text/plain", "text/x.misskeymarkdown"];
[J("features")] public string[] Features => ["pleroma_api", "akkoma_api", "mastodon_api", "mastodon_api_streaming", "polls", "quote_posting", "editing", "pleroma_emoji_reactions", "exposable_reactions", "custom_emoji_reactions"];
[J("features")]
public string[] Features =>
[
"pleroma_api",
"akkoma_api",
"mastodon_api",
"mastodon_api_streaming",
"polls",
"quote_posting",
"editing",
"pleroma_emoji_reactions",
"exposable_reactions",
"custom_emoji_reactions"
];
[J("localBubbleInstances")] public string[] LocalBubbleInstances { get; set; } = []; [J("localBubbleInstances")] public string[] LocalBubbleInstances { get; set; } = [];
// TODO: list of ap object ids i believe? // TODO: list of ap object ids i believe?
[J("staffAccounts")] public string[] StaffAccounts { get; set; } = []; [J("staffAccounts")] public string[] StaffAccounts { get; set; } = [];

View file

@ -64,7 +64,7 @@ public static class BlurhashHelper
var dc = factors[0]; var dc = factors[0];
var acCount = componentsX * componentsY - 1; var acCount = componentsX * componentsY - 1;
var sizeFlag = (componentsX - 1) + (componentsY - 1) * 9; var sizeFlag = componentsX - 1 + (componentsY - 1) * 9;
sizeFlag.EncodeBase83(resultBuffer[..1]); sizeFlag.EncodeBase83(resultBuffer[..1]);
float maximumValue; float maximumValue;

View file

@ -1,4 +1,5 @@
using System.IO.Compression; using System.IO.Compression;
using System.Net;
using System.Text.Json; using System.Text.Json;
using Iceshrimp.Backend.Core.Middleware; using Iceshrimp.Backend.Core.Middleware;
using Microsoft.AspNetCore.StaticFiles; using Microsoft.AspNetCore.StaticFiles;
@ -39,16 +40,18 @@ public class EmojiImportService(
try try
{ {
var meta = archive.GetEntry("meta.json") var meta = archive.GetEntry("meta.json") ??
?? throw GracefulException.BadRequest("Invalid emoji zip. Only Misskey-style emoji zips are supported."); throw GracefulException
.BadRequest("Invalid emoji zip. Only Misskey-style emoji zips are supported.");
var metaJson = await JsonSerializer.DeserializeAsync<EmojiZipMeta>(meta.Open(), SerializerOptions) var metaJson = await JsonSerializer.DeserializeAsync<EmojiZipMeta>(meta.Open(), SerializerOptions) ??
?? throw GracefulException.BadRequest("Invalid emoji zip metadata"); throw GracefulException.BadRequest("Invalid emoji zip metadata");
if (metaJson.MetaVersion < 1 || metaJson.MetaVersion > 2) if (metaJson.MetaVersion < 1 || metaJson.MetaVersion > 2)
throw GracefulException.BadRequest("Unrecognized metaVersion {version}, expected 1 or 2", metaJson.MetaVersion.ToString()); throw GracefulException.BadRequest("Unrecognized metaVersion {version}, expected 1 or 2",
metaJson.MetaVersion.ToString());
return new(metaJson, archive); return new EmojiZip(metaJson, archive);
} }
catch catch
{ {
@ -97,7 +100,7 @@ public class EmojiImportService(
logger.LogDebug("Imported emoji {emoji}", name); logger.LogDebug("Imported emoji {emoji}", name);
} }
catch (GracefulException e) when (e.StatusCode == System.Net.HttpStatusCode.Conflict) catch (GracefulException e) when (e.StatusCode == HttpStatusCode.Conflict)
{ {
logger.LogDebug("Skipping {emoji} as it already exists.", name); logger.LogDebug("Skipping {emoji} as it already exists.", name);
} }

View file

@ -81,6 +81,7 @@ public class ImageProcessor
_logger.LogWarning("No image processor supports the format {format}, skipping", p.Format.MimeType); _logger.LogWarning("No image processor supports the format {format}, skipping", p.Format.MimeType);
return null; return null;
} }
return async () => return async () =>
{ {
if (_concurrency is 0) if (_concurrency is 0)

View file

@ -11,7 +11,8 @@ using Microsoft.EntityFrameworkCore;
namespace Iceshrimp.Backend.Core.Services; namespace Iceshrimp.Backend.Core.Services;
/// <summary> /// <summary>
/// This is needed because static fields in generic classes aren't shared between instances with different generic type arguments /// This is needed because static fields in generic classes aren't shared between instances with different generic type
/// arguments
/// </summary> /// </summary>
file static class PluginStoreHelpers file static class PluginStoreHelpers
{ {

View file

@ -91,7 +91,7 @@ public class AuthorizeModel(DatabaseContext db) : PageModel
RedirectUri = RedirectUri, RedirectUri = RedirectUri,
AutoDetectQuotes = autoDetectQuotes, AutoDetectQuotes = autoDetectQuotes,
SupportsHtmlFormatting = supportsHtmlFormatting, SupportsHtmlFormatting = supportsHtmlFormatting,
IsPleroma = isPleroma, IsPleroma = isPleroma
}; };
await db.AddAsync(token); await db.AddAsync(token);

View file

@ -13,7 +13,7 @@ public class QueueJobModel(DatabaseContext db) : PageModel
{ {
["inbox"] = "body", ["inbox"] = "body",
["deliver"] = "payload", ["deliver"] = "payload",
["pre-deliver"] = "serializedActivity", ["pre-deliver"] = "serializedActivity"
}; };
public Job Job = null!; public Job Job = null!;

View file

@ -1,6 +1,6 @@
@using Iceshrimp.Frontend.Components @using Iceshrimp.Frontend.Components
@using Microsoft.AspNetCore.Components.Authorization @using Microsoft.AspNetCore.Components.Authorization
<ErrorBoundary @ref=ErrorBoundary> <ErrorBoundary @ref="ErrorBoundary">
<ChildContent> <ChildContent>
<Router AppAssembly="@typeof(App).Assembly"> <Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData"> <Found Context="routeData">

View file

@ -1,6 +1,5 @@
@using Iceshrimp.Assets.PhosphorIcons @using Iceshrimp.Assets.PhosphorIcons
@using Iceshrimp.Shared.Schemas.Web @using Iceshrimp.Shared.Schemas.Web
@if (Attachment.ContentType.StartsWith("image")) @if (Attachment.ContentType.StartsWith("image"))
{ {
<div class="wrapper" @onclick="Open" @onclick:stopPropagation="true"> <div class="wrapper" @onclick="Open" @onclick:stopPropagation="true">

View file

@ -1,7 +1,7 @@
<div @onclick="Close" @onclick:stopPropagation="true" class="backdrop @(Darken ? "darken" : "")"></div> <div @onclick="Close" @onclick:stopPropagation="true" class="backdrop @(Darken ? "darken" : "")"></div>
@code { @code {
[Parameter][EditorRequired] public required EventCallback OnClose { get; set; } [Parameter] [EditorRequired] public required EventCallback OnClose { get; set; }
[Parameter] public bool Darken { get; set; } [Parameter] public bool Darken { get; set; }
private void Close() private void Close()

View file

@ -20,7 +20,7 @@
<Icon Name="Icons.X"/> <Icon Name="Icons.X"/>
</button> </button>
<Dropdown TBind="NoteVisibility" Elements="@DropDownCreate()" @bind-Value="NoteDraft.Visibility"/> <Dropdown TBind="NoteVisibility" Elements="@DropDownCreate()" @bind-Value="NoteDraft.Visibility"/>
<StateButton OnClick="SendNote" @ref=SendButton ExtraClasses="post-btn"> <StateButton OnClick="SendNote" @ref="SendButton" ExtraClasses="post-btn">
<Initial> <Initial>
@Loc["ComposePost"]<Icon Name="Icons.PaperPlaneRight"/> @Loc["ComposePost"]<Icon Name="Icons.PaperPlaneRight"/>
</Initial> </Initial>
@ -84,7 +84,12 @@
Cw = null Cw = null
}; };
private Dictionary<string, string> AvailablePlaceholders { get; set; } = new() { { "default", "What's on your mind?" }, { "reply", "Reply goes here!" }, { "quote", "Quote this post!" } }; private Dictionary<string, string> AvailablePlaceholders { get; set; } = new()
{
{ "default", "What's on your mind?" },
{ "reply", "Reply goes here!" },
{ "quote", "Quote this post!" }
};
RenderFragment DropdownIcon(NoteVisibility vis) RenderFragment DropdownIcon(NoteVisibility vis)
{ {
@ -117,7 +122,9 @@
new DropdownElement<NoteVisibility> new DropdownElement<NoteVisibility>
{ {
#pragma warning disable BL0005 // Setting this outside the component is fine until this is reworked #pragma warning disable BL0005 // Setting this outside the component is fine until this is reworked
Icon = DropdownIcon(vis), Content = DropdownContent(vis), Selection = vis Icon = DropdownIcon(vis),
Content = DropdownContent(vis),
Selection = vis
#pragma warning restore BL0005 #pragma warning restore BL0005
}) })
.ToList(); .ToList();
@ -212,7 +219,12 @@
{ {
ReplyOrQuote = null; ReplyOrQuote = null;
Attachments = new List<DriveFileResponse>(); Attachments = new List<DriveFileResponse>();
NoteDraft = new NoteCreateRequest { Text = "", Visibility = NoteVisibility.Followers, Cw = null }; NoteDraft = new NoteCreateRequest
{
Text = "",
Visibility = NoteVisibility.Followers,
Cw = null
};
TextPlaceholder = AvailablePlaceholders["default"]; TextPlaceholder = AvailablePlaceholders["default"];
} }

View file

@ -40,8 +40,7 @@
} }
</div> </div>
@Loc["Logs"] @Loc["Logs"]
@Loc["These logs may contain sensitive information, please do not post them publicly.\n" + @Loc["These logs may contain sensitive information, please do not post them publicly.\n" + "Providing them to developers upon request may help with debugging."]
"Providing them to developers upon request may help with debugging."]
<div class="log-block"> <div class="log-block">
<pre><code> <pre><code>
@foreach (var line in _rawLog) @foreach (var line in _rawLog)
@ -70,14 +69,12 @@
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
_rawLog = LogService.GetLogs(); _rawLog = LogService.GetLogs();
_module = (IJSInProcessObjectReference) await Js.InvokeAsync<IJSObjectReference>("import", "./Components/ErrorUi.razor.js"); _module = (IJSInProcessObjectReference)await Js.InvokeAsync<IJSObjectReference>("import", "./Components/ErrorUi.razor.js");
} }
private void DownloadLogs() private void DownloadLogs()
{ {
var logBytes = LogService.GetLogs().SelectMany(p => Encoding.UTF8.GetBytes(p)).ToArray(); var logBytes = LogService.GetLogs().SelectMany(p => Encoding.UTF8.GetBytes(p)).ToArray();
_module.InvokeVoid("DownloadFile", "log.txt", "text/plain", logBytes); _module.InvokeVoid("DownloadFile", "log.txt", "text/plain", logBytes);
} }
} }

View file

@ -1,8 +1,8 @@
@using Iceshrimp.Assets.PhosphorIcons
@using Iceshrimp.Frontend.Core.Services
@using Iceshrimp.Frontend.Localization @using Iceshrimp.Frontend.Localization
@using Iceshrimp.Shared.Schemas.Web @using Iceshrimp.Shared.Schemas.Web
@using Microsoft.Extensions.Localization @using Microsoft.Extensions.Localization
@using Iceshrimp.Assets.PhosphorIcons
@using Iceshrimp.Frontend.Core.Services
@inject IStringLocalizer<Localization> Loc; @inject IStringLocalizer<Localization> Loc;
@inject ApiService Api; @inject ApiService Api;
<span class="follow-button"> <span class="follow-button">

View file

@ -1,8 +1,8 @@
@using Iceshrimp.Assets.PhosphorIcons
@using Iceshrimp.Frontend.Core.Services
@using Iceshrimp.Frontend.Localization @using Iceshrimp.Frontend.Localization
@using Iceshrimp.Shared.Schemas.Web @using Iceshrimp.Shared.Schemas.Web
@using Microsoft.Extensions.Localization @using Microsoft.Extensions.Localization
@using Iceshrimp.Assets.PhosphorIcons
@using Iceshrimp.Frontend.Core.Services
@inject IStringLocalizer<Localization> Loc; @inject IStringLocalizer<Localization> Loc;
@inject ApiService Api; @inject ApiService Api;
@inject NavigationManager NavigationManager; @inject NavigationManager NavigationManager;

View file

@ -1,4 +1,4 @@
<EmojiPicker></EmojiPicker> <EmojiPicker></EmojiPicker>
@code {
@code {
} }

View file

@ -1,4 +1,3 @@
@if (_display) @if (_display)
{ {
<div class="menu"> <div class="menu">

View file

@ -1,5 +1,4 @@
@using Iceshrimp.Assets.PhosphorIcons @using Iceshrimp.Assets.PhosphorIcons
<div @onclick="OnClick" class="menu-element"> <div @onclick="OnClick" class="menu-element">
@if (Icon != null) @if (Icon != null)
{ {
@ -7,6 +6,7 @@
} }
@Text @Text
</div> </div>
@code { @code {
[Parameter] public IconName? Icon { get; set; } [Parameter] public IconName? Icon { get; set; }
[Parameter] [EditorRequired] public required RenderFragment Text { get; set; } [Parameter] [EditorRequired] public required RenderFragment Text { get; set; }

View file

@ -1,12 +1,12 @@
@using Iceshrimp.Assets.PhosphorIcons @using Iceshrimp.Assets.PhosphorIcons
<CascadingValue Value="this"> <CascadingValue Value="this">
@if (NoteResponse.Renote != null) @if (NoteResponse.Renote != null)
{ {
<div class="renote"> <div class="renote">
<div class="renote-info"> <div class="renote-info">
<span class="user"> <span class="user">
<Icon Name="Icons.Repeat" /> Boosted by <Icon Name="Icons.Repeat"/> Boosted by
@if (NoteResponse.User.DisplayName != null) @if (NoteResponse.User.DisplayName != null)
{ {
@NoteResponse.User.DisplayName @NoteResponse.User.DisplayName
@ -22,9 +22,9 @@
</div> </div>
<NoteComponent Note="NoteResponse.Renote" Quote="NoteResponse.Quote" Indented="Indented"/> <NoteComponent Note="NoteResponse.Renote" Quote="NoteResponse.Quote" Indented="Indented"/>
</div> </div>
} }
else else
{ {
@if (NoteResponse.Filtered is not null && NoteResponse.Filtered.Hide == false) @if (NoteResponse.Filtered is not null && NoteResponse.Filtered.Hide == false)
{ {
<div> <div>
@ -43,11 +43,12 @@ else
</button> </button>
</div> </div>
} }
@if (_overrideHide || NoteResponse.Filtered == null) @if (_overrideHide || NoteResponse.Filtered == null)
{ {
<NoteComponent Note="NoteResponse" Quote="NoteResponse.Quote" Indented="Indented"/> <NoteComponent Note="NoteResponse" Quote="NoteResponse.Quote" Indented="Indented"/>
} }
} }
</CascadingValue> </CascadingValue>

View file

@ -4,7 +4,7 @@
@inject NavigationManager NavigationManager @inject NavigationManager NavigationManager
@inject ComposeService ComposeService @inject ComposeService ComposeService
@inject SessionService Session; @inject SessionService Session;
<div class="note-header"> <div class="note-header">
<NoteUserInfo <NoteUserInfo
AvatarUrl="@Note.User.AvatarUrl" AvatarUrl="@Note.User.AvatarUrl"
DisplayName="@Note.User.DisplayName" DisplayName="@Note.User.DisplayName"
@ -16,16 +16,16 @@
InstanceName="@Note.User.InstanceName" InstanceName="@Note.User.InstanceName"
CreatedAt="DateTime.Parse(Note.CreatedAt)"> CreatedAt="DateTime.Parse(Note.CreatedAt)">
</NoteMetadata> </NoteMetadata>
</div> </div>
<NoteBody NoteBase="Note" OverLength="@CheckLen()" Indented="Indented"/> <NoteBody NoteBase="Note" OverLength="@CheckLen()" Indented="Indented"/>
@if (Quote != null) @if (Quote != null)
{ {
<div @onclick="OpenQuote" @onclick:stopPropagation="true" class="quote"> <div @onclick="OpenQuote" @onclick:stopPropagation="true" class="quote">
<NoteComponent Note="Quote" AsQuote="true"></NoteComponent> <NoteComponent Note="Quote" AsQuote="true"></NoteComponent>
</div> </div>
} }
@if (!AsQuote) @if (!AsQuote)
{ {
<NoteFooter <NoteFooter
Reactions="Note.Reactions" Reactions="Note.Reactions"
Likes="Note.Likes" Likes="Note.Likes"
@ -33,7 +33,7 @@
Renotes="Note.Renotes" Renotes="Note.Renotes"
RenotePossible= RenotePossible=
"@(Note.Visibility == NoteVisibility.Public || Note.Visibility == NoteVisibility.Home || Session.Current?.Id == Note.User.Id)"/> "@(Note.Visibility == NoteVisibility.Public || Note.Visibility == NoteVisibility.Home || Session.Current?.Id == Note.User.Id)"/>
} }
@code { @code {
[Parameter] [EditorRequired] public required NoteBase Note { get; set; } [Parameter] [EditorRequired] public required NoteBase Note { get; set; }

View file

@ -21,7 +21,7 @@
<button class="btn" @onclick="Reply" @onclick:stopPropagation="true"> <button class="btn" @onclick="Reply" @onclick:stopPropagation="true">
<Icon Name="Icons.ArrowUUpLeft" Size="1.3em"/> <Icon Name="Icons.ArrowUUpLeft" Size="1.3em"/>
</button> </button>
<button class="btn @(RenotePossible ? "" : "disabled") positioned" @onclick="@(RenotePossible ? ToggleRenoteMenu : () => {})" @onclick:stopPropagation="true"> <button class="btn @(RenotePossible ? "" : "disabled") positioned" @onclick="@(RenotePossible ? ToggleRenoteMenu : () => { })" @onclick:stopPropagation="true">
@if (RenotePossible) @if (RenotePossible)
{ {
<Icon Name="Icons.Repeat" Size="1.3em"/> <Icon Name="Icons.Repeat" Size="1.3em"/>

View file

@ -7,7 +7,7 @@
@inject NavigationManager NavigationManager @inject NavigationManager NavigationManager
<div class="notification"> <div class="notification">
@if (NotificationResponse is { User: not null, Type: "like" or "follow" or "reaction" or "followRequestReceived" or "followRequestAccepted"}) @if (NotificationResponse is { User: not null, Type: "like" or "follow" or "reaction" or "followRequestReceived" or "followRequestAccepted" })
{ {
<img class="user-avatar" src="@NotificationResponse.User.AvatarUrl"/> <img class="user-avatar" src="@NotificationResponse.User.AvatarUrl"/>
<div class="notification-body"> <div class="notification-body">

View file

@ -101,7 +101,6 @@
Note.Descendants = res; Note.Descendants = res;
StateHasChanged(); StateHasChanged();
} }
} }
private void OpenNote() private void OpenNote()

View file

@ -1,11 +1,11 @@
@using System.Diagnostics.CodeAnalysis @using System.Diagnostics.CodeAnalysis
@* ReSharper disable once RedundantUsingDirective *@
@using Iceshrimp.Frontend.Components.Note
@using Iceshrimp.Frontend.Core.Miscellaneous @using Iceshrimp.Frontend.Core.Miscellaneous
@using Iceshrimp.Frontend.Core.Services @using Iceshrimp.Frontend.Core.Services
@using Iceshrimp.Frontend.Localization @using Iceshrimp.Frontend.Localization
@using Microsoft.Extensions.Localization
@* ReSharper disable once RedundantUsingDirective *@
@using Iceshrimp.Frontend.Components.Note
@using Iceshrimp.Shared.Schemas.Web @using Iceshrimp.Shared.Schemas.Web
@using Microsoft.Extensions.Localization
@inject IStringLocalizer<Localization> Loc; @inject IStringLocalizer<Localization> Loc;
@inject ApiService Api; @inject ApiService Api;
@inject NavigationManager Navigation; @inject NavigationManager Navigation;

View file

@ -25,5 +25,4 @@
</div> </div>
@code { @code {
} }

View file

@ -10,11 +10,11 @@
</button> </button>
@code { @code {
[Parameter, EditorRequired] public required EventCallback OnClick { get; set; } [Parameter] [EditorRequired] public required EventCallback OnClick { get; set; }
[Parameter, EditorRequired] public required RenderFragment Initial { get; set; } [Parameter] [EditorRequired] public required RenderFragment Initial { get; set; }
[Parameter, EditorRequired] public required RenderFragment Loading { get; set; } [Parameter] [EditorRequired] public required RenderFragment Loading { get; set; }
[Parameter, EditorRequired] public required RenderFragment Failed { get; set; } [Parameter] [EditorRequired] public required RenderFragment Failed { get; set; }
[Parameter, EditorRequired] public required RenderFragment Success { get; set; } [Parameter] [EditorRequired] public required RenderFragment Success { get; set; }
[Parameter] public string? ExtraClasses { get; set; } [Parameter] public string? ExtraClasses { get; set; }
public StateEnum State { get; set; } public StateEnum State { get; set; }

View file

@ -34,6 +34,7 @@ public partial class TimelineComponent : IAsyncDisposable
{ {
return false; return false;
} }
State.MaxId = res[0].Id; State.MaxId = res[0].Id;
State.MinId = res.Last().Id; State.MinId = res.Last().Id;
State.Timeline = res; State.Timeline = res;
@ -124,6 +125,7 @@ public partial class TimelineComponent : IAsyncDisposable
{ {
initResult = await Initialize(); initResult = await Initialize();
} }
ComponentState = initResult ? Core.Miscellaneous.State.Loaded : Core.Miscellaneous.State.Empty; ComponentState = initResult ? Core.Miscellaneous.State.Loaded : Core.Miscellaneous.State.Empty;
StateHasChanged(); StateHasChanged();
} }

View file

@ -2,15 +2,12 @@
@using Iceshrimp.Frontend.Components.Note @using Iceshrimp.Frontend.Components.Note
@using Iceshrimp.Shared.Schemas.Web @using Iceshrimp.Shared.Schemas.Web
@inject NavigationManager Navigation @inject NavigationManager Navigation
@if (Note.Filtered is { Hide: true }) @if (Note.Filtered is { Hide: true }) { }
else
{ {
}
else {
<div class="note-container" @onclick="OpenNote" id="@Note.Id"> <div class="note-container" @onclick="OpenNote" id="@Note.Id">
<Note NoteResponse="Note"></Note> <Note NoteResponse="Note"></Note>
</div> </div>
} }
@code { @code {

View file

@ -1,6 +1,6 @@
@inject ApiService Api;
@using Iceshrimp.Frontend.Core.Services @using Iceshrimp.Frontend.Core.Services
@using Iceshrimp.Shared.Schemas.Web @using Iceshrimp.Shared.Schemas.Web
@inject ApiService Api;
@inject NavigationManager NavigationManager; @inject NavigationManager NavigationManager;
@if (UserProfile != null) @if (UserProfile != null)
{ {

View file

@ -1,4 +1,3 @@
<div @ref="@_scroller" class="scroller"> <div @ref="@_scroller" class="scroller">
<div @ref="@_padTopRef" class="padding top" style="height: @(State.PadTop + "px")"></div> <div @ref="@_padTopRef" class="padding top" style="height: @(State.PadTop + "px")"></div>
@if (_loadingTop) @if (_loadingTop)
@ -30,5 +29,4 @@
</div> </div>
@code { @code {
} }

View file

@ -277,7 +277,9 @@ public partial class VirtualScroller : IAsyncDisposable
{ {
if (firstRender) if (firstRender)
{ {
Module = (IJSInProcessObjectReference) await Js.InvokeAsync<IJSObjectReference>("import", "./Components/VirtualScroller.razor.js"); Module =
(IJSInProcessObjectReference)
await Js.InvokeAsync<IJSObjectReference>("import", "./Components/VirtualScroller.razor.js");
await SetupObservers(); await SetupObservers();
} }

View file

@ -6,6 +6,7 @@ namespace Iceshrimp.Frontend.Core.InMemoryLogger;
internal class InMemoryLogService(IOptions<InMemoryLoggerConfiguration> configuration) internal class InMemoryLogService(IOptions<InMemoryLoggerConfiguration> configuration)
{ {
private readonly LogBuffer _buffer = new(configuration.Value.BufferSize); private readonly LogBuffer _buffer = new(configuration.Value.BufferSize);
public void Add(string logline) public void Add(string logline)
{ {
_buffer.Add(logline); _buffer.Add(logline);

View file

@ -2,9 +2,12 @@ using Microsoft.Extensions.Options;
namespace Iceshrimp.Frontend.Core.InMemoryLogger; namespace Iceshrimp.Frontend.Core.InMemoryLogger;
internal class InMemoryLogger (IOptions<InMemoryLoggerConfiguration> config, InMemoryLogService logService) : ILogger internal class InMemoryLogger(IOptions<InMemoryLoggerConfiguration> config, InMemoryLogService logService) : ILogger
{ {
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter) public void Log<TState>(
LogLevel logLevel, EventId eventId, TState state, Exception? exception,
Func<TState, Exception?, string> formatter
)
{ {
logService.Add(formatter(state, exception)); logService.Add(formatter(state, exception));
} }

View file

@ -1,19 +1,19 @@
namespace Iceshrimp.Frontend.Core.InMemoryLogger;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Configuration; using Microsoft.Extensions.Logging.Configuration;
namespace Iceshrimp.Frontend.Core.InMemoryLogger;
internal static class InMemoryLoggerExtension internal static class InMemoryLoggerExtension
{ {
public static void AddInMemoryLogger( public static void AddInMemoryLogger(
this ILoggingBuilder builder, IConfiguration configuration) this ILoggingBuilder builder, IConfiguration configuration
)
{ {
builder.AddConfiguration(); builder.AddConfiguration();
builder.Services.AddOptionsWithValidateOnStart<InMemoryLoggerConfiguration>() builder.Services.AddOptionsWithValidateOnStart<InMemoryLoggerConfiguration>()
.Bind(configuration.GetSection("InMemoryLogger")); .Bind(configuration.GetSection("InMemoryLogger"));
LoggerProviderOptions.RegisterProviderOptions<InMemoryLoggerConfiguration, InMemoryLoggerProvider>(builder.Services); LoggerProviderOptions
.RegisterProviderOptions<InMemoryLoggerConfiguration, InMemoryLoggerProvider>(builder.Services);
builder.Services.TryAddSingleton<InMemoryLogService>(); builder.Services.TryAddSingleton<InMemoryLogService>();
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<ILoggerProvider, InMemoryLoggerProvider>()); builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<ILoggerProvider, InMemoryLoggerProvider>());
} }
@ -28,5 +28,3 @@ internal static class InMemoryLoggerExtension
builder.Services.Configure(configure); builder.Services.Configure(configure);
} }
} }

View file

@ -93,7 +93,7 @@ public readonly struct QueryString : IEquatable<QueryString>
{ {
ArgumentNullException.ThrowIfNull(uri); ArgumentNullException.ThrowIfNull(uri);
string queryValue = uri.GetComponents(UriComponents.Query, UriFormat.UriEscaped); var queryValue = uri.GetComponents(UriComponents.Query, UriFormat.UriEscaped);
if (!string.IsNullOrEmpty(queryValue)) if (!string.IsNullOrEmpty(queryValue))
{ {
queryValue = "?" + queryValue; queryValue = "?" + queryValue;
@ -170,10 +170,10 @@ public readonly struct QueryString : IEquatable<QueryString>
} }
/// <summary> /// <summary>
/// Concatenates <paramref name="other"/> to the current query string. /// Concatenates <paramref name="other" /> to the current query string.
/// </summary> /// </summary>
/// <param name="other">The <see cref="QueryString"/> to concatenate.</param> /// <param name="other">The <see cref="QueryString" /> to concatenate.</param>
/// <returns>The concatenated <see cref="QueryString"/>.</returns> /// <returns>The concatenated <see cref="QueryString" />.</returns>
public QueryString Add(QueryString other) public QueryString Add(QueryString other)
{ {
if (!HasValue || Value.Equals("?", StringComparison.Ordinal)) if (!HasValue || Value.Equals("?", StringComparison.Ordinal))
@ -191,12 +191,12 @@ public readonly struct QueryString : IEquatable<QueryString>
} }
/// <summary> /// <summary>
/// Concatenates a query string with <paramref name="name"/> and <paramref name="value"/> /// Concatenates a query string with <paramref name="name" /> and <paramref name="value" />
/// to the current query string. /// to the current query string.
/// </summary> /// </summary>
/// <param name="name">The name of the query string to concatenate.</param> /// <param name="name">The name of the query string to concatenate.</param>
/// <param name="value">The value of the query string to concatenate.</param> /// <param name="value">The value of the query string to concatenate.</param>
/// <returns>The concatenated <see cref="QueryString"/>.</returns> /// <returns>The concatenated <see cref="QueryString" />.</returns>
public QueryString Add(string name, string value) public QueryString Add(string name, string value)
{ {
ArgumentNullException.ThrowIfNull(name); ArgumentNullException.ThrowIfNull(name);
@ -212,10 +212,10 @@ public readonly struct QueryString : IEquatable<QueryString>
} }
/// <summary> /// <summary>
/// Evalutes if the current query string is equal to <paramref name="other"/>. /// Evalutes if the current query string is equal to <paramref name="other" />.
/// </summary> /// </summary>
/// <param name="other">The <see cref="QueryString"/> to compare.</param> /// <param name="other">The <see cref="QueryString" /> to compare.</param>
/// <returns><see langword="true"/> if the query strings are equal.</returns> /// <returns><see langword="true" /> if the query strings are equal.</returns>
public bool Equals(QueryString other) public bool Equals(QueryString other)
{ {
if (!HasValue && !other.HasValue) if (!HasValue && !other.HasValue)
@ -227,7 +227,7 @@ public readonly struct QueryString : IEquatable<QueryString>
} }
/// <summary> /// <summary>
/// Evaluates if the current query string is equal to an object <paramref name="obj"/>. /// Evaluates if the current query string is equal to an object <paramref name="obj" />.
/// </summary> /// </summary>
/// <param name="obj">An object to compare.</param> /// <param name="obj">An object to compare.</param>
/// <returns><see langword="true" /> if the query strings are equal.</returns> /// <returns><see langword="true" /> if the query strings are equal.</returns>
@ -244,17 +244,17 @@ public readonly struct QueryString : IEquatable<QueryString>
/// <summary> /// <summary>
/// Gets a hash code for the value. /// Gets a hash code for the value.
/// </summary> /// </summary>
/// <returns>The hash code as an <see cref="int"/>.</returns> /// <returns>The hash code as an <see cref="int" />.</returns>
public override int GetHashCode() public override int GetHashCode()
{ {
return (HasValue ? Value.GetHashCode() : 0); return HasValue ? Value.GetHashCode() : 0;
} }
/// <summary> /// <summary>
/// Evaluates if one query string is equal to another. /// Evaluates if one query string is equal to another.
/// </summary> /// </summary>
/// <param name="left">A <see cref="QueryString"/> instance.</param> /// <param name="left">A <see cref="QueryString" /> instance.</param>
/// <param name="right">A <see cref="QueryString"/> instance.</param> /// <param name="right">A <see cref="QueryString" /> instance.</param>
/// <returns><see langword="true" /> if the query strings are equal.</returns> /// <returns><see langword="true" /> if the query strings are equal.</returns>
public static bool operator ==(QueryString left, QueryString right) public static bool operator ==(QueryString left, QueryString right)
{ {
@ -264,8 +264,8 @@ public readonly struct QueryString : IEquatable<QueryString>
/// <summary> /// <summary>
/// Evaluates if one query string is not equal to another. /// Evaluates if one query string is not equal to another.
/// </summary> /// </summary>
/// <param name="left">A <see cref="QueryString"/> instance.</param> /// <param name="left">A <see cref="QueryString" /> instance.</param>
/// <param name="right">A <see cref="QueryString"/> instance.</param> /// <param name="right">A <see cref="QueryString" /> instance.</param>
/// <returns><see langword="true" /> if the query strings are not equal.</returns> /// <returns><see langword="true" /> if the query strings are not equal.</returns>
public static bool operator !=(QueryString left, QueryString right) public static bool operator !=(QueryString left, QueryString right)
{ {
@ -273,11 +273,11 @@ public readonly struct QueryString : IEquatable<QueryString>
} }
/// <summary> /// <summary>
/// Concatenates <paramref name="left"/> and <paramref name="right"/> into a single query string. /// Concatenates <paramref name="left" /> and <paramref name="right" /> into a single query string.
/// </summary> /// </summary>
/// <param name="left">A <see cref="QueryString"/> instance.</param> /// <param name="left">A <see cref="QueryString" /> instance.</param>
/// <param name="right">A <see cref="QueryString"/> instance.</param> /// <param name="right">A <see cref="QueryString" /> instance.</param>
/// <returns>The concatenated <see cref="QueryString"/>.</returns> /// <returns>The concatenated <see cref="QueryString" />.</returns>
public static QueryString operator +(QueryString left, QueryString right) public static QueryString operator +(QueryString left, QueryString right)
{ {
return left.Add(right); return left.Add(right);

View file

@ -24,6 +24,4 @@ internal class EmojiService(ApiService api)
throw new Exception("Failed to fetch emoji"); throw new Exception("Failed to fetch emoji");
} }
} }
} }

View file

@ -1,4 +1,5 @@
using Iceshrimp.Frontend.Components; using Iceshrimp.Frontend.Components;
namespace Iceshrimp.Frontend.Core.Services; namespace Iceshrimp.Frontend.Core.Services;
public class GlobalComponentSvc public class GlobalComponentSvc

View file

@ -9,12 +9,12 @@ internal class MessageService
private readonly Dictionary<(string, Type), EventHandler<NoteResponse>> _noteChangedHandlers = new(); private readonly Dictionary<(string, Type), EventHandler<NoteResponse>> _noteChangedHandlers = new();
public enum Type public enum Type
{ {
Updated, Updated,
Deleted Deleted
} }
public NoteMessageHandler Register(string id, EventHandler<NoteResponse> func, Type type) public NoteMessageHandler Register(string id, EventHandler<NoteResponse> func, Type type)
{ {
var tuple = (id, type); var tuple = (id, type);
@ -52,7 +52,9 @@ internal class MessageService
private readonly Type _type; private readonly Type _type;
private readonly MessageService _messageService; private readonly MessageService _messageService;
public NoteMessageHandler(EventHandler<NoteResponse> handler, string id, Type type, MessageService messageService) public NoteMessageHandler(
EventHandler<NoteResponse> handler, string id, Type type, MessageService messageService
)
{ {
_handler = handler; _handler = handler;
_id = id; _id = id;

View file

@ -14,7 +14,6 @@ public class SingleNote
States.TryGetValue(id, out var state); States.TryGetValue(id, out var state);
return state; return state;
} }
} }
public class SingleNoteState public class SingleNoteState

View file

@ -60,6 +60,7 @@ internal class TimelineState : IDisposable
Timeline.RemoveAt(i); Timeline.RemoveAt(i);
return; return;
} }
if (i == 0) MaxId = Timeline[1].Id; if (i == 0) MaxId = Timeline[1].Id;
if (i == Timeline.Count - 1) MinId = Timeline[^2].Id; if (i == Timeline.Count - 1) MinId = Timeline[^2].Id;
Timeline.RemoveAt(i); Timeline.RemoveAt(i);

View file

@ -1,6 +1,5 @@
@using Microsoft.AspNetCore.Authorization @using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.Sections @using Microsoft.AspNetCore.Components.Sections
@using Microsoft.AspNetCore.Components.Authorization
@inherits LayoutComponentBase @inherits LayoutComponentBase
<div class="page"> <div class="page">

View file

@ -1,8 +1,7 @@
@using Iceshrimp.Frontend.Components
@using Microsoft.AspNetCore.Components.Sections
@using Iceshrimp.Assets.PhosphorIcons @using Iceshrimp.Assets.PhosphorIcons
@using Iceshrimp.Frontend.Components
@using Iceshrimp.Frontend.Localization @using Iceshrimp.Frontend.Localization
@using Microsoft.AspNetCore.Authorization @using Microsoft.AspNetCore.Components.Sections
@using Microsoft.Extensions.Localization @using Microsoft.Extensions.Localization
@inherits LayoutComponentBase @inherits LayoutComponentBase
@layout MainLayout @layout MainLayout

View file

@ -1,13 +1,13 @@
@inject IStringLocalizer<Localization> Loc;
@using Iceshrimp.Assets.PhosphorIcons @using Iceshrimp.Assets.PhosphorIcons
@using Iceshrimp.Frontend.Components @using Iceshrimp.Frontend.Components
@using Iceshrimp.Frontend.Localization @using Iceshrimp.Frontend.Localization
@using Microsoft.Extensions.Localization @using Microsoft.Extensions.Localization
@inject IStringLocalizer<Localization> Loc;
@implements IDisposable @implements IDisposable
@inject NavigationManager Navigation; @inject NavigationManager Navigation;
<GlobalComponents></GlobalComponents> <GlobalComponents></GlobalComponents>
<div @ref="SidebarElementRef" class="sidebar @(_open ? "open" : "")" tabindex=0> <div @ref="SidebarElementRef" class="sidebar @(_open ? "open" : "")" tabindex="0">
<div class="header"> <div class="header">
<account-dropdown/> <account-dropdown/>
</div> </div>
@ -49,7 +49,7 @@
</button> </button>
@if (_open) @if (_open)
{ {
<ClosingBackdrop OnClose="Close" /> <ClosingBackdrop OnClose="Close"/>
} }
</div> </div>

View file

@ -1,11 +1,11 @@
@page "/follow-requests" @page "/follow-requests"
@attribute [Authorize] @using Iceshrimp.Assets.PhosphorIcons
@using Iceshrimp.Frontend.Components @using Iceshrimp.Frontend.Components
@using Iceshrimp.Frontend.Localization @using Iceshrimp.Frontend.Localization
@using Microsoft.Extensions.Localization
@using Microsoft.AspNetCore.Components.Sections
@using Iceshrimp.Assets.PhosphorIcons
@using Microsoft.AspNetCore.Authorization @using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Sections
@using Microsoft.Extensions.Localization
@attribute [Authorize]
@inject IStringLocalizer<Localization> Loc; @inject IStringLocalizer<Localization> Loc;
<SectionContent SectionName="top-bar"> <SectionContent SectionName="top-bar">
@ -13,7 +13,7 @@
@Loc["Follow requests"] @Loc["Follow requests"]
</SectionContent> </SectionContent>
<FollowRequestList /> <FollowRequestList/>
@code {
@code {
} }

View file

@ -1,11 +1,11 @@
@page "/notifications" @page "/notifications"
@attribute [Authorize] @using Iceshrimp.Assets.PhosphorIcons
@using Iceshrimp.Frontend.Components @using Iceshrimp.Frontend.Components
@using Iceshrimp.Frontend.Localization @using Iceshrimp.Frontend.Localization
@using Microsoft.Extensions.Localization
@using Microsoft.AspNetCore.Components.Sections
@using Iceshrimp.Assets.PhosphorIcons
@using Microsoft.AspNetCore.Authorization @using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Sections
@using Microsoft.Extensions.Localization
@attribute [Authorize]
@inject IStringLocalizer<Localization> Loc; @inject IStringLocalizer<Localization> Loc;

View file

@ -1,6 +1,4 @@
@page "/{User}" @page "/{User}"
@attribute [Authorize]
@using System.Text.RegularExpressions @using System.Text.RegularExpressions
@using Iceshrimp.Frontend.Components @using Iceshrimp.Frontend.Components
@using Iceshrimp.Frontend.Components.Note @using Iceshrimp.Frontend.Components.Note
@ -8,6 +6,7 @@
@using Iceshrimp.Frontend.Core.Services @using Iceshrimp.Frontend.Core.Services
@using Iceshrimp.Shared.Schemas.Web @using Iceshrimp.Shared.Schemas.Web
@using Microsoft.AspNetCore.Authorization @using Microsoft.AspNetCore.Authorization
@attribute [Authorize]
@inject ApiService Api @inject ApiService Api
@if (_init) @if (_init)

View file

@ -1,11 +1,11 @@
@page "/search" @page "/search"
@attribute [Authorize] @using Iceshrimp.Assets.PhosphorIcons
@using Iceshrimp.Frontend.Components @using Iceshrimp.Frontend.Components
@using Iceshrimp.Frontend.Localization @using Iceshrimp.Frontend.Localization
@using Microsoft.Extensions.Localization
@using Microsoft.AspNetCore.Components.Sections
@using Iceshrimp.Assets.PhosphorIcons
@using Microsoft.AspNetCore.Authorization @using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Sections
@using Microsoft.Extensions.Localization
@attribute [Authorize]
@inject IStringLocalizer<Localization> Loc; @inject IStringLocalizer<Localization> Loc;
@ -14,7 +14,7 @@
@Loc["Search"] @Loc["Search"]
</SectionContent> </SectionContent>
<SearchComponent /> <SearchComponent/>
@code {
@code {
} }

View file

@ -1,11 +1,11 @@
@page "/settings/about" @page "/settings/about"
@attribute [Authorize]
@using System.Text @using System.Text
@using Iceshrimp.Frontend.Core.InMemoryLogger @using Iceshrimp.Frontend.Core.InMemoryLogger
@using Iceshrimp.Frontend.Core.Services; @using Iceshrimp.Frontend.Core.Services
@using Iceshrimp.Frontend.Localization; @using Iceshrimp.Frontend.Localization
@using Microsoft.AspNetCore.Authorization @using Microsoft.AspNetCore.Authorization
@using Microsoft.Extensions.Localization; @using Microsoft.Extensions.Localization
@attribute [Authorize]
@layout SettingsLayout; @layout SettingsLayout;
@inject VersionService Version; @inject VersionService Version;
@inject IStringLocalizer<Localization> Loc; @inject IStringLocalizer<Localization> Loc;
@ -36,8 +36,7 @@
</div> </div>
<div class="logs"> <div class="logs">
<h1>@Loc["Logs"]</h1> <h1>@Loc["Logs"]</h1>
@Loc["These logs may contain sensitive information, please do not post them publicly.\n" @Loc["These logs may contain sensitive information, please do not post them publicly.\n" + "Providing them to developers upon request may help with debugging."]
+ "Providing them to developers upon request may help with debugging."]
<button class="btn" @onclick="DownloadLogs">@Loc["Download Logs"]</button> <button class="btn" @onclick="DownloadLogs">@Loc["Download Logs"]</button>
</div> </div>
@ -46,7 +45,7 @@
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
_module = (IJSInProcessObjectReference) await Js.InvokeAsync<IJSObjectReference>("import", "./Components/ErrorUi.razor.js"); _module = (IJSInProcessObjectReference)await Js.InvokeAsync<IJSObjectReference>("import", "./Components/ErrorUi.razor.js");
} }
private void DownloadLogs() private void DownloadLogs()
@ -54,5 +53,4 @@
var logBytes = LogService.GetLogs().SelectMany(p => Encoding.UTF8.GetBytes(p)).ToArray(); var logBytes = LogService.GetLogs().SelectMany(p => Encoding.UTF8.GetBytes(p)).ToArray();
_module.InvokeVoid("DownloadFile", "log.txt", "text/plain", logBytes); _module.InvokeVoid("DownloadFile", "log.txt", "text/plain", logBytes);
} }
} }

View file

@ -1,14 +1,14 @@
@page "/settings/filters" @page "/settings/filters"
@attribute [Authorize] @using Iceshrimp.Assets.PhosphorIcons
@using Iceshrimp.Frontend.Core.Miscellaneous
@using Iceshrimp.Frontend.Components @using Iceshrimp.Frontend.Components
@using Iceshrimp.Frontend.Core.Miscellaneous
@using Iceshrimp.Frontend.Core.Services @using Iceshrimp.Frontend.Core.Services
@using Iceshrimp.Frontend.Localization @using Iceshrimp.Frontend.Localization
@using Iceshrimp.Shared.Schemas.Web @using Iceshrimp.Shared.Schemas.Web
@using Microsoft.Extensions.Localization
@using Iceshrimp.Assets.PhosphorIcons
@using Microsoft.AspNetCore.Authorization @using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Sections @using Microsoft.AspNetCore.Components.Sections
@using Microsoft.Extensions.Localization
@attribute [Authorize]
@layout SettingsLayout @layout SettingsLayout
@inject ApiService Api; @inject ApiService Api;
@inject IStringLocalizer<Localization> Loc; @inject IStringLocalizer<Localization> Loc;
@ -177,7 +177,7 @@
private State State { get; set; } = State.Loading; private State State { get; set; } = State.Loading;
private string FilterName { get; set; } = ""; private string FilterName { get; set; } = "";
private string Keyword { get; set; } = ""; private string Keyword { get; set; } = "";
private List<String> FilterKeywords { get; } = []; private List<string> FilterKeywords { get; } = [];
private DateTime? FilterExpiry { get; set; } private DateTime? FilterExpiry { get; set; }
private FilterResponse.FilterAction FilterAction { get; set; } private FilterResponse.FilterAction FilterAction { get; set; }
private Menu MenuFilterAction { get; set; } = null!; private Menu MenuFilterAction { get; set; } = null!;
@ -217,7 +217,7 @@
private async Task TryAddFilter() private async Task TryAddFilter()
{ {
bool valid = true; var valid = true;
if (FilterName.Length < 1) if (FilterName.Length < 1)
{ {

View file

@ -1,14 +1,13 @@
@page "/settings/profile" @page "/settings/profile"
@attribute [Authorize] @using Iceshrimp.Assets.PhosphorIcons
@using Iceshrimp.Frontend.Components
@using Iceshrimp.Frontend.Core.Miscellaneous @using Iceshrimp.Frontend.Core.Miscellaneous
@using Iceshrimp.Frontend.Core.Services @using Iceshrimp.Frontend.Core.Services
@using Iceshrimp.Frontend.Localization @using Iceshrimp.Frontend.Localization
@using Iceshrimp.Shared.Schemas.Web @using Iceshrimp.Shared.Schemas.Web
@using Microsoft.Extensions.Localization
@using Iceshrimp.Assets.PhosphorIcons
@using Iceshrimp.Frontend.Components
@using Microsoft.AspNetCore.Authorization @using Microsoft.AspNetCore.Authorization
@using Microsoft.Extensions.Logging @using Microsoft.Extensions.Localization
@attribute [Authorize]
@layout SettingsLayout @layout SettingsLayout
@inject ApiService Api; @inject ApiService Api;
@inject ILogger<Profile> Logger; @inject ILogger<Profile> Logger;
@ -50,10 +49,18 @@
<div class="section"> <div class="section">
<StateButton OnClick="SaveChanges" @ref="SaveButton"> <StateButton OnClick="SaveChanges" @ref="SaveButton">
<Initial><Icon Name="Icons.FloppyDisk"/>@Loc["Save"]</Initial> <Initial>
<Loading><Icon Name="Icons.Spinner"/></Loading> <Icon Name="Icons.FloppyDisk"/>@Loc["Save"]
<Failed><Icon Name="Icons.X" />@Loc["Error"]</Failed> </Initial>
<Success><Icon Name="Icons.Check"/>@Loc["Saved"]</Success> <Loading>
<Icon Name="Icons.Spinner"/>
</Loading>
<Failed>
<Icon Name="Icons.X"/>@Loc["Error"]
</Failed>
<Success>
<Icon Name="Icons.Check"/>@Loc["Saved"]
</Success>
</StateButton> </StateButton>
</div> </div>
} }

View file

@ -1,11 +1,11 @@
@page "/Settings" @page "/Settings"
@attribute [Authorize]
@using Iceshrimp.Frontend.Components
@using Microsoft.AspNetCore.Components.Sections
@using Iceshrimp.Assets.PhosphorIcons @using Iceshrimp.Assets.PhosphorIcons
@using Iceshrimp.Frontend.Components
@using Iceshrimp.Frontend.Localization @using Iceshrimp.Frontend.Localization
@using Microsoft.AspNetCore.Authorization @using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Sections
@using Microsoft.Extensions.Localization @using Microsoft.Extensions.Localization
@attribute [Authorize]
@* @inject NavigationManager Nav; *@ @* @inject NavigationManager Nav; *@
@inject IStringLocalizer<Localization> Loc; @inject IStringLocalizer<Localization> Loc;

View file

@ -1,16 +1,5 @@
@page "/notes/{NoteId}" @page "/notes/{NoteId}"
@attribute [Authorize] @attribute [Authorize]
@using Iceshrimp.Frontend.Components
@using Iceshrimp.Frontend.Components.Note
@using Iceshrimp.Frontend.Core.Miscellaneous
@using Iceshrimp.Frontend.Core.Services
@using Iceshrimp.Frontend.Core.Services.StateServicePatterns
@using Iceshrimp.Frontend.Localization
@using Iceshrimp.Shared.Schemas.Web
@using Microsoft.Extensions.Localization
@using Microsoft.AspNetCore.Components.Sections
@using Iceshrimp.Assets.PhosphorIcons
@using Microsoft.AspNetCore.Authorization
@inject ApiService ApiService @inject ApiService ApiService
@inject IJSRuntime Js @inject IJSRuntime Js
@inject MessageService MessageService @inject MessageService MessageService
@ -19,6 +8,17 @@
@inject IStringLocalizer<Localization> Loc @inject IStringLocalizer<Localization> Loc
@inject ILogger<SingleNote> Logger; @inject ILogger<SingleNote> Logger;
@using Iceshrimp.Assets.PhosphorIcons
@using Iceshrimp.Frontend.Components
@using Iceshrimp.Frontend.Components.Note
@using Iceshrimp.Frontend.Core.Miscellaneous
@using Iceshrimp.Frontend.Core.Services
@using Iceshrimp.Frontend.Core.Services.StateServicePatterns
@using Iceshrimp.Frontend.Localization
@using Iceshrimp.Shared.Schemas.Web
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Sections
@using Microsoft.Extensions.Localization
@implements IDisposable @implements IDisposable
@if (_componentState == LoadState.Init) @if (_componentState == LoadState.Init)
@ -114,11 +114,13 @@
_componentState = LoadState.Error; _componentState = LoadState.Error;
return; return;
} }
if (RootNote == null) if (RootNote == null)
{ {
_componentState = LoadState.Error; _componentState = LoadState.Error;
return; return;
} }
_componentState = LoadState.Init; _componentState = LoadState.Init;
StateHasChanged(); StateHasChanged();

View file

@ -1,11 +1,11 @@
@page "/" @page "/"
@attribute [Authorize]
@using Iceshrimp.Frontend.Components
@using Microsoft.AspNetCore.Components.Sections
@using Iceshrimp.Assets.PhosphorIcons @using Iceshrimp.Assets.PhosphorIcons
@using Iceshrimp.Frontend.Components
@using Iceshrimp.Frontend.Localization @using Iceshrimp.Frontend.Localization
@using Microsoft.AspNetCore.Authorization @using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Sections
@using Microsoft.Extensions.Localization @using Microsoft.Extensions.Localization
@attribute [Authorize]
@inject IStringLocalizer<Localization> Loc; @inject IStringLocalizer<Localization> Loc;
<SectionContent SectionName="top-bar"> <SectionContent SectionName="top-bar">

View file

@ -4,9 +4,7 @@ open System
open FParsec open FParsec
module SearchQueryFilters = module SearchQueryFilters =
type Filter() = type Filter() = class end
class
end
type WordFilter(neg: bool, value: string) = type WordFilter(neg: bool, value: string) =
inherit Filter() inherit Filter()

View file

@ -268,7 +268,7 @@ public class MfmTests
List<MfmNode> expected = List<MfmNode> expected =
[ [
new MfmTextNode("this is plain text > this is not a quote >this is also not a quote\n"), new MfmTextNode("this is plain text > this is not a quote >this is also not a quote\n"),
new MfmQuoteNode(ListModule.OfSeq<MfmInlineNode>([new MfmTextNode("this is a quote\nthis is part of the same quote\nthis too"),]), false, false), new MfmQuoteNode(ListModule.OfSeq<MfmInlineNode>([new MfmTextNode("this is a quote\nthis is part of the same quote\nthis too")]), false, false),
new MfmTextNode("this is some plain text inbetween\n"), new MfmTextNode("this is some plain text inbetween\n"),
new MfmQuoteNode(ListModule.OfSeq<MfmInlineNode>([new MfmTextNode("this is a second quote\nthis is part of the second quote")]), true, false), new MfmQuoteNode(ListModule.OfSeq<MfmInlineNode>([new MfmTextNode("this is a second quote\nthis is part of the second quote")]), true, false),
new MfmQuoteNode(ListModule.OfSeq<MfmInlineNode>([new MfmTextNode("this is a third quote")]), false, false), new MfmQuoteNode(ListModule.OfSeq<MfmInlineNode>([new MfmTextNode("this is a third quote")]), false, false),