From 51d1cad6cddd94dd22f003bf63ecf87fb53ded3b Mon Sep 17 00:00:00 2001 From: ZickZenni Date: Thu, 29 Aug 2024 16:03:35 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Added=20guild=20channel=20list=20an?= =?UTF-8?q?d=20channel=20messages?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/renderer/App.tsx | 15 ++- src/renderer/pages/Guild/Channel/Channel.css | 58 +++++++++ src/renderer/pages/Guild/Channel/index.tsx | 83 +++++++++++++ src/renderer/pages/Guild/Guild.css | 51 ++++++++ src/renderer/pages/Guild/index.tsx | 124 ++++++++++++++++++- 5 files changed, 327 insertions(+), 4 deletions(-) create mode 100644 src/renderer/pages/Guild/Channel/Channel.css create mode 100644 src/renderer/pages/Guild/Channel/index.tsx create mode 100644 src/renderer/pages/Guild/Guild.css diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index d247623..b5f0e70 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -1,11 +1,18 @@ import { MemoryRouter as Router, Routes, Route } from 'react-router-dom'; -import Loading from './components/Loading'; + +// Styles import './styles/global.css'; import './styles/vars.css'; + +// Components import Titlebar from './components/Titlebar'; -import HomePage from './pages/Home'; import Serverbar from './components/Serverbar'; +import Loading from './components/Loading'; + +// Pages +import HomePage from './pages/Home'; import GuildPage from './pages/Guild'; +import ChannelPage from './pages/Guild/Channel'; export default function App() { return ( @@ -16,7 +23,9 @@ export default function App() {
} /> - } /> + }> + } /> +
diff --git a/src/renderer/pages/Guild/Channel/Channel.css b/src/renderer/pages/Guild/Channel/Channel.css new file mode 100644 index 0000000..6a7743c --- /dev/null +++ b/src/renderer/pages/Guild/Channel/Channel.css @@ -0,0 +1,58 @@ +.channel_page__container { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; +} + +.channel_page__chat { + width: 100%; + height: calc(100% - 130px); +} + +.channel_page__messages { + display: flex; + flex-direction: column-reverse; + overflow-y: auto; + width: 100%; + height: calc(100% - 40px); + gap: 8px; + padding: 20px; +} + +.channel_page__header { + width: calc(100% - 40px); + height: 90px; + background: var(--background); + padding: 20px; + flex-shrink: 0; +} + +.channel_page__message { + display: flex; + gap: 7px; + width: 100%; + min-height: 64px; +} + +.channel_page__message_author { + display: flex; + height: 54px; +} + +.channel_page__message_avatar { + border-radius: 50%; + aspect-ratio: 1/1; + height: 100%; + filter: drop-shadow(0px 3px 1px #00000077); +} + +.channel_page__message_author_name { + margin-top: 6px; + margin-bottom: 4px; + font-weight: 600; +} + +.channel_page__message_content { + font-size: large; +} diff --git a/src/renderer/pages/Guild/Channel/index.tsx b/src/renderer/pages/Guild/Channel/index.tsx new file mode 100644 index 0000000..e67c578 --- /dev/null +++ b/src/renderer/pages/Guild/Channel/index.tsx @@ -0,0 +1,83 @@ +import { useEffect, useState } from 'react'; +import { useParams } from 'react-router-dom'; +import Channel, { ChannelMessage } from '../../../../common/discord/channel'; +import './Channel.css'; + +export default function ChannelPage() { + const params = useParams(); + const channelId = params.channelId ?? ''; + const [channel, setChannel] = useState(null); + const [messages, setMessages] = useState([]); + + useEffect(() => { + if (channelId.length === 0) return; + + const messageList = document.getElementById('channel_page_message_list'); + if (messageList) { + messageList.scrollTop = messageList.scrollHeight; + } + + window.electron.ipcRenderer + .invoke('DISCORD_LOAD_CHANNEL', channelId) + .then(async (data: Channel | null) => { + setChannel(data); + + if (data) + setMessages( + await window.electron.ipcRenderer.invoke( + 'DISCORD_GET_MESSAGES', + channelId, + ), + ); + + return true; + }) + .catch((err) => console.error(err)); + + window.electron.ipcRenderer.sendMessage( + 'DISCORD_SET_LAST_VISITED_GUILD_CHANNEL', + channelId, + ); + }, [channelId]); + + if (channelId.length === 0 || channel === null) return null; + + return ( +
+
+

# {channel.name}

+
+
+
+ {messages.map((message) => { + return ( +
+
+ Avatar Icon +
+
+

+ {message.author.global_name} +

+

+ {message.content} +

+
+
+ ); + })} +
+
+
+ ); +} diff --git a/src/renderer/pages/Guild/Guild.css b/src/renderer/pages/Guild/Guild.css new file mode 100644 index 0000000..c151b52 --- /dev/null +++ b/src/renderer/pages/Guild/Guild.css @@ -0,0 +1,51 @@ +.guild_page__container { + width: 100%; + height: 100%; + display: flex; +} + +.guild_page__channel_list { + flex-shrink: 0; + display: flex; + flex-direction: column; + width: 300px; + overflow: hidden; + height: 100%; + background: var(--background); + overflow-y: auto; +} + +.guild_page__content { + width: calc(100% - 270px - 300px); + height: 100%; +} + +.guild_page__member_list { + flex-shrink: 0; + display: flex; + flex-direction: column; + width: 270px; + height: 100%; + background: var(--background); + overflow-y: auto; +} + +.guild_page__channel { + display: flex; + width: 100%; + height: 28px; + padding: 12px 18px; + gap: 18px; + user-select: none; + text-decoration: none; + color: var(--text); +} + +.guild_page__channel:hover { + background: var(--background-bright); +} + +.guild_page__channel_icon { + filter: invert(57%) sepia(0%) saturate(256%) hue-rotate(170deg) + brightness(101%) contrast(84%); +} diff --git a/src/renderer/pages/Guild/index.tsx b/src/renderer/pages/Guild/index.tsx index fa353ac..a2a4728 100644 --- a/src/renderer/pages/Guild/index.tsx +++ b/src/renderer/pages/Guild/index.tsx @@ -1,3 +1,125 @@ +import { Link, Outlet, useNavigate, useParams } from 'react-router-dom'; +import { ReactNode, useEffect, useState } from 'react'; + +// Channel Icons +import TextChannelIcon from '../../../../assets/app/icons/channel/text-icon.svg'; +import VoiceChannelIcon from '../../../../assets/app/icons/channel/volume-2.svg'; +import AnnouncementChannelIcon from '../../../../assets/app/icons/channel/tv.svg'; +import StageChannelIcon from '../../../../assets/app/icons/channel/radio.svg'; + +import './Guild.css'; +import { Guild } from '../../../common/discord/guild'; +import Channel, { ChannelType } from '../../../common/discord/channel'; + +function ChannelWrapper({ + children, + channel, +}: { + children: ReactNode[]; + channel: Channel; +}) { + if (channel.type !== ChannelType.GuildVoice) { + return ( + + {children} + + ); + } + + return ( +
+ {children} +
+ ); +} + export default function GuildPage() { - return
Guild
; + const navigate = useNavigate(); + const params = useParams(); + const guildId = params.id ?? ''; + + // States + const [guild, setGuild] = useState(undefined); + + useEffect(() => { + if (guild === undefined || (guild && guild.id !== guildId)) { + // Load guild if guild id is not invalid + if (guildId.length !== 0) { + window.electron.ipcRenderer + .invoke('DISCORD_LOAD_GUILD', guildId) + .then((data) => { + setGuild(data); + return true; + }) + .catch((err) => window.logger.error(err)); + + window.electron.ipcRenderer + .invoke('DISCORD_GET_LAST_VISITED_GUILD_CHANNEL', guildId) + .then((channel: Channel | null) => { + if (channel !== null) { + navigate(`/guild/${guildId}/channel/${channel.id}`); + } + return true; + }) + .catch((err) => window.logger.error(err)); + } + } + + return () => { + if (guild && guildId !== guild.id) setGuild(undefined); + }; + }, [guild, guildId, navigate]); + + if (guildId.length === 0) return null; + + // Either the guild does not exist or we are not on this guild + if (guild === null) + return

Server does not exist or you are not in this server!

; + + return ( +
+
+ {guild?.channels.map((channel) => { + const getIcon = () => { + switch (channel.type) { + case ChannelType.GuildVoice: + return VoiceChannelIcon; + case ChannelType.GuildAnnouncement: + return AnnouncementChannelIcon; + case ChannelType.GuildStageVoice: + return StageChannelIcon; + default: + return TextChannelIcon; + } + }; + return ( + + Text Channel Icon +

{channel.name}

+
+ ); + })} +
+
+ +
+
+ {guild?.members.map((member) => { + return ( +
+ {member.user_id} +
+ ); + })} +
+
+ ); }