Skip to content

feat: community creation modal #138

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Jan 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ end_of_line = lf
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
max_line_length = 92
max_line_length = 100

[*.py]
indent_size = 4
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<script lang="ts">
import Toaster from '$lib/components/ui/toast/toaster.svelte';
import { cn } from '$lib/functions/classnames';
import type { Nullable } from '$lib/types/shared';
import type { Snippet } from 'svelte';

type Props = {
children: Snippet<[]>;
open: boolean;
onclose: () => void;
class?: string;
};

let { children, open, onclose, class: klass }: Props = $props();

let dialog_el = $state<Nullable<HTMLDialogElement>>(null);

$effect(() => {
if (open) {
dialog_el?.showModal();
} else {
dialog_el?.close();
}
});
</script>

<dialog bind:this={dialog_el} class="modal modal-bottom px-4 sm:modal-middle" {onclose}>
<div class={cn(klass, 'modal-box duration-300')}>
{@render children()}
</div>
<form method="dialog" class="modal-backdrop">
<button>close</button>
</form>
<!-- render toasts -->
<!-- https://stackoverflow.com/questions/77099074/layering-toast-alerts-above-dialog-modal -->
<!-- https://github.com/saadeghi/daisyui/issues/2858#issuecomment-2010246981 -->
<Toaster />
</dialog>
45 changes: 45 additions & 0 deletions frontend/src/lib/components/modals/_utils/history.svelte.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import type { FormConfig } from '../types';

export function create_form_history<T extends FormConfig>(initial_form: keyof T) {
let history = $state([initial_form]);

function go_to_form(form: keyof T) {
// if navigating to a form thats already in the history stack,
// truncate the stack upto the most recent occurance of that form
// (avoiding duplicate entiries)
const form_index = history.findIndex((entry) => entry === form);
if (form_index > -1) {
// if exists
history = history.slice(0, form_index + 1);
} else {
// otherwise, push current form to prev_form_history
history.push(form);
}
return history.at(-1);
}

function go_back() {
if (history.length > 1) {
// remove current form from history stack
history.pop();
}
return history.at(-1);
}

function go_next(forms: Record<keyof T, unknown>) {
const current_form_index = Object.keys(forms).indexOf(history.at(-1) as string);
const next_index = current_form_index + 1;
if (next_index < Object.keys(forms).length) {
go_to_form(Object.keys(forms)[next_index] as keyof T);
}
}

return {
get history() {
return history;
},
go_to_form,
go_back,
go_next
};
}
95 changes: 0 additions & 95 deletions frontend/src/lib/components/modals/auth/index.svelte

This file was deleted.

15 changes: 0 additions & 15 deletions frontend/src/lib/components/modals/auth/types.ts

This file was deleted.

6 changes: 4 additions & 2 deletions frontend/src/lib/components/modals/index.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<script lang="ts">
import AuthModal from './auth/index.svelte';
import ModalAuth from './modal_auth/index.svelte';
import ModalCreateCommunity from './modal_create_community/index.svelte';
</script>

<AuthModal />
<ModalAuth />
<ModalCreateCommunity />
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@
import QuibbleLogo from '$lib/components/icons/logos/quibble.svelte';
import QuibbleTextLogo from '$lib/components/icons/logos/quibble_text.svelte';
import { cn } from '$lib/functions/classnames';
import type { FormProps } from '../types';
import type { FormProps } from '../../types';
import forms from '../forms';
import type { SubmitFunction } from '@sveltejs/kit';
import type { HTMLInputAttributes } from 'svelte/elements';

let { update_forms_state, goto_form }: FormProps = $props();
let { update_forms_state, goto_form }: FormProps<typeof forms> = $props();

let auth_type = $state<'login' | 'register'>('login');
let password_type = $state<HTMLInputAttributes['type']>('password');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
import QuibbleLogo from '$lib/components/icons/logos/quibble.svelte';
import QuibbleTextLogo from '$lib/components/icons/logos/quibble_text.svelte';
import { cn } from '$lib/functions/classnames';
import type { FormProps } from '../types';
import type { FormProps } from '../../types';
import forms from '../forms';
import type { SubmitFunction } from '@sveltejs/kit';

let { forms_state, update_forms_state, goto_form }: FormProps = $props();
let { forms_state, update_forms_state, goto_form }: FormProps<typeof forms> = $props();

