did some work on the user session and components
This commit is contained in:
parent
33dc08bb82
commit
350c767a13
11 changed files with 172 additions and 60 deletions
|
@ -2,11 +2,16 @@ import { ParentComponent } from "solid-js";
|
||||||
import { Top } from "./top";
|
import { Top } from "./top";
|
||||||
import { Nav } from "./nav";
|
import { Nav } from "./nav";
|
||||||
import css from "./shell.module.css";
|
import css from "./shell.module.css";
|
||||||
|
import { User } from "../user";
|
||||||
|
|
||||||
export const Shell: ParentComponent = (props) => {
|
interface ShellProps {
|
||||||
|
user: User | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Shell: ParentComponent<ShellProps> = (props) => {
|
||||||
return (
|
return (
|
||||||
<main class={css.container}>
|
<main class={css.container}>
|
||||||
<Top />
|
<Top user={props.user} />
|
||||||
<Nav />
|
<Nav />
|
||||||
|
|
||||||
<div class={css.body}>
|
<div class={css.body}>
|
||||||
|
|
|
@ -7,3 +7,31 @@
|
||||||
background-color: inherit;
|
background-color: inherit;
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.accountTrigger {
|
||||||
|
anchor-name: --account-trigger;
|
||||||
|
background: transparent;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
border-radius: var(--radius-round);
|
||||||
|
}
|
||||||
|
|
||||||
|
.accountMenu {
|
||||||
|
position-anchor: --account-trigger;
|
||||||
|
position: absolute;
|
||||||
|
inset: auto;
|
||||||
|
inset-inline-end: anchor(end);
|
||||||
|
inset-block-start: anchor(start);
|
||||||
|
|
||||||
|
display: block grid;
|
||||||
|
grid-auto-flow: row;
|
||||||
|
gap: var(--size-3);
|
||||||
|
padding: var(--size-3);
|
||||||
|
background-color: light-dark(var(--gray-1), var(--gray-9));
|
||||||
|
border-radius: var(--radius-2);
|
||||||
|
box-shadow: var(--shadow-2);
|
||||||
|
|
||||||
|
&:not(:popover-open) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,71 +1,58 @@
|
||||||
import { Component, createEffect, createMemo, Show } from "solid-js";
|
import { Component, createEffect, Show } from "solid-js";
|
||||||
import { ColorSchemePicker } from "../theme";
|
import { signIn, signOut } from "~/auth";
|
||||||
import { signIn, signOut, useSession } from "~/auth";
|
|
||||||
import { hash } from "~/utilities";
|
import { hash } from "~/utilities";
|
||||||
|
import { Avatar, Profile, User } from "../user";
|
||||||
import css from "./top.module.css";
|
import css from "./top.module.css";
|
||||||
import { Avatar } from "../user";
|
|
||||||
|
|
||||||
export const Top: Component = (props) => {
|
interface TopProps {
|
||||||
const session = useSession();
|
user: User | undefined;
|
||||||
const hashedEmail = hash("SHA-256", () => session().data?.user.email);
|
}
|
||||||
|
|
||||||
const login = async () => {
|
export const Top: Component<TopProps> = (props) => {
|
||||||
const response = await signIn.oauth2({
|
const login = async (e: SubmitEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
await signIn.oauth2({
|
||||||
providerId: "authelia",
|
providerId: "authelia",
|
||||||
callbackURL: "/",
|
callbackURL: "/",
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("signin response", response);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const logout = async () => {
|
const logout = async (e: SubmitEvent) => {
|
||||||
const response = await signOut();
|
e.preventDefault();
|
||||||
|
|
||||||
console.log("signout response", response);
|
await signOut();
|
||||||
};
|
};
|
||||||
|
|
||||||
createEffect(() => {
|
|
||||||
console.log(hashedEmail());
|
|
||||||
});
|
|
||||||
|
|
||||||
createEffect(() => {
|
|
||||||
console.log(session().data?.user);
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<aside class={css.top}>
|
<aside class={css.top}>
|
||||||
<Show
|
<Show
|
||||||
when={session().isPending === false && session().isRefetching === false}
|
when={props.user}
|
||||||
|
fallback={
|
||||||
|
<form method="post" onSubmit={login}>
|
||||||
|
<button type="submit">Sign in</button>
|
||||||
|
</form>
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<Show
|
{(user) => (
|
||||||
when={session().data?.user}
|
<>
|
||||||
fallback={
|
<button
|
||||||
<form method="post" onSubmit={login}>
|
class={css.accountTrigger}
|
||||||
<button type="submit">Sign in</button>
|
id="account-menu-trigger"
|
||||||
</form>
|
popovertarget="account-menu-popover"
|
||||||
}
|
>
|
||||||
>
|
<Avatar user={user()} />
|
||||||
{(user) => (
|
</button>
|
||||||
<>
|
<div class={css.accountMenu} id="account-menu-popover" popover>
|
||||||
<div>
|
<Profile user={user()} />
|
||||||
<Avatar />
|
|
||||||
<img
|
|
||||||
src={
|
|
||||||
user().image ??
|
|
||||||
`https://www.gravatar.com/avatar/${hashedEmail()}`
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<span>{user().name}</span>
|
|
||||||
<span>{user().email}</span>
|
|
||||||
</div>
|
|
||||||
<form method="post" onSubmit={logout}>
|
<form method="post" onSubmit={logout}>
|
||||||
<button type="submit">Log out</button>
|
<button type="submit">Log out</button>
|
||||||
</form>
|
</form>
|
||||||
</>
|
</div>
|
||||||
)}
|
</>
|
||||||
</Show>
|
)}
|
||||||
</Show>
|
</Show>
|
||||||
<ColorSchemePicker />
|
{/* <ColorSchemePicker /> */}
|
||||||
</aside>
|
</aside>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
7
src/features/user/avatar.module.css
Normal file
7
src/features/user/avatar.module.css
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
.avatar {
|
||||||
|
inline-size: var(--size-8);
|
||||||
|
border-radius: var(--radius-round);
|
||||||
|
aspect-ratio: 1;
|
||||||
|
object-fit: cover;
|
||||||
|
object-position: center;
|
||||||
|
}
|
|
@ -1,11 +1,27 @@
|
||||||
import { Component, createMemo, Show } from "solid-js";
|
import { Component, createMemo, Show } from "solid-js";
|
||||||
|
import { User } from "./user";
|
||||||
|
import { hash } from "~/utilities";
|
||||||
|
import css from "./avatar.module.css";
|
||||||
|
|
||||||
export const Avatar: Component = (props) => {
|
interface AvatarProps {
|
||||||
const src = createMemo(() => "");
|
user: User | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
export const Avatar: Component<AvatarProps> = (props) => {
|
||||||
<Show when={src()}>
|
const hashedEmail = hash("SHA-256", () => props.user?.email);
|
||||||
<img src={src()} />
|
const src = createMemo(() => {
|
||||||
</Show>
|
const user = props.user;
|
||||||
);
|
|
||||||
|
if (user === undefined) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user.image === null) {
|
||||||
|
return `https://www.gravatar.com/avatar/${hashedEmail()}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return user.image;
|
||||||
|
});
|
||||||
|
|
||||||
|
return <img src={src()} class={css.avatar} />;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1 +1,4 @@
|
||||||
|
export type { User } from "./user";
|
||||||
|
|
||||||
export { Avatar } from "./avatar";
|
export { Avatar } from "./avatar";
|
||||||
|
export { Profile } from "./profile";
|
||||||
|
|
22
src/features/user/profile.module.css
Normal file
22
src/features/user/profile.module.css
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
.profile {
|
||||||
|
display: block grid;
|
||||||
|
grid: auto 1fr / auto 1fr;
|
||||||
|
gap: var(--size-2);
|
||||||
|
place-content: start;
|
||||||
|
background-color: light-dark(var(--gray-1), var(--gray-9));
|
||||||
|
|
||||||
|
& > img {
|
||||||
|
grid-area: span 2 / 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > strong {
|
||||||
|
font-size: var(--size-4);
|
||||||
|
line-height: 1;
|
||||||
|
color: light-dark(var(--gray-7), var(--gray-3));
|
||||||
|
}
|
||||||
|
|
||||||
|
& > span {
|
||||||
|
line-height: 1;
|
||||||
|
color: light-dark(var(--gray-4), var(--gray-6));
|
||||||
|
}
|
||||||
|
}
|
18
src/features/user/profile.tsx
Normal file
18
src/features/user/profile.tsx
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import { Component } from "solid-js";
|
||||||
|
import { User } from "./user";
|
||||||
|
import { Avatar } from "./avatar";
|
||||||
|
import css from "./profile.module.css";
|
||||||
|
|
||||||
|
interface ProfileProps {
|
||||||
|
user: User | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Profile: Component<ProfileProps> = (props) => {
|
||||||
|
return (
|
||||||
|
<div class={css.profile}>
|
||||||
|
<Avatar user={props.user} />
|
||||||
|
<strong>{props.user?.name ?? ""}</strong>
|
||||||
|
<span>{props.user?.email ?? ""}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
|
@ -1,5 +1,5 @@
|
||||||
export interface User {
|
export interface User {
|
||||||
name: string;
|
name: string;
|
||||||
email: string;
|
email: string;
|
||||||
image: string;
|
image: string | null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,36 @@
|
||||||
import { Meta } from "@solidjs/meta";
|
import { Meta } from "@solidjs/meta";
|
||||||
import { query, createAsync, action } from "@solidjs/router";
|
import { query, createAsync } from "@solidjs/router";
|
||||||
import { createEffect, on, ParentProps } from "solid-js";
|
import { createEffect, on, ParentProps } from "solid-js";
|
||||||
|
import { getRequestEvent } from "solid-js/web";
|
||||||
|
import { auth } from "~/auth";
|
||||||
import { Shell } from "~/features/shell";
|
import { Shell } from "~/features/shell";
|
||||||
import { useTheme } from "~/features/theme";
|
import { useTheme } from "~/features/theme";
|
||||||
|
import { User } from "~/features/user";
|
||||||
|
|
||||||
|
const load = query(async (): Promise<User | undefined> => {
|
||||||
|
"use server";
|
||||||
|
|
||||||
|
const session = await auth.api.getSession({
|
||||||
|
headers: getRequestEvent()!.request.headers,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (session === null) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { name, email, image = null } = session.user;
|
||||||
|
|
||||||
|
return { name, email, image };
|
||||||
|
}, "session");
|
||||||
|
|
||||||
|
export const route = {
|
||||||
|
async preload() {
|
||||||
|
return load();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export default function ShellPage(props: ParentProps) {
|
export default function ShellPage(props: ParentProps) {
|
||||||
|
const user = createAsync(() => load());
|
||||||
const themeContext = useTheme();
|
const themeContext = useTheme();
|
||||||
|
|
||||||
createEffect(
|
createEffect(
|
||||||
|
@ -17,7 +43,7 @@ export default function ShellPage(props: ParentProps) {
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Shell>
|
<Shell user={user()}>
|
||||||
<Meta name="color-scheme" content={themeContext.theme.colorScheme} />
|
<Meta name="color-scheme" content={themeContext.theme.colorScheme} />
|
||||||
|
|
||||||
{props.children}
|
{props.children}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue