Files
standardnotes-app-web/packages/snjs/lib/Services/Features/UseCase/GetFeatureStatus.ts

116 lines
3.7 KiB
TypeScript

import { AnyFeatureDescription, FeatureIdentifier, FindNativeFeature } from '@standardnotes/features'
import { DecryptedItemInterface } from '@standardnotes/models'
import { Subscription } from '@standardnotes/security'
import { FeatureStatus, ItemManagerInterface } from '@standardnotes/services'
import { convertTimestampToMilliseconds } from '@standardnotes/utils'
export class GetFeatureStatusUseCase {
constructor(private items: ItemManagerInterface) {}
execute(dto: {
featureId: FeatureIdentifier | string
firstPartyOnlineSubscription: Subscription | undefined
firstPartyRoles: { online: string[] } | { offline: string[] } | undefined
hasPaidAnyPartyOnlineOrOfflineSubscription: boolean
inContextOfItem?: DecryptedItemInterface
}): FeatureStatus {
if (this.isFreeFeature(dto.featureId as FeatureIdentifier)) {
return FeatureStatus.Entitled
}
const nativeFeature = FindNativeFeature(dto.featureId as FeatureIdentifier)
if (!nativeFeature) {
return this.getThirdPartyFeatureStatus(dto.featureId as string)
}
if (nativeFeature.deprecated) {
return this.getDeprecatedNativeFeatureStatus({
nativeFeature,
hasPaidAnyPartyOnlineOrOfflineSubscription: dto.hasPaidAnyPartyOnlineOrOfflineSubscription,
})
}
return this.getNativeFeatureFeatureStatus({
nativeFeature,
firstPartyOnlineSubscription: dto.firstPartyOnlineSubscription,
firstPartyRoles: dto.firstPartyRoles,
inContextOfItem: dto.inContextOfItem,
})
}
private getDeprecatedNativeFeatureStatus(dto: {
hasPaidAnyPartyOnlineOrOfflineSubscription: boolean
nativeFeature: AnyFeatureDescription
}): FeatureStatus {
if (dto.hasPaidAnyPartyOnlineOrOfflineSubscription) {
return FeatureStatus.Entitled
} else {
return FeatureStatus.NoUserSubscription
}
}
private getNativeFeatureFeatureStatus(dto: {
nativeFeature: AnyFeatureDescription
firstPartyOnlineSubscription: Subscription | undefined
firstPartyRoles: { online: string[] } | { offline: string[] } | undefined
inContextOfItem?: DecryptedItemInterface
}): FeatureStatus {
if (dto.inContextOfItem) {
const isSharedVaultItem = dto.inContextOfItem.shared_vault_uuid !== undefined
if (isSharedVaultItem) {
return FeatureStatus.Entitled
}
}
if (!dto.firstPartyOnlineSubscription && !dto.firstPartyRoles) {
return FeatureStatus.NoUserSubscription
}
const roles = !dto.firstPartyRoles
? undefined
: 'online' in dto.firstPartyRoles
? dto.firstPartyRoles.online
: dto.firstPartyRoles.offline
if (dto.nativeFeature.availableInRoles && roles) {
const hasRole = roles.some((role) => {
return dto.nativeFeature.availableInRoles?.includes(role)
})
if (!hasRole) {
return FeatureStatus.NotInCurrentPlan
}
}
if (dto.firstPartyOnlineSubscription) {
const isSubscriptionExpired =
new Date(convertTimestampToMilliseconds(dto.firstPartyOnlineSubscription.endsAt)) < new Date()
if (isSubscriptionExpired) {
return FeatureStatus.InCurrentPlanButExpired
}
}
return FeatureStatus.Entitled
}
private getThirdPartyFeatureStatus(featureId: string): FeatureStatus {
const component = this.items.getDisplayableComponents().find((candidate) => candidate.identifier === featureId)
if (!component) {
return FeatureStatus.NoUserSubscription
}
if (component.isExpired) {
return FeatureStatus.InCurrentPlanButExpired
}
return FeatureStatus.Entitled
}
private isFreeFeature(featureId: FeatureIdentifier) {
return [FeatureIdentifier.DarkTheme, FeatureIdentifier.PlainEditor].includes(featureId)
}
}