#41 add toasts and use for success and error #43
27
view/src/lib/toast.ts
Normal file
27
view/src/lib/toast.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import { writable } from "svelte/store";
|
||||||
|
|
||||||
|
|
||||||
|
type Type = 'success' | 'info' | 'warning' | 'error';
|
||||||
|
type Toast = {
|
||||||
|
message: string;
|
||||||
|
type: Type;
|
||||||
|
}
|
||||||
|
|
||||||
|
const toastStore = writable<Toast[]>([]);
|
||||||
|
|
||||||
|
|
||||||
|
const addToast = (message: string, type: Type) => {
|
||||||
|
const newToast = { message, type };
|
||||||
|
|
||||||
|
toastStore.update((toasts) => {
|
||||||
|
return [...toasts, newToast];
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
toastStore.update((toasts) => toasts.filter((t) => t !== newToast));
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export { toastStore, addToast, };
|
||||||
|
export type { Toast }
|
||||||
@@ -2,29 +2,38 @@
|
|||||||
import '../app.css';
|
import '../app.css';
|
||||||
import { onDestroy, onMount } from 'svelte';
|
import { onDestroy, onMount } from 'svelte';
|
||||||
import type { Auth, User } from 'firebase/auth';
|
import type { Auth, User } from 'firebase/auth';
|
||||||
|
import { addToast, toastStore } from '$lib/toast';
|
||||||
|
import type { Toast } from '$lib/toast';
|
||||||
|
|
||||||
var auth: Auth | null = null;
|
var auth: Auth | null = null;
|
||||||
var user: User | null = null;
|
var user: User | null = null;
|
||||||
var unsub: (() => void) | null = null;
|
var unsubAuth: (() => void) | null = null;
|
||||||
|
|
||||||
|
let toasts: Toast[] = [];
|
||||||
|
let unsubToast = toastStore.subscribe((value) => {
|
||||||
|
toasts = value;
|
||||||
|
});
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
const { auth: _auth } = await import('../lib/firebase');
|
const { auth: _auth } = await import('../lib/firebase');
|
||||||
auth = _auth;
|
auth = _auth;
|
||||||
user = auth.currentUser;
|
user = auth.currentUser;
|
||||||
unsub = auth.onAuthStateChanged((newUser) => {
|
unsubAuth = auth.onAuthStateChanged((newUser) => {
|
||||||
user = newUser;
|
user = newUser;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
if (unsub) {
|
unsubToast();
|
||||||
unsub();
|
if (unsubAuth) {
|
||||||
|
unsubAuth();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const signOut = async () => {
|
const signOut = async () => {
|
||||||
try {
|
try {
|
||||||
await (auth as Auth).signOut();
|
await (auth as Auth).signOut();
|
||||||
|
addToast('Signed out successfully', 'success');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
@@ -46,4 +55,21 @@
|
|||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- This is needed so all class names are inside the bundle -->
|
||||||
|
<!-- The class names are used dynamically, so tailwind can't know -->
|
||||||
|
<div class="hidden">
|
||||||
|
<div class="alert-info text-info-content"></div>
|
||||||
|
<div class="alert-success text-success-content"></div>
|
||||||
|
<div class="alert-warning text-warning-content"></div>
|
||||||
|
<div class="alert-error text-error-content"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="toast toast-end">
|
||||||
|
{#each toasts as toast}
|
||||||
|
<div class="alert alert-{toast.type}">
|
||||||
|
<span class="text-{toast.type}-content">{toast.message}</span>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,8 +3,7 @@
|
|||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import type { Auth } from 'firebase/auth';
|
import type { Auth } from 'firebase/auth';
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
|
import { addToast } from '$lib/toast';
|
||||||
$: errorStr = '';
|
|
||||||
|
|
||||||
var auth: Auth | null = null;
|
var auth: Auth | null = null;
|
||||||
|
|
||||||
@@ -12,8 +11,6 @@
|
|||||||
$: workouts = [];
|
$: workouts = [];
|
||||||
|
|
||||||
async function handleSubmit(_submit: SubmitEvent) {
|
async function handleSubmit(_submit: SubmitEvent) {
|
||||||
errorStr = '';
|
|
||||||
|
|
||||||
const form = _submit.target as HTMLFormElement;
|
const form = _submit.target as HTMLFormElement;
|
||||||
const formData = new FormData(form);
|
const formData = new FormData(form);
|
||||||
|
|
||||||
@@ -30,12 +27,10 @@
|
|||||||
fetchWorkouts();
|
fetchWorkouts();
|
||||||
resetForm();
|
resetForm();
|
||||||
} else {
|
} else {
|
||||||
const data = await response.text();
|
addToast('Failed to save workout: ' + (await response.text()), 'error');
|
||||||
errorStr = data;
|
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
errorStr = error.message;
|
addToast('Failed to save workout: ' + error.message, 'error');
|
||||||
console.error(error);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,10 +57,10 @@
|
|||||||
console.log(data);
|
console.log(data);
|
||||||
workouts = data;
|
workouts = data;
|
||||||
} else {
|
} else {
|
||||||
errorStr = await response.text();
|
addToast('Failed to fetch workouts: ' + (await response.text()), 'error');
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.log(error);
|
addToast('Failed to fetch workouts: ' + error.message, 'error');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,9 +96,6 @@
|
|||||||
<input type="number" class="input input-bordered" placeholder="Reps" name="reps" />
|
<input type="number" class="input input-bordered" placeholder="Reps" name="reps" />
|
||||||
|
|
||||||
<button class="btn btn-primary self-end">Save</button>
|
<button class="btn btn-primary self-end">Save</button>
|
||||||
{#if errorStr}
|
|
||||||
<p class="text-error">{errorStr}</p>
|
|
||||||
{/if}
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div class="overflow-x-auto mx-auto max-w-screen-lg">
|
<div class="overflow-x-auto mx-auto max-w-screen-lg">
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
|
import { addToast } from '$lib/toast';
|
||||||
import { signInWithEmailAndPassword } from 'firebase/auth';
|
import { signInWithEmailAndPassword } from 'firebase/auth';
|
||||||
|
|
||||||
var errorStr: string | null = null;
|
|
||||||
|
|
||||||
const signIn = async (event: SubmitEvent) => {
|
const signIn = async (event: SubmitEvent) => {
|
||||||
errorStr = null;
|
|
||||||
const { auth } = await import('$lib/firebase');
|
const { auth } = await import('$lib/firebase');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -16,9 +14,11 @@
|
|||||||
data.get('password') as string
|
data.get('password') as string
|
||||||
);
|
);
|
||||||
|
|
||||||
|
addToast('Signed in successfully', 'success');
|
||||||
goto('/');
|
goto('/');
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
errorStr = error.code ? error.code : error.message;
|
const errorStr = error.code ? error.code : error.message;
|
||||||
|
addToast('Failed to sign in: ' + errorStr, 'error');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
@@ -65,8 +65,4 @@
|
|||||||
<a href="/signup" class="link gray-500">Sign Up</a>
|
<a href="/signup" class="link gray-500">Sign Up</a>
|
||||||
<button class="btn btn-primary self-end">Sign In</button>
|
<button class="btn btn-primary self-end">Sign In</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if errorStr}
|
|
||||||
<p class="text-red-500">{errorStr}</p>
|
|
||||||
{/if}
|
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
|
import { addToast } from '$lib/toast';
|
||||||
import { createUserWithEmailAndPassword } from 'firebase/auth';
|
import { createUserWithEmailAndPassword } from 'firebase/auth';
|
||||||
|
|
||||||
var errorStr: string | null = null;
|
|
||||||
|
|
||||||
const signUp = async (event: SubmitEvent) => {
|
const signUp = async (event: SubmitEvent) => {
|
||||||
errorStr = null;
|
|
||||||
const { auth } = await import('$lib/firebase');
|
const { auth } = await import('$lib/firebase');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -16,9 +14,11 @@
|
|||||||
data.get('password') as string
|
data.get('password') as string
|
||||||
);
|
);
|
||||||
|
|
||||||
|
addToast('Signed up successfully', 'success');
|
||||||
goto('/');
|
goto('/');
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
errorStr = error.code ? error.code : error.message;
|
const errorStr = error.code ? error.code : error.message;
|
||||||
|
addToast('Failed to sign up: ' + errorStr, 'error');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
@@ -65,8 +65,4 @@
|
|||||||
<a href="/signin" class="link gray-500">Sign In</a>
|
<a href="/signin" class="link gray-500">Sign In</a>
|
||||||
<button class="btn btn-primary self-end">Sign Up</button>
|
<button class="btn btn-primary self-end">Sign Up</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if errorStr}
|
|
||||||
<p class="text-red-500">{errorStr}</p>
|
|
||||||
{/if}
|
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
Reference in New Issue
Block a user