mirror of
https://github.com/revoltchat/revite.git
synced 2024-11-29 02:10:59 -05:00
feat(mobx): add layout (paths + sections)
This commit is contained in:
parent
f87ecfcbd7
commit
a8491267a4
11 changed files with 208 additions and 132 deletions
|
@ -1,7 +1,6 @@
|
||||||
import { ChevronDown } from "@styled-icons/boxicons-regular";
|
import { ChevronDown } from "@styled-icons/boxicons-regular";
|
||||||
|
|
||||||
import { State, store } from "../../redux";
|
import { useApplicationState } from "../../mobx/State";
|
||||||
import { Action } from "../../redux/reducers";
|
|
||||||
|
|
||||||
import Details from "../ui/Details";
|
import Details from "../ui/Details";
|
||||||
|
|
||||||
|
@ -25,27 +24,14 @@ export default function CollapsibleSection({
|
||||||
children,
|
children,
|
||||||
...detailsProps
|
...detailsProps
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const state: State = store.getState();
|
const layout = useApplicationState().layout;
|
||||||
|
|
||||||
function setState(state: boolean) {
|
|
||||||
if (state === defaultValue) {
|
|
||||||
store.dispatch({
|
|
||||||
type: "SECTION_TOGGLE_UNSET",
|
|
||||||
id,
|
|
||||||
} as Action);
|
|
||||||
} else {
|
|
||||||
store.dispatch({
|
|
||||||
type: "SECTION_TOGGLE_SET",
|
|
||||||
id,
|
|
||||||
state,
|
|
||||||
} as Action);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Details
|
<Details
|
||||||
open={state.sectionToggle[id] ?? defaultValue}
|
open={layout.getSectionState(id, defaultValue)}
|
||||||
onToggle={(e) => setState(e.currentTarget.open)}
|
onToggle={(e) =>
|
||||||
|
layout.setSectionState(id, e.currentTarget.open, defaultValue)
|
||||||
|
}
|
||||||
{...detailsProps}>
|
{...detailsProps}>
|
||||||
<summary>
|
<summary>
|
||||||
<div class="padding">
|
<div class="padding">
|
||||||
|
|
|
@ -5,8 +5,7 @@ import styled, { css } from "styled-components";
|
||||||
|
|
||||||
import ConditionalLink from "../../lib/ConditionalLink";
|
import ConditionalLink from "../../lib/ConditionalLink";
|
||||||
|
|
||||||
import { connectState } from "../../redux/connector";
|
import { useApplicationState } from "../../mobx/State";
|
||||||
import { LastOpened } from "../../redux/reducers/last_opened";
|
|
||||||
|
|
||||||
import { useClient } from "../../context/revoltjs/RevoltClient";
|
import { useClient } from "../../context/revoltjs/RevoltClient";
|
||||||
|
|
||||||
|
@ -47,19 +46,14 @@ const Button = styled.a<{ active: boolean }>`
|
||||||
`}
|
`}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
interface Props {
|
export default observer(() => {
|
||||||
lastOpened: LastOpened;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const BottomNavigation = observer(({ lastOpened }: Props) => {
|
|
||||||
const client = useClient();
|
const client = useClient();
|
||||||
|
const layout = useApplicationState().layout;
|
||||||
const user = client.users.get(client.user!._id);
|
const user = client.users.get(client.user!._id);
|
||||||
|
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const path = useLocation().pathname;
|
const path = useLocation().pathname;
|
||||||
|
|
||||||
const channel_id = lastOpened["home"];
|
|
||||||
|
|
||||||
const friendsActive = path.startsWith("/friends");
|
const friendsActive = path.startsWith("/friends");
|
||||||
const settingsActive = path.startsWith("/settings");
|
const settingsActive = path.startsWith("/settings");
|
||||||
const homeActive = !(friendsActive || settingsActive);
|
const homeActive = !(friendsActive || settingsActive);
|
||||||
|
@ -73,14 +67,11 @@ export const BottomNavigation = observer(({ lastOpened }: Props) => {
|
||||||
if (settingsActive) {
|
if (settingsActive) {
|
||||||
if (history.length > 0) {
|
if (history.length > 0) {
|
||||||
history.goBack();
|
history.goBack();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (channel_id) {
|
history.push(layout.getLastHomePath());
|
||||||
history.push(`/channel/${channel_id}`);
|
|
||||||
} else {
|
|
||||||
history.push("/");
|
|
||||||
}
|
|
||||||
}}>
|
}}>
|
||||||
<Message size={24} />
|
<Message size={24} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
@ -117,9 +108,3 @@ export const BottomNavigation = observer(({ lastOpened }: Props) => {
|
||||||
</Base>
|
</Base>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connectState(BottomNavigation, (state) => {
|
|
||||||
return {
|
|
||||||
lastOpened: state.lastOpened,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ import ConditionalLink from "../../../lib/ConditionalLink";
|
||||||
import PaintCounter from "../../../lib/PaintCounter";
|
import PaintCounter from "../../../lib/PaintCounter";
|
||||||
import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice";
|
import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice";
|
||||||
|
|
||||||
|
import { useApplicationState } from "../../../mobx/State";
|
||||||
import { dispatch } from "../../../redux";
|
import { dispatch } from "../../../redux";
|
||||||
import { connectState } from "../../../redux/connector";
|
import { connectState } from "../../../redux/connector";
|
||||||
import { Unreads } from "../../../redux/reducers/unreads";
|
import { Unreads } from "../../../redux/reducers/unreads";
|
||||||
|
@ -37,6 +38,7 @@ type Props = {
|
||||||
const HomeSidebar = observer((props: Props) => {
|
const HomeSidebar = observer((props: Props) => {
|
||||||
const { pathname } = useLocation();
|
const { pathname } = useLocation();
|
||||||
const client = useContext(AppContext);
|
const client = useContext(AppContext);
|
||||||
|
const layout = useApplicationState().layout;
|
||||||
const { channel } = useParams<{ channel: string }>();
|
const { channel } = useParams<{ channel: string }>();
|
||||||
const { openScreen } = useIntermediate();
|
const { openScreen } = useIntermediate();
|
||||||
|
|
||||||
|
@ -52,15 +54,8 @@ const HomeSidebar = observer((props: Props) => {
|
||||||
if (channel && !obj) return <Redirect to="/" />;
|
if (channel && !obj) return <Redirect to="/" />;
|
||||||
if (obj) useUnreads({ ...props, channel: obj });
|
if (obj) useUnreads({ ...props, channel: obj });
|
||||||
|
|
||||||
useEffect(() => {
|
// Track what page the user was last on (in home page).
|
||||||
if (!channel) return;
|
useEffect(() => layout.setLastHomePath(pathname), [pathname]);
|
||||||
|
|
||||||
dispatch({
|
|
||||||
type: "LAST_OPENED_SET",
|
|
||||||
parent: "home",
|
|
||||||
child: channel,
|
|
||||||
});
|
|
||||||
}, [channel]);
|
|
||||||
|
|
||||||
channels.sort((b, a) => a.timestamp.localeCompare(b.timestamp));
|
channels.sort((b, a) => a.timestamp.localeCompare(b.timestamp));
|
||||||
|
|
||||||
|
|
|
@ -12,8 +12,8 @@ import ConditionalLink from "../../../lib/ConditionalLink";
|
||||||
import PaintCounter from "../../../lib/PaintCounter";
|
import PaintCounter from "../../../lib/PaintCounter";
|
||||||
import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice";
|
import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice";
|
||||||
|
|
||||||
|
import { useApplicationState } from "../../../mobx/State";
|
||||||
import { connectState } from "../../../redux/connector";
|
import { connectState } from "../../../redux/connector";
|
||||||
import { LastOpened } from "../../../redux/reducers/last_opened";
|
|
||||||
import { Unreads } from "../../../redux/reducers/unreads";
|
import { Unreads } from "../../../redux/reducers/unreads";
|
||||||
|
|
||||||
import { useIntermediate } from "../../../context/intermediate/Intermediate";
|
import { useIntermediate } from "../../../context/intermediate/Intermediate";
|
||||||
|
@ -195,11 +195,11 @@ function Swoosh() {
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
unreads: Unreads;
|
unreads: Unreads;
|
||||||
lastOpened: LastOpened;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ServerListSidebar = observer(({ unreads, lastOpened }: Props) => {
|
export const ServerListSidebar = observer(({ unreads }: Props) => {
|
||||||
const client = useClient();
|
const client = useClient();
|
||||||
|
const layout = useApplicationState().layout;
|
||||||
|
|
||||||
const { server: server_id } = useParams<{ server?: string }>();
|
const { server: server_id } = useParams<{ server?: string }>();
|
||||||
const server = server_id ? client.servers.get(server_id) : undefined;
|
const server = server_id ? client.servers.get(server_id) : undefined;
|
||||||
|
@ -268,7 +268,7 @@ export const ServerListSidebar = observer(({ unreads, lastOpened }: Props) => {
|
||||||
<ServerList>
|
<ServerList>
|
||||||
<ConditionalLink
|
<ConditionalLink
|
||||||
active={homeActive}
|
active={homeActive}
|
||||||
to={lastOpened.home ? `/channel/${lastOpened.home}` : "/"}>
|
to={layout.getLastHomePath()}>
|
||||||
<ServerEntry home active={homeActive}>
|
<ServerEntry home active={homeActive}>
|
||||||
<Swoosh />
|
<Swoosh />
|
||||||
<div
|
<div
|
||||||
|
@ -295,15 +295,12 @@ export const ServerListSidebar = observer(({ unreads, lastOpened }: Props) => {
|
||||||
<LineDivider />
|
<LineDivider />
|
||||||
{servers.map((entry) => {
|
{servers.map((entry) => {
|
||||||
const active = entry.server._id === server?._id;
|
const active = entry.server._id === server?._id;
|
||||||
const id = lastOpened[entry.server._id];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ConditionalLink
|
<ConditionalLink
|
||||||
key={entry.server._id}
|
key={entry.server._id}
|
||||||
active={active}
|
active={active}
|
||||||
to={`/server/${entry.server._id}${
|
to={layout.getServerPath(entry.server._id)}>
|
||||||
id ? `/channel/${id}` : ""
|
|
||||||
}`}>
|
|
||||||
<ServerEntry
|
<ServerEntry
|
||||||
active={active}
|
active={active}
|
||||||
onContextMenu={attachContextMenu("Menu", {
|
onContextMenu={attachContextMenu("Menu", {
|
||||||
|
@ -359,6 +356,5 @@ export const ServerListSidebar = observer(({ unreads, lastOpened }: Props) => {
|
||||||
export default connectState(ServerListSidebar, (state) => {
|
export default connectState(ServerListSidebar, (state) => {
|
||||||
return {
|
return {
|
||||||
unreads: state.unreads,
|
unreads: state.unreads,
|
||||||
lastOpened: state.lastOpened,
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -10,6 +10,7 @@ import PaintCounter from "../../../lib/PaintCounter";
|
||||||
import { internalEmit } from "../../../lib/eventEmitter";
|
import { internalEmit } from "../../../lib/eventEmitter";
|
||||||
import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice";
|
import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice";
|
||||||
|
|
||||||
|
import { useApplicationState } from "../../../mobx/State";
|
||||||
import { dispatch } from "../../../redux";
|
import { dispatch } from "../../../redux";
|
||||||
import { connectState } from "../../../redux/connector";
|
import { connectState } from "../../../redux/connector";
|
||||||
import { Notifications } from "../../../redux/reducers/notifications";
|
import { Notifications } from "../../../redux/reducers/notifications";
|
||||||
|
@ -58,6 +59,7 @@ const ServerList = styled.div`
|
||||||
|
|
||||||
const ServerSidebar = observer((props: Props) => {
|
const ServerSidebar = observer((props: Props) => {
|
||||||
const client = useClient();
|
const client = useClient();
|
||||||
|
const layout = useApplicationState().layout;
|
||||||
const { server: server_id, channel: channel_id } =
|
const { server: server_id, channel: channel_id } =
|
||||||
useParams<{ server: string; channel?: string }>();
|
useParams<{ server: string; channel?: string }>();
|
||||||
|
|
||||||
|
@ -75,16 +77,15 @@ const ServerSidebar = observer((props: Props) => {
|
||||||
);
|
);
|
||||||
if (channel_id && !channel) return <Redirect to={`/server/${server_id}`} />;
|
if (channel_id && !channel) return <Redirect to={`/server/${server_id}`} />;
|
||||||
|
|
||||||
|
// Handle unreads; FIXME: should definitely not be here
|
||||||
if (channel) useUnreads({ ...props, channel });
|
if (channel) useUnreads({ ...props, channel });
|
||||||
|
|
||||||
|
// Track which channel the user was last on.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!channel_id) return;
|
if (!channel_id) return;
|
||||||
|
if (!server_id) return;
|
||||||
|
|
||||||
dispatch({
|
layout.setLastOpened(server_id, channel_id);
|
||||||
type: "LAST_OPENED_SET",
|
|
||||||
parent: server_id!,
|
|
||||||
child: channel_id!,
|
|
||||||
});
|
|
||||||
}, [channel_id, server_id]);
|
}, [channel_id, server_id]);
|
||||||
|
|
||||||
const uncategorised = new Set(server.channel_ids);
|
const uncategorised = new Set(server.channel_ids);
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { useContext } from "preact/hooks";
|
||||||
import Auth from "./stores/Auth";
|
import Auth from "./stores/Auth";
|
||||||
import Draft from "./stores/Draft";
|
import Draft from "./stores/Draft";
|
||||||
import Experiments from "./stores/Experiments";
|
import Experiments from "./stores/Experiments";
|
||||||
|
import Layout from "./stores/Layout";
|
||||||
import LocaleOptions from "./stores/LocaleOptions";
|
import LocaleOptions from "./stores/LocaleOptions";
|
||||||
|
|
||||||
interface StoreDefinition {
|
interface StoreDefinition {
|
||||||
|
@ -24,6 +25,7 @@ export default class State {
|
||||||
draft: Draft;
|
draft: Draft;
|
||||||
locale: LocaleOptions;
|
locale: LocaleOptions;
|
||||||
experiments: Experiments;
|
experiments: Experiments;
|
||||||
|
layout: Layout;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct new State.
|
* Construct new State.
|
||||||
|
@ -33,6 +35,7 @@ export default class State {
|
||||||
this.draft = new Draft();
|
this.draft = new Draft();
|
||||||
this.locale = new LocaleOptions();
|
this.locale = new LocaleOptions();
|
||||||
this.experiments = new Experiments();
|
this.experiments = new Experiments();
|
||||||
|
this.layout = new Layout();
|
||||||
|
|
||||||
makeAutoObservable(this);
|
makeAutoObservable(this);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,55 +0,0 @@
|
||||||
import { action, computed, makeAutoObservable, ObservableMap } from "mobx";
|
|
||||||
|
|
||||||
import Persistent from "../Persistent";
|
|
||||||
|
|
||||||
interface Data {
|
|
||||||
server?: Record<string, string>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Keeps track of the last open channels, tabs, etc.
|
|
||||||
* Handles providing good UX experience on navigating
|
|
||||||
* back and forth between different parts of the app.
|
|
||||||
*/
|
|
||||||
export default class Experiments implements Persistent<Data> {
|
|
||||||
private server: ObservableMap<string, string>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct new Experiments store.
|
|
||||||
*/
|
|
||||||
constructor() {
|
|
||||||
this.server = new ObservableMap();
|
|
||||||
makeAutoObservable(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
toJSON() {
|
|
||||||
return {
|
|
||||||
server: this.server,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@action hydrate(data: Data) {
|
|
||||||
if (data.server) {
|
|
||||||
Object.keys(data.server).forEach((key) =>
|
|
||||||
this.server.set(key, data.server![key]),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get last opened channel in a server.
|
|
||||||
* @param server Server ID
|
|
||||||
*/
|
|
||||||
@computed get(server: string) {
|
|
||||||
return this.server.get(server);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set last opened channel in a server.
|
|
||||||
* @param server Server ID
|
|
||||||
* @param channel Channel ID
|
|
||||||
*/
|
|
||||||
@action enable(server: string, channel: string) {
|
|
||||||
this.server.set(server, channel);
|
|
||||||
}
|
|
||||||
}
|
|
161
src/mobx/stores/Layout.ts
Normal file
161
src/mobx/stores/Layout.ts
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
import { action, computed, makeAutoObservable, ObservableMap } from "mobx";
|
||||||
|
|
||||||
|
import Persistent from "../Persistent";
|
||||||
|
|
||||||
|
interface Data {
|
||||||
|
lastSection?: "home" | "server";
|
||||||
|
lastHomePath?: string;
|
||||||
|
lastOpened?: Record<string, string>;
|
||||||
|
openSections?: Record<string, boolean>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keeps track of the last open channels, tabs, etc.
|
||||||
|
* Handles providing good UX experience on navigating
|
||||||
|
* back and forth between different parts of the app.
|
||||||
|
*/
|
||||||
|
export default class Layout implements Persistent<Data> {
|
||||||
|
/**
|
||||||
|
* The last 'major section' that the user had open.
|
||||||
|
* This is either the home tab or a channel ID (for a server channel).
|
||||||
|
*/
|
||||||
|
private lastSection: "home" | string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The last path the user had open in the home tab.
|
||||||
|
*/
|
||||||
|
private lastHomePath: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map of last channels viewed in servers.
|
||||||
|
*/
|
||||||
|
private lastOpened: ObservableMap<string, string>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map of section IDs to their current state.
|
||||||
|
*/
|
||||||
|
private openSections: ObservableMap<string, boolean>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct new Layout store.
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
this.lastSection = "home";
|
||||||
|
this.lastHomePath = "/";
|
||||||
|
this.lastOpened = new ObservableMap();
|
||||||
|
this.openSections = new ObservableMap();
|
||||||
|
makeAutoObservable(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON() {
|
||||||
|
return {
|
||||||
|
lastSection: this.lastSection,
|
||||||
|
lastHomePath: this.lastHomePath,
|
||||||
|
lastOpened: this.lastOpened,
|
||||||
|
openSections: this.openSections,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@action hydrate(data: Data) {
|
||||||
|
if (data.lastSection) {
|
||||||
|
this.lastSection = data.lastSection;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.lastHomePath) {
|
||||||
|
this.lastHomePath = data.lastHomePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.lastOpened) {
|
||||||
|
Object.keys(data.lastOpened).forEach((key) =>
|
||||||
|
this.lastOpened.set(key, data.lastOpened![key]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.openSections) {
|
||||||
|
Object.keys(data.openSections).forEach((key) =>
|
||||||
|
this.openSections.set(key, data.openSections![key]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the last 'major section' the user had open.
|
||||||
|
* @returns Last open section
|
||||||
|
*/
|
||||||
|
@computed getLastSection() {
|
||||||
|
return this.lastSection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get last opened channel in a server.
|
||||||
|
* @param server Server ID
|
||||||
|
*/
|
||||||
|
@computed getLastOpened(server: string) {
|
||||||
|
return this.lastOpened.get(server);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the path to a server (as seen on sidebar).
|
||||||
|
* @param server Server ID
|
||||||
|
* @returns Pathname
|
||||||
|
*/
|
||||||
|
@computed getServerPath(server: string) {
|
||||||
|
let path = `/server/${server}`;
|
||||||
|
if (this.lastOpened.has(server)) {
|
||||||
|
path += `/channel/${this.getLastOpened(server)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set last opened channel in a server.
|
||||||
|
* @param server Server ID
|
||||||
|
* @param channel Channel ID
|
||||||
|
*/
|
||||||
|
@action setLastOpened(server: string, channel: string) {
|
||||||
|
this.lastOpened.set(server, channel);
|
||||||
|
this.lastSection = "server";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the last path the user had open in the home tab.
|
||||||
|
* @returns Last home path
|
||||||
|
*/
|
||||||
|
@computed getLastHomePath() {
|
||||||
|
return this.lastHomePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the current path open in the home tab.
|
||||||
|
* @param path Pathname
|
||||||
|
*/
|
||||||
|
@action setLastHomePath(path: string) {
|
||||||
|
this.lastHomePath = path;
|
||||||
|
this.lastSection = "home";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param id Section ID
|
||||||
|
* @returns Whether the section is open
|
||||||
|
* @param def Default state value
|
||||||
|
*/
|
||||||
|
@computed getSectionState(id: string, def?: boolean) {
|
||||||
|
return this.openSections.get(id) ?? def ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the state of a section.
|
||||||
|
* @param id Section ID
|
||||||
|
* @param value New state value
|
||||||
|
* @param def Default state value
|
||||||
|
*/
|
||||||
|
@action setSectionState(id: string, value: boolean, def?: boolean) {
|
||||||
|
if (value === def) {
|
||||||
|
this.openSections.delete(id);
|
||||||
|
} else {
|
||||||
|
this.openSections.set(id, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,21 +1,20 @@
|
||||||
|
import { Hash } from "@styled-icons/boxicons-regular";
|
||||||
|
import { Ghost } from "@styled-icons/boxicons-solid";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import { Channel as ChannelI } from "revolt.js/dist/maps/Channels";
|
import { Channel as ChannelI } from "revolt.js/dist/maps/Channels";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
|
|
||||||
import { Text } from "preact-i18n";
|
import { Text } from "preact-i18n";
|
||||||
|
import { useEffect, useState } from "preact/hooks";
|
||||||
import { useState } from "preact/hooks";
|
|
||||||
|
|
||||||
import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice";
|
import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice";
|
||||||
|
|
||||||
|
import { useApplicationState } from "../../mobx/State";
|
||||||
import { dispatch, getState } from "../../redux";
|
import { dispatch, getState } from "../../redux";
|
||||||
|
|
||||||
import { useClient } from "../../context/revoltjs/RevoltClient";
|
import { useClient } from "../../context/revoltjs/RevoltClient";
|
||||||
|
|
||||||
import { Hash } from "@styled-icons/boxicons-regular";
|
|
||||||
import { Ghost } from "@styled-icons/boxicons-solid";
|
|
||||||
|
|
||||||
import AgeGate from "../../components/common/AgeGate";
|
import AgeGate from "../../components/common/AgeGate";
|
||||||
import MessageBox from "../../components/common/messaging/MessageBox";
|
import MessageBox from "../../components/common/messaging/MessageBox";
|
||||||
import JumpToBottom from "../../components/common/messaging/bars/JumpToBottom";
|
import JumpToBottom from "../../components/common/messaging/bars/JumpToBottom";
|
||||||
|
@ -52,19 +51,19 @@ const PlaceholderBase = styled.div`
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
|
|
||||||
.primary {
|
.primary {
|
||||||
color: var(--secondary-foreground);
|
color: var(--secondary-foreground);
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-size: 22px;
|
font-size: 22px;
|
||||||
margin: 0 0 5px 0;
|
margin: 0 0 5px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.secondary {
|
.secondary {
|
||||||
color: var(--tertiary-foreground);
|
color: var(--tertiary-foreground);
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
}
|
}
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
margin: 2em auto;
|
margin: 2em auto;
|
||||||
fill-opacity: 0.8;
|
fill-opacity: 0.8;
|
||||||
|
@ -94,7 +93,6 @@ const TextChannel = observer(({ channel }: { channel: ChannelI }) => {
|
||||||
getState().sectionToggle[CHANNELS_SIDEBAR_KEY] ?? true,
|
getState().sectionToggle[CHANNELS_SIDEBAR_KEY] ?? true,
|
||||||
);
|
);
|
||||||
|
|
||||||
const id = channel._id;
|
|
||||||
return (
|
return (
|
||||||
<AgeGate
|
<AgeGate
|
||||||
type="channel"
|
type="channel"
|
||||||
|
@ -126,7 +124,7 @@ const TextChannel = observer(({ channel }: { channel: ChannelI }) => {
|
||||||
}}
|
}}
|
||||||
toggleChannelSidebar={() => {
|
toggleChannelSidebar={() => {
|
||||||
if (isTouchscreenDevice) {
|
if (isTouchscreenDevice) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setChannels(!showChannels);
|
setChannels(!showChannels);
|
||||||
|
@ -147,7 +145,7 @@ const TextChannel = observer(({ channel }: { channel: ChannelI }) => {
|
||||||
/>
|
/>
|
||||||
<ChannelMain>
|
<ChannelMain>
|
||||||
<ChannelContent>
|
<ChannelContent>
|
||||||
<VoiceHeader id={id} />
|
<VoiceHeader id={channel._id} />
|
||||||
<MessageArea channel={channel} />
|
<MessageArea channel={channel} />
|
||||||
<TypingIndicator channel={channel} />
|
<TypingIndicator channel={channel} />
|
||||||
<JumpToBottom channel={channel} />
|
<JumpToBottom channel={channel} />
|
||||||
|
@ -173,13 +171,19 @@ function ChannelPlaceholder() {
|
||||||
<PlaceholderBase>
|
<PlaceholderBase>
|
||||||
<Header placement="primary">
|
<Header placement="primary">
|
||||||
<Hash size={24} />
|
<Hash size={24} />
|
||||||
<span className="name"><Text id="app.main.channel.errors.nochannel" /></span>
|
<span className="name">
|
||||||
|
<Text id="app.main.channel.errors.nochannel" />
|
||||||
|
</span>
|
||||||
</Header>
|
</Header>
|
||||||
|
|
||||||
<div className="placeholder">
|
<div className="placeholder">
|
||||||
<Ghost width={80} />
|
<Ghost width={80} />
|
||||||
<div className="primary"><Text id="app.main.channel.errors.title" /></div>
|
<div className="primary">
|
||||||
<div className="secondary"><Text id="app.main.channel.errors.nochannels" /></div>
|
<Text id="app.main.channel.errors.title" />
|
||||||
|
</div>
|
||||||
|
<div className="secondary">
|
||||||
|
<Text id="app.main.channel.errors.nochannels" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</PlaceholderBase>
|
</PlaceholderBase>
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in a new issue