Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
2dbc325
feat(wip): daily notes
moughxyz Oct 26, 2022
9306e56
fix: increase constant
moughxyz Oct 26, 2022
969cb98
chore: tests
moughxyz Oct 26, 2022
6cff7af
refactor: files
moughxyz Oct 26, 2022
ef33bed
Merge branch 'main' of github.com:standardnotes/app into feat/daily-n…
moughxyz Oct 26, 2022
1acd085
refactor: daily items selection
moughxyz Oct 26, 2022
720bcef
feat: tag preference for entry mode
moughxyz Oct 26, 2022
2ea266d
refactor: pass onSelect to item cells instead of selectionController
moughxyz Oct 26, 2022
bf8d407
feat: daily cell flags
moughxyz Oct 26, 2022
f736bb7
refactor: component props
moughxyz Oct 26, 2022
b2eef1a
refactor: note preview component
moughxyz Oct 26, 2022
70cb9ad
refactor: use getter
moughxyz Oct 26, 2022
7e7e6c7
feat: layout design
moughxyz Oct 26, 2022
1cb783c
style: rounded rect
moughxyz Oct 26, 2022
219a3d2
feat: infinite scroll
moughxyz Oct 26, 2022
7c4e89b
refactor: rename
moughxyz Oct 26, 2022
c7e5514
feat: calendar
moughxyz Oct 26, 2022
23f72b7
style: font weight
moughxyz Oct 26, 2022
fd36673
chore: lint
moughxyz Oct 26, 2022
28b5ccd
feat: infinite calendar scroller
moughxyz Oct 27, 2022
338e888
fix: calendar scrolling
moughxyz Oct 27, 2022
a5d1936
feat(wip): calendar
moughxyz Oct 27, 2022
430bc23
fix: prevent top pagination infinite scroll
moughxyz Oct 27, 2022
29bdecc
refactor: no need for interstices
moughxyz Oct 27, 2022
87487a3
refactor: rename to infinite calendar
moughxyz Oct 27, 2022
3428bb7
fix: always queue scroll to month when toggling visibility
moughxyz Oct 27, 2022
986ee31
refactor: use function invoke to change date
moughxyz Oct 27, 2022
c7049d5
feat: feature flag
moughxyz Oct 27, 2022
0986f1c
Merge branch 'main' of github.com:standardnotes/app into feat/daily-n…
moughxyz Oct 27, 2022
c30ddb4
fix: test
moughxyz Oct 27, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/models/src/Domain/Syncable/Tag/Tag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ export class SNTag extends DecryptedItem<TagContent> implements TagContentSpecia
this.preferences = this.payload.content.preferences
}

get isDailyEntry(): boolean {
return this.preferences?.entryMode === 'daily'
}

get noteReferences(): ContentReference[] {
const references = this.payload.references
return references.filter((ref) => ref.content_type === ContentType.Note)
Expand Down
1 change: 1 addition & 0 deletions packages/models/src/Domain/Syncable/Tag/TagPreferences.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ export interface TagPreferences {
newNoteTitleFormat?: NewNoteTitleFormat
customNoteTitleFormat?: string
editorIdentifier?: FeatureIdentifier | string
entryMode?: 'normal' | 'daily'
}
2 changes: 2 additions & 0 deletions packages/services/src/Domain/Item/ItemManagerInterface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
DeletedItemInterface,
ItemContent,
PredicateInterface,
DecryptedPayload,
} from '@standardnotes/models'
import { AbstractService } from '../Service/AbstractService'

