Skip to content

Commit 2673c01

Browse files
authored
feat: community creation modal (#138)
1 parent 38bbd60 commit 2673c01

File tree

26 files changed

+513
-134
lines changed

26 files changed

+513
-134
lines changed

.editorconfig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ end_of_line = lf
77
indent_style = space
88
insert_final_newline = true
99
trim_trailing_whitespace = true
10-
max_line_length = 92
10+
max_line_length = 100
1111

1212
[*.py]
1313
indent_size = 4
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<script lang="ts">
2+
import Toaster from '$lib/components/ui/toast/toaster.svelte';
3+
import { cn } from '$lib/functions/classnames';
4+
import type { Nullable } from '$lib/types/shared';
5+
import type { Snippet } from 'svelte';
6+
7+
type Props = {
8+
children: Snippet<[]>;
9+
open: boolean;
10+
onclose: () => void;
11+
class?: string;
12+
};
13+
14+
let { children, open, onclose, class: klass }: Props = $props();
15+
16+
let dialog_el = $state<Nullable<HTMLDialogElement>>(null);
17+
18+
$effect(() => {
19+
if (open) {
20+
dialog_el?.showModal();
21+
} else {
22+
dialog_el?.close();
23+
}
24+
});
25+
</script>
26+
27+
<dialog bind:this={dialog_el} class="modal modal-bottom px-4 sm:modal-middle" {onclose}>
28+
<div class={cn(klass, 'modal-box duration-300')}>
29+
{@render children()}
30+
</div>
31+
<form method="dialog" class="modal-backdrop">
32+
<button>close</button>
33+
</form>
34+
<!-- render toasts -->
35+
<!-- https://stackoverflow.com/questions/77099074/layering-toast-alerts-above-dialog-modal -->
36+
<!-- https://github.com/saadeghi/daisyui/issues/2858#issuecomment-2010246981 -->
37+
<Toaster />
38+
</dialog>
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import type { FormConfig } from '../types';
2+
3+
export function create_form_history<T extends FormConfig>(initial_form: keyof T) {
4+
let history = $state([initial_form]);
5+
6+
function go_to_form(form: keyof T) {
7+
// if navigating to a form thats already in the history stack,
8+
// truncate the stack upto the most recent occurance of that form
9+
// (avoiding duplicate entiries)
10+
const form_index = history.findIndex((entry) => entry === form);
11+
if (form_index > -1) {
12+
// if exists
13+
history = history.slice(0, form_index + 1);
14+
} else {
15+
// otherwise, push current form to prev_form_history
16+
history.push(form);
17+
}
18+
return history.at(-1);
19+
}
20+
21+
function go_back() {
22+
if (history.length > 1) {
23+
// remove current form from history stack
24+
history.pop();
25+
}
26+
return history.at(-1);
27+
}
28+
29+
function go_next(forms: Record<keyof T, unknown>) {
30+
const current_form_index = Object.keys(forms).indexOf(history.at(-1) as string);
31+
const next_index = current_form_index + 1;
32+
if (next_index < Object.keys(forms).length) {
33+
go_to_form(Object.keys(forms)[next_index] as keyof T);
34+
}
35+
}
36+
37+
return {
38+
get history() {
39+
return history;
40+
},
41+
go_to_form,
42+
go_back,
43+
go_next
44+
};
45+
}

frontend/src/lib/components/modals/auth/index.svelte

Lines changed: 0 additions & 95 deletions
This file was deleted.

frontend/src/lib/components/modals/auth/types.ts

Lines changed: 0 additions & 15 deletions
This file was deleted.
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
<script lang="ts">
2-
import AuthModal from './auth/index.svelte';
2+
import ModalAuth from './modal_auth/index.svelte';
3+
import ModalCreateCommunity from './modal_create_community/index.svelte';
34
</script>
45

5-
<AuthModal />
6+
<ModalAuth />
7+
<ModalCreateCommunity />

frontend/src/lib/components/modals/auth/forms/join.svelte renamed to frontend/src/lib/components/modals/modal_auth/forms/join.svelte

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@
44
import QuibbleLogo from '$lib/components/icons/logos/quibble.svelte';
55
import QuibbleTextLogo from '$lib/components/icons/logos/quibble_text.svelte';
66
import { cn } from '$lib/functions/classnames';
7-
import type { FormProps } from '../types';
7+
import type { FormProps } from '../../types';
8+
import forms from '../forms';
89
import type { SubmitFunction } from '@sveltejs/kit';
910
import type { HTMLInputAttributes } from 'svelte/elements';
1011
11-
let { update_forms_state, goto_form }: FormProps = $props();
12+
let { update_forms_state, goto_form }: FormProps<typeof forms> = $props();
1213
1314
let auth_type = $state<'login' | 'register'>('login');
1415
let password_type = $state<HTMLInputAttributes['type']>('password');

frontend/src/lib/components/modals/auth/forms/profile_create.svelte renamed to frontend/src/lib/components/modals/modal_auth/forms/profile_create.svelte

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@
33
import QuibbleLogo from '$lib/components/icons/logos/quibble.svelte';
44
import QuibbleTextLogo from '$lib/components/icons/logos/quibble_text.svelte';
55
import { cn } from '$lib/functions/classnames';
6-
import type { FormProps } from '../types';
6+
import type { FormProps } from '../../types';
7+
import forms from '../forms';
78
import type { SubmitFunction } from '@sveltejs/kit';
89
9-
let { forms_state, update_forms_state, goto_form }: FormProps = $props();
10+
let { forms_state, update_forms_state, goto_form }: FormProps<typeof forms> = $props();
1011
1112
let errors = $state<Record<string, string> | undefined>();
1213
let pending = $state(false);

frontend/src/lib/components/modals/auth/forms/profile_select.svelte renamed to frontend/src/lib/components/modals/modal_auth/forms/profile_select.svelte

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,16 @@
66
import QuibbleLogo from '$lib/components/icons/logos/quibble.svelte';
77
import QuibbleTextLogo from '$lib/components/icons/logos/quibble_text.svelte';
88
import Avatar from '$lib/components/ui/avatar.svelte';
9+
import { toast } from '$lib/components/ui/toast/toast.svelte';
910
import { createModalsStore } from '$lib/stores/modals.svelte';
10-
import type { FormProps } from '../types';
11+
import type { FormProps } from '../../types';
12+
import forms from '../forms';
1113
import type { SubmitFunction } from '@sveltejs/kit';
1214
import { onMount } from 'svelte';
1315
1416
type Profile = components['schemas']['Profile'];
1517
16-
let { update_forms_state, forms_state, goto_form }: FormProps = $props();
18+
let { update_forms_state, forms_state, goto_form }: FormProps<typeof forms> = $props();
1719
1820
let pending = $state(false);
1921
let status_text = $state<string | null>(null);
@@ -26,10 +28,11 @@
2628
pending = true;
2729
status_text = 'Setting up profile...';
2830
29-
return async () => {
31+
return async ({ formData }) => {
3032
// re-run load functions and close this modal
3133
await invalidateAll();
3234
modalsStore.close('auth');
35+
toast.push({ message: `Logged in as u/${String(formData.get('profile_username'))}` });
3336
3437
pending = false;
3538
status_text = null;
@@ -88,6 +91,7 @@
8891
{#each profiles as profile}
8992
<form method="POST" action="/settings/profile?/select" use:enhance={handle_submit}>
9093
<input type="hidden" name="profile_id" value={profile.id} />
94+
<input type="hidden" name="profile_username" value={profile.username} />
9195
<button type="submit" class="group flex flex-col items-center justify-center gap-2.5">
9296
<Avatar
9397
class="size-20 rounded-2xl outline outline-offset-4 outline-neutral"
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<script lang="ts">
2+
import { createModalsStore } from '$lib/stores/modals.svelte';
3+
import type { Nullable } from '$lib/types/shared';
4+
import BaseModal from '../_components/base_modal.svelte';
5+
import { create_form_history } from '../_utils/history.svelte';
6+
import type { FormsState, FormSubmitData, Forms } from '../types';
7+
import forms from './forms';
8+
9+
type AuthForms = Forms<typeof forms>;
10+
type AuthFormsState = FormsState<typeof forms>;
11+
12+
const modalsStore = createModalsStore();
13+
14+
const form_history = create_form_history<typeof forms>('join');
15+
let form = $derived(forms[form_history.history.at(-1) ?? 'join']);
16+
17+
const initial_forms_state = Object.fromEntries(
18+
Object.keys(forms).map((key) => [key, {}])
19+
) as AuthFormsState;
20+
21+
let forms_state = $state<AuthFormsState>(initial_forms_state);
22+
23+
function update_forms_state(form: AuthForms, data: FormSubmitData) {
24+
forms_state[form] = { ...forms_state[form], ...data };
25+
}
26+
27+
function goto_form(form: AuthForms) {
28+
form_history.go_to_form(form);
29+
}
30+
31+
function handle_go_back() {
32+
form_history.go_back();
33+
}
34+
35+
function handle_modal_close() {
36+
modalsStore.close('auth');
37+
}
38+
</script>
39+
40+
<BaseModal
41+
open={modalsStore.state.get('auth') === true}
42+
onclose={handle_modal_close}
43+
class="max-w-[25rem] md:max-w-[25rem]"
44+
>
45+
{#await form then Form}
46+
<Form.default {forms_state} {update_forms_state} {goto_form} />
47+
{/await}
48+
{#if form_history.history.length > 1}
49+
<div
50+
class="tooltip tooltip-right absolute left-2.5 top-2.5 flex before:capitalize"
51+
data-tip={form_history.history.at(-2)?.replace('_', ' ')}
52+
>
53+
<button
54+
class="btn btn-square btn-circle btn-ghost btn-sm"
55+
aria-label="Close modal"
56+
onclick={handle_go_back}
57+
>
58+
<coreicons-shape-arrow class="size-5" variant="left"></coreicons-shape-arrow>
59+
</button>
60+
</div>
61+
{/if}
62+
<button
63+
class="btn btn-square btn-circle btn-ghost btn-sm absolute right-2.5 top-2.5"
64+
aria-label="Close modal"
65+
onclick={handle_modal_close}
66+
>
67+
<coreicons-shape-x class="size-5" variant="no-border"></coreicons-shape-x>
68+
</button>
69+
</BaseModal>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
const forms = {
2+
introduction: import('./introduction.svelte'),
3+
topics: import('./topics.svelte')
4+
};
5+
6+
export default forms;

0 commit comments

Comments
 (0)