refactor: native feature management (#2350)
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user