diff --git a/Foxnouns.Backend/Controllers/InternalController.cs b/Foxnouns.Backend/Controllers/InternalController.cs index b79de1c..e63b579 100644 --- a/Foxnouns.Backend/Controllers/InternalController.cs +++ b/Foxnouns.Backend/Controllers/InternalController.cs @@ -60,8 +60,7 @@ public partial class InternalController(DatabaseContext db) : ControllerBase { if (endpoint.RoutePattern.RawText == null) continue; - var templateMatcher = new TemplateMatcher(TemplateParser.Parse(endpoint.RoutePattern.RawText), - new RouteValueDictionary()); + var templateMatcher = new TemplateMatcher(TemplateParser.Parse(endpoint.RoutePattern.RawText), new RouteValueDictionary()); if (!templateMatcher.TryMatch(url, new())) continue; var httpMethodAttribute = endpoint.Metadata.GetMetadata(); if (httpMethodAttribute != null && diff --git a/Foxnouns.Backend/Controllers/MembersController.cs b/Foxnouns.Backend/Controllers/MembersController.cs index 1ffc928..113d95b 100644 --- a/Foxnouns.Backend/Controllers/MembersController.cs +++ b/Foxnouns.Backend/Controllers/MembersController.cs @@ -42,8 +42,7 @@ public class MembersController( [HttpPost("/api/v2/users/@me/members")] [ProducesResponseType(StatusCodes.Status200OK)] [Authorize("member.create")] - public async Task CreateMemberAsync([FromBody] CreateMemberRequest req, - CancellationToken ct = default) + public async Task CreateMemberAsync([FromBody] CreateMemberRequest req, CancellationToken ct = default) { ValidationUtils.Validate([ ("name", ValidationUtils.ValidateMemberName(req.Name)), diff --git a/Foxnouns.Backend/Controllers/UsersController.cs b/Foxnouns.Backend/Controllers/UsersController.cs index bb3417c..98e4f9c 100644 --- a/Foxnouns.Backend/Controllers/UsersController.cs +++ b/Foxnouns.Backend/Controllers/UsersController.cs @@ -104,8 +104,7 @@ public class UsersController( [HttpPatch("@me/custom-preferences")] [Authorize("user.update")] [ProducesResponseType>(StatusCodes.Status200OK)] - public async Task UpdateCustomPreferencesAsync([FromBody] List req, - CancellationToken ct = default) + public async Task UpdateCustomPreferencesAsync([FromBody] List req, CancellationToken ct = default) { ValidationUtils.Validate(ValidateCustomPreferences(req)); @@ -181,8 +180,8 @@ public class UsersController( public Pronoun[]? Pronouns { get; init; } public Field[]? Fields { get; init; } } - - + + [HttpGet("@me/settings")] [Authorize("user.read_hidden")] [ProducesResponseType(statusCode: StatusCodes.Status200OK)] @@ -195,8 +194,7 @@ public class UsersController( [HttpPatch("@me/settings")] [Authorize("user.read_hidden", "user.update")] [ProducesResponseType(statusCode: StatusCodes.Status200OK)] - public async Task UpdateUserSettingsAsync([FromBody] UpdateUserSettingsRequest req, - CancellationToken ct = default) + public async Task UpdateUserSettingsAsync([FromBody] UpdateUserSettingsRequest req, CancellationToken ct = default) { var user = await db.Users.FirstAsync(u => u.Id == CurrentUser!.Id, ct); diff --git a/Foxnouns.Backend/Extensions/AvatarObjectExtensions.cs b/Foxnouns.Backend/Extensions/AvatarObjectExtensions.cs index 7c39aa4..cb70adf 100644 --- a/Foxnouns.Backend/Extensions/AvatarObjectExtensions.cs +++ b/Foxnouns.Backend/Extensions/AvatarObjectExtensions.cs @@ -14,13 +14,11 @@ public static class AvatarObjectExtensions private static readonly string[] ValidContentTypes = ["image/png", "image/webp", "image/jpeg"]; public static async Task - DeleteMemberAvatarAsync(this ObjectStorageService objectStorageService, Snowflake id, string hash, - CancellationToken ct = default) => + DeleteMemberAvatarAsync(this ObjectStorageService objectStorageService, Snowflake id, string hash, CancellationToken ct = default) => await objectStorageService.RemoveObjectAsync(MemberAvatarUpdateInvocable.Path(id, hash), ct); public static async Task - DeleteUserAvatarAsync(this ObjectStorageService objectStorageService, Snowflake id, string hash, - CancellationToken ct = default) => + DeleteUserAvatarAsync(this ObjectStorageService objectStorageService, Snowflake id, string hash, CancellationToken ct = default) => await objectStorageService.RemoveObjectAsync(UserAvatarUpdateInvocable.Path(id, hash), ct); public static async Task ConvertBase64UriToAvatar(this string uri) diff --git a/Foxnouns.Backend/Extensions/WebApplicationExtensions.cs b/Foxnouns.Backend/Extensions/WebApplicationExtensions.cs index a5b2af6..132b4d0 100644 --- a/Foxnouns.Backend/Extensions/WebApplicationExtensions.cs +++ b/Foxnouns.Backend/Extensions/WebApplicationExtensions.cs @@ -100,11 +100,11 @@ public static class WebApplicationExtensions // Transient jobs .AddTransient() .AddTransient(); - + if (!config.Logging.EnableMetrics) services.AddHostedService(); }); - + return builder.Services; } diff --git a/Foxnouns.Backend/Foxnouns.Backend.csproj b/Foxnouns.Backend/Foxnouns.Backend.csproj index a9e7b74..987ebbd 100644 --- a/Foxnouns.Backend/Foxnouns.Backend.csproj +++ b/Foxnouns.Backend/Foxnouns.Backend.csproj @@ -8,34 +8,34 @@ - - - - - - - - + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + @@ -44,12 +44,12 @@ - + - - .dockerignore - + + .dockerignore + diff --git a/Foxnouns.Backend/Mailables/AccountCreationMailable.cs b/Foxnouns.Backend/Mailables/AccountCreationMailable.cs index cec17cf..3fc1ff4 100644 --- a/Foxnouns.Backend/Mailables/AccountCreationMailable.cs +++ b/Foxnouns.Backend/Mailables/AccountCreationMailable.cs @@ -2,8 +2,7 @@ using Coravel.Mailer.Mail; namespace Foxnouns.Backend.Mailables; -public class AccountCreationMailable(Config config, AccountCreationMailableView view) - : Mailable +public class AccountCreationMailable(Config config, AccountCreationMailableView view) : Mailable { public override void Build() { diff --git a/Foxnouns.Backend/Services/MemberRendererService.cs b/Foxnouns.Backend/Services/MemberRendererService.cs index ef7b923..962712f 100644 --- a/Foxnouns.Backend/Services/MemberRendererService.cs +++ b/Foxnouns.Backend/Services/MemberRendererService.cs @@ -11,7 +11,6 @@ public class MemberRendererService(DatabaseContext db, Config config) public async Task> RenderUserMembersAsync(User user, Token? token) { var canReadHiddenMembers = token != null && token.UserId == user.Id && token.HasScope("member.read"); - var renderUnlisted = token != null && token.UserId == user.Id && token.HasScope("user.read_hidden"); var canReadMemberList = !user.ListHidden || canReadHiddenMembers; IEnumerable members = canReadMemberList @@ -21,7 +20,7 @@ public class MemberRendererService(DatabaseContext db, Config config) .ToListAsync() : []; if (!canReadHiddenMembers) members = members.Where(m => !m.Unlisted); - return members.Select(m => RenderPartialMember(m, renderUnlisted)); + return members.Select(RenderPartialMember); } public MemberResponse RenderMember(Member member, Token? token) @@ -35,11 +34,10 @@ public class MemberRendererService(DatabaseContext db, Config config) } private UserRendererService.PartialUser RenderPartialUser(User user) => - new(user.Id, user.Username, user.DisplayName, AvatarUrlFor(user), user.CustomPreferences); + new(user.Id, user.Username, user.DisplayName, AvatarUrlFor(user)); - public PartialMember RenderPartialMember(Member member, bool renderUnlisted = false) => new(member.Id, member.Name, - member.DisplayName, member.Bio, AvatarUrlFor(member), member.Names, member.Pronouns, - renderUnlisted ? member.Unlisted : null); + public PartialMember RenderPartialMember(Member member) => new(member.Id, member.Name, + member.DisplayName, member.Bio, AvatarUrlFor(member), member.Names, member.Pronouns); private string? AvatarUrlFor(Member member) => member.Avatar != null ? $"{config.MediaBaseUrl}/members/{member.Id}/avatars/{member.Avatar}.webp" : null; @@ -54,9 +52,7 @@ public class MemberRendererService(DatabaseContext db, Config config) string? Bio, string? AvatarUrl, IEnumerable Names, - IEnumerable Pronouns, - [property: JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - bool? Unlisted); + IEnumerable Pronouns); public record MemberResponse( Snowflake Id, diff --git a/Foxnouns.Backend/Services/UserRendererService.cs b/Foxnouns.Backend/Services/UserRendererService.cs index 8251611..07bdb8b 100644 --- a/Foxnouns.Backend/Services/UserRendererService.cs +++ b/Foxnouns.Backend/Services/UserRendererService.cs @@ -39,7 +39,7 @@ public class UserRendererService(DatabaseContext db, MemberRendererService membe return new UserResponse( user.Id, user.Username, user.DisplayName, user.Bio, user.MemberTitle, AvatarUrlFor(user), user.Links, user.Names, user.Pronouns, user.Fields, user.CustomPreferences, - renderMembers ? members.Select(m => memberRenderer.RenderPartialMember(m, tokenHidden)) : null, + renderMembers ? members.Select(memberRenderer.RenderPartialMember) : null, renderAuthMethods ? authMethods.Select(a => new AuthenticationMethodResponse( a.Id, a.AuthType, a.RemoteId, @@ -52,7 +52,7 @@ public class UserRendererService(DatabaseContext db, MemberRendererService membe } public PartialUser RenderPartialUser(User user) => - new(user.Id, user.Username, user.DisplayName, AvatarUrlFor(user), user.CustomPreferences); + new(user.Id, user.Username, user.DisplayName, AvatarUrlFor(user)); private string? AvatarUrlFor(User user) => user.Avatar != null ? $"{config.MediaBaseUrl}/users/{user.Id}/avatars/{user.Avatar}.webp" : null; @@ -94,7 +94,6 @@ public class UserRendererService(DatabaseContext db, MemberRendererService membe Snowflake Id, string Username, string? DisplayName, - string? AvatarUrl, - Dictionary CustomPreferences + string? AvatarUrl ); } \ No newline at end of file diff --git a/Foxnouns.Backend/Utils/AuthUtils.cs b/Foxnouns.Backend/Utils/AuthUtils.cs index c767198..26965e2 100644 --- a/Foxnouns.Backend/Utils/AuthUtils.cs +++ b/Foxnouns.Backend/Utils/AuthUtils.cs @@ -79,7 +79,7 @@ public static class AuthUtils return false; } } - + public static bool TryParseToken(string? input, out byte[] rawToken) { rawToken = []; diff --git a/Foxnouns.Backend/Utils/ValidationUtils.cs b/Foxnouns.Backend/Utils/ValidationUtils.cs index f29dd24..9020c0f 100644 --- a/Foxnouns.Backend/Utils/ValidationUtils.cs +++ b/Foxnouns.Backend/Utils/ValidationUtils.cs @@ -156,8 +156,7 @@ public static class ValidationUtils break; } - errors = errors.Concat(ValidateFieldEntries(field.Entries, customPreferences, $"fields.{index}.entries")) - .ToList(); + errors = errors.Concat(ValidateFieldEntries(field.Entries, customPreferences, $"fields.{index}.entries")).ToList(); } return errors; @@ -239,14 +238,12 @@ public static class ValidationUtils { case > Limits.FieldEntryTextLimit: errors.Add(($"{errorPrefix}.{entryIdx}.value", - ValidationError.LengthError("Pronoun display text is too long", 1, - Limits.FieldEntryTextLimit, + ValidationError.LengthError("Pronoun display text is too long", 1, Limits.FieldEntryTextLimit, entry.Value.Length))); break; case < 1: errors.Add(($"{errorPrefix}.{entryIdx}.value", - ValidationError.LengthError("Pronoun display text is too short", 1, - Limits.FieldEntryTextLimit, + ValidationError.LengthError("Pronoun display text is too short", 1, Limits.FieldEntryTextLimit, entry.Value.Length))); break; } diff --git a/Foxnouns.Backend/Views/Mail/_ViewImports.cshtml b/Foxnouns.Backend/Views/Mail/_ViewImports.cshtml index f13b1c3..6ececef 100644 --- a/Foxnouns.Backend/Views/Mail/_ViewImports.cshtml +++ b/Foxnouns.Backend/Views/Mail/_ViewImports.cshtml @@ -1,2 +1,2 @@ @using Foxnouns.Backend -@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers \ No newline at end of file +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers diff --git a/Foxnouns.Backend/Views/Mail/_ViewStart.cshtml b/Foxnouns.Backend/Views/Mail/_ViewStart.cshtml index 4080127..b74bab7 100644 --- a/Foxnouns.Backend/Views/Mail/_ViewStart.cshtml +++ b/Foxnouns.Backend/Views/Mail/_ViewStart.cshtml @@ -1,3 +1,3 @@ @{ Layout = "~/Views/Mail/Layout.cshtml"; -} \ No newline at end of file +} diff --git a/Foxnouns.Frontend/app/components/StatusIcon.tsx b/Foxnouns.Frontend/app/components/StatusIcon.tsx index 9f2fa89..d7a068e 100644 --- a/Foxnouns.Frontend/app/components/StatusIcon.tsx +++ b/Foxnouns.Frontend/app/components/StatusIcon.tsx @@ -1,4 +1,4 @@ -import { CustomPreference, defaultPreferences, mergePreferences } from "~/lib/api/user"; +import { CustomPreference, defaultPreferences } from "~/lib/api/user"; import { OverlayTrigger, Tooltip } from "react-bootstrap"; import Icon from "~/components/KeyedIcon"; @@ -9,7 +9,7 @@ export default function StatusIcon({ preferences: Record; status: string; }) { - const mergedPrefs = mergePreferences(preferences); + const mergedPrefs = Object.assign({}, defaultPreferences, preferences); const currentPref = status in mergedPrefs ? mergedPrefs[status] : defaultPreferences.missing; const id = crypto.randomUUID(); diff --git a/Foxnouns.Frontend/app/components/StatusLine.tsx b/Foxnouns.Frontend/app/components/StatusLine.tsx index 704df75..b39222f 100644 --- a/Foxnouns.Frontend/app/components/StatusLine.tsx +++ b/Foxnouns.Frontend/app/components/StatusLine.tsx @@ -2,11 +2,11 @@ import { CustomPreference, defaultPreferences, FieldEntry, - mergePreferences, PreferenceSize, Pronoun, } from "~/lib/api/user"; import classNames from "classnames"; +import { ReactNode } from "react"; import StatusIcon from "~/components/StatusIcon"; import PronounLink from "~/components/PronounLink"; @@ -17,7 +17,7 @@ export default function StatusLine({ entry: FieldEntry | Pronoun; preferences: Record; }) { - const mergedPrefs = mergePreferences(preferences); + const mergedPrefs = Object.assign({}, defaultPreferences, preferences); const currentPref = entry.status in mergedPrefs ? mergedPrefs[entry.status] : defaultPreferences.missing; diff --git a/Foxnouns.Frontend/app/lib/api/user.ts b/Foxnouns.Frontend/app/lib/api/user.ts index 5dc969c..0487ea6 100644 --- a/Foxnouns.Frontend/app/lib/api/user.ts +++ b/Foxnouns.Frontend/app/lib/api/user.ts @@ -3,7 +3,6 @@ export type PartialUser = { username: string; display_name?: string | null; avatar_url?: string | null; - custom_preferences: Record; }; export type User = PartialUser & { @@ -13,6 +12,7 @@ export type User = PartialUser & { names: FieldEntry[]; pronouns: Pronoun[]; fields: Field[]; + custom_preferences: Record; }; export type UserWithMembers = User & { members: PartialMember[] }; @@ -35,7 +35,6 @@ export type PartialMember = { avatar_url: string | null; names: FieldEntry[]; pronouns: Pronoun[]; - unlisted: boolean | null; }; export type FieldEntry = { @@ -64,10 +63,6 @@ export enum PreferenceSize { Small = "SMALL", } -export function mergePreferences(prefs: Record) { - return Object.assign({}, defaultPreferences, prefs); -} - export const defaultPreferences = Object.freeze({ favourite: { icon: "heart-fill", diff --git a/Foxnouns.Frontend/app/lib/utils.ts b/Foxnouns.Frontend/app/lib/utils.ts deleted file mode 100644 index 9a8d8b5..0000000 --- a/Foxnouns.Frontend/app/lib/utils.ts +++ /dev/null @@ -1 +0,0 @@ -export const defaultAvatarUrl = "https://pronouns.cc/default/512.webp"; diff --git a/Foxnouns.Frontend/app/routes/$username/MemberCard.tsx b/Foxnouns.Frontend/app/routes/$username/MemberCard.tsx deleted file mode 100644 index 893782e..0000000 --- a/Foxnouns.Frontend/app/routes/$username/MemberCard.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import { - defaultPreferences, - mergePreferences, - PartialMember, - PartialUser, - Pronoun, -} from "~/lib/api/user"; -import { Link } from "@remix-run/react"; -import { defaultAvatarUrl } from "~/lib/utils"; -import { useTranslation } from "react-i18next"; -import { OverlayTrigger, Tooltip } from "react-bootstrap"; -import { Lock } from "react-bootstrap-icons"; - -export default function MemberCard({ user, member }: { user: PartialUser; member: PartialMember }) { - const { t } = useTranslation(); - - const mergedPrefs = mergePreferences(user.custom_preferences); - const pronouns: Pronoun[] = []; - for (const pronoun of member.pronouns) { - const pref = - pronoun.status in mergedPrefs ? mergedPrefs[pronoun.status] : defaultPreferences.missing; - if (pref.favourite) pronouns.push(pronoun); - } - - const displayedPronouns = pronouns - .map((pronoun) => { - if (pronoun.display_text) { - return pronoun.display_text; - } else { - const split = pronoun.value.split("/"); - if (split.length === 5) return split.splice(0, 2).join("/"); - return pronoun.value; - } - }) - .join(", "); - - return ( -
- - {t("user.member-avatar-alt", - -

- - {member.display_name ?? member.name} - {member.unlisted === true && ( - <> - - {t("user.member-hidden")} - - } - > - - - - - - )} - - {displayedPronouns && <>{displayedPronouns}} -

-
- ); -} diff --git a/Foxnouns.Frontend/app/routes/$username/route.tsx b/Foxnouns.Frontend/app/routes/$username/route.tsx index b2d6406..6a40dad 100644 --- a/Foxnouns.Frontend/app/routes/$username/route.tsx +++ b/Foxnouns.Frontend/app/routes/$username/route.tsx @@ -3,14 +3,11 @@ import { Link, redirect, useLoaderData, useRouteLoaderData } from "@remix-run/re import { UserWithMembers } from "~/lib/api/user"; import serverRequest from "~/lib/request.server"; import { loader as rootLoader } from "~/root"; -import { Alert, Button } from "react-bootstrap"; +import { Alert } from "react-bootstrap"; import { Trans, useTranslation } from "react-i18next"; import { renderMarkdown } from "~/lib/markdown"; import ProfileLink from "~/components/ProfileLink"; import ProfileField from "~/components/ProfileField"; -import { PersonPlusFill } from "react-bootstrap-icons"; -import { defaultAvatarUrl } from "~/lib/utils"; -import MemberCard from "~/routes/$username/MemberCard"; export const meta: MetaFunction = ({ data }) => { const { user } = data!; @@ -42,17 +39,16 @@ export default function UserPage() { const { user } = useLoaderData(); const { meUser } = useRouteLoaderData("root") || { meUser: undefined }; - const isMeUser = meUser && meUser.id === user.id; const bio = renderMarkdown(user.bio); return ( <> - {isMeUser && ( + {meUser && meUser.id === user.id && ( You are currently viewing your public profile.
- Edit your profile + Edit your profile
)} @@ -60,7 +56,7 @@ export default function UserPage() {
{t("user.avatar-alt",
- {user.members.length > 0 || - (isMeUser && ( - <> -
-

- {user.member_title || t("user.heading.members")}{" "} - {/* @ts-expect-error using as=Link causes an error here, even though it runs completely fine */} - -

-
- {user.members.length === 0 ? ( -
- - You don't have any members yet. -
- Members are sub-profiles that can have their own avatar, names, pronouns, and preferred terms. -
- You can create a new member with the "Create member" button above.{" "} - (only you can see this) -
-
- ) : ( -
- {user.members.map((member, i) => ( - - ))} -
- )} -
- - ))} ); } diff --git a/Foxnouns.Frontend/public/favicon.svg b/Foxnouns.Frontend/public/favicon.svg deleted file mode 100644 index 11e664f..0000000 --- a/Foxnouns.Frontend/public/favicon.svg +++ /dev/null @@ -1,2 +0,0 @@ - -image/svg+xml \ No newline at end of file diff --git a/Foxnouns.Frontend/public/locales/en.json b/Foxnouns.Frontend/public/locales/en.json index 20a1fa0..afebe1a 100644 --- a/Foxnouns.Frontend/public/locales/en.json +++ b/Foxnouns.Frontend/public/locales/en.json @@ -1,88 +1,87 @@ { - "error": { - "heading": "An error occurred", - "validation": { - "too-long": "Value is too long, maximum length is {{maxLength}}, current length is {{actualLength}}.", - "too-short": "Value is too short, minimum length is {{minLength}}, current length is {{actualLength}}.", - "disallowed-value": "The value <1>{{actualValue}} is not allowed here. Allowed values are: <4>{{allowedValues}}", - "generic": "The value <1>{{actualValue}} is not allowed here. Reason: {{reason}}", - "generic-no-value": "The value you entered is not allowed here. Reason: {{reason}}" - }, - "errors": { - "authentication-error": "There was an error validating your credentials.", - "authentication-required": "You need to log in.", - "bad-request": "Server rejected your input, please check anything for errors.", - "forbidden": "You are not allowed to perform that action.", - "generic-error": "An unknown error occurred.", - "internal-server-error": "Server experienced an internal error, please try again later.", - "member-not-found": "Member not found, please check your spelling and try again.", - "user-not-found": "User not found, please check your spelling and try again." - }, - "title": "An error occurred", - "more-info": "Click here for a more detailed error" - }, - "navbar": { - "view-profile": "View profile", - "settings": "Settings", - "log-out": "Log out", - "log-in": "Log in or sign up" - }, - "user": { - "member-avatar-alt": "Avatar for {{name}}", - "member-hidden": "This member is unlisted, and not shown in your public member list.", - "own-profile-alert": "You are currently viewing your <1>public profile.<3><4>Edit your profile", - "avatar-alt": "Avatar for @{{username}}", - "heading": { - "names": "Names", - "pronouns": "Pronouns", - "members": "Members" - }, - "create-member-button": "Create member", - "no-members-blurb": "You don't have any members yet.<1>Members are sub-profiles that can have their own avatar, names, pronouns, and preferred terms.<3>You can create a new member with the \"Create member\" button above. <6>(only you can see this)" - }, - "log-in": { - "callback": { - "title": { - "discord-success": "Log in with Discord", - "discord-register": "Register with Discord" - }, - "success": "Successfully logged in!", - "success-link": "Welcome back, <1>@{{username}}!", - "redirect-hint": "If you're not redirected to your profile in a few seconds, press the link above.", - "remote-username": { - "discord": "Your discord username" - }, - "username": "Username", - "sign-up-button": "Sign up", - "invalid-ticket": "Invalid ticket (it might have been too long since you logged in with Discord), please <2>try again.", - "invalid-username": "Invalid username", - "username-taken": "That username is already taken, please try something else." - }, - "title": "Log in", - "form-title": "Log in with email", - "email": "Email address", - "password": "Password", - "log-in-button": "Log in", - "register-with-email": "Register with email", - "3rd-party": { - "title": "Log in with another service", - "desc": "If you prefer, you can also log in with one of these services:", - "discord": "Log in with Discord", - "google": "Log in with Google", - "tumblr": "Log in with Tumblr" - }, - "invalid-credentials": "Invalid email address or password, please check your spelling and try again." - }, - "welcome": { - "title": "Welcome", - "header": "Welcome to pronouns.cc!", - "blurb": "{welcome.blurb}", - "customize-profile": "Customize your profile", - "customize-profile-blurb": "{welcome.customize-profile-blurb}", - "create-members": "Create members", - "create-members-blurb": "{welcome.create-members-blurb}", - "custom-preferences": "Customize your preferences", - "custom-preferences-blurb": "{welcome.custom-preferences-blurb}", - "profile-button": "Go to your profile" - } + "error": { + "heading": "An error occurred", + "validation": { + "too-long": "Value is too long, maximum length is {{maxLength}}, current length is {{actualLength}}.", + "too-short": "Value is too short, minimum length is {{minLength}}, current length is {{actualLength}}.", + "disallowed-value": "The value <1>{{actualValue}} is not allowed here. Allowed values are: <4>{{allowedValues}}", + "generic": "The value <1>{{actualValue}} is not allowed here. Reason: {{reason}}", + "generic-no-value": "The value you entered is not allowed here. Reason: {{reason}}" + }, + "errors": { + "authentication-error": "There was an error validating your credentials.", + "authentication-required": "You need to log in.", + "bad-request": "Server rejected your input, please check anything for errors.", + "forbidden": "You are not allowed to perform that action.", + "generic-error": "An unknown error occurred.", + "internal-server-error": "Server experienced an internal error, please try again later.", + "member-not-found": "Member not found, please check your spelling and try again.", + "user-not-found": "User not found, please check your spelling and try again." + }, + "title": "An error occurred", + "more-info": "Click here for a more detailed error" + }, + "navbar": { + "view-profile": "View profile", + "settings": "Settings", + "log-out": "Log out", + "log-in": "Log in or sign up", + "theme": "Theme", + "theme-auto": "Automatic", + "theme-dark": "Dark", + "theme-light": "Light" + }, + "user": { + "own-profile-alert": "You are currently viewing your <1>public profile.<3><4>Edit your profile", + "avatar-alt": "Avatar for @{{username}}", + "heading": { + "names": "Names", + "pronouns": "Pronouns" + } + }, + "log-in": { + "callback": { + "title": { + "discord-success": "Log in with Discord", + "discord-register": "Register with Discord" + }, + "success": "Successfully logged in!", + "success-link": "Welcome back, <1>@{{username}}!", + "redirect-hint": "If you're not redirected to your profile in a few seconds, press the link above.", + "remote-username": { + "discord": "Your discord username" + }, + "username": "Username", + "sign-up-button": "Sign up", + "invalid-ticket": "Invalid ticket (it might have been too long since you logged in with Discord), please <2>try again.", + "invalid-username": "Invalid username", + "username-taken": "That username is already taken, please try something else." + }, + "title": "Log in", + "form-title": "Log in with email", + "email": "Email address", + "password": "Password", + "log-in-button": "Log in", + "register-with-email": "Register with email", + "3rd-party": { + "title": "Log in with another service", + "desc": "If you prefer, you can also log in with one of these services:", + "discord": "Log in with Discord", + "google": "Log in with Google", + "tumblr": "Log in with Tumblr" + }, + "invalid-credentials": "Invalid email address or password, please check your spelling and try again." + }, + "welcome": { + "title": "Welcome", + "header": "Welcome to pronouns.cc!", + "blurb": "{welcome.blurb}", + "customize-profile": "Customize your profile", + "customize-profile-blurb": "{welcome.customize-profile-blurb}", + "create-members": "Create members", + "create-members-blurb": "{welcome.create-members-blurb}", + "custom-preferences": "Customize your preferences", + "custom-preferences-blurb": "{welcome.custom-preferences-blurb}", + "profile-button": "Go to your profile" + } } diff --git a/Foxnouns.NET.sln.DotSettings b/Foxnouns.NET.sln.DotSettings deleted file mode 100644 index 69e6273..0000000 --- a/Foxnouns.NET.sln.DotSettings +++ /dev/null @@ -1,3 +0,0 @@ - - True - True \ No newline at end of file