-
My Account
+
+
Account
-
-
-
AVATAR
-
-
-
-
DISPLAY NAME
-
-
USERNAME
-
-
PRONOUNS
-
-
ABOUT ME
-
-
-
-
-
-
-
-
-
-
Password (and eventually authenticator)
-
+
E-MAIL
+
+
+
-
Account Deletion
+
PASSWORD
+
+
+
ACCOUNT DELETION
@@ -44,22 +27,13 @@ if (!user) {
alert("could not fetch user info, aborting :(")
}
-let newPfpFile: File;
-
-const saveChanges = async () => {
+async function changeEmail() {
if (!user) return;
-
+
+
const formData = new FormData()
-
- if (newPfpFile) {
- formData.append("avatar", newPfpFile)
- }
-
const bytes = new TextEncoder().encode(JSON.stringify({
- display_name: user.display_name,
- username: user.username,
- pronouns: user.pronouns,
- about: user.about,
+ email: user.email,
}));
formData.append('json', new Blob([bytes], { type: 'application/json' }));
@@ -78,60 +52,24 @@ const saveChanges = async () => {
};
-const removeAvatar = async () => {
- alert("TBD")
- // await fetchWithApi(`/auth/reset-password`);
-}
-
-const changeAvatar = async () => {
- if (!user) return;
-
- const input = document.createElement('input');
- input.type = 'file';
- input.accept = 'image/*';
-
- input.addEventListener("change", (e: Event) => {
- if (input.files?.length && input.files.length > 0) {
- const file = input.files[0];
- if (!file) return;
-
- newPfpFile = file
-
- const reader = new FileReader();
- reader.addEventListener("onload", () => {
- if (reader.result && typeof reader.result === 'string') {
- user.avatar = reader.result;
- }
- });
- reader.readAsDataURL(file);
+async function resetPassword () {
+ await fetchWithApi("/auth/reset-password", {
+ method: 'GET',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ query: {
+ identifier: user?.username
}
- })
-
- input.click()
+ });
}
-const resetPassword = async () => {
- alert("TBD")
- // await fetchWithApi(`/auth/reset-password`);
-}
-
-const deleteAccount = async () => {
+async function deleteAccount() {
alert("TBD")
}
\ No newline at end of file
diff --git a/components/Settings/UserSettings/Profile.vue b/components/Settings/UserSettings/Profile.vue
new file mode 100644
index 0000000..04d1e30
--- /dev/null
+++ b/components/Settings/UserSettings/Profile.vue
@@ -0,0 +1,151 @@
+
+
+
Profile
+
+
+
+
AVATAR
+
+
+
+
DISPLAY NAME
+
+
USERNAME
+
+
PRONOUNS
+
+
ABOUT ME
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/components/UserArea.vue b/components/UserArea.vue
new file mode 100644
index 0000000..d922c3f
--- /dev/null
+++ b/components/UserArea.vue
@@ -0,0 +1,20 @@
+
+
+ HELLO!!
+
+
+
+
+
+
\ No newline at end of file
diff --git a/components/UserPopup.vue b/components/UserPopup.vue
index 092e7d5..a3a15cb 100644
--- a/components/UserPopup.vue
+++ b/components/UserPopup.vue
@@ -1,7 +1,9 @@
@@ -26,48 +30,6 @@ import type { GuildResponse } from '~/types/interfaces';
const loading = useState("loading", () => false);
const guilds: GuildResponse[] | undefined = await fetchWithApi("/me/guilds");
-
-//const servers = await fetchWithApi("/servers") as { uuid: string, name: string, description: string }[];
-//console.log("servers:", servers);
-const members = [
- {
- id: "3287484395",
- displayName: "SauceyRed"
- },
- {
- id: "3287484395",
- displayName: "SauceyRed"
- },
- {
- id: "3287484395",
- displayName: "SauceyRed"
- },
- {
- id: "3287484395",
- displayName: "SauceyRed"
- },
- {
- id: "3287484395",
- displayName: "SauceyRed"
- },
- {
- id: "3287484395",
- displayName: "SauceyRed"
- },
- {
- id: "3287484395",
- displayName: "SauceyRed"
- },
- {
- id: "3287484395",
- displayName: "SauceyRed"
- },
- {
- id: "3287484395",
- displayName: "SauceyRed"
- }
-];
-
\ No newline at end of file
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 07816d4..6a8cf2c 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -20,6 +20,9 @@ importers:
'@pinia/nuxt':
specifier: 0.11.0
version: 0.11.0(magicast@0.3.5)(pinia@3.0.2(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3)))
+ cropperjs:
+ specifier: ^2.0.0
+ version: 2.0.0
dompurify:
specifier: ^3.2.6
version: 3.2.6
@@ -205,6 +208,39 @@ packages:
resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==}
engines: {node: '>=0.1.90'}
+ '@cropper/element-canvas@2.0.0':
+ resolution: {integrity: sha512-GPtGJgSm92crJhhhwUsaMw3rz2KfJWWSz7kRAlufFEV/EHTP5+6r6/Z1BCGRna830i+Avqbm435XLOtA7PVJwA==}
+
+ '@cropper/element-crosshair@2.0.0':
+ resolution: {integrity: sha512-KfPfyrdeFvUC31Ws7ATtcalWWSaMtrC6bMoCipZhqbUOE7wZoL4ecDSL6BUOZxPa74awZUqfzirCDjHvheBfyw==}
+
+ '@cropper/element-grid@2.0.0':
+ resolution: {integrity: sha512-i78SQ0IJTLFveKX6P7svkfMYVdgHrQ8ZmmEw8keFy9n1ZVbK+SK0UHK5FNMRNI/gtVhKJOGEnK/zeyjUdj4Iyw==}
+
+ '@cropper/element-handle@2.0.0':
+ resolution: {integrity: sha512-ZJvW+0MkK9E8xYymGdoruaQn2kwjSHFpNSWinjyq6csuVQiCPxlX5ovAEDldmZ9MWePPtWEi3vLKQOo2Yb0T8g==}
+
+ '@cropper/element-image@2.0.0':
+ resolution: {integrity: sha512-9BxiTS/aHRmrjopaFQb9mQQXmx4ruhYHGkDZMVz24AXpMFjUY6OpqrWse/WjzD9tfhMFvEdu17b3VAekcAgpeg==}
+
+ '@cropper/element-selection@2.0.0':
+ resolution: {integrity: sha512-ensNnbIfJsJ8bhbJTH/RXtk2URFvTOO4TvfRk461n2FPEC588D7rwBmUJxQg74IiTi4y1JbCI+6j+4LyzYBLCQ==}
+
+ '@cropper/element-shade@2.0.0':
+ resolution: {integrity: sha512-jv/2bbNZnhU4W+T4G0c8ADocLIZvQFTXgCf2RFDNhI5UVxurzWBnDdb8Mx8LnVplnkTqO+xUmHZYve0CwgWo+Q==}
+
+ '@cropper/element-viewer@2.0.0':
+ resolution: {integrity: sha512-zY+3VRN5TvpM8twlphYtXw0tzJL2VgzeK7ufhL1BixVqOdRxwP13TprYIhqwGt9EW/SyJZUiaIu396T89kRX8A==}
+
+ '@cropper/element@2.0.0':
+ resolution: {integrity: sha512-lsthn0nQq73GExUE7Mg/ss6Q3RXADGDv055hxoLFwvl/wGHgy6ZkYlfLZ/VmgBHC6jDK5IgPBFnqrPqlXWSGBA==}
+
+ '@cropper/elements@2.0.0':
+ resolution: {integrity: sha512-PQkPo1nUjxLFUQuHYu+6atfHxpX9B41Xribao6wpvmvmNIFML6LQdNqqWYb6LyM7ujsu71CZdBiMT5oetjJVoQ==}
+
+ '@cropper/utils@2.0.0':
+ resolution: {integrity: sha512-cprLYr+7kK3faGgoOsTW9gIn5sefDr2KwOmgyjzIXk+8PLpW8FgFKEg5FoWfRD5zMAmkCBuX6rGKDK3VdUEGrg==}
+
'@dabh/diagnostics@2.0.3':
resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==}
@@ -1900,6 +1936,9 @@ packages:
resolution: {integrity: sha512-onMB0OkDjkXunhdW9htFjEhqrD54+M94i6ackoUkjHKbRnXdyEyKRelp4nJ1kAz32+s27jP1FsebpJCVl0BsvA==}
engines: {node: '>=18.0'}
+ cropperjs@2.0.0:
+ resolution: {integrity: sha512-TO2j0Qre01kPHbow4FuTrbdEB4jTmGRySxW49jyEIqlJZuEBfrvCTT0vC3eRB2WBXudDfKi1Onako6DKWKxeAQ==}
+
cross-spawn@7.0.6:
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
engines: {node: '>= 8'}
@@ -4965,6 +5004,72 @@ snapshots:
'@colors/colors@1.6.0': {}
+ '@cropper/element-canvas@2.0.0':
+ dependencies:
+ '@cropper/element': 2.0.0
+ '@cropper/utils': 2.0.0
+
+ '@cropper/element-crosshair@2.0.0':
+ dependencies:
+ '@cropper/element': 2.0.0
+ '@cropper/utils': 2.0.0
+
+ '@cropper/element-grid@2.0.0':
+ dependencies:
+ '@cropper/element': 2.0.0
+ '@cropper/utils': 2.0.0
+
+ '@cropper/element-handle@2.0.0':
+ dependencies:
+ '@cropper/element': 2.0.0
+ '@cropper/utils': 2.0.0
+
+ '@cropper/element-image@2.0.0':
+ dependencies:
+ '@cropper/element': 2.0.0
+ '@cropper/element-canvas': 2.0.0
+ '@cropper/utils': 2.0.0
+
+ '@cropper/element-selection@2.0.0':
+ dependencies:
+ '@cropper/element': 2.0.0
+ '@cropper/element-canvas': 2.0.0
+ '@cropper/element-image': 2.0.0
+ '@cropper/utils': 2.0.0
+
+ '@cropper/element-shade@2.0.0':
+ dependencies:
+ '@cropper/element': 2.0.0
+ '@cropper/element-canvas': 2.0.0
+ '@cropper/element-selection': 2.0.0
+ '@cropper/utils': 2.0.0
+
+ '@cropper/element-viewer@2.0.0':
+ dependencies:
+ '@cropper/element': 2.0.0
+ '@cropper/element-canvas': 2.0.0
+ '@cropper/element-image': 2.0.0
+ '@cropper/element-selection': 2.0.0
+ '@cropper/utils': 2.0.0
+
+ '@cropper/element@2.0.0':
+ dependencies:
+ '@cropper/utils': 2.0.0
+
+ '@cropper/elements@2.0.0':
+ dependencies:
+ '@cropper/element': 2.0.0
+ '@cropper/element-canvas': 2.0.0
+ '@cropper/element-crosshair': 2.0.0
+ '@cropper/element-grid': 2.0.0
+ '@cropper/element-handle': 2.0.0
+ '@cropper/element-image': 2.0.0
+ '@cropper/element-selection': 2.0.0
+ '@cropper/element-shade': 2.0.0
+ '@cropper/element-viewer': 2.0.0
+
+ '@cropper/utils@2.0.0': {}
+
'@dabh/diagnostics@2.0.3':
dependencies:
colorspace: 1.1.4
@@ -6895,6 +7000,11 @@ snapshots:
croner@9.0.0: {}
+ cropperjs@2.0.0:
+ dependencies:
+ '@cropper/elements': 2.0.0
+ '@cropper/utils': 2.0.0
+
cross-spawn@7.0.6:
dependencies:
path-key: 3.1.1
diff --git a/public/icon.svg b/public/icon.svg
new file mode 100644
index 0000000..2b2d33f
--- /dev/null
+++ b/public/icon.svg
@@ -0,0 +1,121 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/public/themes/ash.css b/public/themes/ash.css
new file mode 100644
index 0000000..b685551
--- /dev/null
+++ b/public/themes/ash.css
@@ -0,0 +1,19 @@
+:root {
+ --text-color: #f0e5e0;
+ --secondary-text-color: #e8e0db;
+
+ --chat-background-color: #2f2e2d;
+ --chat-highlighted-background-color: #3f3b38;
+ --sidebar-background-color: #3e3a37;
+ --sidebar-highlighted-background-color: #46423b;
+ --topbar-background-color: #3a3733;
+
+ --padding-color: #e0e0e0;
+
+ --primary-color: #f07028;
+ --primary-highlighted-color: #f28f4b;
+ --secondary-color: #683820;
+ --secondary-highlighted-color: #885830;
+ --accent-color: #a04b24;
+ --accent-highlighted-color: #b86038;
+}
\ No newline at end of file
diff --git a/public/themes/ash.json b/public/themes/ash.json
new file mode 100644
index 0000000..d5d2a59
--- /dev/null
+++ b/public/themes/ash.json
@@ -0,0 +1,6 @@
+{
+ "displayName": "Ash",
+ "previewGradient": "45deg, #2f2e2d, #46423b",
+ "complementaryColor": "white",
+ "themeUrl": "ash.css"
+}
\ No newline at end of file
diff --git a/public/themes/dark.css b/public/themes/dark.css
new file mode 100644
index 0000000..8245724
--- /dev/null
+++ b/public/themes/dark.css
@@ -0,0 +1,19 @@
+:root {
+ --text-color: #f7eee8;
+ --secondary-text-color: #f0e8e4;
+
+ --chat-background-color: #1f1e1d;
+ --chat-highlighted-background-color: #2f2b28;
+ --sidebar-background-color: #2e2a27;
+ --sidebar-highlighted-background-color: #36322b;
+ --topbar-background-color: #2a2723;
+
+ --padding-color: #484848;
+
+ --primary-color: #f4741f;
+ --primary-highlighted-color: #f68a3f;
+ --secondary-color: #7c4018;
+ --secondary-highlighted-color: #8f5b2c;
+ --accent-color: #b35719;
+ --accent-highlighted-color: #c76a2e;
+}
\ No newline at end of file
diff --git a/public/themes/dark.json b/public/themes/dark.json
new file mode 100644
index 0000000..4731d43
--- /dev/null
+++ b/public/themes/dark.json
@@ -0,0 +1,6 @@
+{
+ "displayName": "Dark",
+ "previewGradient": "45deg, #1f1e1d, #36322b",
+ "complementaryColor": "white",
+ "themeUrl": "dark.css"
+}
\ No newline at end of file
diff --git a/public/themes/light.css b/public/themes/light.css
new file mode 100644
index 0000000..cb51d8c
--- /dev/null
+++ b/public/themes/light.css
@@ -0,0 +1,19 @@
+:root {
+ --text-color: #170f08;
+ --secondary-text-color: #2f2b28;
+
+ --chat-background-color: #f0ebe8;
+ --chat-highlighted-background-color: #e8e4e0;
+ --sidebar-background-color: #dbd8d4;
+ --sidebar-highlighted-background-color: #d4d0ca;
+ --topbar-background-color: #dfdbd6;
+
+ --padding-color: #484848;
+
+ --primary-color: #df5f0b;
+ --primary-highlighted-color: #ef6812;
+ --secondary-color: #e8ac84;
+ --secondary-highlighted-color: #f8b68a;
+ --accent-color: #e68b4e;
+ --accent-highlighted-color: #f69254;
+}
\ No newline at end of file
diff --git a/public/themes/light.json b/public/themes/light.json
new file mode 100644
index 0000000..b95c78b
--- /dev/null
+++ b/public/themes/light.json
@@ -0,0 +1,6 @@
+{
+ "displayName": "Light",
+ "previewGradient": "45deg, #f0ebe8, #d4d0ca",
+ "complementaryColor": "black",
+ "themeUrl": "light.css"
+}
\ No newline at end of file
diff --git a/types/interfaces.ts b/types/interfaces.ts
index b5b1995..610d007 100644
--- a/types/interfaces.ts
+++ b/types/interfaces.ts
@@ -62,7 +62,7 @@ export interface UserResponse {
about: string | null,
email?: string,
email_verified?: boolean
- }
+}
export interface StatsResponse {
accounts: number,
@@ -72,3 +72,14 @@ export interface StatsResponse {
email_verification_required: boolean,
build_number: string
}
+
+export interface ScrollPosition {
+ scrollHeight: number,
+ scrollWidth: number,
+ scrollTop: number,
+ scrollLeft: number
+ offsetHeight: number,
+ offsetWidth: number,
+ offsetTop: number,
+ offsetLeft: number
+}
diff --git a/utils/fetchWithApi.ts b/utils/fetchWithApi.ts
index 79fdae8..4089db8 100644
--- a/utils/fetchWithApi.ts
+++ b/utils/fetchWithApi.ts
@@ -9,8 +9,6 @@ export default async (path: string, options: NitroFetchOptions = {})
path = path.slice(0, path.lastIndexOf("/"));
}
console.log("formatted path:", path);
- const accessToken = useCookie("access_token");
- console.log("access token:", accessToken.value);
const apiBase = useCookie("api_base").value;
const apiVersion = useRuntimeConfig().public.apiVersion;
console.log("heyoooo")
@@ -21,23 +19,24 @@ export default async (path: string, options: NitroFetchOptions = {})
}
console.log("path:", path)
const { revoke, refresh } = useAuth();
- console.log("access token 2:", accessToken.value);
-
+
let headers: HeadersInit = {};
-
- if (accessToken.value) {
- headers = {
- ...options.headers,
- "Authorization": `Bearer ${accessToken.value}`
- };
- } else {
- headers = {
- ...options.headers
- };
- }
-
+
+
let reauthFailed = false;
while (!reauthFailed) {
+ const accessToken = useCookie("access_token");
+ console.log("access token:", accessToken.value);
+ if (accessToken.value) {
+ headers = {
+ ...options.headers,
+ "Authorization": `Bearer ${accessToken.value}`
+ };
+ } else {
+ headers = {
+ ...options.headers
+ };
+ }
try {
console.log("fetching:", URL.parse(apiBase + path));
const res = await $fetch(URL.parse(apiBase + path)!.href, {
@@ -74,9 +73,10 @@ export default async (path: string, options: NitroFetchOptions = {})
console.log("Path is refresh endpoint, throwing error");
throw error;
}
+ } else {
+ console.log("throwing error:", error);
+ throw error;
}
- console.log("throwing error");
- throw error;
}
}
}
diff --git a/utils/getScrollPosition.ts b/utils/getScrollPosition.ts
new file mode 100644
index 0000000..1f4a99f
--- /dev/null
+++ b/utils/getScrollPosition.ts
@@ -0,0 +1,14 @@
+import type { ScrollPosition } from "~/types/interfaces";
+
+export default (element: HTMLElement): ScrollPosition => {
+ return {
+ scrollHeight: element.scrollHeight,
+ scrollWidth: element.scrollWidth,
+ scrollTop: element.scrollTop,
+ scrollLeft: element.scrollLeft,
+ offsetHeight: element.offsetHeight,
+ offsetWidth: element.offsetWidth,
+ offsetTop: element.offsetTop,
+ offsetLeft: element.offsetLeft
+ };
+}
diff --git a/utils/scrollToBottom.ts b/utils/scrollToBottom.ts
index aca99b4..8064bd8 100644
--- a/utils/scrollToBottom.ts
+++ b/utils/scrollToBottom.ts
@@ -1,6 +1,6 @@
-export default (element: Ref) => {
- if (element.value) {
- element.value.scrollTo({ top: element.value.scrollHeight });
+export default (element: HTMLElement) => {
+ if (element) {
+ element.scrollTo({ top: element.scrollHeight });
return;
}
}
diff --git a/utils/setScrollPosition.ts b/utils/setScrollPosition.ts
new file mode 100644
index 0000000..c7ddfd2
--- /dev/null
+++ b/utils/setScrollPosition.ts
@@ -0,0 +1,5 @@
+import type { ScrollPosition } from "~/types/interfaces";
+
+export default (element: HTMLElement, scrollPosition: ScrollPosition) => {
+ return element.scrollTo({ top: scrollPosition.scrollTop, left: scrollPosition.scrollLeft });
+}