Skip to content

Commit 9219630

Browse files
authored
feat: add loading states and refactor to TSX (#40)
* add loading states and refactor to TSX * format * copilot * copilot * copilot
1 parent 710f04d commit 9219630

41 files changed

Lines changed: 1201 additions & 553 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

messages/de.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,5 +111,27 @@
111111
"answer": "<p>We only send emails that are necessary to keep Peels working. That includes one or two account-related emails, and emails to notify you whenever a fellow Peels member has sent you message.</p><p>Email notifications are required for listing hosts, as it means a prospective donor has enquired about dropping off scraps (or picking something up). Hosts who longer wish to receive emails can either hide their listing from the map (making it impossible for new donors to reach out) or delete their listing entirely (meaning previous donors can no longer message, either).</p><p>People without listings cannot be messaged (and thus emailed) unless they initiate contact with a host first.</p><p>Anyone can report or block individual Peels members via our messaging system. Blocking someone means they can no longer message or email you.</p>"
112112
}
113113
}
114+
},
115+
"Contact": {
116+
"title": "Kontakt",
117+
"subtitle": "So erreichst du das Peels-Team.",
118+
"via": {
119+
"therot": "Hallo, Leser von The Rot. Danke fürs Vorbeischauen. Hier ist ein direkter Draht zu Danny.",
120+
"general": "Hallo. Hier sind einige E-Mail-Adressen, unter denen du uns erreichen kannst."
121+
},
122+
"contactLabel": "Wenn du",
123+
"contactOptions": {
124+
"general": "Eine allgemeine Anfrage stellen möchtest",
125+
"support": "Hilfe bei etwas brauchst",
126+
"dw": "Mit Danny sprechen möchtest",
127+
"newsletter": "Über den Newsletter sprechen möchtest"
128+
},
129+
"emailLabel": "Am besten schreibst du an",
130+
"copyButton": {
131+
"copied": "Kopiert",
132+
"copying": "Wird kopiert...",
133+
"copyFailed": "Kopieren fehlgeschlagen",
134+
"copyAddress": "Adresse kopieren"
135+
}
114136
}
115137
}

messages/en.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@
129129
"emailLabel": "You’re best off emailing",
130130
"copyButton": {
131131
"copied": "Copied!",
132+
"copying": "Copying...",
133+
"copyFailed": "Copy failed",
132134
"copyAddress": "Copy address"
133135
}
134136
}

messages/es.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,5 +111,27 @@
111111
"answer": "<p>We only send emails that are necessary to keep Peels working. That includes one or two account-related emails, and emails to notify you whenever a fellow Peels member has sent you message.</p><p>Email notifications are required for listing hosts, as it means a prospective donor has enquired about dropping off scraps (or picking something up). Hosts who longer wish to receive emails can either hide their listing from the map (making it impossible for new donors to reach out) or delete their listing entirely (meaning previous donors can no longer message, either).</p><p>People without listings cannot be messaged (and thus emailed) unless they initiate contact with a host first.</p><p>Anyone can report or block individual Peels members via our messaging system. Blocking someone means they can no longer message or email you.</p>"
112112
}
113113
}
114+
},
115+
"Contact": {
116+
"title": "Contacto",
117+
"subtitle": "Así puedes contactar con el equipo de Peels.",
118+
"via": {
119+
"therot": "Hola, lector de The Rot. Gracias por pasar por aquí. Esta es una línea directa con Danny.",
120+
"general": "Hola. Aquí tienes algunas direcciones de correo para contactarnos."
121+
},
122+
"contactLabel": "Si quieres",
123+
"contactOptions": {
124+
"general": "Hacer una consulta general",
125+
"support": "Obtener ayuda con algo",
126+
"dw": "Hablar con Danny",
127+
"newsletter": "Hablar sobre el boletín"
128+
},
129+
"emailLabel": "Lo mejor es escribir a",
130+
"copyButton": {
131+
"copied": "Copiado",
132+
"copying": "Copiando...",
133+
"copyFailed": "No se pudo copiar",
134+
"copyAddress": "Copiar dirección"
135+
}
114136
}
115137
}

scripts/seed-local-media.mjs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,10 @@ function walkFiles(sourceDir, currentDir = sourceDir) {
7979

8080
return {
8181
absolutePath,
82-
objectPath: path.relative(sourceDir, absolutePath).split(path.sep).join("/"),
82+
objectPath: path
83+
.relative(sourceDir, absolutePath)
84+
.split(path.sep)
85+
.join("/"),
8386
};
8487
});
8588
}
@@ -105,7 +108,8 @@ async function ensureBucket(supabase, bucketName, bucketConfig) {
105108
allowedMimeTypes: bucketConfig.allowedMimeTypes,
106109
};
107110

108-
const { data: buckets, error: listError } = await supabase.storage.listBuckets();
111+
const { data: buckets, error: listError } =
112+
await supabase.storage.listBuckets();
109113

