refactor: native feature management (#2350)

This commit is contained in:
Mo
2023-07-12 12:56:08 -05:00
committed by GitHub
parent 49f7581cd8
commit 078ef3772c
223 changed files with 3996 additions and 3438 deletions

View File

@@ -63,7 +63,7 @@ export class FeaturesController extends AbstractViewController {
case ApplicationEvent.DidPurchaseSubscription:
this.showPurchaseSuccessAlert()
break
case ApplicationEvent.FeaturesUpdated:
case ApplicationEvent.FeaturesAvailabilityChanged:
case ApplicationEvent.Launched:
case ApplicationEvent.LocalDataLoaded:
case ApplicationEvent.UserRolesChanged:

View File

@@ -22,6 +22,7 @@ import {
isSystemView,
NotesAndFilesDisplayControllerOptions,
InternalEventBusInterface,
PrefDefaults,
} from '@standardnotes/snjs'
import { action, computed, makeObservable, observable, reaction, runInAction } from 'mobx'
import { WebApplication } from '../../Application/WebApplication'
@@ -32,7 +33,6 @@ import { SearchOptionsController } from '../SearchOptionsController'
import { SelectedItemsController } from '../SelectedItemsController'
import { NotesController } from '../NotesController/NotesController'
import { formatDateAndTimeForNote } from '@/Utils/DateUtils'
import { PrefDefaults } from '@/Constants/PrefDefaults'
import dayjs from 'dayjs'
import dayjsAdvancedFormat from 'dayjs/plugin/advancedFormat'

View File

@@ -1,7 +1,7 @@
import { FileItemActionType } from '@/Components/AttachedFilesPopover/PopoverFileItemAction'
import { NoteViewController } from '@/Components/NoteView/Controller/NoteViewController'
import { AppPaneId } from '@/Components/Panes/AppPaneMetadata'
import { PrefDefaults } from '@/Constants/PrefDefaults'
import { createLinkFromItem } from '@/Utils/Items/Search/createLinkFromItem'
import { LinkableItem } from '@/Utils/Items/Search/LinkableItem'
import {
@@ -16,6 +16,7 @@ import {
isNote,
InternalEventBusInterface,
isTag,
PrefDefaults,
} from '@standardnotes/snjs'
import { action, computed, makeObservable, observable } from 'mobx'
import { AbstractViewController } from './Abstract/AbstractViewController'
@@ -77,7 +78,7 @@ export class LinkingController extends AbstractViewController {
}
get isEntitledToNoteLinking() {
return !!this.subscriptionController.onlineSubscription
return !!this.subscriptionController.hasFirstPartyOnlineOrOfflineSubscription
}
setIsLinkingPanelOpen = (open: boolean) => {

View File

@@ -12,6 +12,7 @@ import {
EditorLineWidth,
InternalEventBusInterface,
MutationType,
PrefDefaults,
} from '@standardnotes/snjs'
import { makeObservable, observable, action, computed, runInAction } from 'mobx'
import { WebApplication } from '../../Application/WebApplication'
@@ -20,7 +21,6 @@ import { SelectedItemsController } from '../SelectedItemsController'
import { ItemListController } from '../ItemList/ItemListController'
import { NavigationController } from '../Navigation/NavigationController'
import { NotesControllerInterface } from './NotesControllerInterface'
import { PrefDefaults } from '@/Constants/PrefDefaults'
export class NotesController extends AbstractViewController implements NotesControllerInterface {
shouldLinkToParentFolders: boolean

View File

@@ -3,7 +3,13 @@ import {
TOGGLE_LIST_PANE_KEYBOARD_COMMAND,
TOGGLE_NAVIGATION_PANE_KEYBOARD_COMMAND,
} from '@standardnotes/ui-services'
import { ApplicationEvent, InternalEventBusInterface, PrefKey, removeFromArray } from '@standardnotes/snjs'
import {
ApplicationEvent,
InternalEventBusInterface,
PrefKey,
removeFromArray,
PrefDefaults,
} from '@standardnotes/snjs'
import { AppPaneId } from '../../Components/Panes/AppPaneMetadata'
import { isMobileScreen } from '@/Utils'
import { makeObservable, observable, action, computed } from 'mobx'
@@ -11,7 +17,6 @@ import { Disposer } from '@/Types/Disposer'
import { MediaQueryBreakpoints } from '@/Hooks/useMediaQuery'
import { WebApplication } from '@/Application/WebApplication'
import { AbstractViewController } from '../Abstract/AbstractViewController'
import { PrefDefaults } from '@/Constants/PrefDefaults'
import { log, LoggingDomain } from '@/Logging'
import { PaneLayout } from './PaneLayout'
import { panesForLayout } from './panesForLayout'

View File

@@ -1,32 +1,26 @@
import { Subscription } from '@standardnotes/security'
import { destroyAllObjectProperties } from '@/Utils'
import {
ApplicationEvent,
ClientDisplayableError,
convertTimestampToMilliseconds,
InternalEventBusInterface,
Invitation,
InvitationStatus,
SubscriptionClientInterface,
SubscriptionManagerEvent,
SubscriptionManagerInterface,
} from '@standardnotes/snjs'
import { action, computed, makeObservable, observable, runInAction } from 'mobx'
import { computed, makeObservable, observable, runInAction } from 'mobx'
import { WebApplication } from '../../Application/WebApplication'
import { AbstractViewController } from '../Abstract/AbstractViewController'
import { AvailableSubscriptions } from './AvailableSubscriptionsType'
import { Subscription } from './SubscriptionType'
export class SubscriptionController extends AbstractViewController {
private readonly ALLOWED_SUBSCRIPTION_INVITATIONS = 5
onlineSubscription: Subscription | undefined = undefined
availableSubscriptions: AvailableSubscriptions | undefined = undefined
subscriptionInvitations: Invitation[] | undefined = undefined
hasAccount: boolean
hasFirstPartySubscription: boolean
onlineSubscription: Subscription | undefined = undefined
override deinit() {
super.deinit()
;(this.onlineSubscription as unknown) = undefined
;(this.availableSubscriptions as unknown) = undefined
;(this.subscriptionInvitations as unknown) = undefined
destroyAllObjectProperties(this)
@@ -35,39 +29,28 @@ export class SubscriptionController extends AbstractViewController {
constructor(
application: WebApplication,
eventBus: InternalEventBusInterface,
private subscriptionManager: SubscriptionClientInterface,
private subscriptionManager: SubscriptionManagerInterface,
) {
super(application, eventBus)
this.hasAccount = application.hasAccount()
this.hasFirstPartySubscription = application.features.hasFirstPartySubscription()
makeObservable(this, {
onlineSubscription: observable,
availableSubscriptions: observable,
subscriptionInvitations: observable,
hasAccount: observable,
hasFirstPartySubscription: observable,
onlineSubscription: observable,
userSubscriptionName: computed,
userSubscriptionExpirationDate: computed,
isUserSubscriptionExpired: computed,
isUserSubscriptionCanceled: computed,
hasFirstPartyOnlineOrOfflineSubscription: computed,
usedInvitationsCount: computed,
allowedInvitationsCount: computed,
allInvitationsUsed: computed,
setUserSubscription: action,
setAvailableSubscriptions: action,
})
this.disposers.push(
application.addEventObserver(async () => {
if (application.hasAccount()) {
this.getSubscriptionInfo().catch(console.error)
this.reloadSubscriptionInvitations().catch(console.error)
}
runInAction(() => {
this.hasFirstPartySubscription = application.features.hasFirstPartySubscription()
this.hasAccount = application.hasAccount()
})
}, ApplicationEvent.Launched),
@@ -75,15 +58,6 @@ export class SubscriptionController extends AbstractViewController {
this.disposers.push(
application.addEventObserver(async () => {
runInAction(() => {
this.hasFirstPartySubscription = application.features.hasFirstPartySubscription()
})
}, ApplicationEvent.LocalDataLoaded),
)
this.disposers.push(
application.addEventObserver(async () => {
this.getSubscriptionInfo().catch(console.error)
this.reloadSubscriptionInvitations().catch(console.error)
runInAction(() => {
this.hasAccount = application.hasAccount()
@@ -91,50 +65,33 @@ export class SubscriptionController extends AbstractViewController {
}, ApplicationEvent.SignedIn),
)
this.disposers.push(
application.subscriptions.addEventObserver(async (event) => {
if (event === SubscriptionManagerEvent.DidFetchSubscription) {
runInAction(() => {
this.onlineSubscription = application.subscriptions.getOnlineSubscription()
})
}
}),
)
this.disposers.push(
application.addEventObserver(async () => {
this.getSubscriptionInfo().catch(console.error)
this.reloadSubscriptionInvitations().catch(console.error)
runInAction(() => {
this.hasFirstPartySubscription = application.features.hasFirstPartySubscription()
})
}, ApplicationEvent.UserRolesChanged),
)
}
get userSubscriptionName(): string {
if (
this.availableSubscriptions &&
this.onlineSubscription &&
this.availableSubscriptions[this.onlineSubscription.planName]
) {
return this.availableSubscriptions[this.onlineSubscription.planName].name
get hasFirstPartyOnlineOrOfflineSubscription(): boolean {
if (this.application.sessions.isSignedIn()) {
if (!this.application.sessions.isSignedIntoFirstPartyServer()) {
return false
}
return this.application.subscriptions.getOnlineSubscription() !== undefined
} else {
return this.application.features.hasFirstPartyOfflineSubscription()
}
return ''
}
get userSubscriptionExpirationDate(): Date | undefined {
if (!this.onlineSubscription) {
return undefined
}
return new Date(convertTimestampToMilliseconds(this.onlineSubscription.endsAt))
}
get isUserSubscriptionExpired(): boolean {
if (!this.userSubscriptionExpirationDate) {
return false
}
return this.userSubscriptionExpirationDate.getTime() < new Date().getTime()
}
get isUserSubscriptionCanceled(): boolean {
return Boolean(this.onlineSubscription?.cancelled)
}
hasValidSubscription(): boolean {
return this.onlineSubscription != undefined && !this.isUserSubscriptionExpired && !this.isUserSubscriptionCanceled
}
get usedInvitationsCount(): number {
@@ -153,14 +110,6 @@ export class SubscriptionController extends AbstractViewController {
return this.usedInvitationsCount === this.ALLOWED_SUBSCRIPTION_INVITATIONS
}
public setUserSubscription(subscription: Subscription): void {
this.onlineSubscription = subscription
}
public setAvailableSubscriptions(subscriptions: AvailableSubscriptions): void {
this.availableSubscriptions = subscriptions
}
async sendSubscriptionInvitation(inviteeEmail: string): Promise<boolean> {
const success = await this.subscriptionManager.inviteToSubscription(inviteeEmail)
@@ -181,33 +130,6 @@ export class SubscriptionController extends AbstractViewController {
return success
}
private async getAvailableSubscriptions() {
try {
const subscriptions = await this.application.getAvailableSubscriptions()
if (!(subscriptions instanceof ClientDisplayableError)) {
this.setAvailableSubscriptions(subscriptions)
}
} catch (error) {
void error
}
}
private async getSubscription() {
try {
const subscription = await this.application.getUserSubscription()
if (!(subscription instanceof ClientDisplayableError) && subscription) {
this.setUserSubscription(subscription)
}
} catch (error) {
console.error(error)
}
}
private async getSubscriptionInfo() {
await this.getSubscription()
await this.getAvailableSubscriptions()
}
private async reloadSubscriptionInvitations(): Promise<void> {
this.subscriptionInvitations = await this.subscriptionManager.listSubscriptionInvitations()
}

View File

@@ -13,7 +13,7 @@ import { destroyAllObjectProperties } from '@/Utils'
import {
DeinitSource,
WebOrDesktopDeviceInterface,
SubscriptionClientInterface,
SubscriptionManagerInterface,
InternalEventHandlerInterface,
InternalEventInterface,
} from '@standardnotes/snjs'
@@ -75,7 +75,7 @@ export class ViewControllerManager implements InternalEventHandlerInterface {
private appEventObserverRemovers: (() => void)[] = []
private subscriptionManager: SubscriptionClientInterface
private subscriptionManager: SubscriptionManagerInterface
private persistenceService: PersistenceService
private applicationEventObserver: EventObserverInterface
private toastService: ToastServiceInterface