Skip to content
This repository has been archived by the owner on Jul 12, 2021. It is now read-only.

Commit

Permalink
Implement multiple entries per day
Browse files Browse the repository at this point in the history
  • Loading branch information
vaibhawj committed Jun 30, 2020
1 parent 8d20585 commit 16ae6ee
Show file tree
Hide file tree
Showing 36 changed files with 541 additions and 239 deletions.
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "mini-diary",
"productName": "Mini Diary",
"version": "3.3.0",
"version": "3.3.1",
"description": "Simple and secure journal app",
"main": "./bundle/main.js",
"author": {
Expand Down Expand Up @@ -53,6 +53,7 @@
"@types/react-redux": "^7.1.9",
"@types/redux-logger": "^3.0.7",
"@types/semver": "^7.2.0",
"@types/uuid": "^8.0.0",
"draft-js": "^0.11.5",
"draft-js-list-plugin": "^1.0.2",
"draft-js-plugins-editor": "^3.0.0",
Expand All @@ -74,6 +75,7 @@
"react": "^16.13.1",
"react-day-picker": "github:samuelmeuli/react-day-picker",
"react-dom": "^16.13.1",
"react-lines-ellipsis": "^0.14.1",
"react-redux": "^7.2.0",
"redux": "^4.0.5",
"redux-logger": "^3.0.6",
Expand All @@ -82,6 +84,7 @@
"remark-stringify": "^8.0.0",
"semver": "^7.3.2",
"strip-markdown": "^3.1.2",
"uuid": "^8.2.0",
"word-count": "github:samuelmeuli/word-count"
},
"devDependencies": {
Expand Down
4 changes: 2 additions & 2 deletions src/main/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ async function createWindow(): Promise<BrowserWindow> {
const win = new BrowserWindow({
width: 1100,
minWidth: 500,
height: 600,
minHeight: 500,
height: 650,
minHeight: 550,
show: false,
titleBarStyle: "hiddenInset",
webPreferences: {
Expand Down
28 changes: 27 additions & 1 deletion src/renderer/assets/styles/components/_calendar.scss
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

.DayPicker-Body {
display: grid;
grid-template-rows: repeat(6, $input-height);
grid-template-rows: repeat(5, $input-height);
}

.DayPicker-Months {
Expand Down Expand Up @@ -98,3 +98,29 @@
@include background-color("main-hover");
}
}

.day-entries {
flex: 1;
margin-top: 2rem;
margin-bottom: 2rem;
padding: 1px;
overflow-y: auto;
}

.entry {
.button {
width: 100%;
height: 100%;
padding: $spacing-abs-small $spacing-abs-medium;
font-weight: $font-weight-normal;
line-height: $line-height;
}

.button-main * {
@include color("text-button");
}
}

.entry + .entry {
margin-top: $spacing-abs-medium;
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { toIndexDate } from "../../../../../utils/dateFormat";
export interface StateProps {
dateSelected: Moment;
entries: Entries;
entryIdSelected: string | null;
}

type Props = StateProps;
Expand All @@ -17,15 +18,17 @@ type Props = StateProps;
* currently selected diary entry
*/
export default function WordCount(props: Props): ReactElement {
const { dateSelected, entries } = props;
const { dateSelected, entries, entryIdSelected } = props;

let wordCount = 0;

const indexDate = toIndexDate(dateSelected);

if (indexDate in entries) {
const entry = entries[indexDate];
wordCount = countWords(`${entry.title ?? ""}\n${entry.text ?? ""}`);
if (indexDate in entries && entryIdSelected) {
const entry = entries[indexDate].find(e => e.id === entryIdSelected);
if (entry) {
wordCount = countWords(`${entry.title ?? ""}\n${entry.text ?? ""}`);
}
}

return <p className="word-count">{wordCount}</p>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import WordCount, { StateProps } from "./WordCount";
const mapStateToProps = (state: RootState): StateProps => ({
dateSelected: state.diary.dateSelected,
entries: state.file.entries,
entryIdSelected: state.diary.entryIdSelected,
});

export default connect(mapStateToProps)(WordCount);
69 changes: 42 additions & 27 deletions src/renderer/components/elements/editor/editor/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ import debounce from "lodash.debounce";
import { draftToMarkdown, markdownToDraft } from "markdown-draft-js";
import { Moment } from "moment-timezone";
import React, { KeyboardEvent, PureComponent, ReactNode } from "react";
import { v4 } from "uuid";

import { Entries, IndexDate } from "../../../../types";
import { Entries } from "../../../../types";
import { toIndexDate, toLocaleWeekday } from "../../../../utils/dateFormat";
import { translations } from "../../../../utils/i18n";
import EditorToolbar from "../editor-toolbar/editor-toolbar/EditorToolbar";
Expand All @@ -35,10 +36,11 @@ export interface StateProps {
hideTitles: boolean;
dateSelected: Moment;
entries: Entries;
entryIdSelected: string | null;
}

export interface DispatchProps {
updateEntry: (entryDate: IndexDate, title: string, text: string) => void;
updateEntry: (entryDate: string, title: string, text: string, id: string) => void;
}

type Props = StateProps & DispatchProps;
Expand All @@ -47,38 +49,37 @@ interface State {
dateSelected: Moment;
textEditorState: EditorState;
titleEditorState: EditorState;
entryIdSelected: string | null;
}

export default class Editor extends PureComponent<Props, State> {
static getDerivedStateFromProps(props: Props, state: State): State | null {
const { dateSelected: dateProps, entries } = props;
const { dateSelected: dateState } = state;

if (dateProps === dateState) {
return null;
}
const entryState = Editor.getStateFromEntry(entries, dateProps);
return {
...entryState,
dateSelected: dateProps,
};
}

static getStateFromEntry(
entries: Entries,
date: Moment,
): { textEditorState: EditorState; titleEditorState: EditorState } {
entryIdSelected: string | null,
): {
textEditorState: EditorState;
titleEditorState: EditorState;
entryIdSelected: string | null;
} {
const indexDate = toIndexDate(date);
const entry = entries[indexDate];
let text = "";
let title = "";
if (entry) {
({ text, title } = entry);
let entryId = entryIdSelected;
if (entryId) {
const entry = entries[indexDate] && entries[indexDate].find(e => e.id === entryId);

if (entry) {
({ text, title } = entry);
}
} else {
entryId = v4();
}

return {
textEditorState: EditorState.createWithContent(convertFromRaw(markdownToDraft(text))),
titleEditorState: EditorState.createWithContent(ContentState.createFromText(title)),
entryIdSelected: entryId,
};
}

Expand All @@ -93,9 +94,8 @@ export default class Editor extends PureComponent<Props, State> {

constructor(props: Props) {
super(props);
const { dateSelected, entries } = props;

const entryState = Editor.getStateFromEntry(entries, dateSelected);
const { dateSelected, entries, entryIdSelected } = props;
const entryState = Editor.getStateFromEntry(entries, dateSelected, entryIdSelected);
this.state = {
...entryState,
dateSelected,
Expand Down Expand Up @@ -152,27 +152,42 @@ export default class Editor extends PureComponent<Props, State> {

saveEntry = (): void => {
const { dateSelected, updateEntry } = this.props;
const { textEditorState, titleEditorState } = this.state;
const { textEditorState, titleEditorState, entryIdSelected } = this.state;

const indexDate = toIndexDate(dateSelected);
const title = titleEditorState.getCurrentContent().getPlainText();
const text = draftToMarkdown(convertToRaw(textEditorState.getCurrentContent()));
updateEntry(indexDate, title.trim(), text.trim());
if (entryIdSelected) {
updateEntry(indexDate, title.trim(), text.trim(), entryIdSelected);
}
};

// eslint-disable-next-line react/sort-comp
saveEntryDebounced = debounce(this.saveEntry.bind(this), AUTOSAVE_INTERVAL);

render = (): ReactNode => {
const { dateSelected, textEditorState, titleEditorState } = this.state;
const { enableSpellcheck, hideTitles } = this.props;
const {
textEditorState,
titleEditorState,
dateSelected: dateInState,
entryIdSelected: entryIdInState,
} = this.state;
const { enableSpellcheck, hideTitles, entries, entryIdSelected, dateSelected } = this.props;

// Detect active inline/block styles
const blockType = RichUtils.getCurrentBlockType(textEditorState);
const isOl = blockType === "ordered-list-item";
const isUl = blockType === "unordered-list-item";

const weekdayDate = toLocaleWeekday(dateSelected);

if (dateInState !== dateSelected || (entryIdSelected && entryIdInState !== entryIdSelected)) {
const entryState = Editor.getStateFromEntry(entries, dateSelected, entryIdSelected);
this.setState({
...entryState,
dateSelected,
});
}
return (
<form className="editor">
<div className="editor-scrollable">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@ import { connect } from "react-redux";

import { updateEntry } from "../../../../store/file/actionCreators";
import { RootState, ThunkDispatchT } from "../../../../store/store";
import { IndexDate } from "../../../../types";
import Editor, { DispatchProps, StateProps } from "./Editor";

const mapStateToProps = (state: RootState): StateProps => ({
enableSpellcheck: state.app.enableSpellcheck,
hideTitles: state.app.hideTitles,
dateSelected: state.diary.dateSelected,
entries: state.file.entries,
entryIdSelected: state.diary.entryIdSelected,
});

const mapDispatchToProps = (dispatch: ThunkDispatchT): DispatchProps => ({
updateEntry: (entryDate: IndexDate, title: string, text: string): void =>
dispatch(updateEntry(entryDate, title, text)),
updateEntry: (entryDate: string, title: string, text: string, id: string): void =>
dispatch(updateEntry(entryDate, title, text, id)),
});

export default connect(mapStateToProps, mapDispatchToProps)(Editor);
70 changes: 56 additions & 14 deletions src/renderer/components/elements/sidebar/calendar/Calendar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,24 @@ import { Moment } from "moment-timezone";
import React, { PureComponent, ReactNode } from "react";
import DayPicker from "react-day-picker";
import MomentLocaleUtils from "react-day-picker/moment";
import LinesEllipsis from "react-lines-ellipsis";

import { Entries, Weekday } from "../../../../types";
import { createDate, parseDate, toIndexDate } from "../../../../utils/dateFormat";
import { lang } from "../../../../utils/i18n";
import { translations, lang } from "../../../../utils/i18n";
import CalendarNavContainer from "../calendar-nav/CalendarNavContainer";

export interface StateProps {
allowFutureEntries: boolean;
dateSelected: Moment;
entries: Entries;
firstDayOfWeek: Weekday | null;
entryIdSelected: string | null;
}

export interface DispatchProps {
setDateSelected: (date: Moment) => void;
selectEntry: (id: string) => void;
}

type Props = StateProps & DispatchProps;
Expand All @@ -27,6 +30,7 @@ export default class Calendar extends PureComponent<Props, {}> {

// Function bindings
this.onDateSelection = this.onDateSelection.bind(this);
this.onEntrySelection = this.onEntrySelection.bind(this);
}

onDateSelection(date: Date): void {
Expand All @@ -39,8 +43,22 @@ export default class Calendar extends PureComponent<Props, {}> {
}
}

onEntrySelection(id: string): void {
const { selectEntry } = this.props;
selectEntry(id);
}

truncate = (title: string, maxLength = 23): string =>
title.length > maxLength ? `${title.substring(0, maxLength).trim()} ...` : title;

render(): ReactNode {
const { allowFutureEntries, dateSelected, entries, firstDayOfWeek } = this.props;
const {
allowFutureEntries,
dateSelected,
entries,
firstDayOfWeek,
entryIdSelected,
} = this.props;

const today = createDate();
const daysWithEntries = Object.keys(entries);
Expand All @@ -51,21 +69,45 @@ export default class Calendar extends PureComponent<Props, {}> {
};

const dateSelectedObj = dateSelected.toDate();
const indexDate = toIndexDate(dateSelected);
const todayObj = today.toDate();

return (
<DayPicker
month={dateSelectedObj}
selectedDays={dateSelectedObj}
disabledDays={allowFutureEntries ? null : { after: todayObj }}
captionElement={(): null => null}
modifiers={{ hasEntry }}
firstDayOfWeek={firstDayOfWeek ?? undefined}
locale={lang}
localeUtils={MomentLocaleUtils}
navbarElement={<CalendarNavContainer />}
onDayClick={this.onDateSelection}
/>
<div className="sidebar">
<DayPicker
month={dateSelectedObj}
selectedDays={dateSelectedObj}
disabledDays={allowFutureEntries ? null : { after: todayObj }}
captionElement={(): null => null}
modifiers={{ hasEntry }}
firstDayOfWeek={firstDayOfWeek ?? undefined}
locale={lang}
localeUtils={MomentLocaleUtils}
navbarElement={<CalendarNavContainer />}
onDayClick={this.onDateSelection}
/>
<ul className="day-entries">
{entries[indexDate] &&
entries[indexDate].map(e => (
<li key={e.id} className="entry">
<button
type="button"
className={`button ${e.id === entryIdSelected ? "button-main" : ""}`}
onClick={(): void => this.onEntrySelection(e.id)}
>
<p className={`entry-title ${!e.title ? "text-faded" : ""}`}>
<LinesEllipsis
text={e.title || translations["no-title"]}
ellipsis="..."
trimRight
basedOn="words"
/>
</p>
</button>
</li>
))}
</ul>
</div>
);
}
}
Loading

0 comments on commit 16ae6ee

Please sign in to comment.