Skip to content
Open
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: 2 additions & 0 deletions .prettierrc.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
tabWidth: 4
semi: true
46 changes: 34 additions & 12 deletions components/dock/dropdown/account.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,35 +12,57 @@ import { useAppStore } from "@/stores/app-store";
import { useUserPreferences } from "@/stores/user-preferences";
import { driver } from "driver.js";
import { Workflow, User, LifeBuoyIcon } from "lucide-react";
import Image from "next/image";
import { useHasHydrated } from "@/hooks/user-has-hydrated";

type Props = {};

function AccountDropdown({}: Props) {
const hasHydrated = useHasHydrated();
const setAccountConnectionsModal = useAppStore(
(state) => state.setAccountConnectionsModal
(state) => state.setAccountConnectionsModal,
);

const setHasTakenTour = useUserPreferences(
(state) => state.setHasTakenTour
(state) => state.setHasTakenTour,
);

const userData = useUserPreferences((state) => state.userData);
const fullName =
userData !== undefined
? `${userData.firstName} ${userData.lastName}`
: null;

const takeATour = () => {
const config = driverObj(setHasTakenTour);
driver(config).drive();
};

return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
id="dock-account"
variant="outline"
className="flex items-center gap-2"
>
<User size={24} />
<p>Bartek Paczesny</p>
</Button>
</DropdownMenuTrigger>
{hasHydrated && userData ? (
<DropdownMenuTrigger asChild>
<Button
id="dock-account"
variant="outline"
className="flex items-center gap-2"
>
{userData.imageUrl?.trim() !== "" &&
userData.imageUrl !== undefined ? (
<Image
src={userData.imageUrl}
alt={fullName!}
height={24}
width={24}
className="rounded"
/>
) : (
<User size={24} />
)}
<p>{fullName}</p>
</Button>
</DropdownMenuTrigger>
) : null}
<DropdownMenuContent>
<DropdownMenuLabel>My Account</DropdownMenuLabel>
<DropdownMenuSeparator />
Expand Down
77 changes: 77 additions & 0 deletions components/modals/OnboardingModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
"use client";

import { Dialog, DialogContent } from "@/components/ui/dialog";
import { Label } from "@/components/ui/label";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { FormEventHandler, useCallback, useRef } from "react";
import { useUserPreferences } from "@/stores/user-preferences";
import { IUserData } from "@/types";

export function OnboardingModal() {
const firstNameRef = useRef<HTMLInputElement>(null);
const lastNameRef = useRef<HTMLInputElement>(null);
const imageUrlRef = useRef<HTMLInputElement>(null);

const userData = useUserPreferences((state) => state.userData);
const setUserPreferencesAction = useUserPreferences(
(state) => state.setUserData,
);

const setUserPreferences: FormEventHandler<HTMLFormElement> = useCallback(
(e) => {
e.preventDefault();
const data: IUserData = {
firstName: firstNameRef.current!.value,
lastName: lastNameRef.current!.value,
imageUrl: imageUrlRef.current!.value,
};
setUserPreferencesAction(data);
},
[setUserPreferencesAction],
);

return (
<Dialog open={userData === undefined}>
<DialogContent>
<form onSubmit={setUserPreferences} className="mx-2">
<div className="flex justify-between mb-2">
<Label htmlFor="firstName" className="block">
First name
<Input
ref={firstNameRef}
id="firstName"
defaultValue={userData?.firstName}
type="text"
name="firstName"
/>
</Label>
<Label htmlFor="lastName" className="block">
Last name
<Input
ref={lastNameRef}
id="lastName"
defaultValue={userData?.lastName}
type="text"
name="lastName"
/>
</Label>
</div>
<Label htmlFor="imageUrl" className="block">
Image url
<Input
ref={imageUrlRef}
type="text"
defaultValue={userData?.imageUrl}
id="imageUrl"
name="imageUrl"
/>
</Label>
<Button type="submit" className="mt-2">
Create user
</Button>
</form>
</DialogContent>
</Dialog>
);
}
16 changes: 16 additions & 0 deletions hooks/user-has-hydrated.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { useEffect, useState } from "react";

/**
* @desc Small hack around the persisted store causing hydration issues
* @see https://github.com/pmndrs/zustand/issues/324
* @see https://github.com/pmndrs/zustand/issues/938
*/
export function useHasHydrated() {
const [hasHydrated, setHasHydrated] = useState(false);

useEffect(() => {
setHasHydrated(true);
}, []);

return hasHydrated;
}
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,11 @@
"eslint": "^8",
"eslint-config-next": "14.0.4",
"postcss": "^8",
"prettier": "^3.2.4",
"tailwindcss": "^3.3.0",
"typescript": "^5"
},
"volta": {
"node": "20.11.0"
}
}
84 changes: 47 additions & 37 deletions pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,56 +19,66 @@ import { useAppStore } from "@/stores/app-store";
import { useUserPreferences } from "@/stores/user-preferences";
import { BackgroundBlur } from "@/types.d";
import clsx from "clsx";
import { OnboardingModal } from "@/components/modals/OnboardingModal";
import { useHasHydrated } from "@/hooks/user-has-hydrated";

