mirror of
https://github.com/stoatchat/for-legacy-web.git
synced 2026-03-07 17:35:28 +00:00
Merge branch 'mobx'
This commit is contained in:
@@ -1,21 +1,21 @@
|
||||
import { Hash } from "@styled-icons/boxicons-regular";
|
||||
import { Ghost } from "@styled-icons/boxicons-solid";
|
||||
import { reaction } from "mobx";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { Channel as ChannelI } from "revolt.js/dist/maps/Channels";
|
||||
import styled from "styled-components";
|
||||
|
||||
import { Text } from "preact-i18n";
|
||||
|
||||
import { useState } from "preact/hooks";
|
||||
import { useEffect } from "preact/hooks";
|
||||
|
||||
import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice";
|
||||
|
||||
import { dispatch, getState } from "../../redux";
|
||||
import { useApplicationState } from "../../mobx/State";
|
||||
import { SIDEBAR_MEMBERS } from "../../mobx/stores/Layout";
|
||||
|
||||
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 MessageBox from "../../components/common/messaging/MessageBox";
|
||||
import JumpToBottom from "../../components/common/messaging/bars/JumpToBottom";
|
||||
@@ -52,19 +52,19 @@ const PlaceholderBase = styled.div`
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
margin: auto;
|
||||
|
||||
|
||||
.primary {
|
||||
color: var(--secondary-foreground);
|
||||
font-weight: 700;
|
||||
font-size: 22px;
|
||||
margin: 0 0 5px 0;
|
||||
}
|
||||
|
||||
|
||||
.secondary {
|
||||
color: var(--tertiary-foreground);
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
|
||||
svg {
|
||||
margin: 2em auto;
|
||||
fill-opacity: 0.8;
|
||||
@@ -84,17 +84,26 @@ export function Channel({ id }: { id: string }) {
|
||||
return <TextChannel channel={channel} />;
|
||||
}
|
||||
|
||||
const MEMBERS_SIDEBAR_KEY = "sidebar_members";
|
||||
const CHANNELS_SIDEBAR_KEY = "sidebar_channels";
|
||||
const TextChannel = observer(({ channel }: { channel: ChannelI }) => {
|
||||
const [showMembers, setMembers] = useState(
|
||||
getState().sectionToggle[MEMBERS_SIDEBAR_KEY] ?? true,
|
||||
);
|
||||
const [showChannels, setChannels] = useState(
|
||||
getState().sectionToggle[CHANNELS_SIDEBAR_KEY] ?? true,
|
||||
);
|
||||
const layout = useApplicationState().layout;
|
||||
|
||||
// Mark channel as read.
|
||||
useEffect(() => {
|
||||
const checkUnread = () =>
|
||||
channel.unread &&
|
||||
channel.client.unreads!.markRead(
|
||||
channel._id,
|
||||
channel.last_message_id!,
|
||||
true,
|
||||
);
|
||||
|
||||
checkUnread();
|
||||
return reaction(
|
||||
() => channel.last_message_id,
|
||||
() => checkUnread(),
|
||||
);
|
||||
}, [channel]);
|
||||
|
||||
const id = channel._id;
|
||||
return (
|
||||
<AgeGate
|
||||
type="channel"
|
||||
@@ -106,54 +115,19 @@ const TextChannel = observer(({ channel }: { channel: ChannelI }) => {
|
||||
channel.nsfw
|
||||
)
|
||||
}>
|
||||
<ChannelHeader
|
||||
channel={channel}
|
||||
toggleSidebar={() => {
|
||||
setMembers(!showMembers);
|
||||
|
||||
if (showMembers) {
|
||||
dispatch({
|
||||
type: "SECTION_TOGGLE_SET",
|
||||
id: MEMBERS_SIDEBAR_KEY,
|
||||
state: false,
|
||||
});
|
||||
} else {
|
||||
dispatch({
|
||||
type: "SECTION_TOGGLE_UNSET",
|
||||
id: MEMBERS_SIDEBAR_KEY,
|
||||
});
|
||||
}
|
||||
}}
|
||||
toggleChannelSidebar={() => {
|
||||
if (isTouchscreenDevice) {
|
||||
return
|
||||
}
|
||||
|
||||
setChannels(!showChannels);
|
||||
|
||||
if (showChannels) {
|
||||
dispatch({
|
||||
type: "SECTION_TOGGLE_SET",
|
||||
id: CHANNELS_SIDEBAR_KEY,
|
||||
state: false,
|
||||
});
|
||||
} else {
|
||||
dispatch({
|
||||
type: "SECTION_TOGGLE_UNSET",
|
||||
id: CHANNELS_SIDEBAR_KEY,
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<ChannelHeader channel={channel} />
|
||||
<ChannelMain>
|
||||
<ChannelContent>
|
||||
<VoiceHeader id={id} />
|
||||
<VoiceHeader id={channel._id} />
|
||||
<MessageArea channel={channel} />
|
||||
<TypingIndicator channel={channel} />
|
||||
<JumpToBottom channel={channel} />
|
||||
<MessageBox channel={channel} />
|
||||
</ChannelContent>
|
||||
{!isTouchscreenDevice && showMembers && <RightSidebar />}
|
||||
{!isTouchscreenDevice &&
|
||||
layout.getSectionState(SIDEBAR_MEMBERS, true) && (
|
||||
<RightSidebar />
|
||||
)}
|
||||
</ChannelMain>
|
||||
</AgeGate>
|
||||
);
|
||||
@@ -173,13 +147,19 @@ function ChannelPlaceholder() {
|
||||
<PlaceholderBase>
|
||||
<Header placement="primary">
|
||||
<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>
|
||||
|
||||
<div className="placeholder">
|
||||
<Ghost width={80} />
|
||||
<div className="primary"><Text id="app.main.channel.errors.title" /></div>
|
||||
<div className="secondary"><Text id="app.main.channel.errors.nochannels" /></div>
|
||||
<div className="primary">
|
||||
<Text id="app.main.channel.errors.title" />
|
||||
</div>
|
||||
<div className="secondary">
|
||||
<Text id="app.main.channel.errors.nochannels" />
|
||||
</div>
|
||||
</div>
|
||||
</PlaceholderBase>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { At, Hash, Menu, ChevronLeft } from "@styled-icons/boxicons-regular";
|
||||
import { At, Hash } 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";
|
||||
@@ -7,6 +7,9 @@ import styled, { css } from "styled-components";
|
||||
|
||||
import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice";
|
||||
|
||||
import { useApplicationState } from "../../mobx/State";
|
||||
import { SIDEBAR_MEMBERS } from "../../mobx/stores/Layout";
|
||||
|
||||
import { useIntermediate } from "../../context/intermediate/Intermediate";
|
||||
import { getChannelName } from "../../context/revoltjs/util";
|
||||
|
||||
@@ -65,7 +68,7 @@ const Info = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
const IconContainer = styled.div`
|
||||
const IconConainer = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
@@ -84,84 +87,81 @@ const IconContainer = styled.div`
|
||||
`}
|
||||
`;
|
||||
|
||||
export default observer(
|
||||
({ channel, toggleSidebar, toggleChannelSidebar }: ChannelHeaderProps) => {
|
||||
const { openScreen } = useIntermediate();
|
||||
export default observer(({ channel }: ChannelHeaderProps) => {
|
||||
const { openScreen } = useIntermediate();
|
||||
const layout = useApplicationState().layout;
|
||||
|
||||
const name = getChannelName(channel);
|
||||
let icon, recipient: User | undefined;
|
||||
switch (channel.channel_type) {
|
||||
case "SavedMessages":
|
||||
icon = <Notepad size={24} />;
|
||||
break;
|
||||
case "DirectMessage":
|
||||
icon = <At size={24} />;
|
||||
recipient = channel.recipient;
|
||||
break;
|
||||
case "Group":
|
||||
icon = <Group size={24} />;
|
||||
break;
|
||||
case "TextChannel":
|
||||
icon = <Hash size={24} />;
|
||||
break;
|
||||
}
|
||||
const name = getChannelName(channel);
|
||||
let icon, recipient: User | undefined;
|
||||
switch (channel.channel_type) {
|
||||
case "SavedMessages":
|
||||
icon = <Notepad size={24} />;
|
||||
break;
|
||||
case "DirectMessage":
|
||||
icon = <At size={24} />;
|
||||
recipient = channel.recipient;
|
||||
break;
|
||||
case "Group":
|
||||
icon = <Group size={24} />;
|
||||
break;
|
||||
case "TextChannel":
|
||||
icon = <Hash size={24} />;
|
||||
break;
|
||||
}
|
||||
|
||||
return (
|
||||
<Header placement="primary">
|
||||
<HamburgerAction />
|
||||
<IconContainer onClick={toggleChannelSidebar}>
|
||||
{/*isTouchscreenDevice && <ChevronLeft size={18} /> FIXME: requires mobx merge */}
|
||||
{icon}
|
||||
</IconContainer>
|
||||
<Info>
|
||||
<span className="name">{name}</span>
|
||||
{isTouchscreenDevice &&
|
||||
channel.channel_type === "DirectMessage" && (
|
||||
<>
|
||||
<div className="divider" />
|
||||
<span className="desc">
|
||||
<div
|
||||
className="status"
|
||||
style={{
|
||||
backgroundColor:
|
||||
useStatusColour(recipient),
|
||||
}}
|
||||
/>
|
||||
<UserStatus user={recipient} />
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
{!isTouchscreenDevice &&
|
||||
(channel.channel_type === "Group" ||
|
||||
channel.channel_type === "TextChannel") &&
|
||||
channel.description && (
|
||||
<>
|
||||
<div className="divider" />
|
||||
<span
|
||||
className="desc"
|
||||
onClick={() =>
|
||||
openScreen({
|
||||
id: "channel_info",
|
||||
channel,
|
||||
})
|
||||
}>
|
||||
<Markdown
|
||||
content={
|
||||
channel.description.split(
|
||||
"\n",
|
||||
)[0] ?? ""
|
||||
}
|
||||
disallowBigEmoji
|
||||
/>
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
</Info>
|
||||
<HeaderActions
|
||||
channel={channel}
|
||||
toggleSidebar={toggleSidebar}
|
||||
/>
|
||||
</Header>
|
||||
);
|
||||
},
|
||||
);
|
||||
return (
|
||||
<Header placement="primary">
|
||||
<HamburgerAction />
|
||||
<IconConainer
|
||||
onClick={() =>
|
||||
layout.toggleSectionState(SIDEBAR_MEMBERS, true)
|
||||
}>
|
||||
{/*isTouchscreenDevice && <ChevronLeft size={18} /> FIXME: requires mobx merge */}
|
||||
{icon}
|
||||
</IconConainer>
|
||||
<Info>
|
||||
<span className="name">{name}</span>
|
||||
{isTouchscreenDevice &&
|
||||
channel.channel_type === "DirectMessage" && (
|
||||
<>
|
||||
<div className="divider" />
|
||||
<span className="desc">
|
||||
<div
|
||||
className="status"
|
||||
style={{
|
||||
backgroundColor:
|
||||
useStatusColour(recipient),
|
||||
}}
|
||||
/>
|
||||
<UserStatus user={recipient} />
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
{!isTouchscreenDevice &&
|
||||
(channel.channel_type === "Group" ||
|
||||
channel.channel_type === "TextChannel") &&
|
||||
channel.description && (
|
||||
<>
|
||||
<div className="divider" />
|
||||
<span
|
||||
className="desc"
|
||||
onClick={() =>
|
||||
openScreen({
|
||||
id: "channel_info",
|
||||
channel,
|
||||
})
|
||||
}>
|
||||
<Markdown
|
||||
content={
|
||||
channel.description.split("\n")[0] ?? ""
|
||||
}
|
||||
disallowBigEmoji
|
||||
/>
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
</Info>
|
||||
<HeaderActions channel={channel} />
|
||||
</Header>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -14,6 +14,9 @@ import { internalEmit } from "../../../lib/eventEmitter";
|
||||
import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice";
|
||||
import { voiceState, VoiceStatus } from "../../../lib/vortex/VoiceState";
|
||||
|
||||
import { useApplicationState } from "../../../mobx/State";
|
||||
import { SIDEBAR_MEMBERS } from "../../../mobx/stores/Layout";
|
||||
|
||||
import { useIntermediate } from "../../../context/intermediate/Intermediate";
|
||||
|
||||
import UpdateIndicator from "../../../components/common/UpdateIndicator";
|
||||
@@ -21,10 +24,8 @@ import IconButton from "../../../components/ui/IconButton";
|
||||
|
||||
import { ChannelHeaderProps } from "../ChannelHeader";
|
||||
|
||||
export default function HeaderActions({
|
||||
channel,
|
||||
toggleSidebar,
|
||||
}: ChannelHeaderProps) {
|
||||
export default function HeaderActions({ channel }: ChannelHeaderProps) {
|
||||
const layout = useApplicationState().layout;
|
||||
const { openScreen } = useIntermediate();
|
||||
const history = useHistory();
|
||||
|
||||
@@ -40,7 +41,7 @@ export default function HeaderActions({
|
||||
if (isTouchscreenDevice) {
|
||||
openRightSidebar();
|
||||
} else {
|
||||
toggleSidebar?.();
|
||||
layout.toggleSectionState(SIDEBAR_MEMBERS, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,14 +10,12 @@ import styled from "styled-components";
|
||||
import { decodeTime } from "ulid";
|
||||
|
||||
import { Text } from "preact-i18n";
|
||||
import { memo } from "preact/compat";
|
||||
import { useEffect, useState } from "preact/hooks";
|
||||
|
||||
import { internalSubscribe, internalEmit } from "../../../lib/eventEmitter";
|
||||
import { ChannelRenderer } from "../../../lib/renderer/Singleton";
|
||||
|
||||
import { connectState } from "../../../redux/connector";
|
||||
import { QueuedMessage } from "../../../redux/reducers/queue";
|
||||
import { useApplicationState } from "../../../mobx/State";
|
||||
|
||||
import RequiresOnline from "../../../context/revoltjs/RequiresOnline";
|
||||
import { useClient } from "../../../context/revoltjs/RevoltClient";
|
||||
@@ -33,7 +31,6 @@ import MessageEditor from "./MessageEditor";
|
||||
|
||||
interface Props {
|
||||
highlight?: string;
|
||||
queue: QueuedMessage[];
|
||||
renderer: ChannelRenderer;
|
||||
}
|
||||
|
||||
@@ -48,9 +45,10 @@ const BlockedMessage = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
const MessageRenderer = observer(({ renderer, queue, highlight }: Props) => {
|
||||
export default observer(({ renderer, highlight }: Props) => {
|
||||
const client = useClient();
|
||||
const userId = client.user!._id;
|
||||
const queue = useApplicationState().queue;
|
||||
|
||||
const [editing, setEditing] = useState<string | undefined>(undefined);
|
||||
const stopEditing = () => {
|
||||
@@ -192,8 +190,7 @@ const MessageRenderer = observer(({ renderer, queue, highlight }: Props) => {
|
||||
|
||||
const nonces = renderer.messages.map((x) => x.nonce);
|
||||
if (renderer.atBottom) {
|
||||
for (const msg of queue) {
|
||||
if (msg.channel !== renderer.channel._id) continue;
|
||||
for (const msg of queue.get(renderer.channel._id)) {
|
||||
if (nonces.includes(msg.id)) continue;
|
||||
|
||||
if (previous) {
|
||||
@@ -237,11 +234,3 @@ const MessageRenderer = observer(({ renderer, queue, highlight }: Props) => {
|
||||
|
||||
return <>{render}</>;
|
||||
});
|
||||
|
||||
export default memo(
|
||||
connectState<Omit<Props, "queue">>(MessageRenderer, (state) => {
|
||||
return {
|
||||
queue: state.queue,
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user