diff --git a/app.vue b/app.vue index a0a0ed6..7af306b 100644 --- a/app.vue +++ b/app.vue @@ -33,6 +33,7 @@ body { font-family: Arial, Helvetica, sans-serif; box-sizing: border-box; color: var(--text-color); + background: var(--optional-chat-background); background-color: var(--chat-background-color); margin: 0; } diff --git a/components/CropPopup.vue b/components/CropPopup.vue new file mode 100644 index 0000000..12c3a0b --- /dev/null +++ b/components/CropPopup.vue @@ -0,0 +1,93 @@ + + + + + diff --git a/components/MemberEntry.vue b/components/MemberEntry.vue index 3eed3c5..c77c429 100644 --- a/components/MemberEntry.vue +++ b/components/MemberEntry.vue @@ -3,7 +3,7 @@ {{ props.member.user.display_name ?? props.member.user.username }} - + @@ -32,9 +32,4 @@ const hidePopup = () => { .member-item { position: relative; /* Set the position to relative for absolute positioning of the popup */ } - -.profile-popup { - position: absolute; /* Use absolute positioning */ - left: -100px; /* Adjust this value to position the popup to the left */ -} diff --git a/components/Message.vue b/components/Message.vue index e8a91b5..dce8ed4 100644 --- a/components/Message.vue +++ b/components/Message.vue @@ -60,7 +60,16 @@ const sanitized = ref(); onMounted(async () => { const parsed = await parse(props.text, { gfm: true }); - sanitized.value = DOMPurify.sanitize(parsed, { ALLOWED_TAGS: ["strong", "em", "br", "blockquote", "code", "ul", "ol", "li", "a", "h1", "h2", "h3", "h4", "h5", "h6"] }); + sanitized.value = DOMPurify.sanitize(parsed, { + ALLOWED_TAGS: [ + "strong", "em", "br", "blockquote", + "code", "ul", "ol", "li", "a", "h1", + "h2", "h3", "h4", "h5", "h6" + ], + ALLOW_DATA_ATTR: false, + ALLOW_SELF_CLOSE_IN_ATTR: false, + ALLOWED_ATTR: [] + }); console.log("adding listeners") await nextTick(); messageElement.value?.addEventListener("mouseenter", (e: Event) => { @@ -99,6 +108,7 @@ function getDayDifference(date1: Date, date2: Date) { align-items: center; column-gap: 1dvw; width: 100%; + overflow-wrap: anywhere; } .message:hover { diff --git a/components/MessageArea.vue b/components/MessageArea.vue index 7612243..18b9ac5 100644 --- a/components/MessageArea.vue +++ b/components/MessageArea.vue @@ -158,10 +158,12 @@ if (accessToken && apiBase) { function sendMessage(e: Event) { e.preventDefault(); - const text = messageInput.value; - console.log("text:", text); - if (text) { - ws.send(text); + const message = { + message: messageInput.value + } + console.log("message:", message); + if (message.message) { + ws.send(JSON.stringify(message)); messageInput.value = ""; console.log("MESSAGE SENT!!!"); } diff --git a/components/Settings/AppSettings/Appearance.vue b/components/Settings/AppSettings/Appearance.vue index 135c0a1..3adfda6 100644 --- a/components/Settings/AppSettings/Appearance.vue +++ b/components/Settings/AppSettings/Appearance.vue @@ -1,14 +1,100 @@ \ No newline at end of file +.theme-preview-container { + margin: .5em; + width: 5em; + height: 5em; +} + +.theme-preview { + width: 5em; + height: 5em; + border-radius: 100%; + border: .1em solid var(--primary-color); + + display: inline-block; + text-align: center; + align-content: center; + cursor: pointer; +} + +.theme-title { + font-size: .8em; + line-height: 5em; /* same height as the parent to centre it vertically */ +} + diff --git a/components/Settings/UserSettings/Account.vue b/components/Settings/UserSettings/Account.vue index a08cffc..b2ac87a 100644 --- a/components/Settings/UserSettings/Account.vue +++ b/components/Settings/UserSettings/Account.vue @@ -78,13 +78,6 @@ async function deleteAccount() { \ No newline at end of file diff --git a/components/Settings/UserSettings/Profile.vue b/components/Settings/UserSettings/Profile.vue index ed7da0a..04d1e30 100644 --- a/components/Settings/UserSettings/Profile.vue +++ b/components/Settings/UserSettings/Profile.vue @@ -20,15 +20,25 @@ - + + + \ No newline at end of file diff --git a/components/UserPopup.vue b/components/UserPopup.vue index 3a65cf0..a3a15cb 100644 --- a/components/UserPopup.vue +++ b/components/UserPopup.vue @@ -59,12 +59,14 @@ const props = defineProps<{ width: 96px; height: 96px; border: 5px solid #4b3018; + background-color: var(--secondary-color); border-radius: 100%; position: absolute; left: 16px; top: 16px; } + #display-name { margin-top: 60px; margin-bottom: 0; diff --git a/composables/auth.ts b/composables/auth.ts index e520c9c..ebe27de 100644 --- a/composables/auth.ts +++ b/composables/auth.ts @@ -38,13 +38,19 @@ export const useAuth = () => { //await fetchUser(); } - async function logout(password: string) { - console.log("password:", password); + async function logout() { console.log("access:", accessToken.value); - const hashedPass = await hashPassword(password); - console.log("hashed"); - const res = await fetchWithApi("/auth/revoke", { + await fetchWithApi("/auth/logout", { method: "GET", credentials: "include" }); + clearAuth(); + + return await navigateTo("/login"); + } + + async function revoke(password: string) { + const hashedPass = await hashPassword(password); + + await fetchWithApi("/auth/revoke", { method: "POST", body: { @@ -55,10 +61,6 @@ export const useAuth = () => { clearAuth(); } - async function revoke() { - clearAuth(); - } - async function refresh() { console.log("refreshing"); const res = await fetchWithApi("/auth/refresh", { diff --git a/layouts/client.vue b/layouts/client.vue index 4a6ac73..a1ee86d 100644 --- a/layouts/client.vue +++ b/layouts/client.vue @@ -40,7 +40,6 @@ const guilds: GuildResponse[] | undefined = await fetchWithApi("/me/guilds"); grid-template-columns: 1fr 4fr 18fr 4fr; grid-template-rows: 4dvh auto; text-align: center; - } .hidden { @@ -58,6 +57,7 @@ const guilds: GuildResponse[] | undefined = await fetchWithApi("/me/guilds"); display: flex; justify-content: space-evenly; align-items: center; + background: var(--optional-topbar-background); background-color: var(--topbar-background-color); padding-left: 5dvw; padding-right: 5dvw; @@ -98,6 +98,7 @@ const guilds: GuildResponse[] | undefined = await fetchWithApi("/me/guilds"); padding-left: .5dvw; padding-right: .5dvw; border-right: 1px solid var(--padding-color); + background: var(--optional-sidebar-background); background-color: var(--sidebar-background-color); padding-top: 1.5dvh; } diff --git a/nuxt.config.ts b/nuxt.config.ts index 63d9372..46890d1 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -27,7 +27,12 @@ export default defineNuxtConfig({ runtimeConfig: { public: { apiVersion: 1, - messageGroupingMaxDifference: 300000 + messageGroupingMaxDifference: 300000, + buildTimeString: new Date().toISOString(), + gitHash: process.env.GIT_SHORT_REV || "N/A", + defaultThemes: [ + "light", "ash", "dark", "rainbow-capitalism" + ] } }, /* nitro: { diff --git a/package.json b/package.json index b67a598..5d7d19e 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "@nuxt/icon": "1.13.0", "@nuxt/image": "1.10.0", "@pinia/nuxt": "0.11.0", + "cropperjs": "^2.0.0", "dompurify": "^3.2.6", "marked": "^15.0.12", "nuxt": "^3.17.0", diff --git a/pages/servers/[serverId]/channels/[channelId].vue b/pages/servers/[serverId]/channels/[channelId].vue index 699b9a3..6bdcc7c 100644 --- a/pages/servers/[serverId]/channels/[channelId].vue +++ b/pages/servers/[serverId]/channels/[channelId].vue @@ -86,6 +86,7 @@ function handleMemberClick(member: GuildMemberResponse) { padding-left: 1dvw; padding-right: 1dvw; border-right: 1px solid var(--padding-color); + background: var(--optional-channel-list-background); background-color: var(--sidebar-background-color); } @@ -100,6 +101,7 @@ function handleMemberClick(member: GuildMemberResponse) { display: flex; flex-direction: column; overflow-y: scroll; + max-height: 92dvh; padding-left: 1dvw; padding-right: 1dvw; margin-top: 1dvh; diff --git a/pages/settings.vue b/pages/settings.vue index 991d046..1380283 100644 --- a/pages/settings.vue +++ b/pages/settings.vue @@ -2,8 +2,8 @@
@@ -25,9 +40,8 @@ \ 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/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.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/default-themes.json b/public/themes/default-themes.json deleted file mode 100644 index fb9478d..0000000 --- a/public/themes/default-themes.json +++ /dev/null @@ -1,4 +0,0 @@ -[ - "dark.css", - "light.css" -] \ 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/public/themes/rainbow-capitalism.css b/public/themes/rainbow-capitalism.css new file mode 100644 index 0000000..8aeffc8 --- /dev/null +++ b/public/themes/rainbow-capitalism.css @@ -0,0 +1,24 @@ +:root { + --text-color: #161518; + --secondary-text-color: #2b2930; + + --chat-background-color: #80808000; + --chat-highlighted-background-color: #ffffff20; + --sidebar-background-color: #80808000; + --sidebar-highlighted-background-color: #ffffff20; + --topbar-background-color: #80808000; + + --padding-color: #80808000; + + --primary-color: #21b1ff80; + --primary-highlighted-color: #18a0df80; + --secondary-color: #ffd80080; + --secondary-highlighted-color: #dfb80080; + --accent-color: #ff218c80; + --accent-highlighted-color: #df1b6f80; + + --optional-chat-background: linear-gradient(45deg, #ed222480, #ed222480, #ed222480, #ed222480, #ed222480, #ed222480, #f35b2280, #f9962180, #f5c11e80, #f1eb1b80, #f1eb1b80, #f1eb1b80, #63c72080, #0c9b4980, #21878d80, #3954a580, #61379b80, #93288e80); + --optional-topbar-background: linear-gradient(-12.5deg, cyan, pink, white, pink, cyan); + --optional-sidebar-background: linear-gradient(90deg, #55cdfcd0, #f7a8b8d0, #ffffffd0, #f7a8b8d0, #55cdfcd0); + --optional-channel-list-background: linear-gradient(82deg, #d52c00b0, #e29688b0, #ffffffb0, #d27fa4b0, #a20062b0); +} \ No newline at end of file diff --git a/public/themes/rainbow-capitalism.json b/public/themes/rainbow-capitalism.json new file mode 100644 index 0000000..e110ea2 --- /dev/null +++ b/public/themes/rainbow-capitalism.json @@ -0,0 +1,6 @@ +{ + "displayName": "Woke", + "previewGradient": "45deg, #ed2224, #ed2224, #f35b22, #f99621, #f5c11e, #f1eb1b 27%, #f1eb1b, #f1eb1b 33%, #63c720, #0c9b49, #21878d, #3954a5, #61379b, #93288e, #93288e", + "complementaryColor": "white", + "themeUrl": "rainbow-capitalism.css" +} \ No newline at end of file