mirror of
https://github.com/stoatchat/for-legacy-web.git
synced 2026-03-06 17:11:55 +00:00
chore: merge branch 'ui/glass-header'
This commit is contained in:
@@ -1,14 +1,16 @@
|
||||
import { Docked, OverlappingPanels, ShowIf } from "react-overlapping-panels";
|
||||
import { Switch, Route, useLocation } from "react-router-dom";
|
||||
import styled from "styled-components";
|
||||
import styled, { css } from "styled-components";
|
||||
|
||||
import ContextMenus from "../lib/ContextMenus";
|
||||
import { isTouchscreenDevice } from "../lib/isTouchscreenDevice";
|
||||
|
||||
import { useApplicationState } from "../mobx/State";
|
||||
import { SIDEBAR_CHANNELS } from "../mobx/stores/Layout";
|
||||
|
||||
import Popovers from "../context/intermediate/Popovers";
|
||||
import Notifications from "../context/revoltjs/Notifications";
|
||||
import StateMonitor from "../context/revoltjs/StateMonitor";
|
||||
import SyncManager from "../context/revoltjs/SyncManager";
|
||||
|
||||
import { Titlebar } from "../components/native/Titlebar";
|
||||
import BottomNavigation from "../components/navigation/BottomNavigation";
|
||||
@@ -24,12 +26,54 @@ import ChannelSettings from "./settings/ChannelSettings";
|
||||
import ServerSettings from "./settings/ServerSettings";
|
||||
import Settings from "./settings/Settings";
|
||||
|
||||
const Routes = styled.div`
|
||||
const AppContainer = styled.div`
|
||||
background-size: cover !important;
|
||||
background-position: center center !important;
|
||||
`;
|
||||
|
||||
const StatusBar = styled.div`
|
||||
height: 40px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 13px;
|
||||
gap: 14px;
|
||||
|
||||
.button {
|
||||
padding: 5px;
|
||||
border: 1px solid white;
|
||||
border-radius: var(--border-radius);
|
||||
}
|
||||
`;
|
||||
|
||||
const Routes = styled.div.attrs({ "data-component": "routes" })<{
|
||||
borders: boolean;
|
||||
}>`
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
flex-direction: column;
|
||||
|
||||
background: var(--primary-background);
|
||||
|
||||
/*background-color: rgba(
|
||||
var(--primary-background-rgb),
|
||||
max(var(--min-opacity), 0.75)
|
||||
);*/
|
||||
//backdrop-filter: blur(10px);
|
||||
|
||||
${() =>
|
||||
isTouchscreenDevice &&
|
||||
css`
|
||||
overflow: hidden;
|
||||
`}
|
||||
|
||||
${(props) =>
|
||||
props.borders &&
|
||||
css`
|
||||
border-start-start-radius: 8px;
|
||||
`}
|
||||
`;
|
||||
|
||||
export default function App() {
|
||||
@@ -37,6 +81,7 @@ export default function App() {
|
||||
const fixedBottomNav =
|
||||
path === "/" || path === "/settings" || path.startsWith("/friends");
|
||||
const inChannel = path.includes("/channel");
|
||||
const inServer = path.includes("/server");
|
||||
const inSpecial =
|
||||
(path.startsWith("/friends") && isTouchscreenDevice) ||
|
||||
path.startsWith("/invite") ||
|
||||
@@ -44,90 +89,102 @@ export default function App() {
|
||||
|
||||
return (
|
||||
<>
|
||||
{window.isNative && !window.native.getConfig().frame && (
|
||||
<Titlebar />
|
||||
)}
|
||||
<OverlappingPanels
|
||||
width="100vw"
|
||||
height={
|
||||
window.isNative && !window.native.getConfig().frame
|
||||
? "calc(var(--app-height) - var(--titlebar-height))"
|
||||
: "var(--app-height)"
|
||||
}
|
||||
leftPanel={
|
||||
inSpecial
|
||||
? undefined
|
||||
: { width: 288, component: <LeftSidebar /> }
|
||||
}
|
||||
rightPanel={
|
||||
!inSpecial && inChannel
|
||||
? { width: 236, component: <RightSidebar /> }
|
||||
: undefined
|
||||
}
|
||||
bottomNav={{
|
||||
component: <BottomNavigation />,
|
||||
showIf: fixedBottomNav ? ShowIf.Always : ShowIf.Left,
|
||||
height: 50,
|
||||
}}
|
||||
docked={isTouchscreenDevice ? Docked.None : Docked.Left}>
|
||||
<Routes>
|
||||
<Switch>
|
||||
<Route
|
||||
path="/server/:server/channel/:channel/settings/:page"
|
||||
component={ChannelSettings}
|
||||
/>
|
||||
<Route
|
||||
path="/server/:server/channel/:channel/settings"
|
||||
component={ChannelSettings}
|
||||
/>
|
||||
<Route
|
||||
path="/server/:server/settings/:page"
|
||||
component={ServerSettings}
|
||||
/>
|
||||
<Route
|
||||
path="/server/:server/settings"
|
||||
component={ServerSettings}
|
||||
/>
|
||||
<Route
|
||||
path="/channel/:channel/settings/:page"
|
||||
component={ChannelSettings}
|
||||
/>
|
||||
<Route
|
||||
path="/channel/:channel/settings"
|
||||
component={ChannelSettings}
|
||||
/>
|
||||
{/*<StatusBar>
|
||||
<div className="title">Planned outage: CDN (~2 hours)</div>
|
||||
<div className="button">View status</div>
|
||||
</StatusBar>*/}
|
||||
<AppContainer>
|
||||
{window.isNative && !window.native.getConfig().frame && (
|
||||
<Titlebar />
|
||||
)}
|
||||
<OverlappingPanels
|
||||
width="100vw"
|
||||
height={
|
||||
window.isNative && !window.native.getConfig().frame
|
||||
? "calc(var(--app-height) - var(--titlebar-height))"
|
||||
: "var(--app-height)"
|
||||
}
|
||||
leftPanel={
|
||||
inSpecial
|
||||
? undefined
|
||||
: { width: 288, component: <LeftSidebar /> }
|
||||
}
|
||||
rightPanel={
|
||||
!inSpecial && inChannel
|
||||
? { width: 236, component: <RightSidebar /> }
|
||||
: undefined
|
||||
}
|
||||
bottomNav={{
|
||||
component: <BottomNavigation />,
|
||||
showIf: fixedBottomNav ? ShowIf.Always : ShowIf.Left,
|
||||
height: 50,
|
||||
}}
|
||||
docked={isTouchscreenDevice ? Docked.None : Docked.Left}>
|
||||
<Routes borders={inServer}>
|
||||
<Switch>
|
||||
<Route
|
||||
path="/server/:server/channel/:channel/settings/:page"
|
||||
component={ChannelSettings}
|
||||
/>
|
||||
<Route
|
||||
path="/server/:server/channel/:channel/settings"
|
||||
component={ChannelSettings}
|
||||
/>
|
||||
<Route
|
||||
path="/server/:server/settings/:page"
|
||||
component={ServerSettings}
|
||||
/>
|
||||
<Route
|
||||
path="/server/:server/settings"
|
||||
component={ServerSettings}
|
||||
/>
|
||||
<Route
|
||||
path="/channel/:channel/settings/:page"
|
||||
component={ChannelSettings}
|
||||
/>
|
||||
<Route
|
||||
path="/channel/:channel/settings"
|
||||
component={ChannelSettings}
|
||||
/>
|
||||
|
||||
<Route
|
||||
path="/channel/:channel/:message"
|
||||
component={Channel}
|
||||
/>
|
||||
<Route
|
||||
path="/server/:server/channel/:channel/:message"
|
||||
component={Channel}
|
||||
/>
|
||||
<Route
|
||||
path="/channel/:channel/:message"
|
||||
component={Channel}
|
||||
/>
|
||||
<Route
|
||||
path="/server/:server/channel/:channel/:message"
|
||||
component={Channel}
|
||||
/>
|
||||
|
||||
<Route
|
||||
path="/server/:server/channel/:channel"
|
||||
component={Channel}
|
||||
/>
|
||||
<Route path="/server/:server" component={Channel} />
|
||||
<Route path="/channel/:channel" component={Channel} />
|
||||
<Route
|
||||
path="/server/:server/channel/:channel"
|
||||
component={Channel}
|
||||
/>
|
||||
<Route path="/server/:server" component={Channel} />
|
||||
<Route
|
||||
path="/channel/:channel"
|
||||
component={Channel}
|
||||
/>
|
||||
|
||||
<Route path="/settings/:page" component={Settings} />
|
||||
<Route path="/settings" component={Settings} />
|
||||
<Route
|
||||
path="/settings/:page"
|
||||
component={Settings}
|
||||
/>
|
||||
<Route path="/settings" component={Settings} />
|
||||
|
||||
<Route path="/dev" component={Developer} />
|
||||
<Route path="/friends" component={Friends} />
|
||||
<Route path="/open/:id" component={Open} />
|
||||
<Route path="/bot/:id" component={InviteBot} />
|
||||
<Route path="/" component={Home} />
|
||||
</Switch>
|
||||
</Routes>
|
||||
<ContextMenus />
|
||||
<Popovers />
|
||||
<Notifications />
|
||||
<StateMonitor />
|
||||
</OverlappingPanels>
|
||||
<Route path="/dev" component={Developer} />
|
||||
<Route path="/friends" component={Friends} />
|
||||
<Route path="/open/:id" component={Open} />
|
||||
<Route path="/bot/:id" component={InviteBot} />
|
||||
<Route path="/" component={Home} />
|
||||
</Switch>
|
||||
</Routes>
|
||||
<ContextMenus />
|
||||
<Popovers />
|
||||
<Notifications />
|
||||
<StateMonitor />
|
||||
</OverlappingPanels>
|
||||
</AppContainer>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ 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;
|
||||
@@ -37,7 +37,9 @@ const ChannelMain = styled.div`
|
||||
flex-direction: row;
|
||||
`;
|
||||
|
||||
const ChannelContent = styled.div`
|
||||
const ChannelContent = styled.div.attrs({
|
||||
"data-component": "content",
|
||||
})`
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
|
||||
@@ -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 (
|
||||
<PageHeader icon={icon}>
|
||||
<PageHeader icon={icon} transparent>
|
||||
<Info>
|
||||
<span className="name">{name}</span>
|
||||
{isTouchscreenDevice &&
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useHistory } from "react-router-dom";
|
||||
|
||||
import { chainedDefer, defer } from "../../../lib/defer";
|
||||
import { internalEmit } from "../../../lib/eventEmitter";
|
||||
import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice";
|
||||
import { voiceState, VoiceStatus } from "../../../lib/vortex/VoiceState";
|
||||
@@ -29,20 +30,34 @@ export default function HeaderActions({ channel }: ChannelHeaderProps) {
|
||||
const { openScreen } = useIntermediate();
|
||||
const history = useHistory();
|
||||
|
||||
function openRightSidebar() {
|
||||
const panels = document.querySelector("#app > div > div");
|
||||
function slideOpen() {
|
||||
if (!isTouchscreenDevice) return;
|
||||
const panels = document.querySelector("#app > div > div > div");
|
||||
panels?.scrollTo({
|
||||
behavior: "smooth",
|
||||
left: panels.clientWidth * 3,
|
||||
});
|
||||
}
|
||||
|
||||
function openSidebar() {
|
||||
if (isTouchscreenDevice) {
|
||||
openRightSidebar();
|
||||
} else {
|
||||
function openSearch() {
|
||||
if (
|
||||
!isTouchscreenDevice &&
|
||||
!layout.getSectionState(SIDEBAR_MEMBERS, true)
|
||||
) {
|
||||
layout.toggleSectionState(SIDEBAR_MEMBERS, true);
|
||||
}
|
||||
|
||||
slideOpen();
|
||||
chainedDefer(() => internalEmit("RightSidebar", "open", "search"));
|
||||
}
|
||||
|
||||
function openMembers() {
|
||||
if (!isTouchscreenDevice) {
|
||||
layout.toggleSectionState(SIDEBAR_MEMBERS, true);
|
||||
}
|
||||
|
||||
slideOpen();
|
||||
chainedDefer(() => internalEmit("RightSidebar", "open", undefined));
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -74,17 +89,13 @@ export default function HeaderActions({ channel }: ChannelHeaderProps) {
|
||||
)}
|
||||
<VoiceActions channel={channel} />
|
||||
{channel.channel_type !== "VoiceChannel" && (
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
internalEmit("RightSidebar", "open", "search");
|
||||
openRightSidebar();
|
||||
}}>
|
||||
<IconButton onClick={openSearch}>
|
||||
<Search size={25} />
|
||||
</IconButton>
|
||||
)}
|
||||
{(channel.channel_type === "Group" ||
|
||||
channel.channel_type === "TextChannel") && (
|
||||
<IconButton onClick={openSidebar}>
|
||||
<IconButton onClick={openMembers}>
|
||||
<Group size={25} />
|
||||
</IconButton>
|
||||
)}
|
||||
|
||||
@@ -33,13 +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;
|
||||
word-break: break-word;
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
min-height: 150px;
|
||||
}
|
||||
|
||||
> div {
|
||||
display: flex;
|
||||
|
||||
@@ -30,6 +30,7 @@ interface Props {
|
||||
}
|
||||
|
||||
const VoiceBase = styled.div`
|
||||
margin-top: 48px;
|
||||
padding: 20px;
|
||||
background: var(--secondary-background);
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
.list {
|
||||
padding: 0 10px 10px 10px;
|
||||
user-select: none;
|
||||
overflow-y: scroll;
|
||||
|
||||
&[data-empty="true"] {
|
||||
img {
|
||||
@@ -185,12 +184,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Hide the remove friend button on smaller screens.
|
||||
@media only screen and (max-width: 768px) {
|
||||
.list {
|
||||
padding: 0 8px 8px 8px;
|
||||
}
|
||||
|
||||
.remove {
|
||||
.list .remove {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,10 @@ import { UserDetail, MessageAdd, UserPlus } from "@styled-icons/boxicons-solid";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { RelationshipStatus, Presence } from "revolt-api/types/Users";
|
||||
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";
|
||||
@@ -16,7 +18,7 @@ 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";
|
||||
@@ -72,7 +74,7 @@ export default observer(() => {
|
||||
const isEmpty = lists.reduce((p: number, n) => p + n.length, 0) === 0;
|
||||
return (
|
||||
<>
|
||||
<PageHeader icon={<UserDetail size={24} />} noBurger>
|
||||
<PageHeader icon={<UserDetail size={24} />} transparent noBurger>
|
||||
<div className={styles.title}>
|
||||
<Text id="app.navigation.tabs.friends" />
|
||||
</div>
|
||||
@@ -115,99 +117,104 @@ export default observer(() => {
|
||||
*/}
|
||||
</div>
|
||||
</PageHeader>
|
||||
<div
|
||||
className={styles.list}
|
||||
data-empty={isEmpty}
|
||||
data-mobile={isTouchscreenDevice}>
|
||||
{isEmpty && (
|
||||
<>
|
||||
<img src="https://img.insrt.uk/xexu7/XOPoBUTI47.png/raw" />
|
||||
<Text id="app.special.friends.nobody" />
|
||||
</>
|
||||
)}
|
||||
<div data-scroll-offset="true" data-avoids-navigation="true">
|
||||
<div
|
||||
className={classNames(styles.list, "with-padding")}
|
||||
data-empty={isEmpty}
|
||||
data-mobile={isTouchscreenDevice}>
|
||||
{isEmpty && (
|
||||
<>
|
||||
<img src="https://img.insrt.uk/xexu7/XOPoBUTI47.png/raw" />
|
||||
<Text id="app.special.friends.nobody" />
|
||||
</>
|
||||
)}
|
||||
|
||||
{incoming.length > 0 && (
|
||||
<div
|
||||
className={styles.pending}
|
||||
onClick={() =>
|
||||
openScreen({
|
||||
id: "pending_requests",
|
||||
users: incoming,
|
||||
})
|
||||
}>
|
||||
<div className={styles.avatars}>
|
||||
{incoming.map(
|
||||
(x, i) =>
|
||||
i < 3 && (
|
||||
<UserIcon
|
||||
target={x}
|
||||
size={64}
|
||||
mask={
|
||||
i <
|
||||
Math.min(incoming.length - 1, 2)
|
||||
? "url(#overlap)"
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
),
|
||||
)}
|
||||
</div>
|
||||
<div className={styles.details}>
|
||||
<div>
|
||||
<Text id="app.special.friends.pending" />{" "}
|
||||
<span>{incoming.length}</span>
|
||||
</div>
|
||||
<span>
|
||||
{incoming.length > 3 ? (
|
||||
<TextReact
|
||||
id="app.special.friends.from.several"
|
||||
fields={{
|
||||
userlist: userlist.slice(0, 6),
|
||||
count: incoming.length - 3,
|
||||
}}
|
||||
/>
|
||||
) : incoming.length > 1 ? (
|
||||
<TextReact
|
||||
id="app.special.friends.from.multiple"
|
||||
fields={{
|
||||
user: userlist.shift()!,
|
||||
userlist: userlist.slice(1),
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<TextReact
|
||||
id="app.special.friends.from.single"
|
||||
fields={{ user: userlist[0] }}
|
||||
/>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
<ChevronRight size={28} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{lists.map(([i18n, list, section_id], index) => {
|
||||
if (index === 0) return;
|
||||
if (list.length === 0) return;
|
||||
|
||||
return (
|
||||
<CollapsibleSection
|
||||
key={section_id}
|
||||
id={`friends_${section_id}`}
|
||||
defaultValue={true}
|
||||
sticky
|
||||
large
|
||||
summary={
|
||||
<div class="title">
|
||||
<Text id={i18n} /> — {list.length}
|
||||
</div>
|
||||
{incoming.length > 0 && (
|
||||
<div
|
||||
className={styles.pending}
|
||||
onClick={() =>
|
||||
openScreen({
|
||||
id: "pending_requests",
|
||||
users: incoming,
|
||||
})
|
||||
}>
|
||||
{list.map((x) => (
|
||||
<Friend key={x._id} user={x} />
|
||||
))}
|
||||
</CollapsibleSection>
|
||||
);
|
||||
})}
|
||||
<div className={styles.avatars}>
|
||||
{incoming.map(
|
||||
(x, i) =>
|
||||
i < 3 && (
|
||||
<UserIcon
|
||||
target={x}
|
||||
size={64}
|
||||
mask={
|
||||
i <
|
||||
Math.min(
|
||||
incoming.length - 1,
|
||||
2,
|
||||
)
|
||||
? "url(#overlap)"
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
),
|
||||
)}
|
||||
</div>
|
||||
<div className={styles.details}>
|
||||
<div>
|
||||
<Text id="app.special.friends.pending" />{" "}
|
||||
<span>{incoming.length}</span>
|
||||
</div>
|
||||
<span>
|
||||
{incoming.length > 3 ? (
|
||||
<TextReact
|
||||
id="app.special.friends.from.several"
|
||||
fields={{
|
||||
userlist: userlist.slice(0, 6),
|
||||
count: incoming.length - 3,
|
||||
}}
|
||||
/>
|
||||
) : incoming.length > 1 ? (
|
||||
<TextReact
|
||||
id="app.special.friends.from.multiple"
|
||||
fields={{
|
||||
user: userlist.shift()!,
|
||||
userlist: userlist.slice(1),
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<TextReact
|
||||
id="app.special.friends.from.single"
|
||||
fields={{ user: userlist[0] }}
|
||||
/>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
<ChevronRight size={28} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{lists.map(([i18n, list, section_id], index) => {
|
||||
if (index === 0) return;
|
||||
if (list.length === 0) return;
|
||||
|
||||
return (
|
||||
<CollapsibleSection
|
||||
key={section_id}
|
||||
id={`friends_${section_id}`}
|
||||
defaultValue={true}
|
||||
sticky
|
||||
large
|
||||
summary={
|
||||
<div class="title">
|
||||
<Text id={i18n} /> — {list.length}
|
||||
</div>
|
||||
}>
|
||||
{list.map((x) => (
|
||||
<Friend key={x._id} user={x} />
|
||||
))}
|
||||
</CollapsibleSection>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 95%;
|
||||
height: 100%;
|
||||
padding: 12px;
|
||||
|
||||
h3 {
|
||||
@@ -30,15 +30,11 @@
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 16px;
|
||||
max-width: 650px;
|
||||
margin-bottom: 20px;
|
||||
margin-bottom: 30px;
|
||||
|
||||
a {
|
||||
width: 100%;
|
||||
|
||||
&:nth-child(4) {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
div {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ export default observer(() => {
|
||||
</div>
|
||||
)}
|
||||
<div className="content">
|
||||
<PageHeader icon={<HomeIcon size={24} />}>
|
||||
<PageHeader icon={<HomeIcon size={24} />} transparent>
|
||||
<Text id="app.navigation.tabs.home" />
|
||||
</PageHeader>
|
||||
<div className={styles.homeScreen}>
|
||||
@@ -103,8 +103,8 @@ export default observer(() => {
|
||||
Create a group
|
||||
</CategoryButton>
|
||||
</Link>
|
||||
<a
|
||||
href="https://revolt.social"
|
||||
{/*<a
|
||||
href="#"
|
||||
target="_blank"
|
||||
rel="noreferrer">
|
||||
<CategoryButton
|
||||
@@ -115,7 +115,7 @@ export default observer(() => {
|
||||
}>
|
||||
Join a community
|
||||
</CategoryButton>
|
||||
</a>
|
||||
</a>*/}
|
||||
|
||||
{client.servers.get(
|
||||
"01F7ZSBSFHQ8TA81725KQCSDDP",
|
||||
@@ -159,24 +159,24 @@ export default observer(() => {
|
||||
rel="noreferrer">
|
||||
<CategoryButton
|
||||
action="external"
|
||||
description={
|
||||
"Support the project by donating - thank you!"
|
||||
}
|
||||
icon={<Money size={32} />}>
|
||||
<Text id="app.home.donate" />
|
||||
</CategoryButton>
|
||||
</a>
|
||||
|
||||
<Tooltip
|
||||
content={
|
||||
<Text id="app.home.settings-tooltip" />
|
||||
}>
|
||||
<Link to="/settings">
|
||||
<CategoryButton
|
||||
action="chevron"
|
||||
icon={<Cog size={32} />}>
|
||||
<Text id="app.home.settings" />
|
||||
</CategoryButton>
|
||||
</Link>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<Tooltip
|
||||
content={<Text id="app.home.settings-tooltip" />}>
|
||||
<Link to="/settings">
|
||||
<CategoryButton
|
||||
action="chevron"
|
||||
icon={<Cog size={32} />}>
|
||||
<Text id="app.home.settings" />
|
||||
</CategoryButton>
|
||||
</Link>
|
||||
</Tooltip>
|
||||
{isDecember && (
|
||||
<a href="#" onClick={toggleSeasonalTheme}>
|
||||
Turn {seasonalTheme ? "off" : "on"} homescreen
|
||||
|
||||
@@ -102,7 +102,7 @@ export function GenericSettings({
|
||||
/>
|
||||
</Helmet>
|
||||
{isTouchscreenDevice && (
|
||||
<Header placement="primary">
|
||||
<Header placement="primary" transparent>
|
||||
{typeof page === "undefined" ? (
|
||||
<>
|
||||
{showExitButton && (
|
||||
@@ -168,6 +168,9 @@ export function GenericSettings({
|
||||
<div className={styles.content}>
|
||||
<div
|
||||
className={styles.scrollbox}
|
||||
data-scroll-offset={
|
||||
isTouchscreenDevice ? "with-padding" : undefined
|
||||
}
|
||||
ref={(ref) => {
|
||||
// Force scroll to top if page changes.
|
||||
if (ref) {
|
||||
|
||||
@@ -43,18 +43,13 @@
|
||||
background: var(--primary-background);
|
||||
}
|
||||
|
||||
.scrollbox {
|
||||
&::-webkit-scrollbar-thumb {
|
||||
border-top: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Sidebar */
|
||||
.sidebar {
|
||||
overflow-y: auto;
|
||||
|
||||
.container {
|
||||
padding: 20px 8px calc(var(--bottom-navigation-height) + 30px);
|
||||
padding: calc(var(--header-height) + 4px) 8px
|
||||
calc(var(--bottom-navigation-height) + 30px);
|
||||
min-width: 218px;
|
||||
}
|
||||
|
||||
@@ -76,7 +71,7 @@
|
||||
|
||||
.contentcontainer {
|
||||
max-width: unset !important;
|
||||
padding: 16px 12px var(--bottom-navigation-height) !important;
|
||||
padding: 72px 12px var(--bottom-navigation-height) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -117,6 +112,7 @@
|
||||
|
||||
// All children receive custom scrollbar.
|
||||
> * > ::-webkit-scrollbar-thumb {
|
||||
min-height: 100px;
|
||||
width: 4px;
|
||||
background-clip: content-box;
|
||||
border-top: 80px solid transparent;
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
Speaker,
|
||||
Store,
|
||||
Bot,
|
||||
Trash,
|
||||
} from "@styled-icons/boxicons-solid";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Route, Switch, useHistory } from "react-router-dom";
|
||||
@@ -24,16 +25,19 @@ import { LIBRARY_VERSION } from "revolt.js";
|
||||
import styled from "styled-components";
|
||||
|
||||
import styles from "./Settings.module.scss";
|
||||
import { openContextMenu } from "preact-context-menu";
|
||||
import { Text } from "preact-i18n";
|
||||
import { useContext } from "preact/hooks";
|
||||
|
||||
import { useApplicationState } from "../../mobx/State";
|
||||
|
||||
import { useIntermediate } from "../../context/intermediate/Intermediate";
|
||||
import RequiresOnline from "../../context/revoltjs/RequiresOnline";
|
||||
import { AppContext, LogOutContext } from "../../context/revoltjs/RevoltClient";
|
||||
|
||||
import UserIcon from "../../components/common/user/UserIcon";
|
||||
import { Username } from "../../components/common/user/UserShort";
|
||||
import UserStatus from "../../components/common/user/UserStatus";
|
||||
import LineDivider from "../../components/ui/LineDivider";
|
||||
|
||||
import ButtonItem from "../../components/navigation/items/ButtonItem";
|
||||
@@ -54,19 +58,67 @@ import { Sessions } from "./panes/Sessions";
|
||||
import { Sync } from "./panes/Sync";
|
||||
import { ThemeShop } from "./panes/ThemeShop";
|
||||
|
||||
const IndexHeader = styled.div`
|
||||
const AccountHeader = styled.div`
|
||||
display: flex;
|
||||
background: var(--secondary-background);
|
||||
flex-direction: column;
|
||||
border-radius: var(--border-radius);
|
||||
padding: 20px;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.account {
|
||||
padding: 20px;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
background: var(--secondary-background);
|
||||
|
||||
.details {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-size: 12px;
|
||||
gap: 2px;
|
||||
|
||||
> span {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.statusChanger {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: var(--tertiary-background);
|
||||
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
.status {
|
||||
padding-inline-start: 12px;
|
||||
height: 48px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: var(--secondary-foreground);
|
||||
flex-grow: 1;
|
||||
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
svg {
|
||||
width: 48px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default observer(() => {
|
||||
const history = useHistory();
|
||||
const client = useContext(AppContext);
|
||||
const logout = useContext(LogOutContext);
|
||||
const { openScreen } = useIntermediate();
|
||||
const experiments = useApplicationState().experiments;
|
||||
|
||||
function switchPage(to?: string) {
|
||||
@@ -270,10 +322,40 @@ export default observer(() => {
|
||||
</>
|
||||
}
|
||||
indexHeader={
|
||||
<IndexHeader>
|
||||
<UserIcon size={64} target={client.user!} />
|
||||
<Username user={client.user!} prefixAt />
|
||||
</IndexHeader>
|
||||
<AccountHeader>
|
||||
<div className="account">
|
||||
<UserIcon
|
||||
size={64}
|
||||
target={client.user!}
|
||||
status
|
||||
onClick={() => openContextMenu("Status")}
|
||||
/>
|
||||
<div className="details">
|
||||
<Username user={client.user!} prefixAt />
|
||||
<UserStatus user={client.user!} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="statusChanger">
|
||||
<a
|
||||
className="status"
|
||||
onClick={() =>
|
||||
openScreen({
|
||||
id: "special_input",
|
||||
type: "set_custom_status",
|
||||
})
|
||||
}>
|
||||
Change your status...
|
||||
</a>
|
||||
{client.user!.status?.text && (
|
||||
<Trash
|
||||
size={24}
|
||||
onClick={() =>
|
||||
client.users.edit({ remove: "StatusText" })
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</AccountHeader>
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
DisplayEmojiShim,
|
||||
ThemeCustomCSSShim,
|
||||
DisplaySeasonalShim,
|
||||
DisplayTransparencyShim,
|
||||
} from "../../../components/settings/AppearanceShims";
|
||||
import ThemeOverrides from "../../../components/settings/appearance/ThemeOverrides";
|
||||
import ThemeTools from "../../../components/settings/appearance/ThemeTools";
|
||||
@@ -27,6 +28,10 @@ export const Appearance = observer(() => {
|
||||
<hr />
|
||||
<ThemeAccentShim />
|
||||
<hr />
|
||||
<h3>
|
||||
<Text id="app.settings.pages.appearance.theme_options.title" />
|
||||
</h3>
|
||||
<DisplayTransparencyShim />
|
||||
<DisplaySeasonalShim />
|
||||
<hr />
|
||||
<DisplayFontShim />
|
||||
|
||||
@@ -126,7 +126,6 @@ export const Overview = observer(({ server }: Props) => {
|
||||
alignItems: "center",
|
||||
}}>
|
||||
<span style={{ flexShrink: "0", flex: `25%` }}>{i18n}</span>
|
||||
<span>Sends a message when someone joins your server</span>
|
||||
<ComboBox
|
||||
value={
|
||||
systemMessages?.[
|
||||
|
||||
Reference in New Issue
Block a user