wip
Seto Elkahfi committed
Aug 4, 2024 at 17:25 UTC
ea2068f3688ad8e182cf51466ff3f07fb1e0a81c
32 files changed
+392
-357
frontend/splitfire-desktop/app/_ui/components/button.tsx
+2
@@ -1,3 +1,5 @@
1
+"use client";
2
+
3
import * as React from 'react'
4
import { Slot } from '@radix-ui/react-slot'
5
import { cva, type VariantProps } from 'class-variance-authority'
frontend/splitfire-desktop/app/_ui/global-nav.tsx
+1
-1
@@ -7,7 +7,7 @@ import { MenuAlt2Icon, XIcon } from '@heroicons/react/solid';
7
import clsx from 'clsx';
8
import { useContext, useState } from 'react';
9
import Image from 'next/image';
10
-import { UserContext } from '../../lib/CurrentUserContext';
10
+import { UserContext } from '../../lib/current-user-context';
11
import { useLogger } from '@/lib/logger';
12
import { PARAMS_USER_ID } from '@/app/_lib/params';
13
frontend/splitfire-desktop/app/components/index.tsx
+1
-1
@@ -1,6 +1,6 @@
1
"use client";
2
3
-import { TAURI_CONTENT_CAROUSEL } from "@/lib/tauriHandler";
3
+import { TAURI_CONTENT_CAROUSEL } from "@/lib/tauri-handler";
4
import { ContentCarouselResponse } from "@/models/content";
5
import { SongProvider } from "@/models/song-response";
6
import { invoke } from "@tauri-apps/api";
frontend/splitfire-desktop/app/debug/page.tsx
+4
-4
@@ -5,12 +5,12 @@ import { invoke } from "@tauri-apps/api/tauri";
5
import {
6
TAURI_GET_ENVIRONMENT,
7
TAURI_SET_ENVIRONMENT,
8
-} from "../../lib/tauriHandler";
9
-import { Button } from "../_ui/components/button";
8
+} from "@/lib/tauri-handler";
9
+import { Button } from "@/app/_ui/components/button";
10
import { useEffect, useState } from "react";
11
import { TauriResponse } from "@/models/shared";
12
-import { useLogger } from "../../lib/logger";
13
-import { LoadingView } from "../_ui/LoadingView";
12
+import { useLogger } from "@/lib/logger";
13
+import { LoadingView } from "@/app/_ui/LoadingView";
14
15
enum State {
16
LOADING,
frontend/splitfire-desktop/app/layout.tsx
+1
-1
@@ -5,7 +5,7 @@ import { GlobalNav } from "./_ui/global-nav";
5
import "./globals.css";
6
import { useEffect, useState } from "react";
7
import { CurrentUser, CurrentUserType, db } from "../lib/db";
8
-import { UserContext } from "../lib/CurrentUserContext";
8
+import { UserContext } from "../lib/current-user-context";
9
import { useLogger } from "../lib/logger";
10
11
enum State {
frontend/splitfire-desktop/app/login/page.tsx
+9
-7
@@ -2,16 +2,17 @@
2
3
import * as Form from "@radix-ui/react-form";
4
import { invoke } from "@tauri-apps/api/tauri";
5
-import { TAURI_ACCOUNT_LOGIN } from "@/lib/tauriHandler";
5
+import { TAURI_ACCOUNT_LOGIN } from "@/lib/tauri-handler";
6
import { Button } from "@/app/_ui/components/button";
7
import { useState } from "react";
8
-import { UserContext } from "@/lib/CurrentUserContext";
9
-import { CurrentUser, CurrentUserType, db } from "../../lib/db";
8
+import { UserContext } from "@/lib/current-user-context";
9
+import { CurrentUser, CurrentUserType, db } from "@/lib/db";
10
import { AccountLoginResponse } from "@/models/account";
11
import { useRouter } from "next/navigation";
12
import { TauriResponse } from "@/models/shared";
13
import { Mode } from "@/models/mode";
14
-import { LoadingView } from "../_ui/LoadingView";
14
+import { LoadingView } from "@/app/_ui/LoadingView";
15
+import { useLogger } from "@/lib/logger";
16
17
enum State {
18
LOADING,
@@ -20,6 +21,7 @@ enum State {
21
22
export default function Page() {
23
24
+ const log = useLogger("Login/Page");
25
const router = useRouter();
26
const [state, setState] = useState(State.LOADED);
27
const [emailOrUsername, setEmailOrUsername] = useState("");
@@ -31,16 +33,16 @@ export default function Page() {
33
username: emailOrUsername,
34
password: password,
35
};
34
- console.log("payload", payload);
36
+ log.debug("payload", payload);
37
const response: AccountLoginResponse = await invoke(TAURI_ACCOUNT_LOGIN, payload);
36
- console.log("response", response);
38
+ log.debug("response", response);
39
if (response.status === TauriResponse.ERROR) {
40
console.error("Login failed", response);
41
return;
42
}
43
// Sanity check
44
if (response.user === null || response.access_token === null) {
43
- console.error("Login failed", response);
45
+ log.error("Login failed", response);
46
return;
47
}
48
frontend/splitfire-desktop/app/logout/page.tsx
+2
-2
@@ -1,11 +1,11 @@
1
"use client";
2
3
import { useContext, useState } from "react";
4
-import { UserContext } from "../../lib/CurrentUserContext";
4
+import { UserContext } from "../../lib/current-user-context";
5
import { useLogger } from "../../lib/logger";
6
import { CurrentUser, db } from "../../lib/db";
7
import { invoke } from "@tauri-apps/api/tauri";
8
-import { TAURI_ACCOUNT_LOGOUT } from "../../lib/tauriHandler";
8
+import { TAURI_ACCOUNT_LOGOUT } from "../../lib/tauri-handler";
9
import { useRouter } from "next/navigation";
10
import { Button } from "../_ui/components/button";
11
frontend/splitfire-desktop/app/play/_components/audio-info-middle.tsx
+1
-1
@@ -1,4 +1,4 @@
1
-import { useCountDown } from "@/lib/useCountDown";
1
+import { useCountDown } from "@/lib/use-count-down";
2
import { useLogger } from "@/lib/logger";
3
import { useEffect } from "react";
4
frontend/splitfire-desktop/app/play/_components/player.tsx
+1
-1
@@ -9,7 +9,7 @@ import {
9
TAURI_PLAYER_RESUMED,
10
TAURI_PLAYER_SET_VOLUME,
11
TAURI_PLAYER_STOP,
12
-} from "@/lib/tauriHandler";
12
+} from "@/lib/tauri-handler";
13
import { invoke } from "@tauri-apps/api";
14
import { useState } from "react";
15
import { ControlButtons } from "./control-button";
frontend/splitfire-desktop/app/play/page.tsx
+2
-2
@@ -2,14 +2,14 @@
2
3
import { invoke } from "@tauri-apps/api";
4
import { useContext, useEffect, useState } from "react";
5
-import { TAURI_PLAYER_PREPARE } from "../../lib/tauriHandler";
5
+import { TAURI_PLAYER_PREPARE } from "../../lib/tauri-handler";
6
import { useRouter, useSearchParams } from "next/navigation";
7
import { useLogger } from "../../lib/logger";
8
import { IconSpinner } from "../_ui/components/icons";
9
import { PlayerPrepareResponse } from "@/models/content";
10
import { TauriResponse } from "@/models/shared";
11
import Player from "./_components/player";
12
-import { UserContext } from "../../lib/CurrentUserContext";
12
+import { UserContext } from "../../lib/current-user-context";
13
14
enum State {
15
LOADING,
frontend/splitfire-desktop/app/profile/_components/index.tsx
deleted
-3
@@ -1,3 +0,0 @@
1
-
2
-
3
-export default function Index() {}
\ No newline at end of file
frontend/splitfire-desktop/app/profile/components/index.tsx
new
+153
@@ -0,0 +1,153 @@
1
+"use client";
2
+
3
+import { useContext, useEffect, useState } from "react";
4
+import { useLogger } from "@/lib/logger";
5
+import User, { usernameOrId } from "@/models/user";
6
+import { useSearchParams } from "next/navigation";
7
+import Image from "next/image";
8
+import { invoke } from "@tauri-apps/api";
9
+import { TAURI_ACCOUNT_PROFILE } from "@/lib/tauri-handler";
10
+import { AccountProfileResponse } from "@/models/account";
11
+import { TauriResponse } from "@/models/shared";
12
+import { LocationMarkerIcon } from "@heroicons/react/solid";
13
+import { GearIcon } from "@radix-ui/react-icons";
14
+import Link from "next/link";
15
+import { PARAMS_USER_ID } from "@/app/_lib/params";
16
+import { UserContext } from "@/lib/current-user-context";
17
+
18
+enum State {
19
+ LOADING,
20
+ LOADED,
21
+ ERROR,
22
+}
23
+
24
+export default function Index() {
25
+ const log = useLogger("Profile/Page");
26
+ const searchParams = useSearchParams();
27
+ const userId = searchParams.get(PARAMS_USER_ID);
28
+ const [isOwnProfile, setIsOwnProfile] = useState(false);
29
+ const [userDisplayed, setUserDisplayed] = useState<User | null>(null);
30
+ const [state, setState] = useState(State.LOADING);
31
+ const userContext = useContext(UserContext);
32
+ const loggedInUser = userContext.user?.user;
33
+
34
+ useEffect(() => {
35
+ log.debug("Profile page loaded.");
36
+ async function fetchData() {
37
+ try {
38
+ const result = await invoke<AccountProfileResponse>(
39
+ TAURI_ACCOUNT_PROFILE,
40
+ { userId }
41
+ );
42
+ log.debug("ProfileResponse", result);
43
+ if (result.status === TauriResponse.ERROR) {
44
+ log.error("Error fetching profile");
45
+ return;
46
+ }
47
+ if (!result.user) {
48
+ log.error("No user found");
49
+ setState(State.ERROR);
50
+ return;
51
+ }
52
+
53
+ setIsOwnProfile(result.user.id === loggedInUser?.id);
54
+ setUserDisplayed(result.user);
55
+ setState(State.LOADED);
56
+ } catch (error) {
57
+ log.error(error);
58
+ setState(State.ERROR);
59
+ }
60
+ }
61
+ fetchData();
62
+ // eslint-disable-next-line react-hooks/exhaustive-deps
63
+ }, []);
64
+
65
+ if (state === State.LOADING) {
66
+ return (
67
+ <div className="prose prose-sm prose-invert max-w-none grid-rows-3 gap-6">
68
+ Loading...
69
+ </div>
70
+ );
71
+ }
72
+
73
+ if (state === State.ERROR) {
74
+ return (
75
+ <div className="prose prose-sm prose-invert max-w-none grid-rows-3 gap-6">
76
+ Error loading profile
77
+ </div>
78
+ );
79
+ }
80
+
81
+ if (!userDisplayed) {
82
+ return (
83
+ <div className="prose prose-sm prose-invert max-w-none grid-rows-3 gap-6">
84
+ User not found
85
+ </div>
86
+ );
87
+ }
88
+
89
+ return (
90
+ <div className="w-full px-4 mx-auto">
91
+ <div className="relative flex flex-col min-w-0 break-words mb-6 shadow-xl rounded-lg mt-16">
92
+ <div className="px-6">
93
+ <div className="flex flex-wrap justify-center">
94
+ <div className="w-full px-4 flex justify-center">
95
+ <div className="">
96
+ <Image
97
+ src={userDisplayed.gravatar_url}
98
+ width={150}
99
+ height={150}
100
+ className="shadow-xl rounded-full h-auto align-middle border-none absolute -m-16 -ml-20 max-w-150-px"
101
+ alt={userDisplayed.name}
102
+ />
103
+ </div>
104
+ </div>
105
+ <div className="w-full px-4 text-center mt-20">
106
+ <div className="flex justify-center py-4 lg:pt-4 pt-8">
107
+ <div className="mr-4 p-3 text-center">
108
+ <span className="text-xl font-bold block uppercase tracking-wide text-blueGray-600">
109
+ {userDisplayed.followers_count}
110
+ </span>
111
+ <span className="text-sm text-blueGray-400">Followers</span>
112
+ </div>
113
+ <div className="lg:mr-4 p-3 text-center">
114
+ <span className="text-xl font-bold block uppercase tracking-wide text-blueGray-600">
115
+ {userDisplayed.following_count}
116
+ </span>
117
+ <span className="text-sm text-blueGray-400">Following</span>
118
+ </div>
119
+ </div>
120
+ </div>
121
+ </div>
122
+ <div className="text-center mt-12">
123
+ <h3 className="text-xl font-semibold leading-normal text-blueGray-700 mb-2">
124
+ {userDisplayed.name}{" "}
125
+ <span className="text-blueGray-400 font-normal">
126
+ (@{usernameOrId(userDisplayed)})
127
+ </span>
128
+ </h3>
129
+ {isOwnProfile && (
130
+ <div className="absolute -m-4 -mr-4">
131
+ <Link href="/profile/update">
132
+ <GearIcon className="h-6 w-6 text-blueGray-300" />
133
+ </Link>
134
+ </div>
135
+ )}
136
+ <div className="text-sm leading-normal mt-0 mb-2 text-blueGray-400 font-bold uppercase">
137
+ <LocationMarkerIcon className="h-4 w-4 inline-block" /> Stockholm
138
+ </div>
139
+ </div>
140
+ <div className="mt-10 py-10 border-t border-blueGray-200 text-center">
141
+ <div className="flex flex-wrap justify-center">
142
+ <div className="w-full lg:w-9/12 px-4">
143
+ <p className="mb-4 text-lg leading-relaxed text-blueGray-700">
144
+ {userDisplayed.about}
145
+ </p>
146
+ </div>
147
+ </div>
148
+ </div>
149
+ </div>
150
+ </div>
151
+ </div>
152
+ );
153
+}
frontend/splitfire-desktop/app/profile/page.tsx
+2
-143
@@ -1,152 +1,11 @@
1
"use client";
2
3
-import { useContext, useEffect, useState } from "react";
4
-import { useLogger } from "@/lib/logger";
5
-import User, { usernameOrId } from "@/models/user";
6
-import { useSearchParams } from "next/navigation";
7
-import Image from "next/image";
8
-import { invoke } from "@tauri-apps/api";
9
-import { TAURI_ACCOUNT_PROFILE } from "@/lib/tauriHandler";
10
-import { AccountProfileResponse } from "@/models/account";
11
-import { TauriResponse } from "@/models/shared";
12
-import { LocationMarkerIcon } from "@heroicons/react/solid";
13
-import { GearIcon } from "@radix-ui/react-icons";
14
-import Link from "next/link";
15
-import { PARAMS_USER_ID } from "@/app/_lib/params";
16
-import { UserContext } from "@/lib/CurrentUserContext";
17
-
18
-enum State {
19
- LOADING,
20
- LOADED,
21
- ERROR,
22
-}
3
+import Index from "./components";
4
5
export default function Page() {
25
- const log = useLogger("Profile/Page");
26
- const searchParams = useSearchParams();
27
- const userId = searchParams.get(PARAMS_USER_ID);
28
- const [isOwnProfile, setIsOwnProfile] = useState(false);
29
- const [userDisplayed, setUserDisplayed] = useState<User | null>(null);
30
- const [state, setState] = useState(State.LOADING);
31
- const userContext = useContext(UserContext);
32
- const loggedInUser = userContext.user?.user;
33
-
34
- useEffect(() => {
35
- log.debug("Profile page loaded.");
36
- async function fetchData() {
37
- try {
38
- const result = await invoke<AccountProfileResponse>(
39
- TAURI_ACCOUNT_PROFILE,
40
- { userId }
41
- );
42
- log.debug("ProfileResponse", result);
43
- if (result.status === TauriResponse.ERROR) {
44
- log.error("Error fetching profile");
45
- return;
46
- }
47
- if (!result.user) {
48
- log.error("No user found");
49
- setState(State.ERROR);
50
- return;
51
- }
52
-
53
- setIsOwnProfile(result.user.id === loggedInUser?.id);
54
- setUserDisplayed(result.user);
55
- setState(State.LOADED);
56
- } catch (error) {
57
- log.error(error);
58
- setState(State.ERROR);
59
- }
60
- }
61
- fetchData();
62
- // eslint-disable-next-line react-hooks/exhaustive-deps
63
- }, []);
64
-
65
- if (state === State.LOADING) {
66
- return (
67
- <div className="prose prose-sm prose-invert max-w-none grid-rows-3 gap-6">
68
- Loading...
69
- </div>
70
- );
71
- }
72
-
73
- if (state === State.ERROR) {
74
- return (
75
- <div className="prose prose-sm prose-invert max-w-none grid-rows-3 gap-6">
76
- Error loading profile
77
- </div>
78
- );
79
- }
80
-
81
- if (!userDisplayed) {
82
- return (
83
- <div className="prose prose-sm prose-invert max-w-none grid-rows-3 gap-6">
84
- User not found
85
- </div>
86
- );
87
- }
88
-
6
return (
7
<div className="prose prose-sm prose-invert max-w-none grid-rows-3 gap-6">
91
- <div className="w-full px-4 mx-auto">
92
- <div className="relative flex flex-col min-w-0 break-words mb-6 shadow-xl rounded-lg mt-16">
93
- <div className="px-6">
94
- <div className="flex flex-wrap justify-center">
95
- <div className="w-full px-4 flex justify-center">
96
- <div className="">
97
- <Image
98
- src={userDisplayed.gravatar_url}
99
- width={150}
100
- height={150}
101
- className="shadow-xl rounded-full h-auto align-middle border-none absolute -m-16 -ml-20 max-w-150-px"
102
- alt={userDisplayed.name}
103
- />
104
- </div>
105
- </div>
106
- <div className="w-full px-4 text-center mt-20">
107
- <div className="flex justify-center py-4 lg:pt-4 pt-8">
108
- <div className="mr-4 p-3 text-center">
109
- <span className="text-xl font-bold block uppercase tracking-wide text-blueGray-600">
110
- {userDisplayed.followers_count}
111
- </span>
112
- <span className="text-sm text-blueGray-400">Followers</span>
113
- </div>
114
- <div className="lg:mr-4 p-3 text-center">
115
- <span className="text-xl font-bold block uppercase tracking-wide text-blueGray-600">
116
- {userDisplayed.following_count}
117
- </span>
118
- <span className="text-sm text-blueGray-400">Following</span>
119
- </div>
120
- </div>
121
- </div>
122
- </div>
123
- <div className="text-center mt-12">
124
- <h3 className="text-xl font-semibold leading-normal text-blueGray-700 mb-2">
125
- {userDisplayed.name} <span className="text-blueGray-400 font-normal">(@{usernameOrId(userDisplayed)})</span>
126
- </h3>
127
- {isOwnProfile && (
128
- <div className="absolute -m-4 -mr-4">
129
- <Link href="/profile/update">
130
- <GearIcon className="h-6 w-6 text-blueGray-300" />
131
- </Link>
132
- </div>
133
- )}
134
- <div className="text-sm leading-normal mt-0 mb-2 text-blueGray-400 font-bold uppercase">
135
- <LocationMarkerIcon className="h-4 w-4 inline-block" /> Stockholm
136
- </div>
137
- </div>
138
- <div className="mt-10 py-10 border-t border-blueGray-200 text-center">
139
- <div className="flex flex-wrap justify-center">
140
- <div className="w-full lg:w-9/12 px-4">
141
- <p className="mb-4 text-lg leading-relaxed text-blueGray-700">
142
- {userDisplayed.about}
143
- </p>
144
- </div>
145
- </div>
146
- </div>
147
- </div>
148
- </div>
149
- </div>
8
+ <Index />
9
</div>
10
);
11
}
frontend/splitfire-desktop/app/ready-to-play/page.tsx
+1
-1
@@ -3,7 +3,7 @@
3
import { ContentCarouselResponse } from "@/models/content";
4
import { invoke } from "@tauri-apps/api";
5
import { useEffect, useState } from "react";
6
-import { TAURI_CONTENT_READY_TO_PLAY } from "../../lib/tauriHandler";
6
+import { TAURI_CONTENT_READY_TO_PLAY } from "../../lib/tauri-handler";
7
import { SkeletonCard, SongProviderCard, SongProviderPath } from "../_ui/skeleton-card";
8
import { SongProvider } from "../../models/song-response";
9
frontend/splitfire-desktop/app/register/components/index.tsx
new
+29
@@ -0,0 +1,29 @@
1
+"use client";
2
+
3
+import { useLogger } from "@/lib/logger";
4
+import { useState } from "react";
5
+import { State, SignupForm, SignupState } from "./signup-form";
6
+import SignupSuccess from "./signup-success";
7
+
8
+export function Index() {
9
+ const log = useLogger("Register/Page");
10
+ const [state, setState] = useState(State.LOADED);
11
+
12
+ const registerCallback = (state: SignupState) => {
13
+ log.debug("Register callback", state);
14
+ if (state === SignupState.SUCCESS) {
15
+ setState(State.SUCCESS);
16
+ } else {
17
+ setState(State.ERROR);
18
+ }
19
+ }
20
+ const view = state === State.SUCCESS ? <SignupSuccess /> : <SignupForm registerCallback={registerCallback} />;
21
+
22
+ return (
23
+ <div className="prose prose-sm prose-invert max-w-none">
24
+ <div className="grid grid-cols-1 gap-6 lg:grid-cols-3">
25
+ {view}
26
+ </div>
27
+ </div>
28
+ );
29
+}
\ No newline at end of file
frontend/splitfire-desktop/app/register/components/signup-form.tsx
new
+158
@@ -0,0 +1,158 @@
1
+"use client";
2
+
3
+import { useLogger } from "@/lib/logger";
4
+import { TAURI_ACCOUNT_REGISTER } from "@/lib/tauri-handler";
5
+import { AccountRegisterResponse } from "@/models/account";
6
+import { TauriResponse } from "@/models/shared";
7
+import { invoke } from "@tauri-apps/api";
8
+import { useState } from "react";
9
+import * as Form from "@radix-ui/react-form";
10
+import { Button } from "@/app/_ui/components/button";
11
+
12
+type SignupFormPayload = {
13
+ name: string,
14
+ email: string,
15
+ password: string
16
+}
17
+
18
+export enum State {
19
+ LOADING,
20
+ LOADED,
21
+ ERROR,
22
+ SUCCESS
23
+}
24
+
25
+export enum SignupState {
26
+ ERROR,
27
+ SUCCESS
28
+}
29
+
30
+export function SignupForm({ registerCallback }: { registerCallback: (state: SignupState) => void }) {
31
+ const log = useLogger("Register/Form");
32
+ const [name, setName] = useState("");
33
+ const [email, setEmail] = useState("");
34
+ const [password, setPassword] = useState("");
35
+ const [state, setState] = useState(State.LOADED);
36
+
37
+ const register = async () => {
38
+ setState(State.LOADING);
39
+ try {
40
+ const payload: SignupFormPayload = {
41
+ name,
42
+ email,
43
+ password
44
+ };
45
+ log.debug("payload", payload);
46
+ const response = await invoke<AccountRegisterResponse>(TAURI_ACCOUNT_REGISTER, payload);
47
+ log.debug("response", response);
48
+ if (response.status === TauriResponse.ERROR) {
49
+ log.error("Register failed", response);
50
+ setState(State.ERROR);
51
+ return;
52
+ }
53
+ setState(State.SUCCESS);
54
+ // Call the callback to update the parent state
55
+ registerCallback(SignupState.SUCCESS);
56
+ } catch (error) {
57
+ log.error("Register failed", error);
58
+ setState(State.ERROR);
59
+ }
60
+ };
61
+
62
+ const buttonText = state === State.LOADING ? "Registering..." : "Register";
63
+
64
+ return (
65
+ <Form.Root
66
+ className="FormRoot"
67
+ onSubmit={(event) => {
68
+ event.preventDefault();
69
+ }}
70
+ >
71
+ <Form.Field className="FormField my-6" name="name">
72
+ <div
73
+ style={{
74
+ display: "flex",
75
+ alignItems: "baseline",
76
+ justifyContent: "space-between",
77
+ }}
78
+ >
79
+ <Form.Label className="FormLabel">Name</Form.Label>
80
+ <Form.Message className="FormMessage" match="valueMissing">
81
+ Please enter your name
82
+ </Form.Message>
83
+ <Form.Message className="FormMessage" match="typeMismatch">
84
+ Please provide a valid name
85
+ </Form.Message>
86
+ </div>
87
+ <Form.Control asChild>
88
+ <input
89
+ className="Input"
90
+ type="text"
91
+ value={name}
92
+ onChange={(e) => setName(e.currentTarget.value)}
93
+ required
94
+ />
95
+ </Form.Control>
96
+ </Form.Field>
97
+ <Form.Field className="FormField" name="email">
98
+ <div
99
+ style={{
100
+ display: "flex",
101
+ alignItems: "baseline",
102
+ justifyContent: "space-between",
103
+ }}
104
+ >
105
+ <Form.Label className="FormLabel">Email</Form.Label>
106
+ <Form.Message className="FormMessage" match="valueMissing">
107
+ Please enter your email
108
+ </Form.Message>
109
+ <Form.Message className="FormMessage" match="typeMismatch">
110
+ Please provide a valid email
111
+ </Form.Message>
112
+ </div>
113
+ <Form.Control asChild>
114
+ <input
115
+ className="Input"
116
+ type="email"
117
+ value={email}
118
+ onChange={(e) => setEmail(e.currentTarget.value)}
119
+ required
120
+ />
121
+ </Form.Control>
122
+ </Form.Field>
123
+ <Form.Field className="FormField my-6" name="password">
124
+ <div
125
+ style={{
126
+ display: "flex",
127
+ alignItems: "baseline",
128
+ justifyContent: "space-between",
129
+ }}
130
+ >
131
+ <Form.Label className="FormLabel">Password</Form.Label>
132
+ <Form.Message className="FormMessage" match="valueMissing">
133
+ Please enter your email
134
+ </Form.Message>
135
+ <Form.Message className="FormMessage" match="typeMismatch">
136
+ Please provide a valid email
137
+ </Form.Message>
138
+ </div>
139
+ <Form.Control asChild>
140
+ <input
141
+ className="Input"
142
+ type="password"
143
+ value={password}
144
+ onChange={(e) => {
145
+ setPassword(e.currentTarget.value);
146
+ }}
147
+ required
148
+ />
149
+ </Form.Control>
150
+ </Form.Field>
151
+ <Form.Field className="FormField my-6" name="button">
152
+ <Button onClick={register} variant={"outline"} size={"lg"}>
153
+ {buttonText}
154
+ </Button>
155
+ </Form.Field>
156
+ </Form.Root>
157
+ )
158
+}
\ No newline at end of file
frontend/splitfire-desktop/app/register/components/signup-success.tsx
new
+11
@@ -0,0 +1,11 @@
1
+"use client";
2
+
3
+export default function SignupSuccess() {
4
+ return (
5
+ <div>
6
+ <h1>Success</h1>
7
+ <p>Your account has been created successfully.</p>
8
+ <p>Check your email to confirm your account.</p>
9
+ </div>
10
+ );
11
+}
\ No newline at end of file
frontend/splitfire-desktop/app/register/page.tsx
+2
-179
@@ -1,190 +1,13 @@
1
"use client";
2
3
-import { invoke } from "@tauri-apps/api";
4
-import { useState } from "react";
5
-import * as Form from "@radix-ui/react-form";
6
-import { TAURI_ACCOUNT_REGISTER } from "../../lib/tauriHandler";
7
-import { Button } from "../_ui/components/button";
8
-import { useLogger } from "../../lib/logger";
9
-import { AccountRegisterResponse } from "@/models/account";
10
-import { TauriResponse } from "@/models/shared";
11
-
12
-enum State {
13
- LOADING,
14
- LOADED,
15
- ERROR,
16
- SUCCESS
17
-}
18
-
19
-enum SignupState {
20
- ERROR,
21
- SUCCESS
22
-}
23
-
24
-type SignupFormPayload = {
25
- name: string,
26
- email: string,
27
- password: string
28
-}
3
+import { Index } from "./components";
4
5
export default function Page() {
31
- const log = useLogger("Register/Page");
32
- const [state, setState] = useState(State.LOADED);
33
-
34
- const registerCallback = (state: SignupState) => {
35
- log.debug("Register callback", state);
36
- if (state === SignupState.SUCCESS) {
37
- setState(State.SUCCESS);
38
- } else {
39
- setState(State.ERROR);
40
- }
41
- }
42
- const view = state === State.SUCCESS ? <SignupSuccess /> : <SignupForm registerCallback={registerCallback} />;
6
return (
7
<div className="prose prose-sm prose-invert max-w-none">
8
<div className="grid grid-cols-1 gap-6 lg:grid-cols-3">
46
- {view}
9
+ <Index />
10
</div>
11
</div>
12
);
50
-}
51
-
52
-function SignupSuccess() {
53
- return (
54
- <div>
55
- <h1>Success</h1>
56
- <p>Your account has been created successfully.</p>
57
- <p>Check your email to confirm your account.</p>
58
- </div>
59
- );
60
-}
61
-
62
-function SignupForm({ registerCallback }: { registerCallback: (state: SignupState) => void }) {
63
- const log = useLogger("Register/Form");
64
- const [name, setName] = useState("");
65
- const [email, setEmail] = useState("");
66
- const [password, setPassword] = useState("");
67
- const [state, setState] = useState(State.LOADED);
68
-
69
- const register = async () => {
70
- setState(State.LOADING);
71
- try {
72
- const payload: SignupFormPayload = {
73
- name,
74
- email,
75
- password
76
- };
77
- log.debug("payload", payload);
78
- const response = await invoke<AccountRegisterResponse>(TAURI_ACCOUNT_REGISTER, payload);
79
- log.debug("response", response);
80
- if (response.status === TauriResponse.ERROR) {
81
- log.error("Register failed", response);
82
- setState(State.ERROR);
83
- return;
84
- }
85
- setState(State.SUCCESS);
86
- // Call the callback to update the parent state
87
- registerCallback(SignupState.SUCCESS);
88
- } catch (error) {
89
- log.error("Register failed", error);
90
- setState(State.ERROR);
91
- }
92
- };
93
-
94
- const buttonText = state === State.LOADING ? "Registering..." : "Register";
95
-
96
- return (
97
- <Form.Root
98
- className="FormRoot"
99
- onSubmit={(event) => {
100
- event.preventDefault();
101
- }}
102
- >
103
- <Form.Field className="FormField my-6" name="name">
104
- <div
105
- style={{
106
- display: "flex",
107
- alignItems: "baseline",
108
- justifyContent: "space-between",
109
- }}
110
- >
111
- <Form.Label className="FormLabel">Name</Form.Label>
112
- <Form.Message className="FormMessage" match="valueMissing">
113
- Please enter your name
114
- </Form.Message>
115
- <Form.Message className="FormMessage" match="typeMismatch">
116
- Please provide a valid name
117
- </Form.Message>
118
- </div>
119
- <Form.Control asChild>
120
- <input
121
- className="Input"
122
- type="text"
123
- value={name}
124
- onChange={(e) => setName(e.currentTarget.value)}
125
- required
126
- />
127
- </Form.Control>
128
- </Form.Field>
129
- <Form.Field className="FormField" name="email">
130
- <div
131
- style={{
132
- display: "flex",
133
- alignItems: "baseline",
134
- justifyContent: "space-between",
135
- }}
136
- >
137
- <Form.Label className="FormLabel">Email</Form.Label>
138
- <Form.Message className="FormMessage" match="valueMissing">
139
- Please enter your email
140
- </Form.Message>
141
- <Form.Message className="FormMessage" match="typeMismatch">
142
- Please provide a valid email
143
- </Form.Message>
144
- </div>
145
- <Form.Control asChild>
146
- <input
147
- className="Input"
148
- type="email"
149
- value={email}
150
- onChange={(e) => setEmail(e.currentTarget.value)}
151
- required
152
- />
153
- </Form.Control>
154
- </Form.Field>
155
- <Form.Field className="FormField my-6" name="password">
156
- <div
157
- style={{
158
- display: "flex",
159
- alignItems: "baseline",
160
- justifyContent: "space-between",
161
- }}
162
- >
163
- <Form.Label className="FormLabel">Password</Form.Label>
164
- <Form.Message className="FormMessage" match="valueMissing">
165
- Please enter your email
166
- </Form.Message>
167
- <Form.Message className="FormMessage" match="typeMismatch">
168
- Please provide a valid email
169
- </Form.Message>
170
- </div>
171
- <Form.Control asChild>
172
- <input
173
- className="Input"
174
- type="password"
175
- value={password}
176
- onChange={(e) => {
177
- setPassword(e.currentTarget.value);
178
- }}
179
- required
180
- />
181
- </Form.Control>
182
- </Form.Field>
183
- <Form.Field className="FormField my-6" name="button">
184
- <Button onClick={register} variant={"outline"} size={"lg"}>
185
- {buttonText}
186
- </Button>
187
- </Form.Field>
188
- </Form.Root>
189
- )
13
}
\ No newline at end of file
frontend/splitfire-desktop/app/split/_components/button-split.tsx
+1
-1
@@ -1,4 +1,4 @@
1
-import { UserContext } from "@/lib/CurrentUserContext";
1
+import { UserContext } from "@/lib/current-user-context";
2
import { AudioFile, Status } from "@/models/audio-file";
3
import { CountdownTimerIcon, LapTimerIcon, RocketIcon } from "@radix-ui/react-icons";
4
import { useRouter } from "next/navigation";
frontend/splitfire-desktop/app/split/_components/song-votes.tsx
+1
-1
@@ -1,9 +1,9 @@
1
"use client";
2
3
import { SongProvider } from "@/models/song-response";
4
-import { SongProviderVote } from "@/models/SongVotesDetailResponse";
4
import VotesView from "./votes-view";
5
import SongVotesImage from "./song-votes-image";
6
+import { SongProviderVote } from "@/models/song-votes-detail-response";
7
8
export default function SongVotes({
9
songProvider,
frontend/splitfire-desktop/app/split/_components/voters-view.tsx
+2
-1
@@ -1,9 +1,10 @@
1
+"use client";
2
2
-import { SongProviderVote } from "@/models/SongVotesDetailResponse";
3
import { PARAMS_USER_ID } from '@/app/_lib/params';
4
import Image from "next/image";
5
import Link from "next/link";
6
import { VoteType } from "./votes-view-main";
7
+import { SongProviderVote } from '@/models/song-votes-detail-response';
8
9
export function VoterGravatarsViews(props: {
10
voters: SongProviderVote[];
frontend/splitfire-desktop/app/split/_components/votes-view-main.tsx
+3
-3
@@ -2,15 +2,15 @@ import { BsArrowUpCircleFill, BsArrowDownCircleFill } from "react-icons/bs";
2
import { VoterGravatarsViews } from "./voters-view";
3
import { SongProviderResponse } from "@/models/content";
4
import { CSSProperties, useContext, useState } from "react";
5
-import { TAURI_CONTENT_SONG_BRIDGE_VOTE } from "@/lib/tauriHandler";
6
-import { SongProviderVote } from "@/models/SongVotesDetailResponse";
5
+import { TAURI_CONTENT_SONG_BRIDGE_VOTE } from "@/lib/tauri-handler";
6
import { TauriResponse } from "@/models/shared";
7
import { invoke } from "@tauri-apps/api";
9
-import { UserContext } from "@//lib/CurrentUserContext";
8
+import { UserContext } from "@//lib/current-user-context";
9
import { useRouter } from "next/navigation";
10
import { CurrentUser } from "@/lib/db";
11
import { Spinner } from "react-bootstrap";
12
import { useLogger } from "@/lib/logger";
13
+import { SongProviderVote } from "@/models/song-votes-detail-response";
14
15
export enum VoteType {
16
UP = "up",
frontend/splitfire-desktop/app/split/_components/votes-view.tsx
+1
-1
@@ -4,13 +4,13 @@ import {
4
AudioFile,
5
Status,
6
} from "@/models/audio-file";
7
-import { SongProviderVote } from "@/models/SongVotesDetailResponse";
7
import { useRouter } from "next/navigation";
8
import { useState } from "react";
9
import ButtonGenerateBackingTracks from "./button-split";
10
import MainVotesView, { } from "./votes-view-main";
11
import LetsPlayView from "./lets-play";
12
import { useLogger } from "@/lib/logger";
13
+import { SongProviderVote } from "@/models/song-votes-detail-response";
14
15
export default function VotesView({
16
songProviderId,
frontend/splitfire-desktop/app/split/page.tsx
+2
-2
@@ -4,11 +4,11 @@ import { useSearchParams } from "next/navigation";
4
import { useLogger } from "@/lib/logger";
5
import { useEffect, useState } from "react";
6
import { invoke } from "@tauri-apps/api";
7
-import { TAURI_CONTENT_SONG_BRIDGE_DETAIL } from "@/lib/tauriHandler";
7
+import { TAURI_CONTENT_SONG_BRIDGE_DETAIL } from "@/lib/tauri-handler";
8
import { SongProvider } from "@/models/song-response";
9
-import { SongProviderVote } from "@/models/SongVotesDetailResponse";
9
import SongVotes from "./_components/song-votes";
10
import { SongBridgeResponse } from "@/models/content";
11
+import { SongProviderVote } from "@/models/song-votes-detail-response";
12
13
export default function Page() {
14
const log = useLogger("Play");
frontend/splitfire-desktop/app/top-voted/components/index.tsx
+1
-1
@@ -2,7 +2,7 @@
2
3
import { SkeletonCard, SongProviderCard, SongProviderPath } from "@/app/_ui/skeleton-card";
4
import { useLogger } from "@/lib/logger";
5
-import { TAURI_CONTENT_TOP_VOTED } from "@/lib/tauriHandler";
5
+import { TAURI_CONTENT_TOP_VOTED } from "@/lib/tauri-handler";
6
import { ContentCarouselResponse } from "@/models/content";
7
import { SongProvider } from "@/models/song-response";
8
import { invoke } from "@tauri-apps/api";
frontend/splitfire-desktop/lib/current-user-context.ts
renamed
frontend/splitfire-desktop/lib/tauri-handler.ts
renamed
frontend/splitfire-desktop/lib/use-count-down.ts
renamed
frontend/splitfire-desktop/models/content.ts
+1
-1
@@ -1,6 +1,6 @@
1
import { SongProvider } from "@/models/song-response";
2
import { TauriResponse } from "./shared";
3
-import { SongProviderVote } from "@/models/SongVotesDetailResponse";
3
+import { SongProviderVote } from "./song-votes-detail-response";
4
5
// Need to be renamed into more generic name
6
export interface ContentCarouselResponse {