Skip to content

Commit 9ee982d

Browse files
committed
fix(client): restore typecheck and image attachment preview modal
Signed-off-by: Bob-Swan <chrisrobinsonlive@gmail.com>
1 parent 0b47eb2 commit 9ee982d

File tree

6 files changed

+107
-28
lines changed

6 files changed

+107
-28
lines changed

packages/client/components/app/interface/settings/user/Native.tsx

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,14 @@ export default function Native() {
6464
setAutostart(savedValue);
6565
}
6666

67-
const toggles: Partial<Record<keyof DesktopConfig, () => void>> = {
67+
type ToggleKey =
68+
| "minimiseToTray"
69+
| "customFrame"
70+
| "discordRpc"
71+
| "spellchecker"
72+
| "hardwareAcceleration";
73+
74+
const toggles: Record<ToggleKey, () => void> = {
6875
minimiseToTray: () => set({ minimiseToTray: !config().minimiseToTray }),
6976
customFrame: () => set({ customFrame: !config().customFrame }),
7077
discordRpc: () => set({ discordRpc: !config().discordRpc }),
@@ -73,23 +80,23 @@ export default function Native() {
7380
set({ hardwareAcceleration: !config().hardwareAcceleration }),
7481
};
7582

76-
function CheckboxButton<K extends keyof DesktopConfig>(
77-
key: K,
83+
function CheckboxButton(
84+
key: ToggleKey,
7885
icon: string,
7986
label: string,
8087
description: string,
8188
) {
8289
return (
8390
<CategoryButton
8491
action={
85-
<Checkbox
86-
checked={config()[key]}
87-
onClick={(e) => e.stopPropagation()}
88-
onChange={(e) => {
89-
e.stopPropagation();
90-
toggles[key]!();
91-
}}
92-
/>
92+
<span onClick={(e) => e.stopPropagation()}>
93+
<Checkbox
94+
checked={config()[key]}
95+
onChange={() => {
96+
toggles[key]();
97+
}}
98+
/>
99+
</span>
93100
}
94101
onClick={toggles[key]}
95102
icon={<Symbol>{icon}</Symbol>}
@@ -105,11 +112,9 @@ export default function Native() {
105112
<CategoryButton.Group>
106113
<CategoryButton
107114
action={
108-
<Checkbox
109-
checked={autostart()}
110-
onClick={(e) => e.stopPropagation()}
111-
onChange={toggleAutostart}
112-
/>
115+
<span onClick={(e) => e.stopPropagation()}>
116+
<Checkbox checked={autostart()} onChange={toggleAutostart} />
117+
</span>
113118
}
114119
onClick={toggleAutostart}
115120
icon={<Symbol>exit_to_app</Symbol>}

packages/client/components/i18n/index.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { I18nProvider as LinguiProvider } from "@lingui-solid/solid";
44
import { i18n } from "@lingui/core";
55

66
import { type LocaleOptions, Language, Languages } from "./Languages";
7+
// @ts-expect-error generated Lingui catalog modules are JS-only and do not emit d.ts files
78
import { messages as en } from "./catalogs/en/messages";
89
import { initTime, loadTimeLocale } from "./dayjs";
910

@@ -23,7 +24,7 @@ export async function loadAndSwitchLocale(
2324
const data =
2425
Languages[key].i18n === "en"
2526
? en
26-
: (await import(`./catalogs/${Languages[key].i18n}/messages.ts`))
27+
: (await import(`./catalogs/${Languages[key].i18n}/messages`))
2728
.messages;
2829

2930
i18n.load({

packages/client/components/state/stores/Theme.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,10 @@ export type TypeTheme = {
7070
messageGroupSpacing: number;
7171
};
7272

73-
export type SelectedTheme = Pick & {
73+
export type SelectedTheme = Pick<
74+
TypeTheme,
75+
"blur" | "interfaceFont" | "monospaceFont" | "messageSize" | "messageGroupSpacing"
76+
> & {
7477
preset: "you";
7578
darkMode: boolean;
7679

@@ -82,8 +85,8 @@ export type SelectedTheme = Pick & {
8285
/**
8386
* Manages theme information
8487
*/
85-
export class Theme extends AbstractStore {
86-
prefersDark: Accessor;
88+
export class Theme extends AbstractStore<"theme", TypeTheme> {
89+
prefersDark: Accessor<boolean>;
8790

8891
/**
8992
* Construct store
@@ -138,14 +141,14 @@ export class Theme extends AbstractStore {
138141
/**
139142
* Validate the given data to see if it is compliant and return a compliant object
140143
*/
141-
clean(input: Partial): TypeTheme {
144+
clean(input: Partial<TypeTheme>): TypeTheme {
142145
const data: TypeTheme = this.default();
143146

144147
if (["light", "dark", "system"].includes(input.mode!)) {
145148
data.mode = input.mode!;
146149
}
147150

148-
if (["you", "neutral"].includes(input.preset!)) {
151+
if (input.preset === "you") {
149152
data.preset = input.preset!;
150153
}
151154

packages/client/components/ui/components/features/messaging/composition/FileCarousel.tsx

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ interface Props {
3737
* @param fileId ID
3838
*/
3939
removeFile(fileId: string): void;
40+
41+
/**
42+
* Preview file (for images)
43+
* @param fileId ID
44+
* @param dataUri Data URI of the file
45+
*/
46+
onPreview?: (fileId: string, dataUri: string, fileName: string) => void;
4047
}
4148

4249
/**
@@ -70,9 +77,38 @@ export function FileCarousel(props: Props) {
7077
const file = () => props.getFile(id);
7178

7279
/**
73-
* Handler for removing the file
80+
* Get whether this is an image
81+
*/
82+
const isImage = () =>
83+
ALLOWED_IMAGE_TYPES.includes(file().file.type);
84+
85+
/**
86+
* Handler for previewing the file
87+
*/
88+
const onPreview = (e: MouseEvent) => {
89+
e.stopPropagation();
90+
const currentFile = file();
91+
const dataUri = currentFile.dataUri;
92+
93+
if (
94+
ALLOWED_IMAGE_TYPES.includes(currentFile.file.type) &&
95+
props.onPreview &&
96+
dataUri
97+
) {
98+
props.onPreview(id, dataUri, currentFile.file.name);
99+
} else {
100+
props.removeFile(id);
101+
}
102+
};
103+
104+
/**
105+
* Handler for explicitly removing the file.
106+
* Prevents parent click from opening image preview.
74107
*/
75-
const onClick = () => props.removeFile(id);
108+
const onRemove = (e: MouseEvent) => {
109+
e.stopPropagation();
110+
props.removeFile(id);
111+
};
76112

77113
return (
78114
<>
@@ -82,8 +118,8 @@ export function FileCarousel(props: Props) {
82118

83119
<Entry ignored={index() >= CONFIGURATION.MAX_ATTACHMENTS}>
84120
<PreviewBox
85-
onClick={onClick}
86-
image={ALLOWED_IMAGE_TYPES.includes(file().file.type)}
121+
onClick={onPreview}
122+
image={isImage()}
87123
>
88124
<Switch
89125
fallback={
@@ -102,7 +138,7 @@ export function FileCarousel(props: Props) {
102138
/>
103139
</Match>
104140
</Switch>
105-
<Overlay>
141+
<Overlay onClick={onRemove}>
106142
<MdCancel {...iconSize(36)} />
107143
</Overlay>
108144
</PreviewBox>

packages/client/src/interface/channels/text/Composition.tsx

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { useModals } from "@revolt/modal";
2020
import { useState } from "@revolt/state";
2121
import {
2222
CompositionMediaPicker,
23+
Dialog,
2324
FileCarousel,
2425
FileDropAnywhereCollector,
2526
FilePasteCollector,
@@ -30,6 +31,7 @@ import {
3031
} from "@revolt/ui";
3132
import { Symbol } from "@revolt/ui/components/utils/Symbol";
3233
import { useSearchSpace } from "@revolt/ui/components/utils/autoComplete";
34+
import { styled } from "styled-system/jsx";
3335

3436
interface Props {
3537
/**
@@ -88,6 +90,12 @@ export function MessageComposition(props: Props) {
8890
const [nodeReplacement, setNodeReplacement] =
8991
createSignal<readonly [string | "_focus"]>();
9092

93+
// Image preview state
94+
const [previewImage, setPreviewImage] = createSignal<{
95+
dataUri: string;
96+
fileName: string;
97+
} | null>(null);
98+
9199
// bind this composition instance to the global node replacement signal
92100
state.draft._setNodeReplacement = setNodeReplacement;
93101
onCleanup(() => (state.draft._setNodeReplacement = undefined));
@@ -289,7 +297,21 @@ export function MessageComposition(props: Props) {
289297
getFile={state.draft.getFile}
290298
addFile={addFile}
291299
removeFile={removeFile}
300+
onPreview={(fileId, dataUri, fileName) => {
301+
setPreviewImage({ dataUri, fileName });
302+
}}
292303
/>
304+
305+
{/* Image Preview Modal */}
306+
<Dialog
307+
show={!!previewImage()}
308+
onClose={() => setPreviewImage(null)}
309+
title={previewImage()?.fileName}
310+
>
311+
<Show when={previewImage()}>
312+
<PreviewImage src={previewImage()!.dataUri} alt={previewImage()!.fileName} />
313+
</Show>
314+
</Dialog>
293315
<For each={draft().replies ?? []}>
294316
{(reply) => {
295317
const message = client()!.messages.get(reply.id);
@@ -396,3 +418,16 @@ export function MessageComposition(props: Props) {
396418
</>
397419
);
398420
}
421+
422+
/**
423+
* Preview image in modal
424+
*/
425+
const PreviewImage = styled("img", {
426+
base: {
427+
maxWidth: "100%",
428+
maxHeight: "70vh",
429+
objectFit: "contain",
430+
borderRadius: "var(--borderRadius-md)",
431+
},
432+
});
433+

packages/client/tsconfig.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
"types": [
1212
"vite/client",
1313
"vite-plugin-solid-svg/types",
14-
"@testing-library/jest-dom",
1514
"vite-plugin-pwa/client"
1615
],
1716
"noEmit": true,

0 commit comments

Comments
 (0)