From 9298f205fc666257be726ac12c6598ac65c7e477 Mon Sep 17 00:00:00 2001 From: Paul Makles Date: Mon, 20 Dec 2021 13:37:21 +0000 Subject: [PATCH] feat(settings): UI improvements (#448) * Fixed CSS for Settings.tsx + new Theme Shop design * reformat * More changes to UI CSS * Small CSS fixes for Settings.tsx, Account, Bots * Updated theme shop, settings pages, cleanup * chore: force sync language submodule * fix(sidebar): prevent items from shrinking * fix(push): fix timestamp and icon for push notifications * fix(voice): hide grant permission button after grant * chore: hide new shop / chevron before merge * chore(ci): bump node to v16 in dockerfile * fix(sidebar): change width of channel sidebar Co-authored-by: trashtemp <96388163+trashtemp@users.noreply.github.com> --- Dockerfile | 2 +- external/lang | 2 +- src/components/navigation/SidebarBase.tsx | 2 +- .../navigation/left/ServerListSidebar.tsx | 2 + .../navigation/left/ServerSidebar.tsx | 2 +- src/components/ui/Button.tsx | 1 + src/components/ui/Header.tsx | 2 +- src/components/ui/Tip.tsx | 4 +- .../popovers/UserProfile.module.scss | 4 + src/pages/RevoltApp.tsx | 2 +- src/pages/channels/ChannelHeader.tsx | 164 +++++++------- src/pages/home/Home.module.scss | 2 +- src/pages/settings/Settings.module.scss | 12 +- src/pages/settings/Settings.tsx | 2 +- src/pages/settings/panes/Account.tsx | 14 +- src/pages/settings/panes/Appearance.tsx | 31 +-- src/pages/settings/panes/Audio.tsx | 154 ++++++++----- src/pages/settings/panes/MyBots.tsx | 194 +++++++++-------- src/pages/settings/panes/Panes.module.scss | 183 ++++++++++++++-- src/pages/settings/panes/Profile.tsx | 22 +- src/pages/settings/panes/Sync.tsx | 7 + src/pages/settings/panes/ThemeShop.tsx | 204 ++++++++++++++++-- src/sw.ts | 4 +- 23 files changed, 744 insertions(+), 272 deletions(-) diff --git a/Dockerfile b/Dockerfile index dd860d15..fec3aa9c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,7 +12,7 @@ RUN yarn typecheck RUN yarn build RUN npm prune --production -FROM node:15-buster +FROM node:16-buster WORKDIR /usr/src/app COPY --from=builder /usr/src/app . diff --git a/external/lang b/external/lang index 40afd052..9b9858c6 160000 --- a/external/lang +++ b/external/lang @@ -1 +1 @@ -Subproject commit 40afd0527defb106142c50b979bbfc6283e9e48d +Subproject commit 9b9858c64503783364dffba18b2a5356d11cdc30 diff --git a/src/components/navigation/SidebarBase.tsx b/src/components/navigation/SidebarBase.tsx index 2ed36084..d1646f95 100644 --- a/src/components/navigation/SidebarBase.tsx +++ b/src/components/navigation/SidebarBase.tsx @@ -14,7 +14,7 @@ export const GenericSidebarBase = styled.div<{ mobilePadding?: boolean; }>` height: 100%; - width: 240px; + width: 236px; display: flex; flex-shrink: 0; flex-direction: column; diff --git a/src/components/navigation/left/ServerListSidebar.tsx b/src/components/navigation/left/ServerListSidebar.tsx index 3eae8741..36470250 100644 --- a/src/components/navigation/left/ServerListSidebar.tsx +++ b/src/components/navigation/left/ServerListSidebar.tsx @@ -79,7 +79,9 @@ const ServersBase = styled.div` width: 56px; height: 100%; padding-left: 2px; + display: flex; + flex-shrink: 0; flex-direction: column; ${isTouchscreenDevice && diff --git a/src/components/navigation/left/ServerSidebar.tsx b/src/components/navigation/left/ServerSidebar.tsx index 97bdc59c..6c6b68ce 100644 --- a/src/components/navigation/left/ServerSidebar.tsx +++ b/src/components/navigation/left/ServerSidebar.tsx @@ -32,7 +32,7 @@ interface Props { const ServerBase = styled.div` height: 100%; - width: 240px; + width: 236px; display: flex; flex-shrink: 0; flex-direction: column; diff --git a/src/components/ui/Button.tsx b/src/components/ui/Button.tsx index e9027a76..875c14f1 100644 --- a/src/components/ui/Button.tsx +++ b/src/components/ui/Button.tsx @@ -24,6 +24,7 @@ export default styled.button` font-size: 0.875rem; font-family: inherit; font-weight: 500; + flex-shrink: 0; transition: 0.2s ease opacity; transition: 0.2s ease background-color; diff --git a/src/components/ui/Header.tsx b/src/components/ui/Header.tsx index 87c59e4c..1b9a7da1 100644 --- a/src/components/ui/Header.tsx +++ b/src/components/ui/Header.tsx @@ -10,7 +10,7 @@ interface Props { } export default styled.div` - gap: 6px; + gap: 10px; height: 48px; flex: 0 auto; display: flex; diff --git a/src/components/ui/Tip.tsx b/src/components/ui/Tip.tsx index 6a1c6682..1f8c8186 100644 --- a/src/components/ui/Tip.tsx +++ b/src/components/ui/Tip.tsx @@ -49,9 +49,9 @@ export const TipBase = styled.div` ${(props) => props.error && css` - color: var(--error); + color: white; border: 2px solid var(--error); - background: var(--secondary-header); + background: var(--error); `} `; diff --git a/src/context/intermediate/popovers/UserProfile.module.scss b/src/context/intermediate/popovers/UserProfile.module.scss index 764b045d..cfd9b94d 100644 --- a/src/context/intermediate/popovers/UserProfile.module.scss +++ b/src/context/intermediate/popovers/UserProfile.module.scss @@ -29,6 +29,10 @@ align-items: center; flex-direction: row; + > svg { + cursor: pointer; + } + .details { flex-grow: 1; min-width: 0; diff --git a/src/pages/RevoltApp.tsx b/src/pages/RevoltApp.tsx index a28b61cc..41f31980 100644 --- a/src/pages/RevoltApp.tsx +++ b/src/pages/RevoltApp.tsx @@ -58,7 +58,7 @@ export default function App() { leftPanel={ inSpecial ? undefined - : { width: 292, component: } + : { width: 288, component: } } rightPanel={ !inSpecial && inChannel diff --git a/src/pages/channels/ChannelHeader.tsx b/src/pages/channels/ChannelHeader.tsx index 84f188af..b1f18b45 100644 --- a/src/pages/channels/ChannelHeader.tsx +++ b/src/pages/channels/ChannelHeader.tsx @@ -1,4 +1,4 @@ -import { At, Hash, Menu } from "@styled-icons/boxicons-regular"; +import { At, Hash, Menu, ChevronLeft } from "@styled-icons/boxicons-regular"; import { Notepad, Group } from "@styled-icons/boxicons-solid"; import { observer } from "mobx-react-lite"; import { Channel } from "revolt.js/dist/maps/Channels"; @@ -65,85 +65,103 @@ const Info = styled.div` } `; -const IconConainer = styled.div` +const IconContainer = styled.div` + display: flex; + align-items: center; cursor: pointer; color: var(--secondary-foreground); + margin-right: 5px; - ${!isTouchscreenDevice && css` + > svg { + margin-right: -5px; + } + + ${!isTouchscreenDevice && + css` &:hover { color: var(--foreground); } `} -` +`; -export default observer(({ channel, toggleSidebar, toggleChannelSidebar }: ChannelHeaderProps) => { - const { openScreen } = useIntermediate(); +export default observer( + ({ channel, toggleSidebar, toggleChannelSidebar }: ChannelHeaderProps) => { + const { openScreen } = useIntermediate(); - const name = getChannelName(channel); - let icon, recipient: User | undefined; - switch (channel.channel_type) { - case "SavedMessages": - icon = ; - break; - case "DirectMessage": - icon = ; - recipient = channel.recipient; - break; - case "Group": - icon = ; - break; - case "TextChannel": - icon = ; - break; - } + const name = getChannelName(channel); + let icon, recipient: User | undefined; + switch (channel.channel_type) { + case "SavedMessages": + icon = ; + break; + case "DirectMessage": + icon = ; + recipient = channel.recipient; + break; + case "Group": + icon = ; + break; + case "TextChannel": + icon = ; + break; + } - return ( -
- - {icon} - - {name} - {isTouchscreenDevice && - channel.channel_type === "DirectMessage" && ( - <> -
- -
- - - - )} - {!isTouchscreenDevice && - (channel.channel_type === "Group" || - channel.channel_type === "TextChannel") && - channel.description && ( - <> -
- - openScreen({ - id: "channel_info", - channel, - }) - }> - - - - )} - - -
- ); -}); + return ( +
+ + + {/*isTouchscreenDevice && FIXME: requires mobx merge */} + {icon} + + + {name} + {isTouchscreenDevice && + channel.channel_type === "DirectMessage" && ( + <> +
+ +
+ + + + )} + {!isTouchscreenDevice && + (channel.channel_type === "Group" || + channel.channel_type === "TextChannel") && + channel.description && ( + <> +
+ + openScreen({ + id: "channel_info", + channel, + }) + }> + + + + )} + + +
+ ); + }, +); diff --git a/src/pages/home/Home.module.scss b/src/pages/home/Home.module.scss index ffe808f2..4de110c4 100644 --- a/src/pages/home/Home.module.scss +++ b/src/pages/home/Home.module.scss @@ -13,7 +13,7 @@ .actions { gap: 8px; - width: 240px; + width: 236px; margin: auto; display: flex; diff --git a/src/pages/settings/Settings.module.scss b/src/pages/settings/Settings.module.scss index bf815b53..d7c776c7 100644 --- a/src/pages/settings/Settings.module.scss +++ b/src/pages/settings/Settings.module.scss @@ -130,13 +130,18 @@ .container { min-width: 218px; - max-width: 300px; padding: 80px 8px; display: flex; gap: 2px; flex-direction: column; } + @media only screen and (min-width: 800px) { + .container { + max-width: 300px; + } + } + .divider { height: 30px; } @@ -224,6 +229,11 @@ font-weight: 400; } + h6 { + font-size: 1rem; + font-weight: 600; + } + .footer { border-top: 1px solid; margin: 0; diff --git a/src/pages/settings/Settings.tsx b/src/pages/settings/Settings.tsx index 594421d9..f370a3cc 100644 --- a/src/pages/settings/Settings.tsx +++ b/src/pages/settings/Settings.tsx @@ -4,7 +4,6 @@ import { Globe, LogOut, Desktop, - Bot, } from "@styled-icons/boxicons-regular"; import { Bell, @@ -17,6 +16,7 @@ import { Megaphone, Speaker, Store, + Bot, } from "@styled-icons/boxicons-solid"; import { Route, Switch, useHistory } from "react-router-dom"; import { LIBRARY_VERSION } from "revolt.js"; diff --git a/src/pages/settings/panes/Account.tsx b/src/pages/settings/panes/Account.tsx index 89d40969..32e4b6af 100644 --- a/src/pages/settings/panes/Account.tsx +++ b/src/pages/settings/panes/Account.tsx @@ -68,7 +68,17 @@ export const Account = observer(() => { onClick={() => switchPage("profile")} />
- @{client.user!.username} +
+ switchPage("profile")} + /> +
+ @{client.user!.username} +
+
{ <> {value}{" "} stopPropagation( ev, @@ -126,6 +137,7 @@ export const Account = observer(() => { <> •••••••••••@••••••.•••{" "} stopPropagation( ev, diff --git a/src/pages/settings/panes/Appearance.tsx b/src/pages/settings/panes/Appearance.tsx index 7ee6938e..7df77043 100644 --- a/src/pages/settings/panes/Appearance.tsx +++ b/src/pages/settings/panes/Appearance.tsx @@ -1,5 +1,6 @@ import { Reset, Import } from "@styled-icons/boxicons-regular"; import { Pencil, Store } from "@styled-icons/boxicons-solid"; +import { Link } from "react-router-dom"; // @ts-expect-error shade-blend-color does not have typings. import pSBC from "shade-blend-color"; @@ -8,16 +9,13 @@ import { Text } from "preact-i18n"; import { useCallback, useContext, useEffect, useState } from "preact/hooks"; import TextAreaAutoSize from "../../../lib/TextAreaAutoSize"; -import CategoryButton from "../../../components/ui/fluent/CategoryButton"; - - import { debounce } from "../../../lib/debounce"; import { dispatch } from "../../../redux"; import { connectState } from "../../../redux/connector"; +import { isExperimentEnabled } from "../../../redux/reducers/experiments"; import { EmojiPacks, Settings } from "../../../redux/reducers/settings"; - import { DEFAULT_FONT, DEFAULT_MONO_FONT, @@ -40,14 +38,13 @@ import Checkbox from "../../../components/ui/Checkbox"; import ColourSwatches from "../../../components/ui/ColourSwatches"; import ComboBox from "../../../components/ui/ComboBox"; import InputBox from "../../../components/ui/InputBox"; +import CategoryButton from "../../../components/ui/fluent/CategoryButton"; import darkSVG from "../assets/dark.svg"; import lightSVG from "../assets/light.svg"; import mutantSVG from "../assets/mutant_emoji.svg"; import notoSVG from "../assets/noto_emoji.svg"; import openmojiSVG from "../assets/openmoji_emoji.svg"; import twemojiSVG from "../assets/twemoji_emoji.svg"; -import { Link } from "react-router-dom"; -import { isExperimentEnabled } from "../../../redux/reducers/experiments"; interface Props { settings: Settings; @@ -112,8 +109,7 @@ export function Component(props: Props) { draggable={false} data-active={selected === "light"} onClick={() => - selected !== "light" && - setTheme({ base: "light" }) + selected !== "light" && setTheme({ base: "light" }) } onContextMenu={(e) => e.preventDefault()} /> @@ -138,11 +134,20 @@ export function Component(props: Props) {
- {isExperimentEnabled('theme_shop') && - } action="chevron" hover> - - - } + {isExperimentEnabled("theme_shop") && ( + + } + action="chevron" + description={"Browse themes made by the community"} + hover> + + + + )}

diff --git a/src/pages/settings/panes/Audio.tsx b/src/pages/settings/panes/Audio.tsx index 5365b047..2abe0397 100644 --- a/src/pages/settings/panes/Audio.tsx +++ b/src/pages/settings/panes/Audio.tsx @@ -57,11 +57,11 @@ export function Component() { return () => { if (mediaStream) { // close microphone access on unmount - mediaStream.getTracks().forEach(track => { - track.stop() - }) + mediaStream.getTracks().forEach((track) => { + track.stop(); + }); } - } + }; }, [mediaStream]); useEffect(() => { @@ -90,61 +90,109 @@ export function Component() { return ( <>
-

- -

- {!permission && ( -
- - - - -
- )} - - - changeAudioDevice(e.currentTarget.value, "input") - }> - {mediaDevices - ?.filter((device) => device.kind === "audioinput") - .map((device) => { - return ( - - ); - })} - - {error && error.name === "NotAllowedError" && ( - + + + )} {error && permission === "prompt" && ( - - - -
- ), - }} - /> + + + + + + . )} + +
+
+

+ +

+
+ + changeAudioDevice( + e.currentTarget.value, + "input", + ) + }> + {mediaDevices + ?.filter( + (device) => + device.kind === "audioinput", + ) + .map((device) => { + return ( + + ); + })} + + {!permission && ( + + )} + {error && error.name === "NotAllowedError" && ( + + )} +
+
+
+

+ +

+ {/* TOFIX: create audio output combobox*/} + + changeAudioDevice( + e.currentTarget.value, + "output", + ) + }> + {mediaDevices + ?.filter( + (device) => device.kind === "audiooutput", + ) + .map((device) => { + return ( + + ); + })} + +
+
); diff --git a/src/pages/settings/panes/MyBots.tsx b/src/pages/settings/panes/MyBots.tsx index ca9a4468..747d9c90 100644 --- a/src/pages/settings/panes/MyBots.tsx +++ b/src/pages/settings/panes/MyBots.tsx @@ -48,7 +48,7 @@ interface Changes { const BotBadge = styled.div` display: inline-block; - + flex-shrink: 0; height: 1.3em; padding: 0px 4px; font-size: 0.7em; @@ -228,99 +228,103 @@ function BotCard({ bot, onDelete, onUpdate }: Props) { return (
-
-
- {!editMode ? ( - - openScreen({ - id: "profile", - user_id: user._id, - }) - } - /> - ) : ( - editBotAvatar(avatar)} - remove={() => editBotAvatar()} - defaultPreview={user.generateAvatarURL( - { max_side: 256 }, - true, - )} - previewURL={user.generateAvatarURL( - { max_side: 256 }, - true, - )} - /> - )} +
+
+
+ {!editMode ? ( + + openScreen({ + id: "profile", + user_id: user._id, + }) + } + /> + ) : ( + editBotAvatar(avatar)} + remove={() => editBotAvatar()} + defaultPreview={user.generateAvatarURL( + { max_side: 256 }, + true, + )} + previewURL={user.generateAvatarURL( + { max_side: 256 }, + true, + )} + /> + )} - {!editMode ? ( -
-
- {user!.username}{" "} - - - -
+ {!editMode ? ( +
+
+ {user!.username}{" "} + + + +
- -
- ) : ( - - setData({ - ...data, - username: e.currentTarget.value, - }) - } - /> + ) : ( + + setData({ + ...data, + username: e.currentTarget.value, + }) + } + /> + )} +
+ + {!editMode && ( + + }> + {bot.public ? ( + + ) : ( + + )} + )}
- - {!editMode && ( - - }> - {bot.public ? ( - - ) : ( - - )} - - )}
); } diff --git a/src/pages/settings/panes/ThemeShop.tsx b/src/pages/settings/panes/ThemeShop.tsx index 47a4db79..fe22c79e 100644 --- a/src/pages/settings/panes/ThemeShop.tsx +++ b/src/pages/settings/panes/ThemeShop.tsx @@ -1,3 +1,10 @@ +import { Plus, Check } from "@styled-icons/boxicons-regular"; +import { + Star, + BarChartAlt2, + Brush, + Bookmark, +} from "@styled-icons/boxicons-solid"; import styled from "styled-components"; import { useEffect, useState } from "preact/hooks"; @@ -6,6 +13,7 @@ import { dispatch } from "../../../redux"; import { Theme, generateVariables, ThemeOptions } from "../../../context/Theme"; +import InputBox from "../../../components/ui/InputBox"; import Tip from "../../../components/ui/Tip"; import previewPath from "../assets/preview.svg"; @@ -35,13 +43,9 @@ export type Manifest = { // TODO: ability to preview / display the settings set like in the appearance pane const ThemeInfo = styled.article` - display: grid; - grid: - "preview name creator" min-content - "preview desc desc" 1fr - / 200px 1fr 1fr; - - gap: 0.5rem 1rem; + display: flex; + flex-direction: column; + gap: 10px; padding: 1rem; border-radius: var(--border-radius); background: var(--secondary-background); @@ -93,6 +97,7 @@ const ThemeInfo = styled.article` } .name { + margin-top: 5px !important; grid-area: name; margin: 0; } @@ -104,15 +109,115 @@ const ThemeInfo = styled.article` } .description { + margin-bottom: 5px; grid-area: desc; } + + .previewBox { + position: relative; + height: 100%; + width: 100%; + + .hover { + opacity: 0; + font-family: var(--font), sans-serif; + font-variant-ligatures: var(--ligatures); + font-weight: 600; + display: flex; + align-items: center; + justify-content: center; + color: white; + height: 100%; + width: 100%; + z-index: 10; + position: absolute; + background: rgba(0, 0, 0, 0.5); + cursor: pointer; + transition: opacity 0.2s ease-in-out; + + &:hover { + opacity: 1; + } + } + + > svg { + height: 100%; + } + } `; const ThemeList = styled.div` display: grid; + grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 1rem; `; +const Banner = styled.div` + display: flex; + flex-direction: column; +`; + +const Category = styled.div` + display: flex; + gap: 8px; + align-items: center; + + .title { + display: flex; + align-items: center; + gap: 8px; + flex-grow: 1; + } + + .view { + font-size: 12px; + } +`; + +const ActiveTheme = styled.div` + display: flex; + flex-direction: column; + background: var(--secondary-background); + padding: 0; + border-radius: var(--border-radius); + gap: 8px; + overflow: hidden; + + .active-indicator { + display: flex; + gap: 6px; + align-items: center; + background: var(--accent); + width: 100%; + padding: 5px 10px; + font-size: 13px; + font-weight: 400; + color: white; + } + .title { + font-size: 1.2rem; + font-weight: 600; + } + + .author { + font-size: 12px; + margin-bottom: 5px; + } + + .theme { + width: 124px; + height: 80px; + background: var(--tertiary-background); + border-radius: 4px; + } + + .container { + display: flex; + gap: 16px; + padding: 10px 16px 16px; + } +`; + const ThemedSVG = styled.svg<{ theme: Theme }>` ${(props) => props.theme && generateVariables(props.theme)} `; @@ -140,6 +245,10 @@ const ThemePreview = ({ theme, ...props }: ThemePreviewProps) => { const ThemeShopRoot = styled.div` display: grid; gap: 1rem; + + h5 { + margin-bottom: 0; + } `; export function ThemeShop() { @@ -175,18 +284,71 @@ export function ThemeShop() { return ( +
+ Browse hundreds of themes, created and curated by the community. +
+ {/* +
+ Oops! Couldn't load the theme shop. Make sure you're + connected to the internet and try again. +
+
*/} - This section is under construction. + The Theme Shop is currently under construction. + {/* FIXME INTEGRATE WITH MOBX */} + {/* +
+ + Currently active +
+
+
theme svg goes here
+
+
Theme Title
+
by Author
+
This is a theme description.
+
+
+
+ + +
+ + Saved +
+ Manage installed +
+ + +
+ + New this week +
+ View all +
+ + +
+ + Default themes +
+ View all +
+ + +
+ + Highest rated +
+ View all +
*/} + {themeList?.map(([slug, theme]) => ( -

{theme.name}

- {/* Maybe id's of the users should be included as well / instead? */} -
by {theme.creator}
-
{theme.description}
+

{theme.name}

+ {/* Maybe id's of the users should be included as well / instead? */} +
by {theme.creator}
+
{theme.description}
))}
diff --git a/src/sw.ts b/src/sw.ts index c2d7b0dd..f5e6a2c0 100644 --- a/src/sw.ts +++ b/src/sw.ts @@ -17,9 +17,9 @@ self.addEventListener("push", (event) => { icon: data.icon, image: data.image, body: data.body, - timestamp: data.timestamp, + timestamp: data.timestamp * 1000, tag: data.tag, - badge: "https://app.revolt.chat/assets/icons/android-chrome-512x512.png", + badge: "https://app.revolt.chat/assets/icons/monochrome.svg", data: data.url, }); }