Skip to content

Commit

Permalink
Feature/save indicator (#142)
Browse files Browse the repository at this point in the history
* Initial state changes to support marking dirty projects

* Add basic dirty/save indicator

* Make ContextMenu render everything on app load to reduce slow animations, control visibility with height/width instead

* Make sidebar pane opaque when fullscreen

* Bump version

* Cleanup
  • Loading branch information
zachhannum authored Sep 14, 2022
1 parent 0d3128e commit 49cd47d
Show file tree
Hide file tree
Showing 13 changed files with 99 additions and 29 deletions.
8 changes: 8 additions & 0 deletions app/main/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,14 @@ const createWindow = async () => {
mainWindow?.webContents.send('window', 'unmaximize');
});

mainWindow.on('enter-full-screen', () => {
mainWindow?.webContents.send('window', 'maximize');
});

mainWindow.on('leave-full-screen', () => {
mainWindow?.webContents.send('window', 'unmaximize');
});

mainWindow.on('closed', () => {
mainWindow = null;
});
Expand Down
16 changes: 9 additions & 7 deletions app/renderer/components/ContextMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ const StyledRoot = styled.div`
`;

type StyledContextMenuProps = {
show?: boolean;
show: boolean;
visible: boolean;
};
const onOpenKeyframes = keyframes`
from {
Expand All @@ -25,8 +26,11 @@ const onOpenKeyframes = keyframes`
const StyledContextMenu = styled.div<StyledContextMenuProps>`
opacity: ${(p) => (p.show ? '1' : '0')};
transform: ${(p) => (p.show ? 'scale(1)' : 'scale(.7)')};
height: ${(p) => (p.visible ? 'inherit' : '0')};
width: ${(p) => (p.visible ? 'inherit' : '0')};
animation: ${onOpenKeyframes} 100ms ease-in-out alternate;
background-color: ${(p) => Color(p.theme.contextMenuBg).lighten(0.2).hsl().string()};
background-color: ${(p) =>
Color(p.theme.contextMenuBg).lighten(0.2).hsl().string()};
border: ${(p) => p.theme.contextMenuDivider} 1px solid;
backdrop-filter: blur(40px);
border-radius: 10px;
Expand Down Expand Up @@ -152,11 +156,9 @@ const ContextMenu = ({

return (
<StyledRoot ref={root}>
{visible && (
<StyledContextMenu ref={menuRef} show={showMenu}>
{children}
</StyledContextMenu>
)}
<StyledContextMenu ref={menuRef} show={showMenu} visible={visible}>
{children}
</StyledContextMenu>
</StyledRoot>
);
};
Expand Down
24 changes: 24 additions & 0 deletions app/renderer/components/SaveIndicator.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import styled from 'styled-components';
import useStore from '../store/useStore';

type StyledDirtyIndicatorProps = {
show: boolean;
};

const StyledDirtyIndicator = styled.span<StyledDirtyIndicatorProps>`
flex: none;
background-color: ${(p) => p.theme.buttonPrimaryBg};
height: 8px;
width: 8px;
border-radius: 4px;
opacity: ${(p) => (p.show ? '1' : '0')};
transition: opacity ease-in-out 100ms;
`;

const SaveIndicator = () => {
const isProjectDirty = useStore((state) => state.isProjectDirty);

return <StyledDirtyIndicator show={isProjectDirty} />;
};

export default SaveIndicator;
19 changes: 16 additions & 3 deletions app/renderer/components/SidebarProjectContent.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { useEffect, useState } from 'react';
import styled from 'styled-components';
import Color from 'color';
import { Button } from '../controls';
import useStore from '../store/useStore';
import SidebarProjectSections from './SidebarProjectSections';
import SaveIndicator from './SaveIndicator';

const StyledSidebarProjectContent = styled.div`
display: flex;
Expand Down Expand Up @@ -39,13 +41,20 @@ const StyledNoProjectBlock = styled.div`
flex-grow: 1;
`;