Expand Down Expand Up @@ -93,6 +94,7 @@ export interface ItemManagerInterface extends AbstractService {
>(
contentType: ContentType,
content?: C,
override?: Partial<DecryptedPayload<C>>,
): I

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
BackupFile,
DecryptedItemInterface,
DecryptedItemMutator,
DecryptedPayload,
EncryptedItemInterface,
FileItem,
ItemContent,
Expand Down Expand Up @@ -117,6 +118,7 @@ export interface MutatorClientInterface {
>(
contentType: ContentType,
content?: C,
override?: Partial<DecryptedPayload<C>>,
): I

/**
Expand Down
1 change: 1 addition & 0 deletions packages/snjs/lib/Client/NoteViewController.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ describe('note view controller', () => {
expect(application.mutator.createTemplateItem).toHaveBeenCalledWith(
ContentType.Note,
expect.objectContaining({ noteType: NoteType.Plain }),
expect.anything(),
)
})
})
28 changes: 18 additions & 10 deletions packages/snjs/lib/Client/NoteViewController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,22 +34,20 @@ export class NoteViewController implements ItemViewControllerInterface {
private removeStreamObserver?: () => void
public isTemplateNote = false
private saveTimeout?: ReturnType<typeof setTimeout>
private defaultTitle: string | undefined
private defaultTagUuid: UuidString | undefined
private defaultTag?: SNTag
public runtimeId = `${Math.random()}`

constructor(
private application: SNApplication,
item?: SNNote,
templateNoteOptions?: TemplateNoteViewControllerOptions,
public templateNoteOptions?: TemplateNoteViewControllerOptions,
) {
if (item) {
this.item = item
}

if (templateNoteOptions) {
this.defaultTitle = templateNoteOptions.title
this.defaultTagUuid = templateNoteOptions.tag
}

Expand Down Expand Up @@ -80,13 +78,19 @@ export class NoteViewController implements ItemViewControllerInterface {
? this.application.componentManager.componentWithIdentifier(editorIdentifier)
: undefined

const note = this.application.mutator.createTemplateItem<NoteContent, SNNote>(ContentType.Note, {
text: '',
title: this.defaultTitle || '',
noteType: defaultEditor?.noteType || NoteType.Plain,
editorIdentifier: editorIdentifier,
references: [],
})
const note = this.application.mutator.createTemplateItem<NoteContent, SNNote>(
ContentType.Note,
{
text: '',
title: this.templateNoteOptions?.title || '',
noteType: defaultEditor?.noteType || NoteType.Plain,
editorIdentifier: editorIdentifier,
references: [],
},
{
created_at: this.templateNoteOptions?.createdAt || new Date(),
},
)

this.isTemplateNote = true
this.item = note
Expand All @@ -109,6 +113,10 @@ export class NoteViewController implements ItemViewControllerInterface {
}

private streamItems() {
if (this.dealloced) {
return
}

this.removeStreamObserver = this.application.streamItems<SNNote>(
ContentType.Note,
({ changed, inserted, source }) => {
Expand Down
4 changes: 4 additions & 0 deletions packages/snjs/lib/Client/TemplateNoteViewControllerOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,8 @@ import { UuidString } from '@Lib/Types/UuidString'
export type TemplateNoteViewControllerOptions = {
title?: string
tag?: UuidString
createdAt?: Date
autofocusBehavior?: TemplateNoteViewAutofocusBehavior
}

export type TemplateNoteViewAutofocusBehavior = 'title' | 'editor'
2 changes: 1 addition & 1 deletion packages/snjs/lib/Client/Types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ export const STRING_SAVING_WHILE_DOCUMENT_HIDDEN =
export const STRING_INVALID_NOTE =
"The note you are attempting to save can not be found or has been deleted. Changes you make will not be synced. Please copy this note's text and start a new note."
export const STRING_ELLIPSES = '...'
export const NOTE_PREVIEW_CHAR_LIMIT = 80
export const NOTE_PREVIEW_CHAR_LIMIT = 160
export const SAVE_TIMEOUT_DEBOUNCE = 350
export const SAVE_TIMEOUT_NO_DEBOUNCE = 100
1 change: 1 addition & 0 deletions packages/snjs/lib/Client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export * from './NoteViewController'
export * from './FileViewController'
export * from './ItemGroupController'
export * from './ReactNativeToWebEvent'
export * from './TemplateNoteViewControllerOptions'
3 changes: 2 additions & 1 deletion packages/snjs/lib/Services/Items/ItemManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -805,12 +805,13 @@ export class ItemManager
public createTemplateItem<
C extends Models.ItemContent = Models.ItemContent,
I extends Models.DecryptedItemInterface<C> = Models.DecryptedItemInterface<C>,
>(contentType: ContentType, content?: C): I {
>(contentType: ContentType, content?: C, override?: Partial<Models.DecryptedPayload<C>>): I {
const payload = new Models.DecryptedPayload<C>({
uuid: UuidGenerator.GenerateUuid(),
content_type: contentType,
content: Models.FillItemContent<C>(content || {}),
...Models.PayloadTimestampDefaults(),
...override,
})
const item = Models.CreateDecryptedItemFromPayload<C, I>(payload)
return item
Expand Down
5 changes: 3 additions & 2 deletions packages/snjs/lib/Services/Mutator/MutatorService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
CreateEncryptedBackupFileContextPayload,
DecryptedItemInterface,
DecryptedItemMutator,
DecryptedPayload,
DecryptedPayloadInterface,
EncryptedItemInterface,
FileItem,
Expand Down Expand Up @@ -221,8 +222,8 @@ export class MutatorService extends AbstractService implements MutatorClientInte
public createTemplateItem<
C extends ItemContent = ItemContent,
I extends DecryptedItemInterface<C> = DecryptedItemInterface<C>,
>(contentType: ContentType, content?: C): I {
return this.itemManager.createTemplateItem(contentType, content)
>(contentType: ContentType, content?: C, override?: Partial<DecryptedPayload<C>>): I {
return this.itemManager.createTemplateItem(contentType, content, override)
}

public async setItemNeedsSync(
Expand Down
1 change: 1 addition & 0 deletions packages/styles/src/Styles/_colors.scss
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

--sn-stylekit-danger-color: #cc2128;
--sn-stylekit-danger-contrast-color: #ffffff;
--sn-stylekit-danger-light-color: #f9e4e5;

--sn-stylekit-shadow-color: #c8c8c8;
--sn-stylekit-background-color: #ffffff;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { useState, useEffect, FunctionComponent, useMemo } from 'react'
import { CalendarActivity } from './CalendarActivity'
import CalendarDay from './CalendarDay'
import { CalendarDays, CalendarDaysLeap, CalendarDaysOfTheWeek } from './Constants'
import { createActivityRecord, dateToDateOnlyString, isLeapYear, getStartDayOfMonth } from './CalendarUtilts'
import { isDateInSameDay } from '@/Utils/DateUtils'

type Props = {
activities: CalendarActivity[]
onDateSelect: (date: Date) => void
startDate: Date
className?: string
selectedDay?: Date
}

const Calendar: FunctionComponent<Props> = ({ activities, startDate, onDateSelect, selectedDay, className }) => {
const activityMap = useMemo(() => createActivityRecord(activities), [activities])

const [date, setDate] = useState(startDate || new Date())
const [month, setMonth] = useState(date.getMonth())
const [year, setYear] = useState(date.getFullYear())
const [startDay, setStartDay] = useState(getStartDayOfMonth(date))

useEffect(() => {
setDate(startDate)
setMonth(startDate.getMonth())
setYear(startDate.getFullYear())
setStartDay(getStartDayOfMonth(startDate))
}, [startDate])

const today = new Date()
const days = isLeapYear(year) ? CalendarDaysLeap : CalendarDays

return (
<div className={`w-300 ${className} border-left border-right border border-neutral`}>
<div className="mr-auto ml-auto w-70">
<div className="flex w-full flex-wrap">
{CalendarDaysOfTheWeek.map((d) => (
<div className={'flex h-8 w-[14.2%] items-center justify-center'} key={d}>
{d}
</div>
))}
</div>
<div className="flex w-full flex-wrap">
{Array(days[month] + (startDay - 1))
.fill(null)
.map((_, index) => {
const d = index - (startDay - 2)
const date = new Date(year, month, d)
const activities = activityMap[dateToDateOnlyString(date)] || []
return (
<CalendarDay
key={index}
day={d}
isToday={isDateInSameDay(date, today)}
activities={activities}
onClick={() => onDateSelect(date)}
hasPendingEntry={selectedDay && isDateInSameDay(selectedDay, date)}
/>
)
})}
</div>
</div>
</div>
)
}

export default Calendar
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { ListableContentItem } from '@/Components/ContentListView/Types/ListableContentItem'
export type CalendarActivityType = 'created' | 'edited'

export type CalendarActivity = {
date: Date
item: ListableContentItem
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { FunctionComponent } from 'react'
import { CalendarActivity } from './CalendarActivity'

type Props = {
day: number
activities: CalendarActivity[]
isToday: boolean
onClick: () => void
hasPendingEntry?: boolean
}

const CalendarDay: FunctionComponent<Props> = ({ day, activities = [], hasPendingEntry, isToday, onClick }) => {
const hasActivity = day > 0 && activities.length > 0
const todayClassNames = 'bg-danger text-danger-contrast font-bold'
const hasActivityClassNames = 'bg-danger-light text-danger font-bold'
const defaultClassNames = 'bg-transparent hover:bg-contrast'
const hasPendingEntryNames = 'bg-contrast'

return (
<div className="h-7 w-[14.2%] p-0.5">
<div
className={`${
!hasActivity && !isToday ? defaultClassNames : ''
} flex h-full w-full cursor-pointer items-center justify-center rounded ${
isToday ? todayClassNames : hasActivity ? hasActivityClassNames : ''
} ${hasPendingEntry ? hasPendingEntryNames : ''}`}
key={day}
onClick={onClick}
>
{day > 0 ? day : ''}
</div>
</div>
)
}

export default CalendarDay
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export type CalendarMonth = {
/** Any date in the month */
date: Date
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { addMonths, areDatesInSameMonth } from '@/Utils/DateUtils'
import { CalendarMonth } from './CalendarMonth'
import { insertMonthsWithTarget } from './CalendarUtilts'

describe('calendar utils', () => {
it('insertMonthsWithTarget in past', () => {
const today = new Date()
const months: CalendarMonth[] = [{ date: addMonths(today, -1) }, { date: today }, { date: addMonths(today, 1) }]
const targetMonth = addMonths(today, -12)

const result = insertMonthsWithTarget(months, targetMonth)

expect(result).toHaveLength(14)
expect(areDatesInSameMonth(result[0].date, targetMonth))
})

it('insertMonthsWithTarget in future', () => {
const today = new Date()
const months: CalendarMonth[] = [{ date: addMonths(today, -1) }, { date: today }, { date: addMonths(today, 1) }]
const targetMonth = addMonths(today, 12)

const result = insertMonthsWithTarget(months, targetMonth)

expect(result).toHaveLength(14)
expect(areDatesInSameMonth(result[result.length - 1].date, targetMonth))
})
})
Loading