110114
if (listError) {
111115
throw listError;
@@ -147,14 +151,12 @@ async function uploadBucketObjects(supabase, bucketName, bucketConfig) {
147151
const body = readFileSync(file.absolutePath);
148152
const contentType = getContentType(file.absolutePath);
149153

150-
const { error } = await supabase.storage.from(bucketName).upload(
151-
file.objectPath,
152-
body,
153-
{
154+
const { error } = await supabase.storage
155+
.from(bucketName)
156+
.upload(file.objectPath, body, {
154157
contentType,
155158
upsert: true,
156-
}
157-
);
159+
});
158160

159161
if (error) {
160162
throw new Error(

src/app/(forms)/auth/complete/page.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@ export default function AuthCompletePage() {
99
</FormHeader>
1010
<Form as="container">
1111
<p>
12-
We’re securely confirming your link. You’ll be redirected in a
13-
moment.
12+
We’re securely confirming your link. You’ll be redirected in a moment.
1413
</p>
1514
</Form>
1615
</>

src/app/(forms)/forgot-password/page.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,10 @@ export default async function ForgotPassword(props: {
5050
{searchParams.error && (
5151
<FormMessage message={{ error: searchParams.error }} />
5252
)}
53-
<SubmitButton formAction={forgotPasswordAction}>
53+
<SubmitButton
54+
formAction={forgotPasswordAction}
55+
pendingText="Emailing..."
56+
>
5457
Email me the link
5558
</SubmitButton>
5659
</Form>

src/app/(forms)/profile/reset-password/page.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,10 @@ export default async function ResetPassword(props: {
6767
{searchParams.error && (
6868
<FormMessage message={{ error: searchParams.error }} />
6969
)}
70-
<SubmitButton formAction={resetPasswordAction}>
70+
<SubmitButton
71+
formAction={resetPasswordAction}
72+
pendingText="Resetting..."
73+
>
7174
Reset password
7275
</SubmitButton>
7376
</Form>

src/components/AvatarUploadView/AvatarUploadView.jsx renamed to src/components/AvatarUploadView/AvatarUploadView.tsx

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ const LoadingSpinner = styled("div")(({ theme }) => ({
3030
display: "flex",
3131
justifyContent: "center",
3232
alignItems: "center",
33-
color: "white",
3433
fontSize: "20px",
3534

3635
position: "absolute",
@@ -42,6 +41,20 @@ const LoadingSpinner = styled("div")(({ theme }) => ({
4241
borderRadius: theme.corners.avatar,
4342
}));
4443

44+
const AvatarComponent = Avatar as React.ComponentType<any>;
45+
46+
type AvatarUploadViewProps = {
47+
avatar?: string;
48+
onChange: (
49+
event: React.ChangeEvent<HTMLInputElement>
50+
) => void | Promise<void>;
51+
onDelete: () => void | Promise<void>;
52+
getAvatarUrl?: (filename: string) => string;
53+
bucket: string;
54+
inputHintShown?: boolean;
55+
listingType?: string;
56+
};
57+
4558
function AvatarUploadView({
4659
avatar,
4760
onChange,
@@ -50,19 +63,36 @@ function AvatarUploadView({
5063
bucket,
5164
inputHintShown = false,
5265
listingType,
53-
}) {
66+
}: AvatarUploadViewProps) {
5467
// Hidden file input that we'll trigger programmatically
55-
const fileInputRef = useRef(null);
68+
const fileInputRef = useRef<HTMLInputElement>(null);
5669
const [loading, setLoading] = useState(false);
70+
const [isDeleting, setIsDeleting] = useState(false);
71+
const isBusy = loading || isDeleting;
5772

5873
const handleFileSelect = () => {
74+
if (isBusy) return;
5975
fileInputRef.current?.click();
6076
};
6177

62-
const handleUpload = async (event) => {
78+
const handleUpload = async (event: React.ChangeEvent<HTMLInputElement>) => {
6379
setLoading(true);
64-
await onChange(event);
65-
setLoading(false);
80+
try {
81+
await onChange(event);
82+
} finally {
83+
setLoading(false);
84+
}
85+
};
86+
87+
const handleDelete = async () => {
88+
if (isBusy) return;
89+
90+
setIsDeleting(true);
91+
try {
92+
await onDelete();
93+
} finally {
94+
setIsDeleting(false);
95+
}
6696
};
6797

6898
return (
@@ -75,11 +105,12 @@ function AvatarUploadView({
75105
accept="image/*"
76106
multiple={false}
77107
onChange={handleUpload}
108+
disabled={isBusy}
78109
style={{ display: "none" }}
79110
/>
80111

81112
<StyledImgContainer>
82-
<Avatar
113+
<AvatarComponent
83114
bucket={bucket}
84115
filename={avatar}
85116
alt="Your avatar"
@@ -96,6 +127,9 @@ function AvatarUploadView({
96127
variant="secondary"
97128
size="small"
98129
onClick={handleFileSelect}
130+
loading={loading}
131+
loadingText="Uploading..."
132+
disabled={isBusy}
99133
>
100134
Add
101135
</AvatarButton>
@@ -106,6 +140,9 @@ function AvatarUploadView({
106140
as={AvatarButton}
107141
variant="secondary"
108142
size="small"
143+
loading={loading || isDeleting}
144+
loadingText={loading ? "Uploading..." : "Deleting..."}
145+
disabled={isBusy}
109146
>
110147
Edit
111148
</DropdownMenu.Button>
@@ -119,12 +156,20 @@ function AvatarUploadView({
119156
onClick={handleFileSelect}
120157
variant="secondary"
121158
size="small"
159+
disabled={isBusy}
122160
>
123161
Replace
124162
</Button>
125163
</DropdownMenu.Item>
126164
<DropdownMenu.Item>
127-
<Button onClick={onDelete} variant="danger" size="small">
165+
<Button
166+
onClick={handleDelete}
167+
variant="danger"
168+
size="small"
169+
loading={isDeleting}
170+
loadingText="Deleting..."
171+
disabled={isBusy}
172+
>
128173
Delete
129174
</Button>
130175
</DropdownMenu.Item>

0 commit comments

Comments
 (0)