diff --git a/app.vue b/app.vue index 8158615..8f0571f 100644 --- a/app.vue +++ b/app.vue @@ -9,14 +9,13 @@ \ No newline at end of file diff --git a/components/UserInterface/ContextMenu.vue b/components/UserInterface/ContextMenu.vue index dac6efc..1767657 100644 --- a/components/UserInterface/ContextMenu.vue +++ b/components/UserInterface/ContextMenu.vue @@ -50,7 +50,7 @@ function runCallback(item: ContextMenuItem) { height: 2rem; width: 100%; color: var(--text-color); - background-color: var(--sidebar-highlighted-background-color); + background-color: var(--popup-background-color); border: none; text-align: left; padding-left: 1rem; @@ -58,7 +58,7 @@ function runCallback(item: ContextMenuItem) { } .context-menu-item:hover { - background-color: rgb(50, 50, 50); + background-color: var(--popup-highlighted-background-color); } .context-menu-item-danger { diff --git a/components/UserInterface/MessageReply.vue b/components/UserInterface/MessageReply.vue index c3efd2b..f3214b5 100644 --- a/components/UserInterface/MessageReply.vue +++ b/components/UserInterface/MessageReply.vue @@ -58,7 +58,7 @@ function scrollToReply(e: MouseEvent) { console.log("scrolling into view"); reply.scrollIntoView({ behavior: "smooth", block: "center" }); reply.style.transition = "background-color .3s"; - reply.style.backgroundColor = "var(--primary-highlighted-color)"; + reply.style.backgroundColor = "var(--chat-featured-message-color)"; setTimeout(() => { reply.style.backgroundColor = ""; }, 1000); diff --git a/components/UserInterface/ResizableSidebar.vue b/components/UserInterface/ResizableSidebar.vue index 27b0730..79e95bd 100644 --- a/components/UserInterface/ResizableSidebar.vue +++ b/components/UserInterface/ResizableSidebar.vue @@ -34,7 +34,7 @@ const storedWidth = ref(); const contextMenu = useState("contextMenu"); const menuItems: ContextMenuItem[] = [ - { name: "Reset", callback: () => { + { name: "Reset", type: "normal", callback: () => { const defaultWidth = props.width ?? props.minWidth; resizableSidebar.value!.style.width = defaultWidth; if (props.localStorageName) { diff --git a/nuxt.config.ts b/nuxt.config.ts index 46890d1..05a40fa 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -30,9 +30,6 @@ export default defineNuxtConfig({ messageGroupingMaxDifference: 300000, buildTimeString: new Date().toISOString(), gitHash: process.env.GIT_SHORT_REV || "N/A", - defaultThemes: [ - "light", "ash", "dark", "rainbow-capitalism" - ] } }, /* nitro: { diff --git a/pages/servers/[serverId]/channels/[channelId].vue b/pages/servers/[serverId]/channels/[channelId].vue index 07ea532..f07dabd 100644 --- a/pages/servers/[serverId]/channels/[channelId].vue +++ b/pages/servers/[serverId]/channels/[channelId].vue @@ -79,8 +79,6 @@ onActivated(async () => { async function setArrayVariables() { const membersRes = await fetchMembers(route.params.serverId as string); members.value = membersRes.objects; - members.value = await fetchMembers(route.params.serverId as string); - console.log("Placeholder count:", totalMemberCount.value); const guildUrl = `guilds/${route.params.serverId}`; channels.value = await fetchWithApi(`${guildUrl}/channels`); console.log("channels:", channels.value); diff --git a/public/themes/ash.css b/public/themes/ash.css deleted file mode 100644 index d47b51e..0000000 --- a/public/themes/ash.css +++ /dev/null @@ -1,29 +0,0 @@ -:root { - --text-color: #f0e5e0; - --secondary-text-color: #e8e0db; - --reply-text-color: #969696; - --danger-text-color: #ff0000; - - --chat-background-color: #2f2e2d; - --chat-highlighted-background-color: #3f3b38; - --sidebar-background-color: #3e3a37; - --sidebar-highlighted-background-color: #46423b; - --topbar-background-color: #3a3733; - --chatbox-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; - - --sidebar-width: 2.5em; - --standard-radius: .5em; - --button-radius: .6em; - --guild-icon-radius: 20%; - --pfp-radius: 50%; - --preferred-font: Arial; -} \ No newline at end of file diff --git a/public/themes/ash.json b/public/themes/ash.json deleted file mode 100644 index d5d2a59..0000000 --- a/public/themes/ash.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "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 deleted file mode 100644 index efc2bfa..0000000 --- a/public/themes/dark.css +++ /dev/null @@ -1,32 +0,0 @@ -:root { - --text-color: #f7eee8; - --secondary-text-color: #f0e8e4; - --reply-text-color: #969696; - --danger-text-color: #ff0000; - - --chat-background-color: #1f1e1d; - --chat-highlighted-background-color: #2f2b28; - --sidebar-background-color: #2e2a27; - --sidebar-highlighted-background-color: #36322b; - --topbar-background-color: #2a2723; - --chatbox-background-color: #1a1713; - - --padding-color: #484848; - - --primary-color: #f4741f; - --primary-highlighted-color: #f68a3f; - --secondary-color: #7c4018; - --secondary-highlighted-color: #8f5b2c; - --accent-color: #b35719; - --accent-highlighted-color: #c76a2e; - - --sidebar-icon-width: 2.5em; - --sidebar-icon-gap: .25em; - --sidebar-margin: .5em; - - --standard-radius: .5em; - --button-radius: .6em; - --guild-icon-radius: 15%; - --pfp-radius: 50%; - --preferred-font: Arial; -} \ No newline at end of file diff --git a/public/themes/dark.json b/public/themes/dark.json deleted file mode 100644 index 4731d43..0000000 --- a/public/themes/dark.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "displayName": "Dark", - "previewGradient": "45deg, #1f1e1d, #36322b", - "complementaryColor": "white", - "themeUrl": "dark.css" -} \ No newline at end of file diff --git a/public/themes/layout/gorb.css b/public/themes/layout/gorb.css new file mode 100644 index 0000000..1cee0b4 --- /dev/null +++ b/public/themes/layout/gorb.css @@ -0,0 +1,18 @@ +/* +displayName = Gorb +previewImageUrl = gorb.jpg +complementaryColor = white +*/ + +:root { + --sidebar-icon-width: 2.5em; + --sidebar-icon-gap: .25em; + --sidebar-margin: .5em; + + --minor-radius: .35em; + --standard-radius: .5em; + --button-radius: .6em; + --guild-icon-radius: 15%; + --pfp-radius: 50%; + --preferred-font: Arial; +} \ No newline at end of file diff --git a/public/themes/layout/gorb.jpg b/public/themes/layout/gorb.jpg new file mode 100644 index 0000000..301d459 Binary files /dev/null and b/public/themes/layout/gorb.jpg differ diff --git a/public/themes/layout/layouts.json b/public/themes/layout/layouts.json new file mode 100644 index 0000000..48c3932 --- /dev/null +++ b/public/themes/layout/layouts.json @@ -0,0 +1,3 @@ +[ + "gorb.css" +] \ No newline at end of file diff --git a/public/themes/light.json b/public/themes/light.json deleted file mode 100644 index b95c78b..0000000 --- a/public/themes/light.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "displayName": "Light", - "previewGradient": "45deg, #f0ebe8, #d4d0ca", - "complementaryColor": "black", - "themeUrl": "light.css" -} \ No newline at end of file diff --git a/public/themes/rainbow-capitalism.json b/public/themes/rainbow-capitalism.json deleted file mode 100644 index e110ea2..0000000 --- a/public/themes/rainbow-capitalism.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "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 diff --git a/public/themes/style/ash.css b/public/themes/style/ash.css new file mode 100644 index 0000000..e33fbf6 --- /dev/null +++ b/public/themes/style/ash.css @@ -0,0 +1,34 @@ +/* +displayName = Ash +previewGradient = 45deg, #2f2e2d, #46423b +complementaryColor = white +*/ + +:root { + --text-color: #f0e5e0; + --secondary-text-color: #e8e0db; + --reply-text-color: #969696; + --danger-text-color: #ff0000; + + --chat-background-color: #383432; + --chat-highlighted-background-color: #4c4a48; + --chat-important-background-color: #ffcf5f38; + --chat-important-highlighted-background-color: #ffa86f4f; + --chat-featured-message-color: #4f3f2f60; + --popup-background-color: #2f2828; + --popup-highlighted-background-color: #382f2f; + + --sidebar-background-color: #322f2d; + --sidebar-highlighted-background-color: #46423b; + --topbar-background-color: #3a3733; + --chatbox-background-color: #3a3733; + + --padding-color: #4f4f4f; + + --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/style/dark.css b/public/themes/style/dark.css new file mode 100644 index 0000000..420e8c9 --- /dev/null +++ b/public/themes/style/dark.css @@ -0,0 +1,34 @@ +/* +displayName = Dark +previewGradient = 45deg, #1f1e1d, #36322b +complementaryColor = white +*/ + +:root { + --text-color: #f7eee8; + --secondary-text-color: #f0e8e4; + --reply-text-color: #969696; + --danger-text-color: #ff0000; + + --chat-background-color: #282624; + --chat-highlighted-background-color: #383430; + --chat-important-background-color: #ffc44f2f; + --chat-important-highlighted-background-color: #ffa45f4a; + --chat-featured-message-color: #4f2f1f58; + --popup-background-color: #2f1f1f; + --popup-highlighted-background-color: #3f2f2f; + + --sidebar-background-color: #1f1e1d; + --sidebar-highlighted-background-color: #2f2b28; + --topbar-background-color: #2a2723; + --chatbox-background-color: #1a1713; + + --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/description.css b/public/themes/style/description.css similarity index 78% rename from public/themes/description.css rename to public/themes/style/description.css index e30025f..972821c 100644 --- a/public/themes/description.css +++ b/public/themes/style/description.css @@ -1,4 +1,11 @@ +/* +displayName = Description +previewGradient = 45deg, #ff8f8f, #8f8fff +complementaryColor = black +*/ + /* this is not a real theme, but rather a template for themes */ + :root { --text-color: #161518; --secondary-text-color: #2b2930; @@ -6,6 +13,12 @@ --chat-background-color: #80808000; --chat-highlighted-background-color: #ffffff20; + --chat-important-background-color: #ffc44f2f; + --chat-important-highlighted-background-color: #ffa45f4a; + --chat-featured-message-color: #4f2f1f58; + --popup-background-color: #2f1f1f; + --popup-highlighted-background-color: #3f2f2f; + --sidebar-background-color: #80808000; --sidebar-highlighted-background-color: #ffffff20; --topbar-background-color: #80808000; @@ -20,12 +33,6 @@ --accent-color: #ff218c80; --accent-highlighted-color: #df1b6f80; - --sidebar-width: 2.5em; - --standard-radius: .5em; - --button-radius: .6em; - --pfp-radius: 50%; - --preferred-font: Arial; - --optional-body-background: ; /* background element for the body */ --optional-chat-background: ; /* background element for the chat box */ --optional-topbar-background: ; /* background element for the topbar */ diff --git a/public/themes/light.css b/public/themes/style/light.css similarity index 55% rename from public/themes/light.css rename to public/themes/style/light.css index 68d6578..cd1f55b 100644 --- a/public/themes/light.css +++ b/public/themes/style/light.css @@ -1,11 +1,23 @@ +/* +displayName = Light +previewGradient = 45deg, #f0ebe8, #d4d0ca +complementaryColor = black +*/ + :root { --text-color: #170f08; --secondary-text-color: #2f2b28; --reply-text-color: #969696; --danger-text-color: #ff0000; - --chat-background-color: #f0ebe8; - --chat-highlighted-background-color: #e8e4e0; + --chat-background-color: #f0edeb; + --chat-highlighted-background-color: #aba8a4; + --chat-important-background-color: #df5f0b26; + --chat-important-hightlighted-background-color: #df5f0b3d; + --chat-featured-message-color: #e8ac841f; + --popup-background-color: #b8b4b0; + --popup-highlighted-background-color: #a6a4a2; + --sidebar-background-color: #dbd8d4; --sidebar-highlighted-background-color: #d4d0ca; --topbar-background-color: #dfdbd6; @@ -19,10 +31,4 @@ --secondary-highlighted-color: #f8b68a; --accent-color: #e68b4e; --accent-highlighted-color: #f69254; - - --sidebar-width: 2.5em; - --standard-radius: .5em; - --button-radius: .6em; - --pfp-radius: 50%; - --preferred-font: Arial; } \ No newline at end of file diff --git a/public/themes/rainbow-capitalism.css b/public/themes/style/rainbow-capitalism.css similarity index 71% rename from public/themes/rainbow-capitalism.css rename to public/themes/style/rainbow-capitalism.css index a5ec390..8ca3ed7 100644 --- a/public/themes/rainbow-capitalism.css +++ b/public/themes/style/rainbow-capitalism.css @@ -1,11 +1,23 @@ +/* +displayName = Woke +previewGradient = 45deg, #ed2224, #ed2224, #f35b22, #f99621, #f5c11e, #f1eb1b 27%, #f1eb1b, #f1eb1b 33%, #63c720, #0c9b49, #21878d, #3954a5, #61379b, #93288e, #93288e +complementaryColor = white +*/ + :root { - --text-color: #161518; - --secondary-text-color: #2b2930; + --text-color: #000000; + --secondary-text-color: #1f1f1f; --reply-text-color: #969696; --danger-text-color: #ff0000; - --chat-background-color: #80808000; + --chat-background-color: #b0b0b040; --chat-highlighted-background-color: #ffffff20; + --chat-important-background-color: #ff4f4f80; + --chat-important-highlighted-background-color: #ff6f6fa0; + --chat-featured-message-color: #4f8f4f80; + --popup-background-color: #80808080; + --popup-highlighted-background-color: #9f9f9f9f; + --sidebar-background-color: #80808000; --sidebar-highlighted-background-color: #ffffff20; --topbar-background-color: #80808000; @@ -20,12 +32,6 @@ --accent-color: #ff218c80; --accent-highlighted-color: #df1b6f80; - --sidebar-width: 2.5em; - --standard-radius: .5em; - --button-radius: .6em; - --pfp-radius: 50%; - --preferred-font: Arial; - /* --optional-body-background: background */ --optional-body-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); diff --git a/public/themes/style/styles.json b/public/themes/style/styles.json new file mode 100644 index 0000000..955102e --- /dev/null +++ b/public/themes/style/styles.json @@ -0,0 +1,6 @@ +[ + "ash.css", + "dark.css", + "light.css", + "rainbow-capitalism.css" +] \ No newline at end of file diff --git a/types/enums.ts b/types/enums.ts new file mode 100644 index 0000000..ac2daba --- /dev/null +++ b/types/enums.ts @@ -0,0 +1,11 @@ +export const enum Permission { + SendMessage = 1, + ManageChannel = 2, + ManageRole = 4, + CreateInvite = 8, + ManageInvite = 16, + ManageGuild = 32, + ManageMember = 64, + BanMember = 128, + KickMember = 256 +} \ No newline at end of file diff --git a/types/interfaces.ts b/types/interfaces.ts index 276c4dc..d175d76 100644 --- a/types/interfaces.ts +++ b/types/interfaces.ts @@ -19,16 +19,17 @@ export interface GuildResponse { description: string | null, icon: string | null, owner_uuid: string, - roles: [], + roles: RoleResponse[], member_count: number } export interface GuildMemberResponse { uuid: string, nickname: string, - user_uuid: string, guild_uuid: string, - user: UserResponse + is_owner: boolean, + user: UserResponse, + roles: RoleResponse[] } export interface GuildMembersResponse { diff --git a/types/props.ts b/types/props.ts index f25ae8d..5796d83 100644 --- a/types/props.ts +++ b/types/props.ts @@ -1,21 +1,9 @@ -import type { GuildMemberResponse, MessageResponse, UserResponse } from "./interfaces"; +import type { MessageResponse, UserResponse } from "./interfaces"; export interface MessageProps { - class?: string, - img?: string | null, - author: GuildMemberResponse - text: string, - timestamp: number, - format: "12" | "24", - type: "normal" | "grouped", - marginBottom: boolean, - authorColor: string, - last: boolean, - messageId: string, - replyingTo?: boolean, - editing?: boolean, - me: UserResponse message: MessageResponse, - replyMessage?: MessageResponse + replyMessage?: MessageResponse, + type: "normal" | "grouped", + editing?: boolean, isMentioned?: boolean, } \ No newline at end of file diff --git a/types/settings.ts b/types/settings.ts index 28e3bfc..8d24904 100644 --- a/types/settings.ts +++ b/types/settings.ts @@ -1,6 +1,7 @@ export interface ClientSettings { - selectedThemeId?: string, // the ID of the theme, not the URL, for example "dark" timeFormat?: TimeFormat + selectedThemeStyle?: string // URL + selectedThemeLayout?: string // URL } export interface TimeFormat { diff --git a/utils/hasPermission.ts b/utils/hasPermission.ts new file mode 100644 index 0000000..750716e --- /dev/null +++ b/utils/hasPermission.ts @@ -0,0 +1,11 @@ +import type { Permission } from "~/types/enums"; +import type { GuildMemberResponse } from "~/types/interfaces"; + +export default (member: GuildMemberResponse, permission: Permission) => { + for (const role of member.roles) { + if (role.permissions & permission) { + return true; + } + } + return false; +} diff --git a/utils/loadPreferredTheme.ts b/utils/loadPreferredTheme.ts deleted file mode 100644 index 6eb8099..0000000 --- a/utils/loadPreferredTheme.ts +++ /dev/null @@ -1,28 +0,0 @@ -let themeLinkElement: HTMLLinkElement | null; - -export default function loadPreferredTheme() { - const currentTheme = settingsLoad().selectedThemeId ?? "dark" - - if (themeLinkElement) { - themeLinkElement.href = getThemeUrl(currentTheme); - } else { - // create the theme link if one doesn't already exist - useHead({ - link: [{ - id: "main-theme", - rel: "stylesheet", - href: getThemeUrl(currentTheme) - }] - }) - - themeLinkElement = document.getElementById('main-theme') as HTMLLinkElement; - } -} - -function getThemeUrl(id: string): string { - const runtimeConfig = useRuntimeConfig() - const baseURL = runtimeConfig.app.baseURL; - - // this should preferrably use version hash, but that's not implemented yet - return `${baseURL}themes/${id}.css?v=${runtimeConfig.public.buildTimeString}` -} \ No newline at end of file diff --git a/utils/loadPreferredThemes.ts b/utils/loadPreferredThemes.ts new file mode 100644 index 0000000..00a4d1b --- /dev/null +++ b/utils/loadPreferredThemes.ts @@ -0,0 +1,52 @@ +let styleLinkElement: HTMLLinkElement | null; +let layoutLinkElement: HTMLLinkElement | null; + + +export default () => { + const runtimeConfig = useRuntimeConfig() + const baseURL = runtimeConfig.app.baseURL; + + let currentStyle = settingsLoad().selectedThemeStyle ?? undefined + let currentLayout = settingsLoad().selectedThemeLayout ?? `${baseURL}themes/layout/gorb.css` + + if (!currentStyle) { + if (prefersLight()) { + currentStyle = `${baseURL}themes/style/light.css` + } else { + currentStyle = `${baseURL}themes/style/dark.css` + } + } + + if (styleLinkElement) { + styleLinkElement.href = currentStyle; + } else { + createStyleHead("style-theme", currentStyle) + styleLinkElement = document.getElementById('style-theme') as HTMLLinkElement; + } + + if (layoutLinkElement) { + layoutLinkElement.href = currentLayout; + } else { + createStyleHead("style-layout", currentLayout) + layoutLinkElement = document.getElementById('style-layout') as HTMLLinkElement; + } +} + +// create a new theme link if one doesn't already exist +function createStyleHead(id: string, themeUrl: string) { + useHead({ + link: [{ + id: id, + rel: "stylesheet", + href: themeUrl + }] + }) +} + +function prefersLight(): boolean { + if (window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches) { + return true + } + + return false +} \ No newline at end of file diff --git a/utils/replyToMessage.ts b/utils/replyToMessage.ts index b2b31a8..1ff4df7 100644 --- a/utils/replyToMessage.ts +++ b/utils/replyToMessage.ts @@ -2,12 +2,14 @@ import { render } from "vue"; import MessageReply from "~/components/UserInterface/MessageReply.vue"; import type { MessageProps } from "~/types/props"; +const { getDisplayName } = useProfile() + export default (element: HTMLDivElement, props: MessageProps) => { console.log("element:", element); const messageBox = document.getElementById("message-box") as HTMLDivElement; if (messageBox) { const div = document.createElement("div"); - const messageReply = h(MessageReply, { author: getDisplayName(props.author), text: props.text || "", id: props.message.uuid, replyId: props.replyMessage?.uuid || element.dataset.messageId!, maxWidth: "full" }); + const messageReply = h(MessageReply, { author: getDisplayName(props.message.member), text: props.message.message || "", id: props.message.uuid, replyId: props.replyMessage?.uuid || element.dataset.messageId!, maxWidth: "full" }); messageBox.prepend(div); render(messageReply, div); const message = document.querySelector(`.message[data-message-id='${props.message.uuid}']`);