This repository has been archived by the owner on Dec 17, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
146 additions
and
121 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
{ | ||
"imports": { | ||
"webgen/": "https://raw.githubusercontent.com/lucsoft/WebGen/de5703e/", | ||
"webgen/": "https://raw.githubusercontent.com/lucsoft/WebGen/ddee494/", | ||
// "webgen/": "../WebGen/", | ||
"std/": "https://deno.land/[email protected]/", | ||
"shared/": "./pages/shared/" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,69 +1,83 @@ | ||
import { ZodError } from "https://deno.land/x/[email protected]/mod.ts"; | ||
import { API, StreamingUploadHandler, stupidErrorAlert } from "shared/mod.ts"; | ||
import { delay } from "std/async/mod.ts"; | ||
import { AdvancedImage, Box, Grid, IconButton, Image, MIcon, TextInput, Vertical, createFilePicker } from "webgen/mod.ts"; | ||
import { AdvancedImage, Box, Button, CenterV, Empty, Grid, Horizontal, IconButton, Image, Label, MIcon, Spacer, TextInput, Validate, Vertical, asState, createFilePicker, getErrorMessage } from "webgen/mod.ts"; | ||
import { zod } from "webgen/zod.ts"; | ||
import { activeUser, allowedImageFormats, forceRefreshToken } from "../_legacy/helper.ts"; | ||
|
||
export function ChangePersonal() { | ||
return Wizard({ | ||
submitAction: async ([ { data: { data } } ]) => { | ||
await API.user.setMe.post(data) | ||
.then(stupidErrorAlert); | ||
await forceRefreshToken(); | ||
}, | ||
buttonArrangement: "flex-end", | ||
buttonAlignment: "top", | ||
}, () => [ | ||
Page({ | ||
email: activeUser.email, | ||
name: activeUser.username, | ||
loading: false, | ||
profilePicture: activeUser.avatar ?? { type: "loading" } as string | AdvancedImage | undefined | ||
}, (data) => [ | ||
Vertical( | ||
Grid( | ||
data.$profilePicture.map(() => Box(Image(data.profilePicture ?? { type: "loading" }, "Your Avatarimage"), IconButton(MIcon("edit"), "edit-icon")).addClass("upload-image").onClick(async () => { | ||
const file = await createFilePicker(allowedImageFormats.join(",")); | ||
const blobUrl = URL.createObjectURL(file); | ||
data.profilePicture = <AdvancedImage>{ type: "uploading", filename: file.name, blobUrl, percentage: 0 }; | ||
data.loading = true; | ||
setTimeout(() => { | ||
StreamingUploadHandler(`user/set-me/avatar/upload`, { | ||
failure: () => { | ||
data.loading = false; | ||
data.profilePicture = activeUser.avatar; | ||
alert("Your Upload has failed. Please try a different file or try again later"); | ||
}, | ||
uploadDone: () => { | ||
data.profilePicture = <AdvancedImage>{ type: "waiting-upload", filename: file.name, blobUrl }; | ||
}, | ||
backendResponse: () => { | ||
data.loading = false; | ||
data.profilePicture = <AdvancedImage>{ type: "direct", source: async () => await file }; | ||
}, | ||
credentials: () => API.getToken(), | ||
onUploadTick: async (percentage) => { | ||
data.profilePicture = <AdvancedImage>{ type: "uploading", filename: file.name, blobUrl, percentage }; | ||
await delay(2); | ||
} | ||
}, file); | ||
}); | ||
const state = asState({ | ||
email: activeUser.email, | ||
name: activeUser.username, | ||
loading: false, | ||
profilePicture: activeUser.avatar ?? { type: "loading" } as string | AdvancedImage | undefined, | ||
validationState: <ZodError | undefined>undefined, | ||
}); | ||
|
||
})).asRefComponent(), | ||
[ | ||
{ width: 2 }, | ||
Vertical( | ||
TextInput("text", "Name").sync(data, "name"), | ||
TextInput("email", "Email").sync(data, "email") | ||
).setGap("20px") | ||
] | ||
) | ||
.setDynamicColumns(1, "12rem") | ||
.addClass("settings-form") | ||
.setGap("15px") | ||
).setGap("20px").addClass("limited-width"), | ||
]).setValidator((v) => v.object({ | ||
name: v.string().min(2), | ||
email: v.string().email() | ||
}).strip()) | ||
]); | ||
} | ||
return Vertical( | ||
Grid( | ||
state.$profilePicture.map(profilePicture => Box(Image(profilePicture ?? { type: "loading" }, "Your Avatarimage"), IconButton(MIcon("edit"), "edit-icon")).addClass("upload-image").onClick(async () => { | ||
const file = await createFilePicker(allowedImageFormats.join(",")); | ||
const blobUrl = URL.createObjectURL(file); | ||
profilePicture = <AdvancedImage>{ type: "uploading", filename: file.name, blobUrl, percentage: 0 }; | ||
state.loading = true; | ||
setTimeout(() => { | ||
StreamingUploadHandler(`user/set-me/avatar/upload`, { | ||
failure: () => { | ||
state.loading = false; | ||
state.profilePicture = activeUser.avatar; | ||
alert("Your Upload has failed. Please try a different file or try again later"); | ||
}, | ||
uploadDone: () => { | ||
state.profilePicture = <AdvancedImage>{ type: "waiting-upload", filename: file.name, blobUrl }; | ||
}, | ||
backendResponse: () => { | ||
state.loading = false; | ||
state.profilePicture = <AdvancedImage>{ type: "direct", source: async () => await file }; | ||
}, | ||
credentials: () => API.getToken(), | ||
onUploadTick: async (percentage) => { | ||
state.profilePicture = <AdvancedImage>{ type: "uploading", filename: file.name, blobUrl, percentage }; | ||
await delay(2); | ||
} | ||
}, file); | ||
}); | ||
|
||
})).asRefComponent(), | ||
[ | ||
{ width: 2 }, | ||
Vertical( | ||
TextInput("text", "Name").sync(state, "name"), | ||
TextInput("email", "Email").sync(state, "email") | ||
).setGap("20px") | ||
] | ||
) | ||
.setDynamicColumns(1, "12rem") | ||
.addClass("settings-form") | ||
.setGap("15px"), | ||
Horizontal( | ||
Spacer(), | ||
Box(state.$validationState.map(error => error ? CenterV( | ||
Label(getErrorMessage(error)) | ||
.addClass("error-message") | ||
.setMargin("0 0.5rem 0 0") | ||
) | ||
: Empty()).asRefComponent()), | ||
Button("Save").onClick(async () => { | ||
const { error, validate } = Validate( | ||
state, | ||
zod.object({ | ||
name: zod.string().min(2), | ||
email: zod.string().email() | ||
}) | ||
); | ||
|
||
const data = validate(); | ||
if (error.getValue()) return state.validationState = error.getValue(); | ||
if (data) await API.user.setMe.post(state) | ||
.then(stupidErrorAlert); | ||
await forceRefreshToken(); | ||
state.validationState = undefined; | ||
})), | ||
).setGap("20px").addClass("limited-width"); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,23 @@ | ||
import { ZodError } from "https://deno.land/x/[email protected]/ZodError.ts"; | ||
import zod from "https://deno.land/x/[email protected]/index.ts"; | ||
import { API, Navigation } from "shared/mod.ts"; | ||
import { Body, Grid, TextInput, Vertical, WebGen, isMobile } from "webgen/mod.ts"; | ||
import { Body, Box, Button, CenterV, Empty, Grid, Horizontal, Label, Spacer, TextInput, Validate, Vertical, WebGen, asState, getErrorMessage, isMobile } from "webgen/mod.ts"; | ||
import '../../assets/css/main.css'; | ||
import { DynaNavigation } from "../../components/nav.ts"; | ||
import { RegisterAuthRefresh, logOut } from "../_legacy/helper.ts"; | ||
import { ChangePersonal } from "./settings.personal.ts"; | ||
|
||
WebGen({}); | ||
WebGen(); | ||
|
||
await RegisterAuthRefresh(); | ||
|
||
const passwordWizard = zod.object({ | ||
newPassword: zod.string({ invalid_type_error: "New password is missing" }).min(4), | ||
verifyNewPassword: zod.string({ invalid_type_error: "Verify New password is missing" }).min(4) | ||
}) | ||
.refine(val => val.newPassword == val.verifyNewPassword, "Your new password didn't match"); | ||
const state = asState({ | ||
newPassword: <string | undefined>undefined, | ||
verifyNewPassword: <string | undefined>undefined, | ||
validationState: <ZodError | undefined>undefined, | ||
}); | ||
|
||
export const settingsMenu = Navigation({ | ||
const settingsMenu = Navigation({ | ||
title: "Settings", | ||
children: [ | ||
{ | ||
|
@@ -30,32 +32,41 @@ export const settingsMenu = Navigation({ | |
id: "change-password", | ||
title: "Change Password", | ||
children: [ | ||
Wizard({ | ||
submitAction: async ([ { data: { data } } ]) => { | ||
await API.user.setMe.post({ password: data.newPassword }); | ||
logOut(); | ||
}, | ||
buttonArrangement: "flex-end", | ||
buttonAlignment: "top", | ||
}, () => [ | ||
Page({ | ||
newPassword: undefined, | ||
verifyNewPassword: undefined | ||
}, (data) => [ | ||
Vertical( | ||
Grid([ | ||
{ width: 2 }, | ||
Vertical( | ||
Grid([ | ||
{ width: 2 }, | ||
Vertical( | ||
TextInput("password", "New Password").sync(data, "newPassword"), | ||
TextInput("password", "Verify New Password").sync(data, "verifyNewPassword") | ||
).setGap("20px") | ||
]) | ||
.setDynamicColumns(1, "12rem") | ||
.addClass("settings-form") | ||
.setGap("15px") | ||
).setGap("20px"), | ||
]).setValidator(() => passwordWizard) | ||
]) | ||
TextInput("password", "New Password").sync(state, "newPassword"), | ||
TextInput("password", "Verify New Password").sync(state, "verifyNewPassword") | ||
).setGap("20px") | ||
]) | ||
.setDynamicColumns(1, "12rem") | ||
.addClass("settings-form") | ||
.setGap("15px"), | ||
Horizontal( | ||
Spacer(), | ||
Box(state.$validationState.map(error => error ? CenterV( | ||
Label(getErrorMessage(error)) | ||
.addClass("error-message") | ||
.setMargin("0 0.5rem 0 0") | ||
) | ||
: Empty()).asRefComponent()), | ||
Button("Save").onClick(async () => { | ||
const { error, validate } = Validate( | ||
state, | ||
zod.object({ | ||
newPassword: zod.string({ invalid_type_error: "New password is missing" }).min(4), | ||
verifyNewPassword: zod.string({ invalid_type_error: "Verify New password is missing" }).min(4).refine(val => val == state.newPassword, "Your new password didn't match") | ||
}) | ||
); | ||
|
||
const data = validate(); | ||
if (error.getValue()) return state.validationState = error.getValue(); | ||
if (data) await API.user.setMe.post({ password: data.newPassword }); | ||
logOut(); | ||
state.validationState = undefined; | ||
})), | ||
).setGap("20px"), | ||
] | ||
}, | ||
{ | ||
|
Oops, something went wrong.