feat: Added "Whats New" indicator to Preferences button (#2107)
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
import { Environment } from '@standardnotes/models'
|
import { Environment } from '@standardnotes/models'
|
||||||
import { StorageServiceInterface, StorageKey } from '@standardnotes/services'
|
import { StorageServiceInterface, StorageKey } from '@standardnotes/services'
|
||||||
import { Changelog, ChangelogVersion } from './Changelog'
|
import { Changelog, ChangelogVersion } from './Changelog'
|
||||||
import { ChangelogServiceInterface } from './ChangelogServiceInterface'
|
import { ChangelogLastReadVersionListener, ChangelogServiceInterface } from './ChangelogServiceInterface'
|
||||||
import { LegacyWebToDesktopVersionMapping } from './LegacyDesktopMapping'
|
import { LegacyWebToDesktopVersionMapping } from './LegacyDesktopMapping'
|
||||||
import { LegacyWebToMobileVersionMapping } from './LegacyMobileMapping'
|
import { LegacyWebToMobileVersionMapping } from './LegacyMobileMapping'
|
||||||
|
|
||||||
@@ -10,9 +10,18 @@ const DesktopDownloadsUrlBase = 'https://github.com/standardnotes/app/releases/t
|
|||||||
|
|
||||||
export class ChangelogService implements ChangelogServiceInterface {
|
export class ChangelogService implements ChangelogServiceInterface {
|
||||||
private changeLog?: Changelog
|
private changeLog?: Changelog
|
||||||
|
private lastReadChangeListeners: ChangelogLastReadVersionListener[] = []
|
||||||
|
|
||||||
constructor(private environment: Environment, private diskService: StorageServiceInterface) {}
|
constructor(private environment: Environment, private diskService: StorageServiceInterface) {}
|
||||||
|
|
||||||
|
public addLastReadChangeListener(listener: ChangelogLastReadVersionListener) {
|
||||||
|
this.lastReadChangeListeners.push(listener)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
this.lastReadChangeListeners = this.lastReadChangeListeners.filter((l) => l !== listener)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async performDownloadChangelog(): Promise<Changelog> {
|
private async performDownloadChangelog(): Promise<Changelog> {
|
||||||
const response = await fetch(WebChangelogUrl)
|
const response = await fetch(WebChangelogUrl)
|
||||||
const changelog = await response.text()
|
const changelog = await response.text()
|
||||||
@@ -47,7 +56,11 @@ export class ChangelogService implements ChangelogServiceInterface {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.diskService.setValue(StorageKey.LastReadChangelogVersion, this.changeLog.versions[0].version)
|
const version = this.changeLog.versions[0].version
|
||||||
|
this.diskService.setValue(StorageKey.LastReadChangelogVersion, version)
|
||||||
|
if (version) {
|
||||||
|
this.lastReadChangeListeners.forEach((listener) => listener(version))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public getLastReadVersion(): string | undefined {
|
public getLastReadVersion(): string | undefined {
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import { Changelog, ChangelogVersion } from './Changelog'
|
import { Changelog, ChangelogVersion } from './Changelog'
|
||||||
|
|
||||||
|
export type ChangelogLastReadVersionListener = (version: string) => void
|
||||||
|
|
||||||
export interface ChangelogServiceInterface {
|
export interface ChangelogServiceInterface {
|
||||||
|
addLastReadChangeListener(listener: ChangelogLastReadVersionListener): () => void
|
||||||
getChangelog(): Promise<Changelog>
|
getChangelog(): Promise<Changelog>
|
||||||
getVersions(): Promise<ChangelogVersion[]>
|
getVersions(): Promise<ChangelogVersion[]>
|
||||||
getDesktopDownloadsUrl(version: string): string
|
getDesktopDownloadsUrl(version: string): string
|
||||||
|
|||||||
@@ -339,8 +339,11 @@ class Footer extends AbstractComponent<Props, State> {
|
|||||||
this.viewControllerManager.quickSettingsMenuController.closeQuickSettingsMenu()
|
this.viewControllerManager.quickSettingsMenuController.closeQuickSettingsMenu()
|
||||||
}
|
}
|
||||||
|
|
||||||
openPreferences = () => {
|
openPreferences = (openWhatsNew: boolean) => {
|
||||||
this.clickOutsideQuickSettingsMenu()
|
this.clickOutsideQuickSettingsMenu()
|
||||||
|
if (openWhatsNew) {
|
||||||
|
this.viewControllerManager.preferencesController.setCurrentPane('whats-new')
|
||||||
|
}
|
||||||
this.viewControllerManager.preferencesController.openPreferences()
|
this.viewControllerManager.preferencesController.openPreferences()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,27 +1,47 @@
|
|||||||
|
import { compareSemVersions } from '@standardnotes/snjs'
|
||||||
import { keyboardStringForShortcut, OPEN_PREFERENCES_COMMAND } from '@standardnotes/ui-services'
|
import { keyboardStringForShortcut, OPEN_PREFERENCES_COMMAND } from '@standardnotes/ui-services'
|
||||||
import { useMemo } from 'react'
|
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||||
|
import { useApplication } from '../ApplicationProvider'
|
||||||
import { useCommandService } from '../CommandProvider'
|
import { useCommandService } from '../CommandProvider'
|
||||||
import Icon from '../Icon/Icon'
|
import Icon from '../Icon/Icon'
|
||||||
import StyledTooltip from '../StyledTooltip/StyledTooltip'
|
import StyledTooltip from '../StyledTooltip/StyledTooltip'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
openPreferences: () => void
|
openPreferences: (openWhatsNew: boolean) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const PreferencesButton = ({ openPreferences }: Props) => {
|
const PreferencesButton = ({ openPreferences }: Props) => {
|
||||||
const commandService = useCommandService()
|
const application = useApplication()
|
||||||
|
|
||||||
|
const commandService = useCommandService()
|
||||||
const shortcut = useMemo(
|
const shortcut = useMemo(
|
||||||
() => keyboardStringForShortcut(commandService.keyboardShortcutForCommand(OPEN_PREFERENCES_COMMAND)),
|
() => keyboardStringForShortcut(commandService.keyboardShortcutForCommand(OPEN_PREFERENCES_COMMAND)),
|
||||||
[commandService],
|
[commandService],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const [changelogLastReadVersion, setChangelogLastReadVersion] = useState(() =>
|
||||||
|
application.changelogService.getLastReadVersion(),
|
||||||
|
)
|
||||||
|
const isChangelogUnread = useMemo(
|
||||||
|
() => (changelogLastReadVersion ? compareSemVersions(application.version, changelogLastReadVersion) > 0 : false),
|
||||||
|
[application.version, changelogLastReadVersion],
|
||||||
|
)
|
||||||
|
useEffect(
|
||||||
|
() => application.changelogService.addLastReadChangeListener(setChangelogLastReadVersion),
|
||||||
|
[application.changelogService],
|
||||||
|
)
|
||||||
|
|
||||||
|
const onClick = useCallback(() => {
|
||||||
|
openPreferences(isChangelogUnread)
|
||||||
|
}, [isChangelogUnread, openPreferences])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledTooltip label={`Open preferences (${shortcut})`}>
|
<StyledTooltip label={`Open preferences (${shortcut})`}>
|
||||||
<button onClick={openPreferences} className="flex h-full w-8 cursor-pointer items-center justify-center">
|
<button onClick={onClick} className="relative flex h-full w-8 cursor-pointer items-center justify-center">
|
||||||
<div className="h-5">
|
<div className="h-5">
|
||||||
<Icon type="tune" className="rounded hover:text-info" />
|
<Icon type="tune" className="rounded hover:text-info" />
|
||||||
</div>
|
</div>
|
||||||
|
{isChangelogUnread && <div className="absolute top-0.5 right-0.5 h-2 w-2 rounded-full bg-info" />}
|
||||||
</button>
|
</button>
|
||||||
</StyledTooltip>
|
</StyledTooltip>
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user