UI
Made UI changes, mainly fixing bugs and changing some theming
This commit is contained in:
parent
be59453ce4
commit
25b379aadd
26 changed files with 1027 additions and 655 deletions
2
.idea/discord.xml
generated
2
.idea/discord.xml
generated
|
|
@ -1,7 +1,7 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="DiscordProjectSettings">
|
<component name="DiscordProjectSettings">
|
||||||
<option name="show" value="ASK" />
|
<option name="show" value="PROJECT_FILES" />
|
||||||
<option name="description" value="" />
|
<option name="description" value="" />
|
||||||
<option name="applicationTheme" value="default" />
|
<option name="applicationTheme" value="default" />
|
||||||
<option name="iconsTheme" value="default" />
|
<option name="iconsTheme" value="default" />
|
||||||
|
|
|
||||||
207
package-lock.json
generated
207
package-lock.json
generated
|
|
@ -1,11 +1,11 @@
|
||||||
{
|
{
|
||||||
"name": "whispr",
|
"name": "sipher",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "whispr",
|
"name": "sipher",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@radix-ui/react-avatar": "^1.1.1",
|
"@radix-ui/react-avatar": "^1.1.1",
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
"@radix-ui/react-separator": "^1.1.0",
|
"@radix-ui/react-separator": "^1.1.0",
|
||||||
"@radix-ui/react-slot": "^1.1.0",
|
"@radix-ui/react-slot": "^1.1.0",
|
||||||
"@radix-ui/react-toast": "^1.2.2",
|
"@radix-ui/react-toast": "^1.2.2",
|
||||||
|
"@radix-ui/react-tooltip": "^1.1.4",
|
||||||
"@supabase/ssr": "^0.5.2",
|
"@supabase/ssr": "^0.5.2",
|
||||||
"@supabase/supabase-js": "^2.47.3",
|
"@supabase/supabase-js": "^2.47.3",
|
||||||
"argon2": "^0.41.1",
|
"argon2": "^0.41.1",
|
||||||
|
|
@ -58,6 +59,44 @@
|
||||||
"tslib": "^2.4.0"
|
"tslib": "^2.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@floating-ui/core": {
|
||||||
|
"version": "1.6.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.8.tgz",
|
||||||
|
"integrity": "sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@floating-ui/utils": "^0.2.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@floating-ui/dom": {
|
||||||
|
"version": "1.6.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.12.tgz",
|
||||||
|
"integrity": "sha512-NP83c0HjokcGVEMeoStg317VD9W7eDlGK7457dMBANbKA6GJZdc7rjujdgqzTaz93jkGgc5P/jeWbaCHnMNc+w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@floating-ui/core": "^1.6.0",
|
||||||
|
"@floating-ui/utils": "^0.2.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@floating-ui/react-dom": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@floating-ui/dom": "^1.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.8.0",
|
||||||
|
"react-dom": ">=16.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@floating-ui/utils": {
|
||||||
|
"version": "0.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.8.tgz",
|
||||||
|
"integrity": "sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@img/sharp-darwin-arm64": {
|
"node_modules/@img/sharp-darwin-arm64": {
|
||||||
"version": "0.33.5",
|
"version": "0.33.5",
|
||||||
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz",
|
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz",
|
||||||
|
|
@ -643,6 +682,29 @@
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.0.tgz",
|
||||||
"integrity": "sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA=="
|
"integrity": "sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA=="
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-arrow": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-FmlW1rCg7hBpEBwFbjHwCW6AmWLQM6g/v0Sn8XbP9NvmSZ2San1FpQeyPtufzOMSIx7Y4dzjlHoifhp+7NkZhw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-primitive": "2.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-avatar": {
|
"node_modules/@radix-ui/react-avatar": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.1.1.tgz",
|
||||||
|
|
@ -783,6 +845,24 @@
|
||||||
"react": "^16.x || ^17.x || ^18.x || ^19.0.0 || ^19.0.0-rc"
|
"react": "^16.x || ^17.x || ^18.x || ^19.0.0 || ^19.0.0-rc"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-id": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-use-layout-effect": "1.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-label": {
|
"node_modules/@radix-ui/react-label": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.0.tgz",
|
||||||
|
|
@ -805,6 +885,53 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-popper": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-ZnRMshKF43aBxVWPWvbj21+7TQCvhuULWJ4gNIKYpRlQt5xGRhLx66tMp8pya2UkGHTSlhpXwmjqltDYHhw7Vg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@floating-ui/react-dom": "^2.0.0",
|
||||||
|
"@radix-ui/react-arrow": "1.1.0",
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.0",
|
||||||
|
"@radix-ui/react-context": "1.1.0",
|
||||||
|
"@radix-ui/react-primitive": "2.0.0",
|
||||||
|
"@radix-ui/react-use-callback-ref": "1.1.0",
|
||||||
|
"@radix-ui/react-use-layout-effect": "1.1.0",
|
||||||
|
"@radix-ui/react-use-rect": "1.1.0",
|
||||||
|
"@radix-ui/react-use-size": "1.1.0",
|
||||||
|
"@radix-ui/rect": "1.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-popper/node_modules/@radix-ui/react-context": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-portal": {
|
"node_modules/@radix-ui/react-portal": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.2.tgz",
|
||||||
|
|
@ -975,6 +1102,40 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-tooltip": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-QpObUH/ZlpaO4YgHSaYzrLO2VuO+ZBFFgGzjMUPwtiYnAzzNNDPJeEGRrT7qNOrWm/Jr08M1vlp+vTHtnSQ0Uw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/primitive": "1.1.0",
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.0",
|
||||||
|
"@radix-ui/react-context": "1.1.1",
|
||||||
|
"@radix-ui/react-dismissable-layer": "1.1.1",
|
||||||
|
"@radix-ui/react-id": "1.1.0",
|
||||||
|
"@radix-ui/react-popper": "1.2.0",
|
||||||
|
"@radix-ui/react-portal": "1.1.2",
|
||||||
|
"@radix-ui/react-presence": "1.1.1",
|
||||||
|
"@radix-ui/react-primitive": "2.0.0",
|
||||||
|
"@radix-ui/react-slot": "1.1.0",
|
||||||
|
"@radix-ui/react-use-controllable-state": "1.1.0",
|
||||||
|
"@radix-ui/react-visually-hidden": "1.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-use-callback-ref": {
|
"node_modules/@radix-ui/react-use-callback-ref": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz",
|
||||||
|
|
@ -1037,6 +1198,42 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-use-rect": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/rect": "1.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-use-size": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-use-layout-effect": "1.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-visually-hidden": {
|
"node_modules/@radix-ui/react-visually-hidden": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.1.0.tgz",
|
||||||
|
|
@ -1059,6 +1256,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/rect": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@supabase/auth-js": {
|
"node_modules/@supabase/auth-js": {
|
||||||
"version": "2.66.1",
|
"version": "2.66.1",
|
||||||
"resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.66.1.tgz",
|
"resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.66.1.tgz",
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"name": "whispr",
|
"name": "sipher",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
"@radix-ui/react-separator": "^1.1.0",
|
"@radix-ui/react-separator": "^1.1.0",
|
||||||
"@radix-ui/react-slot": "^1.1.0",
|
"@radix-ui/react-slot": "^1.1.0",
|
||||||
"@radix-ui/react-toast": "^1.2.2",
|
"@radix-ui/react-toast": "^1.2.2",
|
||||||
|
"@radix-ui/react-tooltip": "^1.1.4",
|
||||||
"@supabase/ssr": "^0.5.2",
|
"@supabase/ssr": "^0.5.2",
|
||||||
"@supabase/supabase-js": "^2.47.3",
|
"@supabase/supabase-js": "^2.47.3",
|
||||||
"argon2": "^0.41.1",
|
"argon2": "^0.41.1",
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ export async function POST(request: Request) {
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (typeof error === "object") {
|
if (typeof error === "object") {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{error: `Registration failed: ${JSON.stringify(error)}`},
|
{error: JSON.stringify(error)},
|
||||||
{status: 400}
|
{status: 400}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import React, {useEffect, useState} from 'react'
|
import React, {useCallback, useEffect, useState} from 'react'
|
||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
import {motion} from 'framer-motion'
|
import {motion} from 'framer-motion'
|
||||||
import {Button} from "@/components/ui/button"
|
import {Button} from "@/components/ui/button"
|
||||||
|
|
@ -26,20 +26,26 @@ export default function AuthPage() {
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
|
const check = useCallback(async () => {
|
||||||
|
const isAuthenticated = await checkAuth("Called on Login page");
|
||||||
|
if (isAuthenticated) {
|
||||||
|
router.replace('/');
|
||||||
|
} else {
|
||||||
|
setMounted(true);
|
||||||
|
}
|
||||||
|
}, [checkAuth, router, setMounted])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const check = async () => {
|
check().then(() => {
|
||||||
const isAuthenticated = await checkAuth();
|
console.log("Login page check finished")
|
||||||
if (isAuthenticated) {
|
})
|
||||||
router.replace('/');
|
}, []);
|
||||||
} else {
|
|
||||||
setMounted(true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
check();
|
if (!mounted) {
|
||||||
}, [checkAuth, router]);
|
return <div className="min-h-screen flex items-center justify-center">
|
||||||
|
{/* Optional: Add a loading spinner or skeleton here */}
|
||||||
if (!mounted) return null;
|
</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const getTheme = () => {
|
const getTheme = () => {
|
||||||
|
|
@ -76,25 +82,37 @@ export default function AuthPage() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.code !== 200) {
|
if (response.code !== 200) {
|
||||||
if (isLogin && response.code === 400) {
|
const msg = response.message
|
||||||
console.log(response)
|
|
||||||
toast({
|
|
||||||
title: "E-mail not verified",
|
|
||||||
description: response.message,
|
|
||||||
variant: "destructive",
|
|
||||||
duration: 5000, // Increased duration for better visibility
|
|
||||||
action: response.action!
|
|
||||||
});
|
|
||||||
setIsSubmitting(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
toast({
|
try {
|
||||||
title: "Error",
|
const parsed = JSON.parse(msg);
|
||||||
description: response.message,
|
let desc = parsed.name;
|
||||||
variant: "destructive",
|
|
||||||
duration: 5000, // Increased duration for better visibility
|
switch (desc) {
|
||||||
});
|
case "AuthWeakPasswordError": {
|
||||||
|
desc = "Password too weak, please try again.";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
desc = "An unknown error occurred";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toast({
|
||||||
|
title: "Error",
|
||||||
|
description: desc,
|
||||||
|
variant: "destructive",
|
||||||
|
duration: 5000
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
// If msg isn't valid JSON, show the raw message
|
||||||
|
toast({
|
||||||
|
title: "Error",
|
||||||
|
description: msg,
|
||||||
|
variant: "destructive",
|
||||||
|
duration: 5000
|
||||||
|
});
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
toast({
|
toast({
|
||||||
title: "Success",
|
title: "Success",
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
* @param password - The plain-text password of the user. Will be encrypted later by Supabase
|
* @param password - The plain-text password of the user. Will be encrypted later by Supabase
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
export default async function Register(password: string, username: string) {
|
export default async function Register(username: string, password: string) {
|
||||||
try {
|
try {
|
||||||
// Sends the request to the API
|
// Sends the request to the API
|
||||||
let res = await fetch('/api/auth/register', {
|
let res = await fetch('/api/auth/register', {
|
||||||
|
|
@ -19,8 +19,8 @@ export default async function Register(password: string, username: string) {
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
let data = await res.json();
|
let data = await res.json();
|
||||||
return {
|
return {
|
||||||
code: data.code,
|
code: res.status,
|
||||||
message: data.message
|
message: data.error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -32,7 +32,7 @@ export default async function Register(password: string, username: string) {
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
return {
|
return {
|
||||||
code: 500,
|
code: 500,
|
||||||
message: `An unknown error occurred: ${e.message}`
|
message: e.error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,9 @@ import {UserProvider} from "@/contexts/user";
|
||||||
import Sidebar from "@/components/main/sidebar/sidebar";
|
import Sidebar from "@/components/main/sidebar/sidebar";
|
||||||
import {getAuthenticatedUser} from "@/lib/auth";
|
import {getAuthenticatedUser} from "@/lib/auth";
|
||||||
import {SharedStateProvider} from "@/hooks/shared-states";
|
import {SharedStateProvider} from "@/hooks/shared-states";
|
||||||
import {ThemeProvider} from "next-themes";
|
import ThemeProvider from "@/components/ui/theme-provider";
|
||||||
|
import {headers} from "next/headers";
|
||||||
|
import {Toaster} from "@/components/ui/toaster";
|
||||||
|
|
||||||
const publicSans = Public_Sans({
|
const publicSans = Public_Sans({
|
||||||
subsets: ['latin'],
|
subsets: ['latin'],
|
||||||
|
|
@ -15,34 +17,31 @@ const publicSans = Public_Sans({
|
||||||
});
|
});
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Create Next App",
|
title: "SiPher - Where Shadows Live",
|
||||||
description: "Generated by create next app",
|
description: "Secrecy? Not here, absolutely.",
|
||||||
|
icons: [{rel: "icon", url: "/logos/logo.png"}],
|
||||||
};
|
};
|
||||||
|
|
||||||
export default async function RootLayout({
|
export default async function RootLayout(
|
||||||
children,
|
{
|
||||||
}: {
|
children,
|
||||||
children: React.ReactNode & { props?: { childProp?: { segment?: string } } };
|
}: {
|
||||||
}) {
|
children: React.ReactNode & { props?: { childProp?: { segment?: string } } };
|
||||||
|
}) {
|
||||||
const initialUser = await getAuthenticatedUser();
|
const initialUser = await getAuthenticatedUser();
|
||||||
const isAuthPage = (children as any)?.props?.childProp?.segment === 'auth';
|
const isAuthPage = (await headers()).get("x-current-pathname")?.includes("auth");
|
||||||
|
|
||||||
// Auth layout
|
// Auth layout
|
||||||
if (isAuthPage) {
|
if (isAuthPage) {
|
||||||
return (
|
return (
|
||||||
<html lang="en">
|
<html lang="en" suppressHydrationWarning>
|
||||||
<body className={`${publicSans.variable} font-sans antialiased`}>
|
<body className={`${publicSans.variable} font-sans antialiased`}>
|
||||||
<ThemeProvider
|
<ThemeProvider>
|
||||||
attribute="class"
|
|
||||||
defaultTheme="system"
|
|
||||||
enableSystem
|
|
||||||
>
|
|
||||||
<UserProvider initialUser={initialUser}>
|
<UserProvider initialUser={initialUser}>
|
||||||
<main className="min-h-screen flex items-center justify-center">
|
{children}
|
||||||
{children}
|
|
||||||
</main>
|
|
||||||
</UserProvider>
|
</UserProvider>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
|
<Toaster/>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
|
|
@ -52,11 +51,7 @@ export default async function RootLayout({
|
||||||
return (
|
return (
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<body className={`${publicSans.variable} font-sans antialiased`}>
|
<body className={`${publicSans.variable} font-sans antialiased`}>
|
||||||
<ThemeProvider
|
<ThemeProvider>
|
||||||
attribute="class"
|
|
||||||
defaultTheme="system"
|
|
||||||
enableSystem
|
|
||||||
>
|
|
||||||
<UserProvider initialUser={initialUser}>
|
<UserProvider initialUser={initialUser}>
|
||||||
<SharedStateProvider>
|
<SharedStateProvider>
|
||||||
<div className={`max-h-[1080px] p-6 bg-secondary`}>
|
<div className={`max-h-[1080px] p-6 bg-secondary`}>
|
||||||
|
|
@ -68,6 +63,7 @@ export default async function RootLayout({
|
||||||
</div>
|
</div>
|
||||||
</SharedStateProvider>
|
</SharedStateProvider>
|
||||||
</UserProvider>
|
</UserProvider>
|
||||||
|
<Toaster/>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
102
src/app/page.tsx
102
src/app/page.tsx
|
|
@ -1,12 +1,106 @@
|
||||||
"use client"
|
"use client"
|
||||||
import {useTheme} from "next-themes";
|
import {useTheme} from "next-themes";
|
||||||
|
import Image from "next/image";
|
||||||
|
import {Feather, Search} from "lucide-react";
|
||||||
|
import {useEffect, useState} from "react";
|
||||||
|
|
||||||
export default function SiPher() {
|
export default function SiPher() {
|
||||||
const {theme} = useTheme()
|
const {theme, systemTheme} = useTheme();
|
||||||
|
const [isSearchExpanded, setIsSearchExpanded] = useState(false);
|
||||||
|
const [mounted, setMounted] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setMounted(true);
|
||||||
|
|
||||||
|
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const getTheme = () => {
|
||||||
|
if (!mounted) return "light";
|
||||||
|
if (theme === "system") {
|
||||||
|
return systemTheme === "dark" ? "dark" : "light";
|
||||||
|
}
|
||||||
|
return theme === "dark" ? "dark" : "light";
|
||||||
|
};
|
||||||
|
|
||||||
|
const currentTheme = getTheme();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`flex-1 ${theme === "dark" ? "dark" : ""}`}>
|
<div
|
||||||
abc
|
className={`relative flex-1 ${currentTheme === "dark" ? "dark" : ""} w-full max-h-[600px] bg-gradient-to-b from-background to-background/95`}>
|
||||||
|
{/* Animated background elements */}
|
||||||
|
<div className="absolute inset-0 overflow-hidden pointer-events-none">
|
||||||
|
<div
|
||||||
|
className="absolute inset-0 bg-[radial-gradient(circle_500px_at_50%_50%,rgba(120,120,120,0.05),transparent)]"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="relative flex flex-col justify-center items-center h-screen px-4 select-none space-y-8">
|
||||||
|
{/* Logo section with subtle hover effect */}
|
||||||
|
<div className="relative group">
|
||||||
|
<div
|
||||||
|
className="absolute inset-0 bg-primary/5 rounded-full blur-xl group-hover:bg-primary/10 transition-all duration-500"/>
|
||||||
|
<Image
|
||||||
|
priority
|
||||||
|
src={`/logos/logo.png`}
|
||||||
|
alt="SiPher"
|
||||||
|
width={128}
|
||||||
|
height={128}
|
||||||
|
draggable={false}
|
||||||
|
className="relative transform transition-transform duration-500 group-hover:scale-105"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Main text content with improved typography and spacing */}
|
||||||
|
<div className="max-w-2xl space-y-6 text-center">
|
||||||
|
<p className="text-lg md:text-xl font-medium leading-relaxed text-primary">
|
||||||
|
Where shadows dance and secrets nest, Silent Whisper serves as the dark sanctuary for those
|
||||||
|
who value discretion above all. Born from ancient corvid traditions, this messenger's haven ensures your
|
||||||
|
whispers remain unheard by all but their intended recipients.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p className="text-sm md:text-base font-medium text-muted-foreground leading-relaxed">
|
||||||
|
Like the sacred ravens of old, your messages fly through the darkness, their contents sealed by shadows and
|
||||||
|
protected by forgotten wards. Each member of our dark fellowship is known only by their chosen name, their
|
||||||
|
true identity shrouded in mystery.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Enhanced search component */}
|
||||||
|
<div className="relative mt-8">
|
||||||
|
<div
|
||||||
|
className={`flex items-center rounded-full transition-all duration-300 ${
|
||||||
|
isSearchExpanded
|
||||||
|
? "bg-secondary/30 backdrop-blur-sm border border-primary/20 shadow-lg"
|
||||||
|
: ""
|
||||||
|
}`}
|
||||||
|
style={{
|
||||||
|
width: isSearchExpanded ? "240px" : "40px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
className={`flex-shrink-0 w-10 h-10 flex items-center justify-center rounded-full
|
||||||
|
${currentTheme === "dark" ? "hover:bg-secondary/60" : "hover:bg-primary/10"}
|
||||||
|
transition-colors duration-200`}
|
||||||
|
onClick={() => setIsSearchExpanded(!isSearchExpanded)}
|
||||||
|
>
|
||||||
|
<Search className="w-5 h-5"/>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Find fellow shadows..."
|
||||||
|
className={`w-full bg-transparent focus:outline-none text-primary placeholder-primary/50
|
||||||
|
transition-all duration-300 ${isSearchExpanded ? "px-4" : "w-0 px-0"}`}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Decorative feather icon */}
|
||||||
|
<Feather
|
||||||
|
className={`absolute -right-6 top-1/2 -translate-y-1/2 w-4 h-4 text-primary/30 transform rotate-45
|
||||||
|
transition-opacity duration-300 ${isSearchExpanded ? "opacity-100" : "opacity-0"}`}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -1,58 +1,59 @@
|
||||||
|
"use client"
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Button } from "@/components/ui/button"
|
import {Button} from "@/components/ui/button"
|
||||||
import { HamburgerMenuIcon } from "@radix-ui/react-icons"
|
import {HamburgerMenuIcon} from "@radix-ui/react-icons"
|
||||||
import { useTheme } from "next-themes"
|
import {useTheme} from "next-themes"
|
||||||
import Image from "next/image"
|
import Image from "next/image"
|
||||||
import { useUIState } from "@/hooks/shared-states"
|
import {useUIState} from "@/hooks/shared-states"
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
|
||||||
const MobileHeader: React.FC = () => {
|
const MobileHeader: React.FC = () => {
|
||||||
const { setIsDrawerOpen } = useUIState()
|
const {setIsDrawerOpen} = useUIState()
|
||||||
const { theme, systemTheme } = useTheme()
|
const {theme, systemTheme} = useTheme()
|
||||||
|
|
||||||
const getTheme = () => {
|
const getTheme = () => {
|
||||||
if (theme === "system") {
|
if (theme === "system") {
|
||||||
switch (systemTheme) {
|
switch (systemTheme) {
|
||||||
case "dark":
|
case "dark":
|
||||||
return "dark"
|
return "dark"
|
||||||
default:
|
default:
|
||||||
return "light"
|
return "light"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return theme === "dark" ? "dark" : "light"
|
return theme === "dark" ? "dark" : "light"
|
||||||
}
|
}
|
||||||
|
|
||||||
const logoSrc = getTheme() === 'dark' ? '/logos/logo-light.png' : '/logos/logo.png'
|
const logoSrc = getTheme() === 'dark' ? '/logos/logo-light.png' : '/logos/logo.png'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header className="fixed top-0 left-0 right-0 z-50 lg:hidden pb-10">
|
<header className="fixed top-0 left-0 right-0 z-50 lg:hidden pb-10">
|
||||||
<div className="flex items-center justify-between px-4 py-2 border-b border-border bg-background">
|
<div className="flex items-center justify-between px-4 py-2 border-b border-border bg-background">
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="icon"
|
size="icon"
|
||||||
onClick={() => setIsDrawerOpen(true)}
|
onClick={() => setIsDrawerOpen(true)}
|
||||||
className="rounded-full"
|
className="rounded-full"
|
||||||
>
|
>
|
||||||
<HamburgerMenuIcon className="w-6 h-6" />
|
<HamburgerMenuIcon className="w-6 h-6"/>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<div className="flex items-center justify-center flex-1">
|
<div className="flex items-center justify-center flex-1">
|
||||||
<Link href="/" className="block">
|
<Link href="/" className="block">
|
||||||
<Image
|
<Image
|
||||||
src={logoSrc}
|
src={logoSrc}
|
||||||
alt="Logo"
|
alt="Logo"
|
||||||
width={48}
|
width={48}
|
||||||
height={48}
|
height={48}
|
||||||
className="w-12 h-12 cursor-pointer rounded-full hover:bg-secondary/20"
|
className="w-12 h-12 cursor-pointer rounded-full hover:bg-secondary/20"
|
||||||
/>
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Empty div to maintain center alignment */}
|
{/* Empty div to maintain center alignment */}
|
||||||
<div className="w-10 mb-8" />
|
<div className="w-10 mb-8"/>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default MobileHeader
|
export default MobileHeader
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
"use client"
|
"use client"
|
||||||
import React, {useCallback, useEffect, useRef, useState} from "react"
|
import React, {useCallback, useEffect, useState} from "react"
|
||||||
import {usePathname} from "next/navigation"
|
import {usePathname} from "next/navigation"
|
||||||
import Link from "next/link"
|
import Link from "next/link"
|
||||||
import {AnimatePresence, motion} from "framer-motion"
|
import {AnimatePresence, motion} from "framer-motion"
|
||||||
|
|
@ -8,13 +8,14 @@ import {Button} from "@/components/ui/button"
|
||||||
import {Avatar, AvatarFallback} from "@/components/ui/avatar"
|
import {Avatar, AvatarFallback} from "@/components/ui/avatar"
|
||||||
import {Separator} from "@/components/ui/separator"
|
import {Separator} from "@/components/ui/separator"
|
||||||
import {ScrollArea} from "@/components/ui/scroll-area"
|
import {ScrollArea} from "@/components/ui/scroll-area"
|
||||||
import {useTheme} from "next-themes"
|
|
||||||
import {GearIcon} from "@radix-ui/react-icons"
|
import {GearIcon} from "@radix-ui/react-icons"
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import MobileHeader from "@/components/main/sidebar/mobile";
|
import MobileHeader from "@/components/main/sidebar/mobile";
|
||||||
import {useUser} from "@/contexts/user";
|
import {useUser} from "@/contexts/user";
|
||||||
import {useUIState} from "@/hooks/shared-states";
|
import {useRefs, useUIState} from "@/hooks/shared-states";
|
||||||
import {useToast} from "@/hooks/use-toast";
|
import {useToast} from "@/hooks/use-toast";
|
||||||
|
import {useTheme} from "next-themes";
|
||||||
|
import {Tooltip, TooltipContent, TooltipProvider, TooltipTrigger} from "@/components/ui/tooltip";
|
||||||
|
|
||||||
type SidebarProps = {
|
type SidebarProps = {
|
||||||
children?: React.ReactNode
|
children?: React.ReactNode
|
||||||
|
|
@ -26,41 +27,51 @@ function Sidebar(
|
||||||
}: SidebarProps
|
}: SidebarProps
|
||||||
) {
|
) {
|
||||||
const pathname = usePathname()
|
const pathname = usePathname()
|
||||||
const drawerRef = useRef<HTMLDivElement>(null)
|
|
||||||
|
|
||||||
const [selectedThreads, setSelectedThreads] = useState("");
|
const [selectedThreads, setSelectedThreads] = useState("");
|
||||||
const [threads, setThreads] = useState<SiPher.Messages[] | []>([]);
|
const [threads, setThreads] = useState<SiPher.Messages[] | []>([]);
|
||||||
const [threadMenu, setThreadMenu] = useState<SiPher.Messages[] | []>([]);
|
const [threadMenu, setThreadMenu] = useState<SiPher.Messages[] | []>([]);
|
||||||
|
const [copied, setCopied] = useState<boolean>(false);
|
||||||
|
const {theme, systemTheme} = useTheme()
|
||||||
const {toast} = useToast();
|
const {toast} = useToast();
|
||||||
|
|
||||||
|
const {isDrawerOpen, setIsDrawerOpen} = useUIState()
|
||||||
|
const {drawerRef} = useRefs();
|
||||||
|
|
||||||
|
const user = useUser().user!;
|
||||||
|
|
||||||
|
const {
|
||||||
|
username,
|
||||||
|
suuid
|
||||||
|
} = user
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const getThreads = async () => {
|
const getThreads = async () => {
|
||||||
const req = await fetch("/api/user/get/threads")
|
try {
|
||||||
|
const req = await fetch("/api/user/get/threads")
|
||||||
if (req.ok) {
|
if (req.ok) {
|
||||||
const {threads} = await req.json() as { threads: SiPher.Messages[] | [] }
|
const {threads} = await req.json() as { threads: SiPher.Messages[] | [] }
|
||||||
setThreads(threads)
|
setThreads(threads)
|
||||||
return;
|
} else {
|
||||||
} else {
|
setThreads([])
|
||||||
setThreads([]);
|
toast({
|
||||||
toast({
|
title: "Error",
|
||||||
title: "Error",
|
description: "An unknown error occurred",
|
||||||
description: "An unknown error occurred",
|
variant: "destructive",
|
||||||
variant: "destructive",
|
duration: 5000,
|
||||||
duration: 5000, // Increased duration for better visibility
|
})
|
||||||
})
|
}
|
||||||
|
} catch (error) {
|
||||||
|
setThreads([])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getThreads();
|
getThreads()
|
||||||
|
return () => setThreads([])
|
||||||
return () => {
|
}, [toast])
|
||||||
setThreads([]);
|
|
||||||
}
|
|
||||||
}, [setThreads])
|
|
||||||
|
|
||||||
const generateThreads = useCallback(() => {
|
const generateThreads = useCallback(() => {
|
||||||
threads.map(async(thread) => {
|
threads.map(async (thread) => {
|
||||||
if (thread.participants.length > 2) {
|
if (thread.participants.length > 2) {
|
||||||
return (
|
return (
|
||||||
<li key={thread.id}>
|
<li key={thread.id}>
|
||||||
|
|
@ -78,46 +89,37 @@ function Sidebar(
|
||||||
</li>
|
</li>
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
const fetchOtherUser = await useUser().getUser(thread.id)
|
const fetchOtherUser = await useUser().getUser("fetchOtherUser - const", thread.id)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, [threads])
|
}, [threads])
|
||||||
|
|
||||||
const user = useUser().user!;
|
const isDarkMode = theme === "system"
|
||||||
|
? systemTheme === "dark"
|
||||||
const {
|
: theme === "dark"
|
||||||
username,
|
|
||||||
suuid
|
|
||||||
} = user
|
|
||||||
|
|
||||||
const {isDrawerOpen, setIsDrawerOpen} = useUIState()
|
|
||||||
|
|
||||||
const {theme, systemTheme} = useTheme()
|
|
||||||
const getTheme = () => {
|
|
||||||
if (theme === "system") {
|
|
||||||
switch (systemTheme) {
|
|
||||||
case "dark":
|
|
||||||
return "dark"
|
|
||||||
default:
|
|
||||||
return "light"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return theme === "dark" ? "dark" : "light"
|
|
||||||
}
|
|
||||||
const isDarkMode = getTheme() === "dark";
|
|
||||||
|
|
||||||
const RightSidebarContent = () => (
|
const RightSidebarContent = () => (
|
||||||
<div className={`flex flex-col h-full w-[240px]`}>
|
<div className={`flex flex-col h-full w-[240px]`}>
|
||||||
|
<TooltipProvider>
|
||||||
|
<Tooltip open={copied} onOpenChange={setCopied}>
|
||||||
|
<TooltipTrigger/>
|
||||||
|
<TooltipContent arrowPadding={10} className={"p-2 shadow-cyan-950 shadow-md"}>
|
||||||
|
Copied SUUID to clipboard!
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</TooltipProvider>
|
||||||
<div
|
<div
|
||||||
className={`flex items-center p-3 m-2 ${isDarkMode ? "hover:bg-accent/90" : "hover:bg-secondary/20"} rounded-full transition-colors duration-200`}>
|
onClick={() => {
|
||||||
|
setCopied(true)
|
||||||
|
navigator.clipboard.writeText(suuid)
|
||||||
|
}}
|
||||||
|
className={`flex items-center p-3 m-2 ${isDarkMode ? "hover:bg-accent/90" : "hover:bg-secondary/20"} rounded-full transition-colors duration-200 cursor-pointer select-none`}>
|
||||||
<Avatar className="w-12 h-12 mr-3">
|
<Avatar className="w-12 h-12 mr-3">
|
||||||
<AvatarFallback>{username.charAt(0)}</AvatarFallback>
|
<AvatarFallback>{username.charAt(0)}</AvatarFallback>
|
||||||
</Avatar>
|
</Avatar>
|
||||||
<div>
|
<div>
|
||||||
<h3 className={`font-semibold text-[17px] ${isDarkMode ? "text-white" : "text-black"}`}>{username}</h3>
|
<h3 className={`font-semibold text-[17px] ${isDarkMode ? "text-white" : "text-black"}`}>{username}</h3>
|
||||||
<p className="text-sm text-muted-foreground">@{username}</p>
|
<p className="text-xs text-muted-foreground">${suuid}</p>
|
||||||
<p className="text-xs text-muted">${suuid}</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Separator className="my-2"/>
|
<Separator className="my-2"/>
|
||||||
|
|
@ -179,15 +181,16 @@ function Sidebar(
|
||||||
isDarkMode ? "bg-background" : "white"
|
isDarkMode ? "bg-background" : "white"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="flex justify-items-start w-[240px] mt-1.5">
|
<div className="flex justify-items-start w-[240px] mt-1.5 hover:scale-105 transition-all duration-300">
|
||||||
<Link href={"/"} passHref>
|
<Link href={"/"} passHref className={"flex flex-row items-center ml-1.5"}>
|
||||||
<Image
|
<Image
|
||||||
src={isDarkMode ? "/logos/logo.png" : "/logos/logo-light.png"}
|
src={isDarkMode ? "/logos/logo.png" : "/logos/logo-light.png"}
|
||||||
alt="Tocka‘s Nest"
|
alt="Tocka‘s Nest"
|
||||||
width={64}
|
width={64}
|
||||||
height={64}
|
height={64}
|
||||||
className="w-16 h-16 cursor-pointer rounded-full hover:bg-secondary/20"
|
className="w-16 h-16 cursor-pointer rounded-full antialiased"
|
||||||
/>
|
/>
|
||||||
|
<p className={"text-center text-xl font-bold antialiased"}>SiPher</p>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<RightSidebarContent/>
|
<RightSidebarContent/>
|
||||||
|
|
|
||||||
|
|
@ -3,48 +3,48 @@
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import * as AvatarPrimitive from "@radix-ui/react-avatar"
|
import * as AvatarPrimitive from "@radix-ui/react-avatar"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import {cn} from "@/lib/utils"
|
||||||
|
|
||||||
const Avatar = React.forwardRef<
|
const Avatar = React.forwardRef<
|
||||||
React.ElementRef<typeof AvatarPrimitive.Root>,
|
React.ElementRef<typeof AvatarPrimitive.Root>,
|
||||||
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
|
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
|
||||||
>(({ className, ...props }, ref) => (
|
>(({className, ...props}, ref) => (
|
||||||
<AvatarPrimitive.Root
|
<AvatarPrimitive.Root
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
|
"relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
Avatar.displayName = AvatarPrimitive.Root.displayName
|
Avatar.displayName = AvatarPrimitive.Root.displayName
|
||||||
|
|
||||||
const AvatarImage = React.forwardRef<
|
const AvatarImage = React.forwardRef<
|
||||||
React.ElementRef<typeof AvatarPrimitive.Image>,
|
React.ElementRef<typeof AvatarPrimitive.Image>,
|
||||||
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
|
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
|
||||||
>(({ className, ...props }, ref) => (
|
>(({className, ...props}, ref) => (
|
||||||
<AvatarPrimitive.Image
|
<AvatarPrimitive.Image
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn("aspect-square h-full w-full", className)}
|
className={cn("aspect-square h-full w-full", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
AvatarImage.displayName = AvatarPrimitive.Image.displayName
|
AvatarImage.displayName = AvatarPrimitive.Image.displayName
|
||||||
|
|
||||||
const AvatarFallback = React.forwardRef<
|
const AvatarFallback = React.forwardRef<
|
||||||
React.ElementRef<typeof AvatarPrimitive.Fallback>,
|
React.ElementRef<typeof AvatarPrimitive.Fallback>,
|
||||||
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
|
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
|
||||||
>(({ className, ...props }, ref) => (
|
>(({className, ...props}, ref) => (
|
||||||
<AvatarPrimitive.Fallback
|
<AvatarPrimitive.Fallback
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex h-full w-full items-center justify-center rounded-full bg-muted",
|
"flex h-full w-full items-center justify-center rounded-full bg-muted",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName
|
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName
|
||||||
|
|
||||||
export { Avatar, AvatarImage, AvatarFallback }
|
export {Avatar, AvatarImage, AvatarFallback}
|
||||||
|
|
|
||||||
|
|
@ -1,57 +1,57 @@
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import { Slot } from "@radix-ui/react-slot"
|
import {Slot} from "@radix-ui/react-slot"
|
||||||
import { cva, type VariantProps } from "class-variance-authority"
|
import {cva, type VariantProps} from "class-variance-authority"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import {cn} from "@/lib/utils"
|
||||||
|
|
||||||
const buttonVariants = cva(
|
const buttonVariants = cva(
|
||||||
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
||||||
{
|
{
|
||||||
variants: {
|
variants: {
|
||||||
variant: {
|
variant: {
|
||||||
default:
|
default:
|
||||||
"bg-primary text-primary-foreground shadow hover:bg-primary/90",
|
"bg-primary text-primary-foreground shadow hover:bg-primary/90",
|
||||||
destructive:
|
destructive:
|
||||||
"bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
|
"bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
|
||||||
outline:
|
outline:
|
||||||
"border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
|
"border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
|
||||||
secondary:
|
secondary:
|
||||||
"bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
|
"bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
|
||||||
ghost: "hover:bg-accent hover:text-accent-foreground",
|
ghost: "hover:bg-accent hover:text-accent-foreground",
|
||||||
link: "text-primary underline-offset-4 hover:underline",
|
link: "text-primary underline-offset-4 hover:underline",
|
||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
default: "h-9 px-4 py-2",
|
default: "h-9 px-4 py-2",
|
||||||
sm: "h-8 rounded-md px-3 text-xs",
|
sm: "h-8 rounded-md px-3 text-xs",
|
||||||
lg: "h-10 rounded-md px-8",
|
lg: "h-10 rounded-md px-8",
|
||||||
icon: "h-9 w-9",
|
icon: "h-9 w-9",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
defaultVariants: {
|
defaultVariants: {
|
||||||
variant: "default",
|
variant: "default",
|
||||||
size: "default",
|
size: "default",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
export interface ButtonProps
|
export interface ButtonProps
|
||||||
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||||
VariantProps<typeof buttonVariants> {
|
VariantProps<typeof buttonVariants> {
|
||||||
asChild?: boolean
|
asChild?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||||
({ className, variant, size, asChild = false, ...props }, ref) => {
|
({className, variant, size, asChild = false, ...props}, ref) => {
|
||||||
const Comp = asChild ? Slot : "button"
|
const Comp = asChild ? Slot : "button"
|
||||||
return (
|
return (
|
||||||
<Comp
|
<Comp
|
||||||
className={cn(buttonVariants({ variant, size, className }))}
|
className={cn(buttonVariants({variant, size, className}))}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
Button.displayName = "Button"
|
Button.displayName = "Button"
|
||||||
|
|
||||||
export { Button, buttonVariants }
|
export {Button, buttonVariants}
|
||||||
|
|
|
||||||
|
|
@ -1,76 +1,76 @@
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import {cn} from "@/lib/utils"
|
||||||
|
|
||||||
const Card = React.forwardRef<
|
const Card = React.forwardRef<
|
||||||
HTMLDivElement,
|
HTMLDivElement,
|
||||||
React.HTMLAttributes<HTMLDivElement>
|
React.HTMLAttributes<HTMLDivElement>
|
||||||
>(({ className, ...props }, ref) => (
|
>(({className, ...props}, ref) => (
|
||||||
<div
|
<div
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"rounded-xl border bg-card text-card-foreground shadow",
|
"rounded-xl border bg-card text-card-foreground shadow",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
Card.displayName = "Card"
|
Card.displayName = "Card"
|
||||||
|
|
||||||
const CardHeader = React.forwardRef<
|
const CardHeader = React.forwardRef<
|
||||||
HTMLDivElement,
|
HTMLDivElement,
|
||||||
React.HTMLAttributes<HTMLDivElement>
|
React.HTMLAttributes<HTMLDivElement>
|
||||||
>(({ className, ...props }, ref) => (
|
>(({className, ...props}, ref) => (
|
||||||
<div
|
<div
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn("flex flex-col space-y-1.5 p-6", className)}
|
className={cn("flex flex-col space-y-1.5 p-6", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
CardHeader.displayName = "CardHeader"
|
CardHeader.displayName = "CardHeader"
|
||||||
|
|
||||||
const CardTitle = React.forwardRef<
|
const CardTitle = React.forwardRef<
|
||||||
HTMLDivElement,
|
HTMLDivElement,
|
||||||
React.HTMLAttributes<HTMLDivElement>
|
React.HTMLAttributes<HTMLDivElement>
|
||||||
>(({ className, ...props }, ref) => (
|
>(({className, ...props}, ref) => (
|
||||||
<div
|
<div
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn("font-semibold leading-none tracking-tight", className)}
|
className={cn("font-semibold leading-none tracking-tight", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
CardTitle.displayName = "CardTitle"
|
CardTitle.displayName = "CardTitle"
|
||||||
|
|
||||||
const CardDescription = React.forwardRef<
|
const CardDescription = React.forwardRef<
|
||||||
HTMLDivElement,
|
HTMLDivElement,
|
||||||
React.HTMLAttributes<HTMLDivElement>
|
React.HTMLAttributes<HTMLDivElement>
|
||||||
>(({ className, ...props }, ref) => (
|
>(({className, ...props}, ref) => (
|
||||||
<div
|
<div
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn("text-sm text-muted-foreground", className)}
|
className={cn("text-sm text-muted-foreground", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
CardDescription.displayName = "CardDescription"
|
CardDescription.displayName = "CardDescription"
|
||||||
|
|
||||||
const CardContent = React.forwardRef<
|
const CardContent = React.forwardRef<
|
||||||
HTMLDivElement,
|
HTMLDivElement,
|
||||||
React.HTMLAttributes<HTMLDivElement>
|
React.HTMLAttributes<HTMLDivElement>
|
||||||
>(({ className, ...props }, ref) => (
|
>(({className, ...props}, ref) => (
|
||||||
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
|
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
|
||||||
))
|
))
|
||||||
CardContent.displayName = "CardContent"
|
CardContent.displayName = "CardContent"
|
||||||
|
|
||||||
const CardFooter = React.forwardRef<
|
const CardFooter = React.forwardRef<
|
||||||
HTMLDivElement,
|
HTMLDivElement,
|
||||||
React.HTMLAttributes<HTMLDivElement>
|
React.HTMLAttributes<HTMLDivElement>
|
||||||
>(({ className, ...props }, ref) => (
|
>(({className, ...props}, ref) => (
|
||||||
<div
|
<div
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn("flex items-center p-6 pt-0", className)}
|
className={cn("flex items-center p-6 pt-0", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
CardFooter.displayName = "CardFooter"
|
CardFooter.displayName = "CardFooter"
|
||||||
|
|
||||||
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
|
export {Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent}
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,22 @@
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import {cn} from "@/lib/utils"
|
||||||
|
|
||||||
const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>(
|
const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>(
|
||||||
({ className, type, ...props }, ref) => {
|
({className, type, ...props}, ref) => {
|
||||||
return (
|
return (
|
||||||
<input
|
<input
|
||||||
type={type}
|
type={type}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
Input.displayName = "Input"
|
Input.displayName = "Input"
|
||||||
|
|
||||||
export { Input }
|
export {Input}
|
||||||
|
|
|
||||||
|
|
@ -2,25 +2,25 @@
|
||||||
|
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import * as LabelPrimitive from "@radix-ui/react-label"
|
import * as LabelPrimitive from "@radix-ui/react-label"
|
||||||
import { cva, type VariantProps } from "class-variance-authority"
|
import {cva, type VariantProps} from "class-variance-authority"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import {cn} from "@/lib/utils"
|
||||||
|
|
||||||
const labelVariants = cva(
|
const labelVariants = cva(
|
||||||
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
)
|
)
|
||||||
|
|
||||||
const Label = React.forwardRef<
|
const Label = React.forwardRef<
|
||||||
React.ElementRef<typeof LabelPrimitive.Root>,
|
React.ElementRef<typeof LabelPrimitive.Root>,
|
||||||
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
|
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
|
||||||
VariantProps<typeof labelVariants>
|
VariantProps<typeof labelVariants>
|
||||||
>(({ className, ...props }, ref) => (
|
>(({className, ...props}, ref) => (
|
||||||
<LabelPrimitive.Root
|
<LabelPrimitive.Root
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(labelVariants(), className)}
|
className={cn(labelVariants(), className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
Label.displayName = LabelPrimitive.Root.displayName
|
Label.displayName = LabelPrimitive.Root.displayName
|
||||||
|
|
||||||
export { Label }
|
export {Label}
|
||||||
|
|
|
||||||
|
|
@ -3,46 +3,46 @@
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area"
|
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import {cn} from "@/lib/utils"
|
||||||
|
|
||||||
const ScrollArea = React.forwardRef<
|
const ScrollArea = React.forwardRef<
|
||||||
React.ElementRef<typeof ScrollAreaPrimitive.Root>,
|
React.ElementRef<typeof ScrollAreaPrimitive.Root>,
|
||||||
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>
|
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>
|
||||||
>(({ className, children, ...props }, ref) => (
|
>(({className, children, ...props}, ref) => (
|
||||||
<ScrollAreaPrimitive.Root
|
<ScrollAreaPrimitive.Root
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn("relative overflow-hidden", className)}
|
className={cn("relative overflow-hidden", className)}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit]">
|
<ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit]">
|
||||||
{children}
|
{children}
|
||||||
</ScrollAreaPrimitive.Viewport>
|
</ScrollAreaPrimitive.Viewport>
|
||||||
<ScrollBar />
|
<ScrollBar/>
|
||||||
<ScrollAreaPrimitive.Corner />
|
<ScrollAreaPrimitive.Corner/>
|
||||||
</ScrollAreaPrimitive.Root>
|
</ScrollAreaPrimitive.Root>
|
||||||
))
|
))
|
||||||
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName
|
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName
|
||||||
|
|
||||||
const ScrollBar = React.forwardRef<
|
const ScrollBar = React.forwardRef<
|
||||||
React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,
|
React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,
|
||||||
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>
|
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>
|
||||||
>(({ className, orientation = "vertical", ...props }, ref) => (
|
>(({className, orientation = "vertical", ...props}, ref) => (
|
||||||
<ScrollAreaPrimitive.ScrollAreaScrollbar
|
<ScrollAreaPrimitive.ScrollAreaScrollbar
|
||||||
ref={ref}
|
ref={ref}
|
||||||
orientation={orientation}
|
orientation={orientation}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex touch-none select-none transition-colors",
|
"flex touch-none select-none transition-colors",
|
||||||
orientation === "vertical" &&
|
orientation === "vertical" &&
|
||||||
"h-full w-2.5 border-l border-l-transparent p-[1px]",
|
"h-full w-2.5 border-l border-l-transparent p-[1px]",
|
||||||
orientation === "horizontal" &&
|
orientation === "horizontal" &&
|
||||||
"h-2.5 flex-col border-t border-t-transparent p-[1px]",
|
"h-2.5 flex-col border-t border-t-transparent p-[1px]",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-border" />
|
<ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-border"/>
|
||||||
</ScrollAreaPrimitive.ScrollAreaScrollbar>
|
</ScrollAreaPrimitive.ScrollAreaScrollbar>
|
||||||
))
|
))
|
||||||
ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName
|
ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName
|
||||||
|
|
||||||
export { ScrollArea, ScrollBar }
|
export {ScrollArea, ScrollBar}
|
||||||
|
|
|
||||||
|
|
@ -3,29 +3,29 @@
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import * as SeparatorPrimitive from "@radix-ui/react-separator"
|
import * as SeparatorPrimitive from "@radix-ui/react-separator"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import {cn} from "@/lib/utils"
|
||||||
|
|
||||||
const Separator = React.forwardRef<
|
const Separator = React.forwardRef<
|
||||||
React.ElementRef<typeof SeparatorPrimitive.Root>,
|
React.ElementRef<typeof SeparatorPrimitive.Root>,
|
||||||
React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
|
React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
|
||||||
>(
|
>(
|
||||||
(
|
(
|
||||||
{ className, orientation = "horizontal", decorative = true, ...props },
|
{className, orientation = "horizontal", decorative = true, ...props},
|
||||||
ref
|
ref
|
||||||
) => (
|
) => (
|
||||||
<SeparatorPrimitive.Root
|
<SeparatorPrimitive.Root
|
||||||
ref={ref}
|
ref={ref}
|
||||||
decorative={decorative}
|
decorative={decorative}
|
||||||
orientation={orientation}
|
orientation={orientation}
|
||||||
className={cn(
|
className={cn(
|
||||||
"shrink-0 bg-border",
|
"shrink-0 bg-border",
|
||||||
orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]",
|
orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
Separator.displayName = SeparatorPrimitive.Root.displayName
|
Separator.displayName = SeparatorPrimitive.Root.displayName
|
||||||
|
|
||||||
export { Separator }
|
export {Separator}
|
||||||
|
|
|
||||||
19
src/components/ui/theme-provider.tsx
Normal file
19
src/components/ui/theme-provider.tsx
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
// components/providers/theme-provider.tsx
|
||||||
|
'use client'
|
||||||
|
|
||||||
|
import {ThemeProvider as NextThemesProvider, type ThemeProviderProps} from "next-themes"
|
||||||
|
import {useEffect, useState} from "react"
|
||||||
|
|
||||||
|
export default function ThemeProvider({children, ...props}: ThemeProviderProps) {
|
||||||
|
const [mounted, setMounted] = useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setMounted(true)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
if (!mounted) {
|
||||||
|
return <>{children}</>
|
||||||
|
}
|
||||||
|
|
||||||
|
return <NextThemesProvider {...props}>{children}</NextThemesProvider>
|
||||||
|
}
|
||||||
|
|
@ -2,113 +2,113 @@
|
||||||
|
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import * as ToastPrimitives from "@radix-ui/react-toast"
|
import * as ToastPrimitives from "@radix-ui/react-toast"
|
||||||
import { cva, type VariantProps } from "class-variance-authority"
|
import {cva, type VariantProps} from "class-variance-authority"
|
||||||
import { X } from "lucide-react"
|
import {X} from "lucide-react"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import {cn} from "@/lib/utils"
|
||||||
|
|
||||||
const ToastProvider = ToastPrimitives.Provider
|
const ToastProvider = ToastPrimitives.Provider
|
||||||
|
|
||||||
const ToastViewport = React.forwardRef<
|
const ToastViewport = React.forwardRef<
|
||||||
React.ElementRef<typeof ToastPrimitives.Viewport>,
|
React.ElementRef<typeof ToastPrimitives.Viewport>,
|
||||||
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Viewport>
|
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Viewport>
|
||||||
>(({ className, ...props }, ref) => (
|
>(({className, ...props}, ref) => (
|
||||||
<ToastPrimitives.Viewport
|
<ToastPrimitives.Viewport
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]",
|
"fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
ToastViewport.displayName = ToastPrimitives.Viewport.displayName
|
ToastViewport.displayName = ToastPrimitives.Viewport.displayName
|
||||||
|
|
||||||
const toastVariants = cva(
|
const toastVariants = cva(
|
||||||
"group pointer-events-auto relative flex w-full items-center justify-between space-x-2 overflow-hidden rounded-md border p-4 pr-6 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full",
|
"group pointer-events-auto relative flex w-full items-center justify-between space-x-2 overflow-hidden rounded-md border p-4 pr-6 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full",
|
||||||
{
|
{
|
||||||
variants: {
|
variants: {
|
||||||
variant: {
|
variant: {
|
||||||
default: "border bg-background text-foreground",
|
default: "border bg-background text-foreground",
|
||||||
destructive:
|
destructive:
|
||||||
"destructive group border-destructive bg-destructive text-destructive-foreground",
|
"destructive group border-destructive bg-destructive text-destructive-foreground",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
defaultVariants: {
|
defaultVariants: {
|
||||||
variant: "default",
|
variant: "default",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const Toast = React.forwardRef<
|
const Toast = React.forwardRef<
|
||||||
React.ElementRef<typeof ToastPrimitives.Root>,
|
React.ElementRef<typeof ToastPrimitives.Root>,
|
||||||
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> &
|
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> &
|
||||||
VariantProps<typeof toastVariants>
|
VariantProps<typeof toastVariants>
|
||||||
>(({ className, variant, ...props }, ref) => {
|
>(({className, variant, ...props}, ref) => {
|
||||||
return (
|
return (
|
||||||
<ToastPrimitives.Root
|
<ToastPrimitives.Root
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(toastVariants({ variant }), className)}
|
className={cn(toastVariants({variant}), className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
Toast.displayName = ToastPrimitives.Root.displayName
|
Toast.displayName = ToastPrimitives.Root.displayName
|
||||||
|
|
||||||
const ToastAction = React.forwardRef<
|
const ToastAction = React.forwardRef<
|
||||||
React.ElementRef<typeof ToastPrimitives.Action>,
|
React.ElementRef<typeof ToastPrimitives.Action>,
|
||||||
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Action>
|
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Action>
|
||||||
>(({ className, ...props }, ref) => (
|
>(({className, ...props}, ref) => (
|
||||||
<ToastPrimitives.Action
|
<ToastPrimitives.Action
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium transition-colors hover:bg-secondary focus:outline-none focus:ring-1 focus:ring-ring disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive",
|
"inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium transition-colors hover:bg-secondary focus:outline-none focus:ring-1 focus:ring-ring disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
ToastAction.displayName = ToastPrimitives.Action.displayName
|
ToastAction.displayName = ToastPrimitives.Action.displayName
|
||||||
|
|
||||||
const ToastClose = React.forwardRef<
|
const ToastClose = React.forwardRef<
|
||||||
React.ElementRef<typeof ToastPrimitives.Close>,
|
React.ElementRef<typeof ToastPrimitives.Close>,
|
||||||
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Close>
|
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Close>
|
||||||
>(({ className, ...props }, ref) => (
|
>(({className, ...props}, ref) => (
|
||||||
<ToastPrimitives.Close
|
<ToastPrimitives.Close
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"absolute right-1 top-1 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-1 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600",
|
"absolute right-1 top-1 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-1 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
toast-close=""
|
toast-close=""
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<X className="h-4 w-4" />
|
<X className="h-4 w-4"/>
|
||||||
</ToastPrimitives.Close>
|
</ToastPrimitives.Close>
|
||||||
))
|
))
|
||||||
ToastClose.displayName = ToastPrimitives.Close.displayName
|
ToastClose.displayName = ToastPrimitives.Close.displayName
|
||||||
|
|
||||||
const ToastTitle = React.forwardRef<
|
const ToastTitle = React.forwardRef<
|
||||||
React.ElementRef<typeof ToastPrimitives.Title>,
|
React.ElementRef<typeof ToastPrimitives.Title>,
|
||||||
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title>
|
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title>
|
||||||
>(({ className, ...props }, ref) => (
|
>(({className, ...props}, ref) => (
|
||||||
<ToastPrimitives.Title
|
<ToastPrimitives.Title
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn("text-sm font-semibold [&+div]:text-xs", className)}
|
className={cn("text-sm font-semibold [&+div]:text-xs", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
ToastTitle.displayName = ToastPrimitives.Title.displayName
|
ToastTitle.displayName = ToastPrimitives.Title.displayName
|
||||||
|
|
||||||
const ToastDescription = React.forwardRef<
|
const ToastDescription = React.forwardRef<
|
||||||
React.ElementRef<typeof ToastPrimitives.Description>,
|
React.ElementRef<typeof ToastPrimitives.Description>,
|
||||||
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Description>
|
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Description>
|
||||||
>(({ className, ...props }, ref) => (
|
>(({className, ...props}, ref) => (
|
||||||
<ToastPrimitives.Description
|
<ToastPrimitives.Description
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn("text-sm opacity-90", className)}
|
className={cn("text-sm opacity-90", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
ToastDescription.displayName = ToastPrimitives.Description.displayName
|
ToastDescription.displayName = ToastPrimitives.Description.displayName
|
||||||
|
|
||||||
|
|
@ -117,13 +117,13 @@ type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>
|
||||||
type ToastActionElement = React.ReactElement<typeof ToastAction>
|
type ToastActionElement = React.ReactElement<typeof ToastAction>
|
||||||
|
|
||||||
export {
|
export {
|
||||||
type ToastProps,
|
type ToastProps,
|
||||||
type ToastActionElement,
|
type ToastActionElement,
|
||||||
ToastProvider,
|
ToastProvider,
|
||||||
ToastViewport,
|
ToastViewport,
|
||||||
Toast,
|
Toast,
|
||||||
ToastTitle,
|
ToastTitle,
|
||||||
ToastDescription,
|
ToastDescription,
|
||||||
ToastClose,
|
ToastClose,
|
||||||
ToastAction,
|
ToastAction,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,35 +1,28 @@
|
||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { useToast } from "@/hooks/use-toast"
|
import {useToast} from "@/hooks/use-toast"
|
||||||
import {
|
import {Toast, ToastClose, ToastDescription, ToastProvider, ToastTitle, ToastViewport,} from "@/components/ui/toast"
|
||||||
Toast,
|
|
||||||
ToastClose,
|
|
||||||
ToastDescription,
|
|
||||||
ToastProvider,
|
|
||||||
ToastTitle,
|
|
||||||
ToastViewport,
|
|
||||||
} from "@/components/ui/toast"
|
|
||||||
|
|
||||||
export function Toaster() {
|
export function Toaster() {
|
||||||
const { toasts } = useToast()
|
const {toasts} = useToast()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ToastProvider>
|
<ToastProvider>
|
||||||
{toasts.map(function ({ id, title, description, action, ...props }) {
|
{toasts.map(function ({id, title, description, action, ...props}) {
|
||||||
return (
|
return (
|
||||||
<Toast key={id} {...props}>
|
<Toast key={id} {...props}>
|
||||||
<div className="grid gap-1">
|
<div className="grid gap-1">
|
||||||
{title && <ToastTitle>{title}</ToastTitle>}
|
{title && <ToastTitle>{title}</ToastTitle>}
|
||||||
{description && (
|
{description && (
|
||||||
<ToastDescription>{description}</ToastDescription>
|
<ToastDescription>{description}</ToastDescription>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{action}
|
{action}
|
||||||
<ToastClose />
|
<ToastClose/>
|
||||||
</Toast>
|
</Toast>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
<ToastViewport />
|
<ToastViewport/>
|
||||||
</ToastProvider>
|
</ToastProvider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
32
src/components/ui/tooltip.tsx
Normal file
32
src/components/ui/tooltip.tsx
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import * as TooltipPrimitive from "@radix-ui/react-tooltip"
|
||||||
|
|
||||||
|
import {cn} from "@/lib/utils"
|
||||||
|
|
||||||
|
const TooltipProvider = TooltipPrimitive.Provider
|
||||||
|
|
||||||
|
const Tooltip = TooltipPrimitive.Root
|
||||||
|
|
||||||
|
const TooltipTrigger = TooltipPrimitive.Trigger
|
||||||
|
|
||||||
|
const TooltipContent = React.forwardRef<
|
||||||
|
React.ElementRef<typeof TooltipPrimitive.Content>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
|
||||||
|
>(({className, sideOffset = 4, ...props}, ref) => (
|
||||||
|
<TooltipPrimitive.Portal>
|
||||||
|
<TooltipPrimitive.Content
|
||||||
|
ref={ref}
|
||||||
|
sideOffset={sideOffset}
|
||||||
|
className={cn(
|
||||||
|
"z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
</TooltipPrimitive.Portal>
|
||||||
|
))
|
||||||
|
TooltipContent.displayName = TooltipPrimitive.Content.displayName
|
||||||
|
|
||||||
|
export {Tooltip, TooltipTrigger, TooltipContent, TooltipProvider}
|
||||||
|
|
@ -6,7 +6,7 @@ import {useRouter} from 'next/navigation';
|
||||||
|
|
||||||
interface UserContextType {
|
interface UserContextType {
|
||||||
user: NonNullable<SiPher.User>;
|
user: NonNullable<SiPher.User>;
|
||||||
getUser: () => Promise<NonNullable<SiPher.User>>;
|
getUser: (context: string) => Promise<NonNullable<SiPher.User>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const UserContext = createContext<UserContextType | null>(null);
|
const UserContext = createContext<UserContextType | null>(null);
|
||||||
|
|
@ -21,7 +21,11 @@ export function useUser() {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
user: context.user,
|
user: context.user,
|
||||||
getUser: async (userId?: string) => {
|
getUser: async (context: string, userId?: string) => {
|
||||||
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
|
console.log(`useUser().getUser(): Being called by ${context}`)
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/api/auth/get_user?${
|
const response = await fetch(`/api/auth/get_user?${
|
||||||
userId && `uuid=${
|
userId && `uuid=${
|
||||||
|
|
@ -44,9 +48,12 @@ export function useUser() {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
checkAuth: async () => {
|
checkAuth: async (context: string) => {
|
||||||
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
|
console.log(`useUser().checkAuth(): Being called by ${context}`)
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/auth/get/user');
|
const response = await fetch('/api/auth/get_user');
|
||||||
return response.ok;
|
return response.ok;
|
||||||
} catch {
|
} catch {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -68,7 +75,7 @@ export function UserProvider(
|
||||||
<UserContext.Provider value={{
|
<UserContext.Provider value={{
|
||||||
user: initialUser,
|
user: initialUser,
|
||||||
getUser: async () => {
|
getUser: async () => {
|
||||||
const response = await fetch('/api/auth/get/user');
|
const response = await fetch('/api/auth/get_user');
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error('Failed to get user');
|
throw new Error('Failed to get user');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,192 +3,189 @@
|
||||||
// Inspired by react-hot-toast library
|
// Inspired by react-hot-toast library
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
|
|
||||||
import type {
|
import type {ToastActionElement, ToastProps,} from "@/components/ui/toast"
|
||||||
ToastActionElement,
|
|
||||||
ToastProps,
|
|
||||||
} from "@/components/ui/toast"
|
|
||||||
|
|
||||||
const TOAST_LIMIT = 1
|
const TOAST_LIMIT = 1
|
||||||
const TOAST_REMOVE_DELAY = 1000000
|
const TOAST_REMOVE_DELAY = 1000000
|
||||||
|
|
||||||
type ToasterToast = ToastProps & {
|
type ToasterToast = ToastProps & {
|
||||||
id: string
|
id: string
|
||||||
title?: React.ReactNode
|
title?: React.ReactNode
|
||||||
description?: React.ReactNode
|
description?: React.ReactNode
|
||||||
action?: ToastActionElement
|
action?: ToastActionElement
|
||||||
}
|
}
|
||||||
|
|
||||||
const actionTypes = {
|
const actionTypes = {
|
||||||
ADD_TOAST: "ADD_TOAST",
|
ADD_TOAST: "ADD_TOAST",
|
||||||
UPDATE_TOAST: "UPDATE_TOAST",
|
UPDATE_TOAST: "UPDATE_TOAST",
|
||||||
DISMISS_TOAST: "DISMISS_TOAST",
|
DISMISS_TOAST: "DISMISS_TOAST",
|
||||||
REMOVE_TOAST: "REMOVE_TOAST",
|
REMOVE_TOAST: "REMOVE_TOAST",
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
let count = 0
|
let count = 0
|
||||||
|
|
||||||
function genId() {
|
function genId() {
|
||||||
count = (count + 1) % Number.MAX_SAFE_INTEGER
|
count = (count + 1) % Number.MAX_SAFE_INTEGER
|
||||||
return count.toString()
|
return count.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
type ActionType = typeof actionTypes
|
type ActionType = typeof actionTypes
|
||||||
|
|
||||||
type Action =
|
type Action =
|
||||||
| {
|
| {
|
||||||
type: ActionType["ADD_TOAST"]
|
type: ActionType["ADD_TOAST"]
|
||||||
toast: ToasterToast
|
toast: ToasterToast
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
type: ActionType["UPDATE_TOAST"]
|
type: ActionType["UPDATE_TOAST"]
|
||||||
toast: Partial<ToasterToast>
|
toast: Partial<ToasterToast>
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
type: ActionType["DISMISS_TOAST"]
|
type: ActionType["DISMISS_TOAST"]
|
||||||
toastId?: ToasterToast["id"]
|
toastId?: ToasterToast["id"]
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
type: ActionType["REMOVE_TOAST"]
|
type: ActionType["REMOVE_TOAST"]
|
||||||
toastId?: ToasterToast["id"]
|
toastId?: ToasterToast["id"]
|
||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
toasts: ToasterToast[]
|
toasts: ToasterToast[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>()
|
const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>()
|
||||||
|
|
||||||
const addToRemoveQueue = (toastId: string) => {
|
const addToRemoveQueue = (toastId: string) => {
|
||||||
if (toastTimeouts.has(toastId)) {
|
if (toastTimeouts.has(toastId)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const timeout = setTimeout(() => {
|
const timeout = setTimeout(() => {
|
||||||
toastTimeouts.delete(toastId)
|
toastTimeouts.delete(toastId)
|
||||||
dispatch({
|
dispatch({
|
||||||
type: "REMOVE_TOAST",
|
type: "REMOVE_TOAST",
|
||||||
toastId: toastId,
|
toastId: toastId,
|
||||||
})
|
})
|
||||||
}, TOAST_REMOVE_DELAY)
|
}, TOAST_REMOVE_DELAY)
|
||||||
|
|
||||||
toastTimeouts.set(toastId, timeout)
|
toastTimeouts.set(toastId, timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const reducer = (state: State, action: Action): State => {
|
export const reducer = (state: State, action: Action): State => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case "ADD_TOAST":
|
case "ADD_TOAST":
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
|
toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
|
||||||
}
|
}
|
||||||
|
|
||||||
case "UPDATE_TOAST":
|
case "UPDATE_TOAST":
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
toasts: state.toasts.map((t) =>
|
toasts: state.toasts.map((t) =>
|
||||||
t.id === action.toast.id ? { ...t, ...action.toast } : t
|
t.id === action.toast.id ? {...t, ...action.toast} : t
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
case "DISMISS_TOAST": {
|
case "DISMISS_TOAST": {
|
||||||
const { toastId } = action
|
const {toastId} = action
|
||||||
|
|
||||||
// ! Side effects ! - This could be extracted into a dismissToast() action,
|
// ! Side effects ! - This could be extracted into a dismissToast() action,
|
||||||
// but I'll keep it here for simplicity
|
// but I'll keep it here for simplicity
|
||||||
if (toastId) {
|
if (toastId) {
|
||||||
addToRemoveQueue(toastId)
|
addToRemoveQueue(toastId)
|
||||||
} else {
|
} else {
|
||||||
state.toasts.forEach((toast) => {
|
state.toasts.forEach((toast) => {
|
||||||
addToRemoveQueue(toast.id)
|
addToRemoveQueue(toast.id)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
toasts: state.toasts.map((t) =>
|
toasts: state.toasts.map((t) =>
|
||||||
t.id === toastId || toastId === undefined
|
t.id === toastId || toastId === undefined
|
||||||
? {
|
? {
|
||||||
...t,
|
...t,
|
||||||
open: false,
|
open: false,
|
||||||
}
|
}
|
||||||
: t
|
: t
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "REMOVE_TOAST":
|
case "REMOVE_TOAST":
|
||||||
if (action.toastId === undefined) {
|
if (action.toastId === undefined) {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
toasts: [],
|
toasts: [],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
toasts: state.toasts.filter((t) => t.id !== action.toastId),
|
toasts: state.toasts.filter((t) => t.id !== action.toastId),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const listeners: Array<(state: State) => void> = []
|
const listeners: Array<(state: State) => void> = []
|
||||||
|
|
||||||
let memoryState: State = { toasts: [] }
|
let memoryState: State = {toasts: []}
|
||||||
|
|
||||||
function dispatch(action: Action) {
|
function dispatch(action: Action) {
|
||||||
memoryState = reducer(memoryState, action)
|
memoryState = reducer(memoryState, action)
|
||||||
listeners.forEach((listener) => {
|
listeners.forEach((listener) => {
|
||||||
listener(memoryState)
|
listener(memoryState)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
type Toast = Omit<ToasterToast, "id">
|
type Toast = Omit<ToasterToast, "id">
|
||||||
|
|
||||||
function toast({ ...props }: Toast) {
|
function toast({...props}: Toast) {
|
||||||
const id = genId()
|
const id = genId()
|
||||||
|
|
||||||
const update = (props: ToasterToast) =>
|
const update = (props: ToasterToast) =>
|
||||||
dispatch({
|
dispatch({
|
||||||
type: "UPDATE_TOAST",
|
type: "UPDATE_TOAST",
|
||||||
toast: { ...props, id },
|
toast: {...props, id},
|
||||||
})
|
})
|
||||||
const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id })
|
const dismiss = () => dispatch({type: "DISMISS_TOAST", toastId: id})
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: "ADD_TOAST",
|
type: "ADD_TOAST",
|
||||||
toast: {
|
toast: {
|
||||||
...props,
|
...props,
|
||||||
id,
|
id,
|
||||||
open: true,
|
open: true,
|
||||||
onOpenChange: (open) => {
|
onOpenChange: (open) => {
|
||||||
if (!open) dismiss()
|
if (!open) dismiss()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: id,
|
id: id,
|
||||||
dismiss,
|
dismiss,
|
||||||
update,
|
update,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function useToast() {
|
function useToast() {
|
||||||
const [state, setState] = React.useState<State>(memoryState)
|
const [state, setState] = React.useState<State>(memoryState)
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
listeners.push(setState)
|
listeners.push(setState)
|
||||||
return () => {
|
return () => {
|
||||||
const index = listeners.indexOf(setState)
|
const index = listeners.indexOf(setState)
|
||||||
if (index > -1) {
|
if (index > -1) {
|
||||||
listeners.splice(index, 1)
|
listeners.splice(index, 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [state])
|
}, [state])
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
toast,
|
toast,
|
||||||
dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
|
dismiss: (toastId?: string) => dispatch({type: "DISMISS_TOAST", toastId}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { useToast, toast }
|
export {useToast, toast}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
"use server"
|
||||||
// lib/auth/index.ts
|
// lib/auth/index.ts
|
||||||
import {createClient} from '@/lib/supabase/server';
|
import {createClient} from '@/lib/supabase/server';
|
||||||
import {headers} from 'next/headers';
|
import {headers} from 'next/headers';
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { clsx, type ClassValue } from "clsx"
|
import {type ClassValue, clsx} from "clsx"
|
||||||
import { twMerge } from "tailwind-merge"
|
import {twMerge} from "tailwind-merge"
|
||||||
|
|
||||||
export function cn(...inputs: ClassValue[]) {
|
export function cn(...inputs: ClassValue[]) {
|
||||||
return twMerge(clsx(inputs))
|
return twMerge(clsx(inputs))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,14 @@ const isPublicRoute = (path: string) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function middleware(request: NextRequest) {
|
export async function middleware(request: NextRequest) {
|
||||||
|
|
||||||
|
const requestHeaders = new Headers(request.headers);
|
||||||
|
requestHeaders.set('x-current-pathname', request.url)
|
||||||
|
requestHeaders.set('x-next-pathname', request.nextUrl.pathname);
|
||||||
|
|
||||||
let response = NextResponse.next({
|
let response = NextResponse.next({
|
||||||
request: {
|
request: {
|
||||||
headers: request.headers,
|
headers: requestHeaders,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -33,7 +38,9 @@ export async function middleware(request: NextRequest) {
|
||||||
redirectUrl.search = request.nextUrl.search;
|
redirectUrl.search = request.nextUrl.search;
|
||||||
}
|
}
|
||||||
redirectUrl.searchParams.set('redirectTo', request.nextUrl.pathname);
|
redirectUrl.searchParams.set('redirectTo', request.nextUrl.pathname);
|
||||||
return NextResponse.redirect(redirectUrl);
|
const redirect = NextResponse.redirect(redirectUrl);
|
||||||
|
redirect.headers.set('x-current-pathname', path);
|
||||||
|
return redirect;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user && path.startsWith('/auth/') && !path.includes("/auth/complete")) {
|
if (user && path.startsWith('/auth/') && !path.includes("/auth/complete")) {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue