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 { Nav } from "./nav";
|
||||
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 (
|
||||
<main class={css.container}>
|
||||
<Top />
|
||||
<Top user={props.user} />
|
||||
<Nav />
|
||||
|
||||
<div class={css.body}>
|
||||
|
|
|
@ -7,3 +7,31 @@
|
|||
background-color: inherit;
|
||||
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,44 +1,33 @@
|
|||
import { Component, createEffect, createMemo, Show } from "solid-js";
|
||||
import { ColorSchemePicker } from "../theme";
|
||||
import { signIn, signOut, useSession } from "~/auth";
|
||||
import { Component, createEffect, Show } from "solid-js";
|
||||
import { signIn, signOut } from "~/auth";
|
||||
import { hash } from "~/utilities";
|
||||
import { Avatar, Profile, User } from "../user";
|
||||
import css from "./top.module.css";
|
||||
import { Avatar } from "../user";
|
||||
|
||||
export const Top: Component = (props) => {
|
||||
const session = useSession();
|
||||
const hashedEmail = hash("SHA-256", () => session().data?.user.email);
|
||||
interface TopProps {
|
||||
user: User | undefined;
|
||||
}
|
||||
|
||||
const login = async () => {
|
||||
const response = await signIn.oauth2({
|
||||
export const Top: Component<TopProps> = (props) => {
|
||||
const login = async (e: SubmitEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
await signIn.oauth2({
|
||||
providerId: "authelia",
|
||||
callbackURL: "/",
|
||||
});
|
||||
|
||||
console.log("signin response", response);
|
||||
};
|
||||
|
||||
const logout = async () => {
|
||||
const response = await signOut();
|
||||
const logout = async (e: SubmitEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
console.log("signout response", response);
|
||||
await signOut();
|
||||
};
|
||||
|
||||
createEffect(() => {
|
||||
console.log(hashedEmail());
|
||||
});
|
||||
|
||||
createEffect(() => {
|
||||
console.log(session().data?.user);
|
||||
});
|
||||
|
||||
return (
|
||||
<aside class={css.top}>
|
||||
<Show
|
||||
when={session().isPending === false && session().isRefetching === false}
|
||||
>
|
||||
<Show
|
||||
when={session().data?.user}
|
||||
when={props.user}
|
||||
fallback={
|
||||
<form method="post" onSubmit={login}>
|
||||
<button type="submit">Sign in</button>
|
||||
|
@ -47,25 +36,23 @@ export const Top: Component = (props) => {
|
|||
>
|
||||
{(user) => (
|
||||
<>
|
||||
<div>
|
||||
<Avatar />
|
||||
<img
|
||||
src={
|
||||
user().image ??
|
||||
`https://www.gravatar.com/avatar/${hashedEmail()}`
|
||||
}
|
||||
/>
|
||||
<span>{user().name}</span>
|
||||
<span>{user().email}</span>
|
||||
</div>
|
||||
<button
|
||||
class={css.accountTrigger}
|
||||
id="account-menu-trigger"
|
||||
popovertarget="account-menu-popover"
|
||||
>
|
||||
<Avatar user={user()} />
|
||||
</button>
|
||||
<div class={css.accountMenu} id="account-menu-popover" popover>
|
||||
<Profile user={user()} />
|
||||
<form method="post" onSubmit={logout}>
|
||||
<button type="submit">Log out</button>
|
||||
</form>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</Show>
|
||||
</Show>
|
||||
<ColorSchemePicker />
|
||||
{/* <ColorSchemePicker /> */}
|
||||
</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 { User } from "./user";
|
||||
import { hash } from "~/utilities";
|
||||
import css from "./avatar.module.css";
|
||||
|
||||
export const Avatar: Component = (props) => {
|
||||
const src = createMemo(() => "");
|
||||
interface AvatarProps {
|
||||
user: User | undefined;
|
||||
}
|
||||
|
||||
return (
|
||||
<Show when={src()}>
|
||||
<img src={src()} />
|
||||
</Show>
|
||||
);
|
||||
export const Avatar: Component<AvatarProps> = (props) => {
|
||||
const hashedEmail = hash("SHA-256", () => props.user?.email);
|
||||
const src = createMemo(() => {
|
||||
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 { 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 {
|
||||
name: string;
|
||||
email: string;
|
||||
image: string;
|
||||
image: string | null;
|
||||
}
|
||||
|
|
|
@ -1,10 +1,36 @@
|
|||
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 { getRequestEvent } from "solid-js/web";
|
||||
import { auth } from "~/auth";
|
||||
import { Shell } from "~/features/shell";
|
||||
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) {
|
||||
const user = createAsync(() => load());
|
||||
const themeContext = useTheme();
|
||||
|
||||
createEffect(
|
||||
|
@ -17,7 +43,7 @@ export default function ShellPage(props: ParentProps) {
|
|||
);
|
||||
|
||||
return (
|
||||
<Shell>
|
||||
<Shell user={user()}>
|
||||
<Meta name="color-scheme" content={themeContext.theme.colorScheme} />
|
||||
|
||||
{props.children}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue