mirror of
https://github.com/revoltchat/revite.git
synced 2024-12-24 22:52:09 -05:00
Fix: Icons collapsing in flex.
Feature: Remember what channel was opened last. Channels: ESC to focus message box / cancel editing.
This commit is contained in:
parent
558ec17726
commit
363789c825
12 changed files with 137 additions and 34 deletions
|
@ -14,6 +14,8 @@ interface IconModifiers {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default styled.svg<IconModifiers>`
|
export default styled.svg<IconModifiers>`
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
img {
|
img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
@ -26,6 +28,7 @@ export default styled.svg<IconModifiers>`
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const ImageIconBase = styled.img<IconModifiers>`
|
export const ImageIconBase = styled.img<IconModifiers>`
|
||||||
|
flex-shrink: 0;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
|
|
||||||
${ props => !props.square && css`
|
${ props => !props.square && css`
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Localizer, Text } from "preact-i18n";
|
import { Localizer, Text } from "preact-i18n";
|
||||||
import { useContext } from "preact/hooks";
|
import { useContext, useEffect } from "preact/hooks";
|
||||||
import { Home, Users, Tool, Save } from "@styled-icons/feather";
|
import { Home, Users, Tool, Save } from "@styled-icons/feather";
|
||||||
|
|
||||||
import Category from '../../ui/Category';
|
import Category from '../../ui/Category';
|
||||||
|
@ -10,6 +10,7 @@ import { connectState } from "../../../redux/connector";
|
||||||
import ConnectionStatus from '../items/ConnectionStatus';
|
import ConnectionStatus from '../items/ConnectionStatus';
|
||||||
import { WithDispatcher } from "../../../redux/reducers";
|
import { WithDispatcher } from "../../../redux/reducers";
|
||||||
import { Unreads } from "../../../redux/reducers/unreads";
|
import { Unreads } from "../../../redux/reducers/unreads";
|
||||||
|
import ConditionalLink from "../../../lib/ConditionalLink";
|
||||||
import { mapChannelWithUnread, useUnreads } from "./common";
|
import { mapChannelWithUnread, useUnreads } from "./common";
|
||||||
import { Users as UsersNS } from 'revolt.js/dist/api/objects';
|
import { Users as UsersNS } from 'revolt.js/dist/api/objects';
|
||||||
import ButtonItem, { ChannelButton } from '../items/ButtonItem';
|
import ButtonItem, { ChannelButton } from '../items/ButtonItem';
|
||||||
|
@ -37,6 +38,16 @@ function HomeSidebar(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(() => {
|
||||||
|
if (!channel) return;
|
||||||
|
|
||||||
|
props.dispatcher({
|
||||||
|
type: 'LAST_OPENED_SET',
|
||||||
|
parent: 'home',
|
||||||
|
child: channel
|
||||||
|
});
|
||||||
|
}, [ channel ]);
|
||||||
|
|
||||||
const channelsArr = channels
|
const channelsArr = channels
|
||||||
.filter(x => x.channel_type !== 'SavedMessages')
|
.filter(x => x.channel_type !== 'SavedMessages')
|
||||||
.map(x => mapChannelWithUnread(x, props.unreads));
|
.map(x => mapChannelWithUnread(x, props.unreads));
|
||||||
|
@ -55,13 +66,13 @@ function HomeSidebar(props: Props) {
|
||||||
<GenericSidebarList>
|
<GenericSidebarList>
|
||||||
{!isTouchscreenDevice && (
|
{!isTouchscreenDevice && (
|
||||||
<>
|
<>
|
||||||
<Link to="/">
|
<ConditionalLink active={pathname === "/"} to="/">
|
||||||
<ButtonItem active={pathname === "/"}>
|
<ButtonItem active={pathname === "/"}>
|
||||||
<Home size={20} />
|
<Home size={20} />
|
||||||
<span><Text id="app.navigation.tabs.home" /></span>
|
<span><Text id="app.navigation.tabs.home" /></span>
|
||||||
</ButtonItem>
|
</ButtonItem>
|
||||||
</Link>
|
</ConditionalLink>
|
||||||
<Link to="/friends">
|
<ConditionalLink active={pathname === "/friends"} to="/friends">
|
||||||
<ButtonItem
|
<ButtonItem
|
||||||
active={pathname === "/friends"}
|
active={pathname === "/friends"}
|
||||||
alert={
|
alert={
|
||||||
|
@ -75,15 +86,15 @@ function HomeSidebar(props: Props) {
|
||||||
<Users size={20} />
|
<Users size={20} />
|
||||||
<span><Text id="app.navigation.tabs.friends" /></span>
|
<span><Text id="app.navigation.tabs.friends" /></span>
|
||||||
</ButtonItem>
|
</ButtonItem>
|
||||||
</Link>
|
</ConditionalLink>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<Link to="/open/saved">
|
<ConditionalLink active={obj?.channel_type === "SavedMessages"} to="/open/saved">
|
||||||
<ButtonItem active={obj?.channel_type === "SavedMessages"}>
|
<ButtonItem active={obj?.channel_type === "SavedMessages"}>
|
||||||
<Save size={20} />
|
<Save size={20} />
|
||||||
<span><Text id="app.navigation.tabs.saved" /></span>
|
<span><Text id="app.navigation.tabs.saved" /></span>
|
||||||
</ButtonItem>
|
</ButtonItem>
|
||||||
</Link>
|
</ConditionalLink>
|
||||||
{import.meta.env.DEV && (
|
{import.meta.env.DEV && (
|
||||||
<Link to="/dev">
|
<Link to="/dev">
|
||||||
<ButtonItem active={pathname === "/dev"}>
|
<ButtonItem active={pathname === "/dev"}>
|
||||||
|
@ -115,7 +126,7 @@ function HomeSidebar(props: Props) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link to={`/channel/${x._id}`}>
|
<ConditionalLink active={x._id === channel} to={`/channel/${x._id}`}>
|
||||||
<ChannelButton
|
<ChannelButton
|
||||||
user={user}
|
user={user}
|
||||||
channel={x}
|
channel={x}
|
||||||
|
@ -123,7 +134,7 @@ function HomeSidebar(props: Props) {
|
||||||
alertCount={x.alertCount}
|
alertCount={x.alertCount}
|
||||||
active={x._id === channel}
|
active={x._id === channel}
|
||||||
/>
|
/>
|
||||||
</Link>
|
</ConditionalLink>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
<PaintCounter />
|
<PaintCounter />
|
||||||
|
|
|
@ -8,9 +8,11 @@ import { PlusCircle } from "@styled-icons/feather";
|
||||||
import PaintCounter from "../../../lib/PaintCounter";
|
import PaintCounter from "../../../lib/PaintCounter";
|
||||||
import { attachContextMenu } from 'preact-context-menu';
|
import { attachContextMenu } from 'preact-context-menu';
|
||||||
import { connectState } from "../../../redux/connector";
|
import { connectState } from "../../../redux/connector";
|
||||||
|
import { useLocation, useParams } from "react-router-dom";
|
||||||
import { Unreads } from "../../../redux/reducers/unreads";
|
import { Unreads } from "../../../redux/reducers/unreads";
|
||||||
|
import ConditionalLink from "../../../lib/ConditionalLink";
|
||||||
import { Channel, Servers } from "revolt.js/dist/api/objects";
|
import { Channel, Servers } from "revolt.js/dist/api/objects";
|
||||||
import { Link, useLocation, useParams } from "react-router-dom";
|
import { LastOpened } from "../../../redux/reducers/last_opened";
|
||||||
import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice";
|
import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice";
|
||||||
import { useIntermediate } from "../../../context/intermediate/Intermediate";
|
import { useIntermediate } from "../../../context/intermediate/Intermediate";
|
||||||
import { useChannels, useForceUpdate, useServers } from "../../../context/revoltjs/hooks";
|
import { useChannels, useForceUpdate, useServers } from "../../../context/revoltjs/hooks";
|
||||||
|
@ -104,9 +106,10 @@ const ServerEntry = styled.div<{ active: boolean, invert?: boolean }>`
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
unreads: Unreads;
|
unreads: Unreads;
|
||||||
|
lastOpened: LastOpened;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ServerListSidebar({ unreads }: Props) {
|
export function ServerListSidebar({ unreads, lastOpened }: Props) {
|
||||||
const ctx = useForceUpdate();
|
const ctx = useForceUpdate();
|
||||||
const activeServers = useServers(undefined, ctx) as Servers.Server[];
|
const activeServers = useServers(undefined, ctx) as Servers.Server[];
|
||||||
const channels = (useChannels(undefined, ctx) as Channel[])
|
const channels = (useChannels(undefined, ctx) as Channel[])
|
||||||
|
@ -148,31 +151,36 @@ export function ServerListSidebar({ unreads }: Props) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (alertCount > 0) homeUnread = 'mention';
|
if (alertCount > 0) homeUnread = 'mention';
|
||||||
|
const homeActive = typeof server === 'undefined' && !path.startsWith('/invite');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ServersBase>
|
<ServersBase>
|
||||||
<ServerList>
|
<ServerList>
|
||||||
<Link to={`/`}>
|
<ConditionalLink active={homeActive} to={lastOpened.home ? `/channel/${lastOpened.home}` : '/'}>
|
||||||
<ServerEntry invert
|
<ServerEntry invert active={homeActive}>
|
||||||
active={typeof server === 'undefined' && !path.startsWith('/invite')}>
|
|
||||||
<Icon size={36} unread={homeUnread}>
|
<Icon size={36} unread={homeUnread}>
|
||||||
<img src={logoSVG} />
|
<img src={logoSVG} />
|
||||||
</Icon>
|
</Icon>
|
||||||
</ServerEntry>
|
</ServerEntry>
|
||||||
</Link>
|
</ConditionalLink>
|
||||||
<LineDivider />
|
<LineDivider />
|
||||||
{
|
{
|
||||||
servers.map(entry =>
|
servers.map(entry => {
|
||||||
<Link to={`/server/${entry!._id}`}>
|
const active = entry!._id === server?._id;
|
||||||
<ServerEntry
|
const id = lastOpened[entry!._id];
|
||||||
active={entry!._id === server?._id}
|
|
||||||
onContextMenu={attachContextMenu('Menu', { server: entry!._id })}>
|
return (
|
||||||
<Icon size={36} unread={entry.unread}>
|
<ConditionalLink active={active} to={`/server/${entry!._id}` + (id ? `/channel/${id}` : '')}>
|
||||||
<ServerIcon size={32} target={entry} />
|
<ServerEntry
|
||||||
</Icon>
|
active={active}
|
||||||
</ServerEntry>
|
onContextMenu={attachContextMenu('Menu', { server: entry!._id })}>
|
||||||
</Link>
|
<Icon size={36} unread={entry.unread}>
|
||||||
)
|
<ServerIcon size={32} target={entry} />
|
||||||
|
</Icon>
|
||||||
|
</ServerEntry>
|
||||||
|
</ConditionalLink>
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
<IconButton onClick={() => openScreen({ id: 'special_input', type: 'create_server' })}>
|
<IconButton onClick={() => openScreen({ id: 'special_input', type: 'create_server' })}>
|
||||||
<PlusCircle size={36} />
|
<PlusCircle size={36} />
|
||||||
|
@ -187,7 +195,8 @@ export default connectState(
|
||||||
ServerListSidebar,
|
ServerListSidebar,
|
||||||
state => {
|
state => {
|
||||||
return {
|
return {
|
||||||
unreads: state.unreads
|
unreads: state.unreads,
|
||||||
|
lastOpened: state.lastOpened
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -15,6 +15,8 @@ import PaintCounter from "../../../lib/PaintCounter";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { attachContextMenu } from 'preact-context-menu';
|
import { attachContextMenu } from 'preact-context-menu';
|
||||||
import ServerHeader from "../../common/ServerHeader";
|
import ServerHeader from "../../common/ServerHeader";
|
||||||
|
import { useEffect } from "preact/hooks";
|
||||||
|
import ConditionalLink from "../../../lib/ConditionalLink";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
unreads: Unreads;
|
unreads: Unreads;
|
||||||
|
@ -51,24 +53,37 @@ function ServerSidebar(props: Props & WithDispatcher) {
|
||||||
.map(x => mapChannelWithUnread(x, props.unreads));
|
.map(x => mapChannelWithUnread(x, props.unreads));
|
||||||
|
|
||||||
const channel = channels.find(x => x?._id === channel_id);
|
const channel = channels.find(x => x?._id === channel_id);
|
||||||
|
if (channel_id && !channel) return <Redirect to={`/server/${server_id}`} />;
|
||||||
if (channel) useUnreads({ ...props, channel }, ctx);
|
if (channel) useUnreads({ ...props, channel }, ctx);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!channel_id) return;
|
||||||
|
|
||||||
|
props.dispatcher({
|
||||||
|
type: 'LAST_OPENED_SET',
|
||||||
|
parent: server_id!,
|
||||||
|
child: channel_id!
|
||||||
|
});
|
||||||
|
}, [ channel_id ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ServerBase>
|
<ServerBase>
|
||||||
<ServerHeader server={server} ctx={ctx} />
|
<ServerHeader server={server} ctx={ctx} />
|
||||||
<ConnectionStatus />
|
<ConnectionStatus />
|
||||||
<ServerList onContextMenu={attachContextMenu('Menu', { server_list: server._id })}>
|
<ServerList onContextMenu={attachContextMenu('Menu', { server_list: server._id })}>
|
||||||
{channels.map(entry => {
|
{channels.map(entry => {
|
||||||
|
const active = channel?._id === entry._id;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link to={`/server/${server._id}/channel/${entry._id}`}>
|
<ConditionalLink active={active} to={`/server/${server._id}/channel/${entry._id}`}>
|
||||||
<ChannelButton
|
<ChannelButton
|
||||||
key={entry._id}
|
key={entry._id}
|
||||||
channel={entry}
|
channel={entry}
|
||||||
active={channel?._id === entry._id}
|
active={active}
|
||||||
alert={entry.unread}
|
alert={entry.unread}
|
||||||
compact
|
compact
|
||||||
/>
|
/>
|
||||||
</Link>
|
</ConditionalLink>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</ServerList>
|
</ServerList>
|
||||||
|
|
|
@ -24,11 +24,9 @@ interface Props {
|
||||||
}
|
}
|
||||||
|
|
||||||
function Settings({ settings, children }: Props) {
|
function Settings({ settings, children }: Props) {
|
||||||
console.info(settings.notification);
|
|
||||||
const play = useMemo(() => {
|
const play = useMemo(() => {
|
||||||
const enabled: SoundOptions = defaultsDeep(settings.notification ?? {}, DEFAULT_SOUNDS);
|
const enabled: SoundOptions = defaultsDeep(settings.notification ?? {}, DEFAULT_SOUNDS);
|
||||||
return (sound: Sounds) => {
|
return (sound: Sounds) => {
|
||||||
console.info('check if we can play sound', enabled[sound]);
|
|
||||||
if (enabled[sound]) {
|
if (enabled[sound]) {
|
||||||
playSound(sound);
|
playSound(sound);
|
||||||
}
|
}
|
||||||
|
|
15
src/lib/ConditionalLink.tsx
Normal file
15
src/lib/ConditionalLink.tsx
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import { Link, LinkProps } from "react-router-dom";
|
||||||
|
|
||||||
|
type Props = LinkProps & JSX.HTMLAttributes<HTMLAnchorElement> & {
|
||||||
|
active: boolean
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function ConditionalLink(props: Props) {
|
||||||
|
const { active, ...linkProps } = props;
|
||||||
|
|
||||||
|
if (active) {
|
||||||
|
return <a>{ props.children }</a>;
|
||||||
|
} else {
|
||||||
|
return <Link {...linkProps} />;
|
||||||
|
}
|
||||||
|
}
|
|
@ -35,7 +35,7 @@ export default function HeaderActions({ channel, toggleSidebar }: ChannelHeaderP
|
||||||
</>
|
</>
|
||||||
) }
|
) }
|
||||||
<VoiceActions channel={channel} />
|
<VoiceActions channel={channel} />
|
||||||
{ channel.channel_type === "Group" && !isTouchscreenDevice && (
|
{ (channel.channel_type === "Group" || channel.channel_type === "TextChannel") && !isTouchscreenDevice && (
|
||||||
<IconButton onClick={toggleSidebar}>
|
<IconButton onClick={toggleSidebar}>
|
||||||
<SidebarIcon size={22} />
|
<SidebarIcon size={22} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
|
|
@ -12,6 +12,7 @@ import { IntermediateContext } from "../../../context/intermediate/Intermediate"
|
||||||
import { ClientStatus, StatusContext } from "../../../context/revoltjs/RevoltClient";
|
import { ClientStatus, StatusContext } from "../../../context/revoltjs/RevoltClient";
|
||||||
import { useContext, useEffect, useLayoutEffect, useRef, useState } from "preact/hooks";
|
import { useContext, useEffect, useLayoutEffect, useRef, useState } from "preact/hooks";
|
||||||
import { defer } from "../../../lib/defer";
|
import { defer } from "../../../lib/defer";
|
||||||
|
import { internalEmit } from "../../../lib/eventEmitter";
|
||||||
|
|
||||||
const Area = styled.div`
|
const Area = styled.div`
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
@ -246,6 +247,7 @@ export function MessageArea({ id }: Props) {
|
||||||
function keyUp(e: KeyboardEvent) {
|
function keyUp(e: KeyboardEvent) {
|
||||||
if (e.key === "Escape" && !focusTaken) {
|
if (e.key === "Escape" && !focusTaken) {
|
||||||
SingletonMessageRenderer.jumpToBottom(id, true);
|
SingletonMessageRenderer.jumpToBottom(id, true);
|
||||||
|
internalEmit("TextArea", "focus", "message");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { useContext, useState } from "preact/hooks";
|
import { useContext, useEffect, useState } from "preact/hooks";
|
||||||
import TextAreaAutoSize from "../../../lib/TextAreaAutoSize";
|
import TextAreaAutoSize from "../../../lib/TextAreaAutoSize";
|
||||||
import { MessageObject } from "../../../context/revoltjs/util";
|
import { MessageObject } from "../../../context/revoltjs/util";
|
||||||
import { AppContext } from "../../../context/revoltjs/RevoltClient";
|
import { AppContext } from "../../../context/revoltjs/RevoltClient";
|
||||||
import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice";
|
import { isTouchscreenDevice } from "../../../lib/isTouchscreenDevice";
|
||||||
|
import { IntermediateContext } from "../../../context/intermediate/Intermediate";
|
||||||
|
|
||||||
const EditorBase = styled.div`
|
const EditorBase = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -38,6 +39,7 @@ interface Props {
|
||||||
|
|
||||||
export default function MessageEditor({ message, finish }: Props) {
|
export default function MessageEditor({ message, finish }: Props) {
|
||||||
const [ content, setContent ] = useState(message.content as string ?? '');
|
const [ content, setContent ] = useState(message.content as string ?? '');
|
||||||
|
const { focusTaken } = useContext(IntermediateContext);
|
||||||
const client = useContext(AppContext);
|
const client = useContext(AppContext);
|
||||||
|
|
||||||
async function save() {
|
async function save() {
|
||||||
|
@ -55,6 +57,18 @@ export default function MessageEditor({ message, finish }: Props) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ? Stop editing when pressing ESC.
|
||||||
|
useEffect(() => {
|
||||||
|
function keyUp(e: KeyboardEvent) {
|
||||||
|
if (e.key === "Escape" && !focusTaken) {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.body.addEventListener("keyup", keyUp);
|
||||||
|
return () => document.body.removeEventListener("keyup", keyUp);
|
||||||
|
}, [focusTaken]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EditorBase>
|
<EditorBase>
|
||||||
<TextAreaAutoSize
|
<TextAreaAutoSize
|
||||||
|
|
|
@ -12,6 +12,7 @@ import { SyncOptions } from "./reducers/sync";
|
||||||
import { Settings } from "./reducers/settings";
|
import { Settings } from "./reducers/settings";
|
||||||
import { QueuedMessage } from "./reducers/queue";
|
import { QueuedMessage } from "./reducers/queue";
|
||||||
import { ExperimentOptions } from "./reducers/experiments";
|
import { ExperimentOptions } from "./reducers/experiments";
|
||||||
|
import { LastOpened } from "./reducers/last_opened";
|
||||||
|
|
||||||
export type State = {
|
export type State = {
|
||||||
config: Core.RevoltNodeConfiguration,
|
config: Core.RevoltNodeConfiguration,
|
||||||
|
@ -24,6 +25,7 @@ export type State = {
|
||||||
drafts: Drafts;
|
drafts: Drafts;
|
||||||
sync: SyncOptions;
|
sync: SyncOptions;
|
||||||
experiments: ExperimentOptions;
|
experiments: ExperimentOptions;
|
||||||
|
lastOpened: LastOpened;
|
||||||
};
|
};
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
@ -51,6 +53,7 @@ store.subscribe(() => {
|
||||||
drafts,
|
drafts,
|
||||||
sync,
|
sync,
|
||||||
experiments,
|
experiments,
|
||||||
|
lastOpened
|
||||||
} = store.getState() as State;
|
} = store.getState() as State;
|
||||||
|
|
||||||
localForage.setItem("state", {
|
localForage.setItem("state", {
|
||||||
|
@ -63,5 +66,6 @@ store.subscribe(() => {
|
||||||
drafts,
|
drafts,
|
||||||
sync,
|
sync,
|
||||||
experiments,
|
experiments,
|
||||||
|
lastOpened
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -11,6 +11,7 @@ import { typing, TypingAction } from "./typing";
|
||||||
import { drafts, DraftAction } from "./drafts";
|
import { drafts, DraftAction } from "./drafts";
|
||||||
import { sync, SyncAction } from "./sync";
|
import { sync, SyncAction } from "./sync";
|
||||||
import { experiments, ExperimentsAction } from "./experiments";
|
import { experiments, ExperimentsAction } from "./experiments";
|
||||||
|
import { lastOpened, LastOpenedAction } from "./last_opened";
|
||||||
|
|
||||||
export default combineReducers({
|
export default combineReducers({
|
||||||
config,
|
config,
|
||||||
|
@ -23,6 +24,7 @@ export default combineReducers({
|
||||||
drafts,
|
drafts,
|
||||||
sync,
|
sync,
|
||||||
experiments,
|
experiments,
|
||||||
|
lastOpened
|
||||||
});
|
});
|
||||||
|
|
||||||
export type Action =
|
export type Action =
|
||||||
|
@ -36,6 +38,7 @@ export type Action =
|
||||||
| DraftAction
|
| DraftAction
|
||||||
| SyncAction
|
| SyncAction
|
||||||
| ExperimentsAction
|
| ExperimentsAction
|
||||||
|
| LastOpenedAction
|
||||||
| { type: "__INIT"; state: State };
|
| { type: "__INIT"; state: State };
|
||||||
|
|
||||||
export type WithDispatcher = { dispatcher: (action: Action) => void };
|
export type WithDispatcher = { dispatcher: (action: Action) => void };
|
||||||
|
|
29
src/redux/reducers/last_opened.ts
Normal file
29
src/redux/reducers/last_opened.ts
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
export interface LastOpened {
|
||||||
|
[key: string]: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type LastOpenedAction =
|
||||||
|
| { type: undefined }
|
||||||
|
| {
|
||||||
|
type: "LAST_OPENED_SET";
|
||||||
|
parent: string;
|
||||||
|
child: string;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: "RESET";
|
||||||
|
};
|
||||||
|
|
||||||
|
export function lastOpened(state = {} as LastOpened, action: LastOpenedAction): LastOpened {
|
||||||
|
switch (action.type) {
|
||||||
|
case "LAST_OPENED_SET": {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
[action.parent]: action.child
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "RESET":
|
||||||
|
return {};
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue