diff --git a/external/lang b/external/lang index 4ef73335..e200511d 160000 --- a/external/lang +++ b/external/lang @@ -1 +1 @@ -Subproject commit 4ef73335436f3118d660cec24ff7972d3ccd5984 +Subproject commit e200511dcb2c655e6ee89580fee9ef96c9f1f387 diff --git a/src/context/Locale.tsx b/src/context/Locale.tsx index 95fd9b79..0385745c 100644 --- a/src/context/Locale.tsx +++ b/src/context/Locale.tsx @@ -125,10 +125,34 @@ function Locale({ children, locale }: Props) { const [defns, setDefinition] = useState(definition); const lang = Languages[locale]; + function transformLanguage(obj: { [key: string]: any }) { + const dayjs = obj.dayjs; + const defaults = dayjs.defaults; + + const twelvehour = defaults?.twelvehour === 'yes' || true; + const separator: '/' | '-' | '.' = defaults?.date_separator ?? '/'; + const date: 'traditional' | 'simplified' | 'ISO8601' = defaults?.date_format ?? 'traditional'; + + const DATE_FORMATS = { + traditional: `DD${separator}MM${separator}YYYY`, + simplified: `MM${separator}DD${separator}YYYY`, + ISO8601: 'YYYY-MM-DD' + } + + dayjs['sameElse'] = DATE_FORMATS[date]; + Object.keys(dayjs) + .filter(k => k !== 'defaults') + .forEach(k => dayjs[k] = dayjs[k].replace(/{{time}}/g, twelvehour ? 'LT' : 'HH:mm')); + + return obj; + } + useEffect(() => { if (locale === "en") { + transformLanguage(definition); setDefinition(definition); dayjs.locale("en"); + dayjs.updateLocale('en', { calendar: definition.dayjs }); return; } @@ -141,6 +165,7 @@ function Locale({ children, locale }: Props) { import(`../../external/lang/${lang.i18n}.json`).then( async (lang_file) => { const defn = lang_file.default; + transformLanguage(defn); const target = lang.dayjs ?? lang.i18n; const dayjs_locale = await import(`../../node_modules/dayjs/esm/locale/${target}.js`); diff --git a/src/context/intermediate/Intermediate.tsx b/src/context/intermediate/Intermediate.tsx index 3fb81674..7cb97202 100644 --- a/src/context/intermediate/Intermediate.tsx +++ b/src/context/intermediate/Intermediate.tsx @@ -1,4 +1,4 @@ -import { Attachment, Channels, EmbedImage, Servers } from "revolt.js/dist/api/objects"; +import { Attachment, Channels, EmbedImage, Servers, Users } from "revolt.js/dist/api/objects"; import { useContext, useEffect, useMemo, useState } from "preact/hooks"; import { internalSubscribe } from "../../lib/eventEmitter"; import { Action } from "../../components/ui/Modal"; @@ -26,6 +26,8 @@ export type Screen = { type: "create_invite", target: Channels.TextChannel | Channels.GroupChannel } | { type: "kick_member", target: Servers.Server, user: string } | { type: "ban_member", target: Servers.Server, user: string } | + { type: "unfriend_user", target: Users.User } | + { type: "block_user", target: Users.User } | { type: "create_channel", target: Servers.Server } )) | ({ id: "special_input" } & ( diff --git a/src/context/intermediate/modals/Input.tsx b/src/context/intermediate/modals/Input.tsx index 68e00937..fd8d44ec 100644 --- a/src/context/intermediate/modals/Input.tsx +++ b/src/context/intermediate/modals/Input.tsx @@ -35,6 +35,7 @@ export function InputModal({ disabled={processing} actions={[ { + confirmation: true, text: , onClick: () => { setProcessing(true); diff --git a/src/context/intermediate/modals/Prompt.tsx b/src/context/intermediate/modals/Prompt.tsx index 78048d8f..1af00210 100644 --- a/src/context/intermediate/modals/Prompt.tsx +++ b/src/context/intermediate/modals/Prompt.tsx @@ -10,10 +10,11 @@ import Overline from "../../../components/ui/Overline"; import { AppContext } from "../../revoltjs/RevoltClient"; import { mapMessage, takeError } from "../../revoltjs/util"; import Modal, { Action } from "../../../components/ui/Modal"; -import { Channels, Servers } from "revolt.js/dist/api/objects"; +import { Channels, Servers, Users } from "revolt.js/dist/api/objects"; import { useContext, useEffect, useState } from "preact/hooks"; import UserIcon from "../../../components/common/user/UserIcon"; import Message from "../../../components/common/messaging/Message"; +import { TextReact } from "../../../lib/i18n"; interface Props { onClose: () => void; @@ -48,6 +49,8 @@ type SpecialProps = { onClose: () => void } & ( { type: "create_invite", target: Channels.TextChannel | Channels.GroupChannel } | { type: "kick_member", target: Servers.Server, user: string } | { type: "ban_member", target: Servers.Server, user: string } | + { type: "unfriend_user", target: Users.User } | + { type: "block_user", target: Users.User } | { type: "create_channel", target: Servers.Server } ) @@ -62,23 +65,33 @@ export function SpecialPromptModal(props: SpecialProps) { case 'close_dm': case 'leave_server': case 'delete_server': - case 'delete_channel': { + case 'delete_channel': + case 'unfriend_user': + case 'block_user': { const EVENTS = { - 'close_dm': 'confirm_close_dm', - 'delete_server': 'confirm_delete', - 'delete_channel': 'confirm_delete', - 'leave_group': 'confirm_leave', - 'leave_server': 'confirm_leave' + 'close_dm': ['confirm_close_dm', 'close'], + 'delete_server': ['confirm_delete', 'delete'], + 'delete_channel': ['confirm_delete', 'delete'], + 'leave_group': ['confirm_leave', 'leave'], + 'leave_server': ['confirm_leave', 'leave'], + 'unfriend_user': ['unfriend_user', 'remove'], + 'block_user': ['block_user', 'block'] }; let event = EVENTS[props.type]; - let name = props.type === 'close_dm' ? client.users.get(client.channels.getRecipient(props.target._id))?.username : props.target.name; + let name; + switch (props.type) { + case 'unfriend_user': + case 'block_user': name = props.target.username; break; + case 'close_dm': name = client.users.get(client.channels.getRecipient(props.target._id))?.username; break; + default: name = props.target.name; + } return ( } actions={[ @@ -86,15 +99,23 @@ export function SpecialPromptModal(props: SpecialProps) { confirmation: true, contrast: true, error: true, - text: , + text: , onClick: async () => { setProcessing(true); try { - if (props.type === 'leave_group' || props.type === 'close_dm' || props.type === 'delete_channel') { - await client.channels.delete(props.target._id); - } else { - await client.servers.delete(props.target._id); + switch (props.type) { + case 'unfriend_user': + await client.users.removeFriend(props.target._id); break; + case 'block_user': + await client.users.blockUser(props.target._id); break; + case 'leave_group': + case 'close_dm': + case 'delete_channel': + await client.channels.delete(props.target._id); break; + case 'leave_server': + case 'delete_server': + await client.servers.delete(props.target._id); break; } onClose(); @@ -106,7 +127,7 @@ export function SpecialPromptModal(props: SpecialProps) { }, { text: , onClick: onClose } ]} - content={} + content={{ name } }} />} disabled={processing} error={error} /> diff --git a/src/lib/ContextMenus.tsx b/src/lib/ContextMenus.tsx index 265ca804..97e28529 100644 --- a/src/lib/ContextMenus.tsx +++ b/src/lib/ContextMenus.tsx @@ -60,11 +60,11 @@ type Action = | { action: "ban_member"; target: Servers.Server; user: string } | { action: "view_profile"; user: string } | { action: "message_user"; user: string } - | { action: "block_user"; user: string } - | { action: "unblock_user"; user: string } - | { action: "add_friend"; user: string } - | { action: "remove_friend"; user: string } - | { action: "cancel_friend"; user: string } + | { action: "block_user"; user: Users.User } + | { action: "unblock_user"; user: Users.User } + | { action: "add_friend"; user: Users.User } + | { action: "remove_friend"; user: Users.User } + | { action: "cancel_friend"; user: Users.User } | { action: "set_presence"; presence: Users.Presence } | { action: "set_status" } | { action: "clear_status" } @@ -264,22 +264,21 @@ function ContextMenus(props: Props) { case "add_friend": { - let user = client.users.get(data.user); - if (user) { - await client.users.addFriend(user.username); - } + await client.users.addFriend(data.user.username); } break; case "block_user": - await client.users.blockUser(data.user); + openScreen({ id: 'special_prompt', type: 'block_user', target: data.user }); break; case "unblock_user": - await client.users.unblockUser(data.user); + await client.users.unblockUser(data.user._id); break; case "remove_friend": + openScreen({ id: 'special_prompt', type: 'unfriend_user', target: data.user }); + break; case "cancel_friend": - await client.users.removeFriend(data.user); + await client.users.removeFriend(data.user._id); break; case "set_presence": @@ -466,7 +465,7 @@ function ContextMenus(props: Props) { for (const action of actions) { generateAction({ action: action as any, - user: user._id + user }); } } diff --git a/src/pages/friends/Friend.tsx b/src/pages/friends/Friend.tsx index 74b33a24..d50198cc 100644 --- a/src/pages/friends/Friend.tsx +++ b/src/pages/friends/Friend.tsx @@ -70,7 +70,7 @@ export function Friend({ user }: Props) { actions.push( stopPropagation(ev, client.users.removeFriend(user._id))}> + onClick={ev => stopPropagation(ev, openScreen({ id: 'special_prompt', type: 'unfriend_user', target: user }))}> );