From bad745856007c729ebe7ec77b2495dcf534d456c Mon Sep 17 00:00:00 2001 From: Paul Makles Date: Thu, 30 Dec 2021 18:15:31 +0000 Subject: [PATCH] feat: provide and consume scroll offsets --- src/components/common/ServerHeader.tsx | 12 ++---- src/components/ui/Header.tsx | 43 +++++++++++--------- src/context/Theme.tsx | 6 ++- src/mobx/stores/Layout.ts | 10 +++++ src/mobx/stores/helpers/STheme.ts | 20 ++++++--- src/pages/RevoltApp.tsx | 2 +- src/pages/channels/Channel.tsx | 11 ++--- src/pages/channels/ChannelHeader.tsx | 3 +- src/pages/channels/actions/HeaderActions.tsx | 8 +++- src/pages/channels/messaging/MessageArea.tsx | 10 +++-- src/pages/developer/Developer.tsx | 2 +- src/pages/friends/Friend.module.scss | 11 ++--- src/pages/friends/Friends.tsx | 31 +++----------- src/pages/home/Home.tsx | 2 +- src/pages/settings/GenericSettings.tsx | 5 ++- src/pages/settings/Settings.module.scss | 11 +---- src/styles/_elements.scss | 23 ++++++++++- 17 files changed, 113 insertions(+), 97 deletions(-) diff --git a/src/components/common/ServerHeader.tsx b/src/components/common/ServerHeader.tsx index 5eb3fc71..7bbf0b66 100644 --- a/src/components/common/ServerHeader.tsx +++ b/src/components/common/ServerHeader.tsx @@ -10,7 +10,6 @@ import { Text } from "preact-i18n"; import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice"; -import Header from "../ui/Header"; import IconButton from "../ui/IconButton"; import Tooltip from "./Tooltip"; @@ -20,7 +19,7 @@ interface Props { background?: boolean; } -const ServerBanner = styled.div` +const ServerBanner = styled.div>` background-color: var(--secondary-header); flex-shrink: 0; display: flex; @@ -44,7 +43,8 @@ const ServerBanner = styled.div` `} .container { - height: 48px; + height: var(--header-height); + display: flex; align-items: center; padding: 0 14px; @@ -54,12 +54,6 @@ const ServerBanner = styled.div` text-overflow: ellipsis; gap: 8px; - ${() => - isTouchscreenDevice && - css` - height: 56px; - `} - .title { white-space: nowrap; overflow: hidden; diff --git a/src/components/ui/Header.tsx b/src/components/ui/Header.tsx index f3b69134..3750704c 100644 --- a/src/components/ui/Header.tsx +++ b/src/components/ui/Header.tsx @@ -16,12 +16,12 @@ import { Children } from "../../types/Preact"; interface Props { borders?: boolean; background?: boolean; + transparent?: boolean; placement: "primary" | "secondary"; } const Header = styled.div` gap: 10px; - height: 48px; flex: 0 auto; display: flex; flex-shrink: 0; @@ -29,16 +29,11 @@ const Header = styled.div` font-weight: 600; user-select: none; align-items: center; + + height: var(--header-height); + background-size: cover !important; background-position: center !important; - background-color: rgba( - var(--primary-header-rgb), - max(var(--min-opacity), 0.75) - ); - backdrop-filter: blur(10px); - z-index: 20; - position: absolute; - width: 100%; svg { flex-shrink: 0; @@ -49,11 +44,21 @@ const Header = styled.div` color: var(--secondary-foreground); } - ${() => - isTouchscreenDevice && - css` - height: 56px; - `} + ${(props) => + props.transparent + ? css` + background-color: rgba( + var(--primary-header-rgb), + max(var(--min-opacity), 0.75) + ); + backdrop-filter: blur(10px); + z-index: 20; + position: absolute; + width: 100%; + ` + : css` + background-color: var(--primary-header); + `} ${(props) => props.background && @@ -99,19 +104,19 @@ const IconContainer = styled.div` `} `; -interface PageHeaderProps { +type PageHeaderProps = Omit & { noBurger?: boolean; children: Children; icon: Children; -} +}; export const PageHeader = observer( - ({ children, icon, noBurger }: PageHeaderProps) => { + ({ children, icon, noBurger, ...props }: PageHeaderProps) => { const layout = useApplicationState().layout; const visible = layout.getSectionState(SIDEBAR_CHANNELS, true); return ( -
+
{!noBurger && } @@ -136,7 +141,7 @@ export function HamburgerAction() { function openSidebar() { document - .querySelector("#app > div > div") + .querySelector("#app > div > div > div") ?.scrollTo({ behavior: "smooth", left: 0 }); } diff --git a/src/context/Theme.tsx b/src/context/Theme.tsx index c30fbbd0..ceb2a401 100644 --- a/src/context/Theme.tsx +++ b/src/context/Theme.tsx @@ -72,10 +72,14 @@ export type Theme = Overrides & { font?: Fonts; css?: string; monospaceFont?: MonospaceFonts; - "min-opacity"?: number; }; +export type ComputedVariables = Theme & { + "header-height"?: string; + "effective-bottom-offset"?: string; +}; + export interface ThemeOptions { base?: string; ligatures?: boolean; diff --git a/src/mobx/stores/Layout.ts b/src/mobx/stores/Layout.ts index 324626ad..1c572333 100644 --- a/src/mobx/stores/Layout.ts +++ b/src/mobx/stores/Layout.ts @@ -138,6 +138,16 @@ export default class Layout implements Store, Persistent { return this.lastHomePath; } + /** + * Get the last path the user had open. + * @returns Last path + */ + @computed getLastPath() { + return this.lastSection === "home" + ? this.lastHomePath + : this.getLastOpened(this.lastSection); + } + /** * Set the current path open in the home tab. * @param path Pathname diff --git a/src/mobx/stores/helpers/STheme.ts b/src/mobx/stores/helpers/STheme.ts index 394a8ee0..c4ebb173 100644 --- a/src/mobx/stores/helpers/STheme.ts +++ b/src/mobx/stores/helpers/STheme.ts @@ -1,6 +1,8 @@ import rgba from "color-rgba"; import { makeAutoObservable, computed, action } from "mobx"; +import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice"; + import { Theme, PRESETS, @@ -9,6 +11,7 @@ import { DEFAULT_MONO_FONT, Fonts, MonospaceFonts, + ComputedVariables, } from "../../../context/Theme"; import Settings from "../Settings"; @@ -94,14 +97,10 @@ export default class STheme { ...PRESETS[this.getBase()], ...this.settings.get("appearance:theme:overrides"), light: this.isLight(), - - "min-opacity": this.settings.get("appearance:transparency", true) - ? 0 - : 1, }; } - @computed computeVariables(): Theme { + @computed computeVariables(): ComputedVariables { const variables = this.getVariables() as Record< string, string | boolean | number @@ -114,7 +113,16 @@ export default class STheme { } } - return variables as unknown as Theme; + return { + ...(variables as unknown as Theme), + "min-opacity": this.settings.get("appearance:transparency", true) + ? 0 + : 1, + "header-height": isTouchscreenDevice ? "56px" : "48px", + "effective-bottom-offset": isTouchscreenDevice + ? "var(--bottom-navigation-height)" + : "0px", + }; } @action setVariable(key: Variables, value: string) { diff --git a/src/pages/RevoltApp.tsx b/src/pages/RevoltApp.tsx index a4947036..9fa31811 100644 --- a/src/pages/RevoltApp.tsx +++ b/src/pages/RevoltApp.tsx @@ -45,7 +45,7 @@ const StatusBar = styled.div` } `; -const Routes = styled.div` +const Routes = styled.div.attrs({ "data-component": "routes" })` min-width: 0; display: flex; flex-direction: column; diff --git a/src/pages/channels/Channel.tsx b/src/pages/channels/Channel.tsx index e1322386..9ff0a59a 100644 --- a/src/pages/channels/Channel.tsx +++ b/src/pages/channels/Channel.tsx @@ -29,20 +29,17 @@ import ChannelHeader from "./ChannelHeader"; import { MessageArea } from "./messaging/MessageArea"; import VoiceHeader from "./voice/VoiceHeader"; -const ChannelMain = styled.div` +const ChannelMain = styled.div.attrs({ "data-component": "channel" })` flex-grow: 1; display: flex; min-height: 0; overflow: hidden; flex-direction: row; - - > * > ::-webkit-scrollbar-thumb { - background-clip: content-box; - border-top: 48px solid transparent; - } `; -const ChannelContent = styled.div` +const ChannelContent = styled.div.attrs({ + "data-component": "content", +})` flex-grow: 1; display: flex; overflow: hidden; diff --git a/src/pages/channels/ChannelHeader.tsx b/src/pages/channels/ChannelHeader.tsx index 70b8f34d..8c8244fc 100644 --- a/src/pages/channels/ChannelHeader.tsx +++ b/src/pages/channels/ChannelHeader.tsx @@ -78,7 +78,6 @@ const Info = styled.div` export default observer(({ channel }: ChannelHeaderProps) => { const { openScreen } = useIntermediate(); - const layout = useApplicationState().layout; const name = getChannelName(channel); let icon, recipient: User | undefined; @@ -99,7 +98,7 @@ export default observer(({ channel }: ChannelHeaderProps) => { } return ( - + {name} {isTouchscreenDevice && diff --git a/src/pages/channels/actions/HeaderActions.tsx b/src/pages/channels/actions/HeaderActions.tsx index 0466e0ca..c8cc235d 100644 --- a/src/pages/channels/actions/HeaderActions.tsx +++ b/src/pages/channels/actions/HeaderActions.tsx @@ -30,7 +30,7 @@ export default function HeaderActions({ channel }: ChannelHeaderProps) { const history = useHistory(); function openRightSidebar() { - const panels = document.querySelector("#app > div > div"); + const panels = document.querySelector("#app > div > div > div"); panels?.scrollTo({ behavior: "smooth", left: panels.clientWidth * 3, @@ -84,7 +84,11 @@ export default function HeaderActions({ channel }: ChannelHeaderProps) { )} {(channel.channel_type === "Group" || channel.channel_type === "TextChannel") && ( - + { + internalEmit("RightSidebar", "open", undefined); + openRightSidebar(); + }}> )} diff --git a/src/pages/channels/messaging/MessageArea.tsx b/src/pages/channels/messaging/MessageArea.tsx index 16836f60..ee356c38 100644 --- a/src/pages/channels/messaging/MessageArea.tsx +++ b/src/pages/channels/messaging/MessageArea.tsx @@ -33,14 +33,18 @@ import Preloader from "../../../components/ui/Preloader"; import ConversationStart from "./ConversationStart"; import MessageRenderer from "./MessageRenderer"; -const Area = styled.div` +const Area = styled.div.attrs({ "data-scroll-offset": "with-padding" })` height: 100%; flex-grow: 1; min-height: 0; + word-break: break-word; + overflow-x: hidden; overflow-y: scroll; - padding-top: 48px; - word-break: break-word; + + &::-webkit-scrollbar-thumb { + min-height: 150px; + } > div { display: flex; diff --git a/src/pages/developer/Developer.tsx b/src/pages/developer/Developer.tsx index f45cab52..d64cf5f6 100644 --- a/src/pages/developer/Developer.tsx +++ b/src/pages/developer/Developer.tsx @@ -29,7 +29,7 @@ export default function Developer() { return (
}>Developer Tab -
+
diff --git a/src/pages/friends/Friend.module.scss b/src/pages/friends/Friend.module.scss index 402b4db2..0316dab6 100644 --- a/src/pages/friends/Friend.module.scss +++ b/src/pages/friends/Friend.module.scss @@ -12,9 +12,7 @@ .list { padding: 0 10px 10px 10px; - padding-top: 48px; user-select: none; - /*overflow-y: scroll;*/ &[data-empty="true"] { img { @@ -186,12 +184,9 @@ } } +// Hide the remove friend button on smaller screens. @media only screen and (max-width: 768px) { - .list { - padding: 56px 8px 8px 8px; - - .remove { - display: none; - } + .list .remove { + display: none; } } diff --git a/src/pages/friends/Friends.tsx b/src/pages/friends/Friends.tsx index e8104864..24fdfafa 100644 --- a/src/pages/friends/Friends.tsx +++ b/src/pages/friends/Friends.tsx @@ -6,6 +6,7 @@ import { User } from "revolt.js/dist/maps/Users"; import styled, { css } from "styled-components"; import styles from "./Friend.module.scss"; +import classNames from "classnames"; import { Text } from "preact-i18n"; import { TextReact } from "../../lib/i18n"; @@ -17,32 +18,12 @@ import { useClient } from "../../context/revoltjs/RevoltClient"; import CollapsibleSection from "../../components/common/CollapsibleSection"; import Tooltip from "../../components/common/Tooltip"; import UserIcon from "../../components/common/user/UserIcon"; -import Header, { PageHeader } from "../../components/ui/Header"; +import { PageHeader } from "../../components/ui/Header"; import IconButton from "../../components/ui/IconButton"; import { Children } from "../../types/Preact"; import { Friend } from "./Friend"; -const Container = styled.div` - overflow-y: scroll; - - ::-webkit-scrollbar-thumb { - min-height: 100px; - width: 4px; - background-clip: content-box; - border-top: 48px solid transparent; - } - ${() => - isTouchscreenDevice && - css` - ::-webkit-scrollbar-thumb { - min-height: 150px; - border-top: 56px solid transparent; - border-bottom: var(--bottom-navigation-height) solid transparent; - } - `} -`; - export default observer(() => { const { openScreen } = useIntermediate(); @@ -93,7 +74,7 @@ export default observer(() => { const isEmpty = lists.reduce((p: number, n) => p + n.length, 0) === 0; return ( <> - } noBurger> + } transparent noBurger>
@@ -136,9 +117,9 @@ export default observer(() => { */}
- +
{isEmpty && ( @@ -234,7 +215,7 @@ export default observer(() => { ); })}
- +
); }); diff --git a/src/pages/home/Home.tsx b/src/pages/home/Home.tsx index f4eab6e6..22e70269 100644 --- a/src/pages/home/Home.tsx +++ b/src/pages/home/Home.tsx @@ -83,7 +83,7 @@ export default observer(() => {
)}
- }> + } transparent>
diff --git a/src/pages/settings/GenericSettings.tsx b/src/pages/settings/GenericSettings.tsx index a3480b49..12a4665c 100644 --- a/src/pages/settings/GenericSettings.tsx +++ b/src/pages/settings/GenericSettings.tsx @@ -102,7 +102,7 @@ export function GenericSettings({ /> {isTouchscreenDevice && ( -
+
{typeof page === "undefined" ? ( <> {showExitButton && ( @@ -168,6 +168,9 @@ export function GenericSettings({
{ // Force scroll to top if page changes. if (ref) { diff --git a/src/pages/settings/Settings.module.scss b/src/pages/settings/Settings.module.scss index e5956ed7..f18c0ee8 100644 --- a/src/pages/settings/Settings.module.scss +++ b/src/pages/settings/Settings.module.scss @@ -43,20 +43,13 @@ background: var(--primary-background); } - .scrollbox { - &::-webkit-scrollbar-thumb { - min-height: 150px; - border-top: 56px solid transparent; - //border-bottom: var(--bottom-navigation-height) solid transparent; - } - } - /* Sidebar */ .sidebar { overflow-y: auto; .container { - padding: 76px 8px calc(var(--bottom-navigation-height) + 30px); + padding: calc(var(--header-height) + 4px) 8px + calc(var(--bottom-navigation-height) + 30px); min-width: 218px; } diff --git a/src/styles/_elements.scss b/src/styles/_elements.scss index f7843a9e..35d28c9b 100644 --- a/src/styles/_elements.scss +++ b/src/styles/_elements.scss @@ -13,13 +13,32 @@ } ::-webkit-scrollbar-thumb { - min-height: 15px; - min-width: 15px; + min-height: 30px; + min-width: 30px; background-clip: content-box; background: var(--scrollbar-thumb); } +[data-scroll-offset] { + overflow-y: scroll; +} + +[data-scroll-offset="with-padding"], +[data-scroll-offset] .with-padding { + padding-top: var(--header-height); +} + +[data-scroll-offset]::-webkit-scrollbar-thumb { + background-clip: content-box; + border-top: var(--header-height) solid transparent; +} + +[data-avoids-navigation]::-webkit-scrollbar-thumb { + background-clip: content-box; + border-bottom: var(--effective-bottom-offset) solid transparent; +} + ::-webkit-scrollbar-corner { background: transparent; }