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 }))}>
);