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();
|
||||||
|
|
||||||
useEffect(() => {
|
const check = useCallback(async () => {
|
||||||
const check = async () => {
|
const isAuthenticated = await checkAuth("Called on Login page");
|
||||||
const isAuthenticated = await checkAuth();
|
|
||||||
if (isAuthenticated) {
|
if (isAuthenticated) {
|
||||||
router.replace('/');
|
router.replace('/');
|
||||||
} else {
|
} else {
|
||||||
setMounted(true);
|
setMounted(true);
|
||||||
}
|
}
|
||||||
};
|
}, [checkAuth, router, setMounted])
|
||||||
|
|
||||||
check();
|
useEffect(() => {
|
||||||
}, [checkAuth, router]);
|
check().then(() => {
|
||||||
|
console.log("Login page check finished")
|
||||||
|
})
|
||||||
|
}, []);
|
||||||
|
|
||||||
if (!mounted) return null;
|
if (!mounted) {
|
||||||
|
return <div className="min-h-screen flex items-center justify-center">
|
||||||
|
{/* Optional: Add a loading spinner or skeleton here */}
|
||||||
|
</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({
|
try {
|
||||||
title: "E-mail not verified",
|
const parsed = JSON.parse(msg);
|
||||||
description: response.message,
|
let desc = parsed.name;
|
||||||
variant: "destructive",
|
|
||||||
duration: 5000, // Increased duration for better visibility
|
switch (desc) {
|
||||||
action: response.action!
|
case "AuthWeakPasswordError": {
|
||||||
});
|
desc = "Password too weak, please try again.";
|
||||||
setIsSubmitting(false);
|
break;
|
||||||
return;
|
}
|
||||||
|
default: {
|
||||||
|
desc = "An unknown error occurred";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
toast({
|
toast({
|
||||||
title: "Error",
|
title: "Error",
|
||||||
description: response.message,
|
description: desc,
|
||||||
variant: "destructive",
|
variant: "destructive",
|
||||||
duration: 5000, // Increased duration for better visibility
|
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>
|
||||||
)
|
|
||||||
|
<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>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
"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"
|
||||||
|
|
|
||||||
|
|
@ -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,38 +27,48 @@ 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 () => {
|
||||||
|
try {
|
||||||
const req = await fetch("/api/user/get/threads")
|
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, // Increased duration for better visibility
|
duration: 5000,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
} 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) => {
|
||||||
|
|
@ -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/>
|
||||||
|
|
|
||||||
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>
|
||||||
|
}
|
||||||
|
|
@ -1,14 +1,7 @@
|
||||||
"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()
|
||||||
|
|
|
||||||
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,10 +3,7 @@
|
||||||
// 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
|
||||||
|
|
|
||||||
|
|
@ -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,4 +1,4 @@
|
||||||
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[]) {
|
||||||
|
|
|
||||||
|
|
@ -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