let errors = $state<Record<string, string> | undefined>();
let pending = $state(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@
import QuibbleLogo from '$lib/components/icons/logos/quibble.svelte';
import QuibbleTextLogo from '$lib/components/icons/logos/quibble_text.svelte';
import Avatar from '$lib/components/ui/avatar.svelte';
import { toast } from '$lib/components/ui/toast/toast.svelte';
import { createModalsStore } from '$lib/stores/modals.svelte';
import type { FormProps } from '../types';
import type { FormProps } from '../../types';
import forms from '../forms';
import type { SubmitFunction } from '@sveltejs/kit';
import { onMount } from 'svelte';

type Profile = components['schemas']['Profile'];

let { update_forms_state, forms_state, goto_form }: FormProps = $props();
let { update_forms_state, forms_state, goto_form }: FormProps<typeof forms> = $props();

let pending = $state(false);
let status_text = $state<string | null>(null);
Expand All @@ -26,10 +28,11 @@
pending = true;
status_text = 'Setting up profile...';

return async () => {
return async ({ formData }) => {
// re-run load functions and close this modal
await invalidateAll();
modalsStore.close('auth');
toast.push({ message: `Logged in as u/${String(formData.get('profile_username'))}` });

pending = false;
status_text = null;
Expand Down Expand Up @@ -88,6 +91,7 @@
{#each profiles as profile}
<form method="POST" action="/settings/profile?/select" use:enhance={handle_submit}>
<input type="hidden" name="profile_id" value={profile.id} />
<input type="hidden" name="profile_username" value={profile.username} />
<button type="submit" class="group flex flex-col items-center justify-center gap-2.5">
<Avatar
class="size-20 rounded-2xl outline outline-offset-4 outline-neutral"
Expand Down
69 changes: 69 additions & 0 deletions frontend/src/lib/components/modals/modal_auth/index.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<script lang="ts">
import { createModalsStore } from '$lib/stores/modals.svelte';
import type { Nullable } from '$lib/types/shared';

Check failure on line 3 in frontend/src/lib/components/modals/modal_auth/index.svelte

View workflow job for this annotation

GitHub Actions / lint

'Nullable' is defined but never used
import BaseModal from '../_components/base_modal.svelte';
import { create_form_history } from '../_utils/history.svelte';
import type { FormsState, FormSubmitData, Forms } from '../types';
import forms from './forms';

type AuthForms = Forms<typeof forms>;
type AuthFormsState = FormsState<typeof forms>;

const modalsStore = createModalsStore();

const form_history = create_form_history<typeof forms>('join');
let form = $derived(forms[form_history.history.at(-1) ?? 'join']);

const initial_forms_state = Object.fromEntries(
Object.keys(forms).map((key) => [key, {}])
) as AuthFormsState;

let forms_state = $state<AuthFormsState>(initial_forms_state);

function update_forms_state(form: AuthForms, data: FormSubmitData) {
forms_state[form] = { ...forms_state[form], ...data };
}

function goto_form(form: AuthForms) {
form_history.go_to_form(form);
}

function handle_go_back() {
form_history.go_back();
}

function handle_modal_close() {
modalsStore.close('auth');
}
</script>

<BaseModal
open={modalsStore.state.get('auth') === true}
onclose={handle_modal_close}
class="max-w-[25rem] md:max-w-[25rem]"
>
{#await form then Form}
<Form.default {forms_state} {update_forms_state} {goto_form} />
{/await}
{#if form_history.history.length > 1}
<div
class="tooltip tooltip-right absolute left-2.5 top-2.5 flex before:capitalize"
data-tip={form_history.history.at(-2)?.replace('_', ' ')}
>
<button
class="btn btn-square btn-circle btn-ghost btn-sm"
aria-label="Close modal"
onclick={handle_go_back}
>
<coreicons-shape-arrow class="size-5" variant="left"></coreicons-shape-arrow>
</button>
</div>
{/if}
<button
class="btn btn-square btn-circle btn-ghost btn-sm absolute right-2.5 top-2.5"
aria-label="Close modal"
onclick={handle_modal_close}
>
<coreicons-shape-x class="size-5" variant="no-border"></coreicons-shape-x>
</button>
</BaseModal>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const forms = {
introduction: import('./introduction.svelte'),
topics: import('./topics.svelte')
};

export default forms;
Loading
Loading