export default function Page() {
const hasHydrated = useHasHydrated();
const currentTab = useStore(useAppStore, (state) => state.currentTab);
const wallpaper = useStore(useUserPreferences, (state) => state.wallpaper);
const backgroundBlur = useStore(
useUserPreferences,
(state) => state.backgroundBlur
(state) => state.backgroundBlur,
);
const searchEnabled = useStore(
useUserPreferences,
(state) => state.searchEnabled
(state) => state.searchEnabled,
);

const userData = useUserPreferences((state) => state.userData);

return (
<>
<AccountConnectionsModal />
<DisplaySettingsModal />
<NewsSettingsModal />
<ArticleReadModal />
<EditShortcutModal />
<ChangeWallpaperModal />
<NewShortcutModal />
<div
className={clsx(
"absolute inset-0 z-0 w-full h-full opacity-25 dark:opacity-10 bg-center bg-no-repeat bg-cover",
backgroundBlur == BackgroundBlur.none
? ""
: backgroundBlur == BackgroundBlur.low
? "blur-[2px]"
: backgroundBlur == BackgroundBlur.medium
? "blur-sm"
: backgroundBlur == BackgroundBlur.high
? "blur-md"
: ""
)}
style={
wallpaper
? {
backgroundImage: `url(${wallpaper})`,
}
: {}
}
/>
<section className="relative z-10 flex flex-col items-center justify-end w-screen h-screen gap-6 p-10 bg-center bg-cover">
<Dock />
{currentTab == "home" && <Home />}
{currentTab == "atlassian" && <Atlassian />}
{currentTab == "news" && <Articles />}
{searchEnabled && <Search />}
</section>
{hasHydrated && userData === undefined && <OnboardingModal />}
{hasHydrated && userData !== undefined && (
<>
<AccountConnectionsModal />
<DisplaySettingsModal />
<NewsSettingsModal />
<ArticleReadModal />
<EditShortcutModal />
<ChangeWallpaperModal />
<NewShortcutModal />
<div
className={clsx(
"absolute inset-0 z-0 w-full h-full opacity-25 dark:opacity-10 bg-center bg-no-repeat bg-cover",
backgroundBlur == BackgroundBlur.none
? ""
: backgroundBlur == BackgroundBlur.low
? "blur-[2px]"
: backgroundBlur == BackgroundBlur.medium
? "blur-sm"
: backgroundBlur == BackgroundBlur.high
? "blur-md"
: "",
)}
style={
wallpaper
? {
backgroundImage: `url(${wallpaper})`,
}
: {}
}
/>
<section className="relative z-10 flex flex-col items-center justify-end w-screen h-screen gap-6 p-10 bg-center bg-cover">
<Dock />
{currentTab == "home" && <Home />}
{currentTab == "atlassian" && <Atlassian />}
{currentTab == "news" && <Articles />}
{searchEnabled && <Search />}
</section>
</>
)}
{/* <Toaster /> */}
</>
);
Expand Down
9 changes: 9 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading