feat: display number of files for 'Files' view (#2065)

* feat: display number of files for 'Files' view

* feat: include files count in Preferences > Security
This commit is contained in:
Mo
2022-11-28 15:38:50 -06:00
committed by GitHub
parent 052e7d2f90
commit 767d354780
25 changed files with 69 additions and 91 deletions

View File

@@ -74,7 +74,6 @@ export class WebApplication extends SNApplication implements WebApplicationInter
defaultHost: defaultSyncServerHost,
appVersion: deviceInterface.appVersion,
webSocketUrl: webSocketUrl,
supportsFileNavigation: true,
})
makeObservable(this, {

View File

@@ -2,6 +2,7 @@ import { WebApplication } from '@/Application/Application'
import { FeaturesController } from '@/Controllers/FeaturesController'
import { SubscriptionController } from '@/Controllers/Subscription/SubscriptionController'
import { observer } from 'mobx-react-lite'
import { useCallback } from 'react'
type Props = {
application: WebApplication
@@ -13,13 +14,13 @@ const UpgradeNow = ({ application, featuresController, subscriptionContoller }:
const shouldShowCTA = !featuresController.hasFolders
const hasAccount = subscriptionContoller.hasAccount
const onClick = () => {
if (application.isNativeIOS()) {
const onClick = useCallback(() => {
if (hasAccount && application.isNativeIOS()) {
application.showPremiumModal()
} else {
application.openPurchaseFlow()
}
}
}, [application, hasAccount])
return shouldShowCTA ? (
<div className="flex h-full items-center px-2">

View File

@@ -27,7 +27,7 @@ const Encryption: FunctionComponent<Props> = ({ viewControllerManager }) => {
<Title>Encryption</Title>
<Text>{encryptionStatusString}</Text>
{isEncryptionEnabled && <EncryptionEnabled viewControllerManager={viewControllerManager} />}
{isEncryptionEnabled && <EncryptionEnabled />}
</PreferencesSegment>
</PreferencesGroup>
)

View File

@@ -1,34 +1,37 @@
import { useApplication } from '@/Components/ApplicationView/ApplicationProvider'
import Icon from '@/Components/Icon/Icon'
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
import { ContentType, ItemCounter } from '@standardnotes/snjs'
import { observer } from 'mobx-react-lite'
import { FunctionComponent } from 'react'
import EncryptionStatusItem from './EncryptionStatusItem'
import { formatCount } from './formatCount'
type Props = {
viewControllerManager: ViewControllerManager
}
const EncryptionEnabled: FunctionComponent<Props> = ({ viewControllerManager }) => {
const count = viewControllerManager.accountMenuController.structuredNotesAndTagsCount
const EncryptionEnabled: FunctionComponent = () => {
const application = useApplication()
const itemCounter = new ItemCounter()
const count = itemCounter.countNotesAndTags(application.items.getItems([ContentType.Note, ContentType.Tag]))
const files = application.items.getItems([ContentType.File])
const notes = formatCount(count.notes, 'notes')
const tags = formatCount(count.tags, 'tags')
const archived = formatCount(count.archived, 'archived notes')
const deleted = formatCount(count.deleted, 'trashed notes')
const filesCount = formatCount(files.length, 'files')
const noteIcon = <Icon type="rich-text" className="min-h-5 min-w-5" />
const tagIcon = <Icon type="hashtag" className="min-h-5 min-w-5" />
const archiveIcon = <Icon type="archive" className="min-h-5 min-w-5" />
const trashIcon = <Icon type="trash" className="min-h-5 min-w-5" />
const filesIcon = <Icon type="folder" className="min-h-5 min-w-5" />
return (
<>
<div className="flex flex-row flex-wrap items-start pt-1.5 md:pb-1">
<EncryptionStatusItem status={notes} icon={noteIcon} />
<div className="min-w-3" />
<EncryptionStatusItem status={filesCount} icon={filesIcon} />
<div className="min-w-3" />
<EncryptionStatusItem status={tags} icon={tagIcon} />
</div>
<div className="flex flex-row flex-wrap items-start">
<div className="min-w-3" />
<EncryptionStatusItem status={archived} icon={archiveIcon} />
<div className="min-w-3" />
<EncryptionStatusItem status={deleted} icon={trashIcon} />

View File

@@ -1 +1 @@
export const formatCount = (count: number, itemType: string) => `${count} / ${count} ${itemType}`
export const formatCount = (count: number, itemType: string) => `${count} ${itemType}`

View File

@@ -10,7 +10,6 @@ type Props = {
application: WebApplication
featureName?: FeatureName | string
hasSubscription: boolean
hasAccount: boolean
onClose: () => void
showModal: boolean
type: PremiumFeatureModalType
@@ -20,7 +19,6 @@ const PremiumFeaturesModal: FunctionComponent<Props> = ({
application,
featureName,
hasSubscription,
hasAccount,
onClose,
showModal,
type = PremiumFeatureModalType.UpgradePrompt,
@@ -40,7 +38,6 @@ const PremiumFeaturesModal: FunctionComponent<Props> = ({
featureName={featureName}
ctaRef={ctaButtonRef}
application={application}
hasAccount={hasAccount}
hasSubscription={hasSubscription}
onClose={onClose}
/>

View File

@@ -10,26 +10,22 @@ export const UpgradePrompt = ({
ctaRef,
application,
hasSubscription,
hasAccount,
onClose,
}: {
featureName?: string
ctaRef: React.RefObject<HTMLButtonElement>
application: WebApplication
hasSubscription: boolean
hasAccount: boolean
onClose: () => void
}) => {
const handleClick = useCallback(() => {
if (hasSubscription) {
void openSubscriptionDashboard(application)
} else if (hasAccount) {
} else {
void application.openPurchaseFlow()
} else if (window.plansUrl) {
window.location.assign(window.plansUrl)
}
onClose()
}, [application, hasSubscription, hasAccount, onClose])
}, [application, hasSubscription, onClose])
return (
<>

View File

@@ -140,6 +140,7 @@ const SmartViewsListItem: FunctionComponent<Props> = ({ view, tagsState, setEdit
)}
<div className={'count text-base lg:text-sm'}>
{view.uuid === SystemViewId.AllNotes && tagsState.allNotesCount}
{view.uuid === SystemViewId.Files && tagsState.allFilesCount}
</div>
</div>

View File

@@ -8,7 +8,7 @@ export const STRING_DEFAULT_FILE_ERROR =
'Please use FileSafe or the Bold Editor to attach images and files. Learn more at standardnotes.com/filesafe.'
export const STRING_GENERIC_SYNC_ERROR =
'There was an error syncing. Please try again. If all else fails, try signing out and signing back in.'
export function StringSyncException(data: any) {
export function StringSyncException(data: unknown) {
return `There was an error while trying to save your items. Please contact support and share this message: ${JSON.stringify(
data,
)}.`
@@ -108,7 +108,6 @@ export const STRING_FAILED_TO_UPDATE_USER_SETTING =
export const Strings = {
protectingNoteWithoutProtectionSources:
'Access to this note will not be restricted until you set up a passcode or account.',
openAccountMenu: 'Open Account Menu',
trashItemsTitle: 'Move to Trash',
trashNotesText: 'Are you sure you want to move these notes to the trash?',
trashFilesText: 'Are you sure you want to move these files to the trash?',

View File

@@ -1,14 +1,6 @@
import { destroyAllObjectProperties, isDev } from '@/Utils'
import { action, computed, makeObservable, observable, runInAction } from 'mobx'
import {
ApplicationEvent,
ContentType,
InternalEventBus,
SNNote,
SNTag,
ItemCounterInterface,
ItemCounts,
} from '@standardnotes/snjs'
import { ApplicationEvent, ContentType, InternalEventBus, SNNote, SNTag } from '@standardnotes/snjs'
import { WebApplication } from '@/Application/Application'
import { AccountMenuPane } from '@/Components/AccountMenu/AccountMenuPane'
import { AbstractViewController } from '../Abstract/AbstractViewController'
@@ -36,7 +28,7 @@ export class AccountMenuController extends AbstractViewController {
destroyAllObjectProperties(this)
}
constructor(application: WebApplication, eventBus: InternalEventBus, private itemCounter: ItemCounterInterface) {
constructor(application: WebApplication, eventBus: InternalEventBus) {
super(application, eventBus)
makeObservable(this, {
@@ -165,8 +157,4 @@ export class AccountMenuController extends AbstractViewController {
get notesAndTagsCount(): number {
return this.notesAndTags.length
}
get structuredNotesAndTagsCount(): ItemCounts {
return this.itemCounter.countNotesAndTags(this.notesAndTags)
}
}

View File

@@ -34,6 +34,7 @@ export class NavigationController
smartViews: SmartView[] = []
starredTags: SNTag[] = []
allNotesCount_ = 0
allFilesCount_ = 0
selectedUuid: AnyTag['uuid'] | undefined = undefined
selected_: AnyTag | undefined = undefined
selectedLocation: TagListSectionType | undefined = undefined
@@ -62,8 +63,11 @@ export class NavigationController
starredTags: observable,
smartViews: observable.ref,
allNotesCount_: observable,
allFilesCount_: observable,
allNotesCount: computed,
allFilesCount: computed,
setAllNotesCount: action,
setAllFilesCount: action,
selected_: observable,
selectedLocation: observable,
@@ -135,6 +139,7 @@ export class NavigationController
this.application.items.addNoteCountChangeObserver((tagUuid) => {
if (!tagUuid) {
this.setAllNotesCount(this.application.items.allCountableNotesCount())
this.setAllFilesCount(this.application.items.allCountableFilesCount())
} else {
const tag = this.application.items.findItem<SNTag>(tagUuid)
if (tag) {
@@ -411,6 +416,14 @@ export class NavigationController
this.allNotesCount_ = allNotesCount
}
setAllFilesCount(allFilesCount: number) {
this.allFilesCount_ = allFilesCount
}
public get allFilesCount(): number {
return this.allFilesCount_
}
public get allNotesCount(): number {
return this.allNotesCount_
}

View File

@@ -14,8 +14,6 @@ import {
DeinitSource,
WebOrDesktopDeviceInterface,
InternalEventBus,
ItemCounterInterface,
ItemCounter,
SubscriptionClientInterface,
InternalEventHandlerInterface,
InternalEventInterface,
@@ -74,7 +72,6 @@ export class ViewControllerManager implements InternalEventHandlerInterface {
private appEventObserverRemovers: (() => void)[] = []
private eventBus: InternalEventBus
private itemCounter: ItemCounterInterface
private subscriptionManager: SubscriptionClientInterface
private persistenceService: PersistenceService
private applicationEventObserver: EventObserverInterface
@@ -88,8 +85,6 @@ export class ViewControllerManager implements InternalEventHandlerInterface {
this.eventBus.addEventHandler(this, CrossControllerEvent.HydrateFromPersistedValues)
this.eventBus.addEventHandler(this, CrossControllerEvent.RequestValuePersistence)
this.itemCounter = new ItemCounter()
this.subscriptionManager = application.subscriptions
this.quickSettingsMenuController = new QuickSettingsController(application, this.eventBus)
@@ -135,7 +130,7 @@ export class ViewControllerManager implements InternalEventHandlerInterface {
this.noAccountWarningController = new NoAccountWarningController(application, this.eventBus)
this.accountMenuController = new AccountMenuController(application, this.eventBus, this.itemCounter)
this.accountMenuController = new AccountMenuController(application, this.eventBus)
this.subscriptionController = new SubscriptionController(application, this.eventBus, this.subscriptionManager)

View File

@@ -34,8 +34,6 @@ const PremiumModalProvider: FunctionComponent<Props> = observer(
const hasSubscription = application.hasValidSubscription()
const hasAccount = application.hasAccount()
const activate = useCallback(
(feature: string) => {
featuresController.showPremiumAlert(feature).catch(console.error)
@@ -54,7 +52,6 @@ const PremiumModalProvider: FunctionComponent<Props> = observer(
application={application}
featureName={featureName}
hasSubscription={hasSubscription}
hasAccount={hasAccount}
onClose={close}
showModal={featuresController.premiumAlertType != undefined}
type={featuresController.premiumAlertType}