const StyledTitle = styled.div`
const StyledTitleContainer = styled.div`
display: flex;
flex-direction: row;
align-items: center;
`;

const StyledTitle = styled.span`
color: ${(p) => p.theme.sidebarFgText};
padding-right: 10px;
font-weight: 500; //semi-bold
font-size: 1.1em;
`;

const StyledAuthorName = styled.div`
const StyledAuthorName = styled.span`
color: ${(p) => p.theme.sidebarFgTextSecondary};
font-weight: 500;
font-size: 1em;
Expand All @@ -69,12 +78,16 @@ const SidebarProjectContent = () => {
const bookTitle = useStore((state) => state.bookTitle);
const authorName = useStore((state) => state.authorName);
const setNewBookModalOpen = useStore((state) => state.setNewBookModalOpen);

return (
<StyledSidebarProjectContent>
{isProjectOpen ? (
<>
<StyledTitleBlock>
<StyledTitle>{bookTitle}</StyledTitle>
<StyledTitleContainer>
<StyledTitle>{bookTitle}</StyledTitle>
<SaveIndicator />
</StyledTitleContainer>
<StyledAuthorName>{authorName}</StyledAuthorName>
</StyledTitleBlock>
<StyledContentBlock>
Expand Down
1 change: 1 addition & 0 deletions app/renderer/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ export { default as SectionContextMenu } from './SectionContextMenu';
export { default as ContextMenu } from './ContextMenu';
export { default as TooltipText } from './TooltipText';
export { default as Editor } from './codemirror/Editor';
export { default as SaveIndicator } from './SaveIndicator';
2 changes: 2 additions & 0 deletions app/renderer/hooks/useOpenProject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const useOpenProject = () => {
setPreviewEnabled,
setPreviewContent,
clearEditorStateMap,
setIsProjectDirty,
} = useStore.getState();
setProjectFolder(folderPath);
setProjectFileName(fileName);
Expand All @@ -47,6 +48,7 @@ const useOpenProject = () => {
setPreviewContent('');
setPreviewEnabled(false);
setIsProjectOpen(true);
setIsProjectDirty(false);
});
});
};
Expand Down
13 changes: 10 additions & 3 deletions app/renderer/panes/SidebarPane.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect } from 'react';
import { useCallback, useEffect, useMemo } from 'react';
import styled, { useTheme, css } from 'styled-components';
import Color from 'color';
import {
Expand All @@ -13,7 +13,7 @@ import {
HelpIcon,
SettingsIcon,
} from '../icons';
import { useToggle } from '../hooks';
import { useIsWindowMaxized, useToggle } from '../hooks';
import useStore from '../store/useStore';

const SidebarTopContainer = styled.div`
Expand Down Expand Up @@ -86,6 +86,13 @@ const SidebarPane = () => {
const theme = useTheme();
const [open, toggleOpen] = useToggle(true);
const setSidebarOpen = useStore((state) => state.setSidebarOpen);
const isWindowMaximized = useIsWindowMaxized();
const sidebarBackground = useMemo(() => {
if (isWindowMaximized) {
return Color(theme.sidebarBg).alpha(1).hsl().toString();
}
return theme.sidebarBg;
}, [isWindowMaximized]);

useEffect(() => {
setSidebarOpen(open);
Expand All @@ -95,7 +102,7 @@ const SidebarPane = () => {
<Pane
enabled={open}
defaultWidth="250px"
backgroundColor={theme.sidebarBg}
backgroundColor={sidebarBackground}
styleMixin={paneStyleMixin}
>
<SidebarTopContainer>
Expand Down
35 changes: 23 additions & 12 deletions app/renderer/store/slices/createProjectSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ import type { CalamusState } from '../useStore';

export interface ProjectSlice extends Project {
isProjectOpen: boolean;
setIsProjectOpen: (val: boolean) => void;
isProjectDirty: boolean;
setIsProjectDirty: (val: boolean) => void;
projectFolder: string;
setProjectFolder: (val: string) => void;
projectFileName: string;
setProjectFileName: (val: string) => void;
setIsProjectOpen: (val: boolean) => void;
setBookTitle: (val: string) => void;
setBookSubTitle: (val: string) => void;
setAuthorName: (val: string) => void;
Expand Down Expand Up @@ -48,59 +50,68 @@ const createProjectSlice = (
setIsProjectOpen: (val: boolean) => {
set(() => ({ isProjectOpen: val }));
},
isProjectDirty: false,
setIsProjectDirty: (val: boolean) => {
set(() => ({ isProjectDirty: val }));
},
projectFolder: '',
setProjectFolder: (val: string) => {
set(() => ({ projectFolder: val }));
set(() => ({ projectFolder: val, isProjectDirty: true }));
},
projectFileName: '',
setProjectFileName: (val: string) => {
set(() => ({ projectFileName: val }));
set(() => ({ projectFileName: val, isProjectDirty: true }));
},
bookTitle: '',
setBookTitle: (val: string) => {
set(() => ({ bookTitle: val }));
set(() => ({ bookTitle: val, isProjectDirty: true }));
},
bookSubTitle: '',
setBookSubTitle: (val: string) => {
set(() => ({ bookSubTitle: val }));
set(() => ({ bookSubTitle: val, isProjectDirty: true }));
},
authorName: '',
setAuthorName: (val: string) => {
set(() => ({ authorName: val }));
set(() => ({ authorName: val, isProjectDirty: true }));
},
seriesName: '',
setSeriesName: (val: string) => {
set(() => ({ seriesName: val }));
set(() => ({ seriesName: val, isProjectDirty: true }));
},
ISBN: '',
setISBN: (val: string) => {
set(() => ({ ISBN: val }));
set(() => ({ ISBN: val, isProjectDirty: true }));
},
language: '',
setLanguage: (val: string) => {
set(() => ({ language: val }));
set(() => ({ language: val, isProjectDirty: true }));
},
publisher: '',
setPublisher: (val: string) => {
set(() => ({ publisher: val }));
set(() => ({ publisher: val, isProjectDirty: true }));
},
content: [],
setContentArray: (val: Section[]) => {
set(() => ({ content: val }));
set(() => ({ content: val, isProjectDirty: true }));
},
updateSectionContent: (id: string, newContent: string) => {
set((state) => ({
content: updateSectionContentDeep(state.content, id, newContent),
isProjectDirty: true,
}));
},
addNewSection: (val: Section) =>
set(
produce((state: CalamusState) => {
state.content.push(val);
state.isProjectDirty = true;
})
),
addNewSectionAt: (val: Section, atId: string) => {
set((state) => ({ content: addSectionAt(val, state.content, atId) }));
set((state) => ({
content: addSectionAt(val, state.content, atId),
isProjectDirty: true,
}));
},
addingSections: false,
setAddingSections: (val: boolean) => set(() => ({ addingSections: val })),
Expand Down
2 changes: 2 additions & 0 deletions app/renderer/utils/projectUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ const saveProject = () => {
folderPath,
fileName,
});
const { setIsProjectDirty } = useStore.getState();
setIsProjectDirty(false);
};

const updateSectionName = (id: string, newName: string): boolean => {
Expand Down
2 changes: 1 addition & 1 deletion release/app/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "calamus",
"version": "0.5.6-pre-alpha",
"version": "0.5.7-pre-alpha",
"description": "Write and Publish Books with Ease",
"main": "./dist/main/main.js",
"author": {
Expand Down
2 changes: 1 addition & 1 deletion test_projects/alice.cala

Large diffs are not rendered by default.

Loading

0 comments on commit 49cd47d

Please sign in to comment.