mirror of
https://github.com/stoatchat/for-legacy-web.git
synced 2026-03-07 09:25:27 +00:00
Merge branch 'mobx'
This commit is contained in:
@@ -5,8 +5,7 @@ import styled, { css } from "styled-components";
|
||||
|
||||
import ConditionalLink from "../../lib/ConditionalLink";
|
||||
|
||||
import { connectState } from "../../redux/connector";
|
||||
import { LastOpened } from "../../redux/reducers/last_opened";
|
||||
import { useApplicationState } from "../../mobx/State";
|
||||
|
||||
import { useClient } from "../../context/revoltjs/RevoltClient";
|
||||
|
||||
@@ -47,19 +46,14 @@ const Button = styled.a<{ active: boolean }>`
|
||||
`}
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
lastOpened: LastOpened;
|
||||
}
|
||||
|
||||
export const BottomNavigation = observer(({ lastOpened }: Props) => {
|
||||
export default observer(() => {
|
||||
const client = useClient();
|
||||
const layout = useApplicationState().layout;
|
||||
const user = client.users.get(client.user!._id);
|
||||
|
||||
const history = useHistory();
|
||||
const path = useLocation().pathname;
|
||||
|
||||
const channel_id = lastOpened["home"];
|
||||
|
||||
const friendsActive = path.startsWith("/friends");
|
||||
const settingsActive = path.startsWith("/settings");
|
||||
const homeActive = !(friendsActive || settingsActive);
|
||||
@@ -73,14 +67,11 @@ export const BottomNavigation = observer(({ lastOpened }: Props) => {
|
||||
if (settingsActive) {
|
||||
if (history.length > 0) {
|
||||
history.goBack();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (channel_id) {
|
||||
history.push(`/channel/${channel_id}`);
|
||||
} else {
|
||||
history.push("/");
|
||||
}
|
||||
history.push(layout.getLastHomePath());
|
||||
}}>
|
||||
<Message size={24} />
|
||||
</IconButton>
|
||||
@@ -117,9 +108,3 @@ export const BottomNavigation = observer(({ lastOpened }: Props) => {
|
||||
</Base>
|
||||
);
|
||||
});
|
||||
|
||||
export default connectState(BottomNavigation, (state) => {
|
||||
return {
|
||||
lastOpened: state.lastOpened,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import { Route, Switch } from "react-router";
|
||||
|
||||
import { useApplicationState } from "../../mobx/State";
|
||||
import { SIDEBAR_CHANNELS } from "../../mobx/stores/Layout";
|
||||
|
||||
import SidebarBase from "./SidebarBase";
|
||||
import HomeSidebar from "./left/HomeSidebar";
|
||||
import ServerListSidebar from "./left/ServerListSidebar";
|
||||
import ServerSidebar from "./left/ServerSidebar";
|
||||
import { useSelector } from "react-redux";
|
||||
import { State } from "../../redux";
|
||||
|
||||
export default function LeftSidebar() {
|
||||
const isOpen = useSelector((state: State) => state.sectionToggle['sidebar_channels'] ?? true)
|
||||
const layout = useApplicationState().layout;
|
||||
const isOpen = layout.getSectionState(SIDEBAR_CHANNELS, true);
|
||||
|
||||
return (
|
||||
<SidebarBase>
|
||||
|
||||
@@ -117,7 +117,7 @@
|
||||
}
|
||||
|
||||
&[data-muted="true"] {
|
||||
color: var(--tertiary-foreground);
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
&[data-alert="true"],
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
Notepad,
|
||||
} from "@styled-icons/boxicons-solid";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Link, Redirect, useLocation, useParams } from "react-router-dom";
|
||||
import { Link, useLocation, useParams } from "react-router-dom";
|
||||
import { RelationshipStatus } from "revolt-api/types/Users";
|
||||
|
||||
import { Text } from "preact-i18n";
|
||||
@@ -15,54 +15,38 @@ import ConditionalLink from "../../../lib/ConditionalLink";
|
||||
import PaintCounter from "../../../lib/PaintCounter";
|
||||
import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice";
|
||||
|
||||
import { dispatch } from "../../../redux";
|
||||
import { connectState } from "../../../redux/connector";
|
||||
import { Unreads } from "../../../redux/reducers/unreads";
|
||||
import { useApplicationState } from "../../../mobx/State";
|
||||
|
||||
import { useIntermediate } from "../../../context/intermediate/Intermediate";
|
||||
import { AppContext } from "../../../context/revoltjs/RevoltClient";
|
||||
|
||||
import Category from "../../ui/Category";
|
||||
import placeholderSVG from "../items/placeholder.svg";
|
||||
import { mapChannelWithUnread, useUnreads } from "./common";
|
||||
|
||||
import { GenericSidebarBase, GenericSidebarList } from "../SidebarBase";
|
||||
import ButtonItem, { ChannelButton } from "../items/ButtonItem";
|
||||
import ConnectionStatus from "../items/ConnectionStatus";
|
||||
|
||||
type Props = {
|
||||
unreads: Unreads;
|
||||
};
|
||||
|
||||
const HomeSidebar = observer((props: Props) => {
|
||||
export default observer(() => {
|
||||
const { pathname } = useLocation();
|
||||
const client = useContext(AppContext);
|
||||
const { channel } = useParams<{ channel: string }>();
|
||||
const state = useApplicationState();
|
||||
const { channel: currentChannel } = useParams<{ channel: string }>();
|
||||
const { openScreen } = useIntermediate();
|
||||
|
||||
const channels = [...client.channels.values()]
|
||||
.filter(
|
||||
(x) =>
|
||||
x.channel_type === "DirectMessage" ||
|
||||
x.channel_type === "Group",
|
||||
)
|
||||
.map((x) => mapChannelWithUnread(x, props.unreads));
|
||||
const channels = [...client.channels.values()].filter(
|
||||
(x) => x.channel_type === "DirectMessage" || x.channel_type === "Group",
|
||||
);
|
||||
|
||||
const obj = client.channels.get(channel);
|
||||
if (channel && !obj) return <Redirect to="/" />;
|
||||
if (obj) useUnreads({ ...props, channel: obj });
|
||||
const obj = client.channels.get(currentChannel);
|
||||
|
||||
useEffect(() => {
|
||||
if (!channel) return;
|
||||
// ! FIXME: move this globally
|
||||
// Track what page the user was last on (in home page).
|
||||
useEffect(() => state.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.last_message_id_or_past.localeCompare(b.last_message_id_or_past),
|
||||
);
|
||||
|
||||
return (
|
||||
<GenericSidebarBase mobilePadding>
|
||||
@@ -132,31 +116,37 @@ const HomeSidebar = observer((props: Props) => {
|
||||
{channels.length === 0 && (
|
||||
<img src={placeholderSVG} loading="eager" />
|
||||
)}
|
||||
{channels.map((x) => {
|
||||
{channels.map((channel) => {
|
||||
let user;
|
||||
if (x.channel.channel_type === "DirectMessage") {
|
||||
if (!x.channel.active) return null;
|
||||
user = x.channel.recipient;
|
||||
if (channel.channel_type === "DirectMessage") {
|
||||
if (!channel.active) return null;
|
||||
user = channel.recipient;
|
||||
|
||||
if (!user) {
|
||||
console.warn(
|
||||
`Skipped DM ${x.channel._id} because user was missing.`,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
if (!user) return null;
|
||||
}
|
||||
|
||||
const isUnread = channel.isUnread(state.notifications);
|
||||
const mentionCount = channel.getMentions(
|
||||
state.notifications,
|
||||
).length;
|
||||
|
||||
return (
|
||||
<ConditionalLink
|
||||
key={x.channel._id}
|
||||
active={x.channel._id === channel}
|
||||
to={`/channel/${x.channel._id}`}>
|
||||
key={channel._id}
|
||||
active={channel._id === currentChannel}
|
||||
to={`/channel/${channel._id}`}>
|
||||
<ChannelButton
|
||||
user={user}
|
||||
channel={x.channel}
|
||||
alert={x.unread}
|
||||
alertCount={x.alertCount}
|
||||
active={x.channel._id === channel}
|
||||
channel={channel}
|
||||
alert={
|
||||
mentionCount > 0
|
||||
? "mention"
|
||||
: isUnread
|
||||
? "unread"
|
||||
: undefined
|
||||
}
|
||||
alertCount={mentionCount}
|
||||
active={channel._id === currentChannel}
|
||||
/>
|
||||
</ConditionalLink>
|
||||
);
|
||||
@@ -166,13 +156,3 @@ const HomeSidebar = observer((props: Props) => {
|
||||
</GenericSidebarBase>
|
||||
);
|
||||
});
|
||||
|
||||
export default connectState(
|
||||
HomeSidebar,
|
||||
(state) => {
|
||||
return {
|
||||
unreads: state.unreads,
|
||||
};
|
||||
},
|
||||
true,
|
||||
);
|
||||
|
||||
@@ -12,9 +12,7 @@ import ConditionalLink from "../../../lib/ConditionalLink";
|
||||
import PaintCounter from "../../../lib/PaintCounter";
|
||||
import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice";
|
||||
|
||||
import { connectState } from "../../../redux/connector";
|
||||
import { LastOpened } from "../../../redux/reducers/last_opened";
|
||||
import { Unreads } from "../../../redux/reducers/unreads";
|
||||
import { useApplicationState } from "../../../mobx/State";
|
||||
|
||||
import { useIntermediate } from "../../../context/intermediate/Intermediate";
|
||||
import { useClient } from "../../../context/revoltjs/RevoltClient";
|
||||
@@ -25,7 +23,6 @@ import UserHover from "../../common/user/UserHover";
|
||||
import UserIcon from "../../common/user/UserIcon";
|
||||
import IconButton from "../../ui/IconButton";
|
||||
import LineDivider from "../../ui/LineDivider";
|
||||
import { mapChannelWithUnread } from "./common";
|
||||
|
||||
import { Children } from "../../../types/Preact";
|
||||
|
||||
@@ -195,46 +192,14 @@ function Swoosh() {
|
||||
);
|
||||
}
|
||||
|
||||
interface Props {
|
||||
unreads: Unreads;
|
||||
lastOpened: LastOpened;
|
||||
}
|
||||
|
||||
export const ServerListSidebar = observer(({ unreads, lastOpened }: Props) => {
|
||||
export default observer(() => {
|
||||
const client = useClient();
|
||||
const state = useApplicationState();
|
||||
|
||||
const { server: server_id } = useParams<{ server?: string }>();
|
||||
const server = server_id ? client.servers.get(server_id) : undefined;
|
||||
const activeServers = [...client.servers.values()];
|
||||
const channels = [...client.channels.values()].map((x) =>
|
||||
mapChannelWithUnread(x, unreads),
|
||||
);
|
||||
|
||||
const unreadChannels = channels
|
||||
.filter((x) => x.unread)
|
||||
.map((x) => x.channel?._id);
|
||||
|
||||
const servers = activeServers.map((server) => {
|
||||
let alertCount = 0;
|
||||
for (const id of server.channel_ids) {
|
||||
const channel = channels.find((x) => x.channel?._id === id);
|
||||
if (channel?.alertCount) {
|
||||
alertCount += channel.alertCount;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
server,
|
||||
unread: (typeof server.channel_ids.find((x) =>
|
||||
unreadChannels.includes(x),
|
||||
) !== "undefined"
|
||||
? alertCount > 0
|
||||
? "mention"
|
||||
: "unread"
|
||||
: undefined) as "mention" | "unread" | undefined,
|
||||
alertCount,
|
||||
};
|
||||
});
|
||||
const servers = [...client.servers.values()];
|
||||
const channels = [...client.channels.values()];
|
||||
|
||||
const history = useHistory();
|
||||
const path = useLocation().pathname;
|
||||
@@ -242,16 +207,16 @@ export const ServerListSidebar = observer(({ unreads, lastOpened }: Props) => {
|
||||
|
||||
let homeUnread: "mention" | "unread" | undefined;
|
||||
let alertCount = 0;
|
||||
for (const x of channels) {
|
||||
if (x.channel?.channel_type === "Group" && x.unread) {
|
||||
for (const channel of channels) {
|
||||
if (channel?.channel_type === "Group" && channel.unread) {
|
||||
homeUnread = "unread";
|
||||
alertCount += x.alertCount ?? 0;
|
||||
alertCount += channel.mentions.length;
|
||||
}
|
||||
|
||||
if (
|
||||
x.channel?.channel_type === "DirectMessage" &&
|
||||
x.channel.active &&
|
||||
x.unread
|
||||
channel.channel_type === "DirectMessage" &&
|
||||
channel.active &&
|
||||
channel.unread
|
||||
) {
|
||||
alertCount++;
|
||||
}
|
||||
@@ -270,7 +235,7 @@ export const ServerListSidebar = observer(({ unreads, lastOpened }: Props) => {
|
||||
<ServerList>
|
||||
<ConditionalLink
|
||||
active={homeActive}
|
||||
to={lastOpened.home ? `/channel/${lastOpened.home}` : "/"}>
|
||||
to={state.layout.getLastHomePath()}>
|
||||
<ServerEntry home active={homeActive}>
|
||||
<Swoosh />
|
||||
<div
|
||||
@@ -278,13 +243,13 @@ export const ServerListSidebar = observer(({ unreads, lastOpened }: Props) => {
|
||||
onClick={() =>
|
||||
homeActive && history.push("/settings")
|
||||
}>
|
||||
<UserHover user={client.user}>
|
||||
<UserHover user={client.user ?? undefined}>
|
||||
<Icon
|
||||
size={42}
|
||||
unread={homeUnread}
|
||||
count={alertCount}>
|
||||
<UserIcon
|
||||
target={client.user}
|
||||
target={client.user ?? undefined}
|
||||
size={32}
|
||||
status
|
||||
hover
|
||||
@@ -295,35 +260,40 @@ export const ServerListSidebar = observer(({ unreads, lastOpened }: Props) => {
|
||||
</ServerEntry>
|
||||
</ConditionalLink>
|
||||
<LineDivider />
|
||||
{servers.map((entry) => {
|
||||
const active = entry.server._id === server?._id;
|
||||
const id = lastOpened[entry.server._id];
|
||||
{servers.map((server) => {
|
||||
const active = server._id === server_id;
|
||||
|
||||
const isUnread = server.isUnread(state.notifications);
|
||||
const mentionCount = server.getMentions(
|
||||
state.notifications,
|
||||
).length;
|
||||
|
||||
return (
|
||||
<ConditionalLink
|
||||
key={entry.server._id}
|
||||
key={server._id}
|
||||
active={active}
|
||||
to={`/server/${entry.server._id}${
|
||||
id ? `/channel/${id}` : ""
|
||||
}`}>
|
||||
to={state.layout.getServerPath(server._id)}>
|
||||
<ServerEntry
|
||||
active={active}
|
||||
onContextMenu={attachContextMenu("Menu", {
|
||||
server: entry.server._id,
|
||||
unread: entry.unread,
|
||||
server: server._id,
|
||||
unread: isUnread,
|
||||
})}>
|
||||
<Swoosh />
|
||||
<Tooltip
|
||||
content={entry.server.name}
|
||||
content={server.name}
|
||||
placement="right">
|
||||
<Icon
|
||||
size={42}
|
||||
unread={entry.unread}
|
||||
count={entry.alertCount}>
|
||||
<ServerIcon
|
||||
size={32}
|
||||
target={entry.server}
|
||||
/>
|
||||
unread={
|
||||
mentionCount > 0
|
||||
? "mention"
|
||||
: isUnread
|
||||
? "unread"
|
||||
: undefined
|
||||
}
|
||||
count={mentionCount}>
|
||||
<ServerIcon size={32} target={server} />
|
||||
</Icon>
|
||||
</Tooltip>
|
||||
</ServerEntry>
|
||||
@@ -357,10 +327,3 @@ export const ServerListSidebar = observer(({ unreads, lastOpened }: Props) => {
|
||||
</ServersBase>
|
||||
);
|
||||
});
|
||||
|
||||
export default connectState(ServerListSidebar, (state) => {
|
||||
return {
|
||||
unreads: state.unreads,
|
||||
lastOpened: state.lastOpened,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -10,26 +10,17 @@ import PaintCounter from "../../../lib/PaintCounter";
|
||||
import { internalEmit } from "../../../lib/eventEmitter";
|
||||
import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice";
|
||||
|
||||
import { dispatch } from "../../../redux";
|
||||
import { connectState } from "../../../redux/connector";
|
||||
import { Notifications } from "../../../redux/reducers/notifications";
|
||||
import { Unreads } from "../../../redux/reducers/unreads";
|
||||
import { useApplicationState } from "../../../mobx/State";
|
||||
|
||||
import { useClient } from "../../../context/revoltjs/RevoltClient";
|
||||
|
||||
import CollapsibleSection from "../../common/CollapsibleSection";
|
||||
import ServerHeader from "../../common/ServerHeader";
|
||||
import Category from "../../ui/Category";
|
||||
import { mapChannelWithUnread, useUnreads } from "./common";
|
||||
|
||||
import { ChannelButton } from "../items/ButtonItem";
|
||||
import ConnectionStatus from "../items/ConnectionStatus";
|
||||
|
||||
interface Props {
|
||||
unreads: Unreads;
|
||||
notifications: Notifications;
|
||||
}
|
||||
|
||||
const ServerBase = styled.div`
|
||||
height: 100%;
|
||||
width: 232px;
|
||||
@@ -57,8 +48,9 @@ const ServerList = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
const ServerSidebar = observer((props: Props) => {
|
||||
export default observer(() => {
|
||||
const client = useClient();
|
||||
const state = useApplicationState();
|
||||
const { server: server_id, channel: channel_id } =
|
||||
useParams<{ server: string; channel?: string }>();
|
||||
|
||||
@@ -76,16 +68,13 @@ const ServerSidebar = observer((props: Props) => {
|
||||
);
|
||||
if (channel_id && !channel) return <Redirect to={`/server/${server_id}`} />;
|
||||
|
||||
if (channel) useUnreads({ ...props, channel });
|
||||
|
||||
// ! FIXME: move this globally
|
||||
// Track which channel the user was last on.
|
||||
useEffect(() => {
|
||||
if (!channel_id) return;
|
||||
if (!server_id) return;
|
||||
|
||||
dispatch({
|
||||
type: "LAST_OPENED_SET",
|
||||
parent: server_id!,
|
||||
child: channel_id!,
|
||||
});
|
||||
state.layout.setLastOpened(server_id, channel_id);
|
||||
}, [channel_id, server_id]);
|
||||
|
||||
const uncategorised = new Set(server.channel_ids);
|
||||
@@ -96,7 +85,8 @@ const ServerSidebar = observer((props: Props) => {
|
||||
if (!entry) return;
|
||||
|
||||
const active = channel?._id === entry._id;
|
||||
const muted = props.notifications[id] === "none";
|
||||
const isUnread = entry.isUnread(state.notifications);
|
||||
const mentionCount = entry.getMentions(state.notifications);
|
||||
|
||||
return (
|
||||
<ConditionalLink
|
||||
@@ -117,10 +107,15 @@ const ServerSidebar = observer((props: Props) => {
|
||||
<ChannelButton
|
||||
channel={entry}
|
||||
active={active}
|
||||
// ! FIXME: pull it out directly
|
||||
alert={mapChannelWithUnread(entry, props.unreads).unread}
|
||||
alert={
|
||||
mentionCount.length > 0
|
||||
? "mention"
|
||||
: isUnread
|
||||
? "unread"
|
||||
: undefined
|
||||
}
|
||||
compact
|
||||
muted={muted}
|
||||
muted={state.notifications.isMuted(entry)}
|
||||
/>
|
||||
</ConditionalLink>
|
||||
);
|
||||
@@ -163,10 +158,3 @@ const ServerSidebar = observer((props: Props) => {
|
||||
</ServerBase>
|
||||
);
|
||||
});
|
||||
|
||||
export default connectState(ServerSidebar, (state) => {
|
||||
return {
|
||||
unreads: state.unreads,
|
||||
notifications: state.notifications,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
import { reaction } from "mobx";
|
||||
import { Channel } from "revolt.js/dist/maps/Channels";
|
||||
|
||||
import { useLayoutEffect, useRef } from "preact/hooks";
|
||||
|
||||
import { dispatch } from "../../../redux";
|
||||
import { Unreads } from "../../../redux/reducers/unreads";
|
||||
|
||||
type UnreadProps = {
|
||||
channel: Channel;
|
||||
unreads: Unreads;
|
||||
};
|
||||
|
||||
export function useUnreads({ channel, unreads }: UnreadProps) {
|
||||
// const firstLoad = useRef(true);
|
||||
useLayoutEffect(() => {
|
||||
function checkUnread(target: Channel) {
|
||||
if (!target) return;
|
||||
if (target._id !== channel._id) return;
|
||||
if (
|
||||
target.channel_type === "SavedMessages" ||
|
||||
target.channel_type === "VoiceChannel"
|
||||
)
|
||||
return;
|
||||
|
||||
const unread = unreads[channel._id]?.last_id;
|
||||
if (target.last_message_id) {
|
||||
if (
|
||||
!unread ||
|
||||
(unread && target.last_message_id.localeCompare(unread) > 0)
|
||||
) {
|
||||
dispatch({
|
||||
type: "UNREADS_MARK_READ",
|
||||
channel: channel._id,
|
||||
message: target.last_message_id,
|
||||
});
|
||||
|
||||
channel.ack(target.last_message_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkUnread(channel);
|
||||
return reaction(
|
||||
() => channel.last_message,
|
||||
() => checkUnread(channel),
|
||||
);
|
||||
}, [channel, unreads]);
|
||||
}
|
||||
|
||||
export function mapChannelWithUnread(channel: Channel, unreads: Unreads) {
|
||||
const last_message_id = channel.last_message_id;
|
||||
|
||||
let unread: "mention" | "unread" | undefined;
|
||||
let alertCount: undefined | number;
|
||||
if (last_message_id && unreads) {
|
||||
const u = unreads[channel._id];
|
||||
if (u) {
|
||||
if (u.mentions && u.mentions.length > 0) {
|
||||
alertCount = u.mentions.length;
|
||||
unread = "mention";
|
||||
} else if (
|
||||
u.last_id &&
|
||||
(last_message_id as string).localeCompare(u.last_id) > 0
|
||||
) {
|
||||
unread = "unread";
|
||||
}
|
||||
} else {
|
||||
unread = "unread";
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
channel,
|
||||
timestamp: last_message_id ?? channel._id,
|
||||
unread,
|
||||
alertCount,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user