feat: display feature status (#745)
* feat: display feature status * fix: hook dep * fix: react hooks dep
This commit is contained in:
@@ -1,14 +1,34 @@
|
||||
import { FeatureStatus } from '@standardnotes/snjs';
|
||||
import { FunctionalComponent } from 'preact';
|
||||
|
||||
interface IProps {
|
||||
expiredDate: string;
|
||||
componentName: string;
|
||||
featureStatus: FeatureStatus;
|
||||
reloadStatus: () => void;
|
||||
manageSubscription: () => void;
|
||||
}
|
||||
|
||||
const statusString = (featureStatus: FeatureStatus, expiredDate: string, componentName: string) => {
|
||||
switch (featureStatus) {
|
||||
case FeatureStatus.InCurrentPlanButExpired:
|
||||
return `Your subscription expired on ${expiredDate}`;
|
||||
case FeatureStatus.NoUserSubscription:
|
||||
return `You do not have an active subscription`;
|
||||
case FeatureStatus.NotInCurrentPlan:
|
||||
return `Please upgrade your plan to access ${componentName}`;
|
||||
default:
|
||||
return `${componentName} is valid and you should not be seeing this message`;
|
||||
}
|
||||
};
|
||||
|
||||
export const IsExpired: FunctionalComponent<IProps> = ({
|
||||
expiredDate,
|
||||
reloadStatus
|
||||
}) => {
|
||||
expiredDate,
|
||||
featureStatus,
|
||||
reloadStatus,
|
||||
componentName,
|
||||
manageSubscription
|
||||
}) => {
|
||||
return (
|
||||
<div className={'sn-component'}>
|
||||
<div className={'sk-app-bar no-edges no-top-edge dynamic-height'}>
|
||||
@@ -19,37 +39,23 @@ export const IsExpired: FunctionalComponent<IProps> = ({
|
||||
</div>
|
||||
<div className={'sk-app-bar-item-column'}>
|
||||
<div>
|
||||
<a
|
||||
className={'sk-label sk-base'}
|
||||
href={'https://dashboard.standardnotes.com'}
|
||||
rel={'noopener'}
|
||||
target={'_blank'}
|
||||
>
|
||||
Your subscription expired on {expiredDate}
|
||||
</a>
|
||||
<strong>
|
||||
{statusString(featureStatus, expiredDate, componentName)}
|
||||
</strong>
|
||||
<div className={'sk-p'}>
|
||||
Extensions are in a read-only state.
|
||||
{componentName} is in a read-only state.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={'right'}>
|
||||
<div className={'sk-app-bar-item'} onClick={() => manageSubscription()}>
|
||||
<button className={'sn-button small success'}>Manage Subscription</button>
|
||||
</div>
|
||||
<div className={'sk-app-bar-item'} onClick={() => reloadStatus()}>
|
||||
<button className={'sn-button small info'}>Reload</button>
|
||||
</div>
|
||||
<div className={'sk-app-bar-item'}>
|
||||
<div className={'sk-app-bar-item-column'}>
|
||||
<a
|
||||
className={'sn-button small warning'}
|
||||
href={'https://standardnotes.com/help/41/my-extensions-appear-as-expired-even-though-my-subscription-is-still-valid'}
|
||||
rel={'noopener'}
|
||||
target={'_blank'}
|
||||
>
|
||||
Help
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ComponentAction, LiveItem, SNComponent } from '@node_modules/@standardnotes/snjs';
|
||||
import { ComponentAction, FeatureStatus, LiveItem, SNComponent, dateToLocalizedString } from '@standardnotes/snjs';
|
||||
import { WebApplication } from '@/ui_models/application';
|
||||
import { FunctionalComponent } from 'preact';
|
||||
import { toDirective } from '@/components/utils';
|
||||
@@ -12,6 +12,7 @@ import { IsExpired } from '@/components/ComponentView/IsExpired';
|
||||
import { IssueOnLoading } from '@/components/ComponentView/IssueOnLoading';
|
||||
import { AppState } from '@/ui_models/app_state';
|
||||
import { ComponentArea } from '@node_modules/@standardnotes/features';
|
||||
import { openSubscriptionDashboard } from '@/hooks/manageSubscription';
|
||||
|
||||
interface IProps {
|
||||
application: WebApplication;
|
||||
@@ -36,8 +37,7 @@ export const ComponentView: FunctionalComponent<IProps> = observer(
|
||||
appState,
|
||||
onLoad,
|
||||
componentUuid,
|
||||
templateComponent,
|
||||
manualDealloc = false,
|
||||
templateComponent
|
||||
}) => {
|
||||
const liveComponentRef = useRef<LiveItem<SNComponent> | null>(null);
|
||||
const iframeRef = useRef<HTMLIFrameElement>(null);
|
||||
@@ -46,7 +46,7 @@ export const ComponentView: FunctionalComponent<IProps> = observer(
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isReloading, setIsReloading] = useState(false);
|
||||
const [loadTimeout, setLoadTimeout] = useState<number | undefined>(undefined);
|
||||
const [isExpired, setIsExpired] = useState(false);
|
||||
const [featureStatus, setFeatureStatus] = useState<FeatureStatus | undefined>(undefined);
|
||||
const [isComponentValid, setIsComponentValid] = useState(true);
|
||||
const [error, setError] = useState<'offline-restricted' | 'url-missing' | undefined>(undefined);
|
||||
const [isDeprecated, setIsDeprecated] = useState(false);
|
||||
@@ -68,6 +68,10 @@ export const ComponentView: FunctionalComponent<IProps> = observer(
|
||||
});
|
||||
};
|
||||
|
||||
const manageSubscription = useCallback(() => {
|
||||
openSubscriptionDashboard(application);
|
||||
}, [application]);
|
||||
|
||||
const reloadStatus = useCallback(() => {
|
||||
if (!component) {
|
||||
return;
|
||||
@@ -82,12 +86,12 @@ export const ComponentView: FunctionalComponent<IProps> = observer(
|
||||
}
|
||||
}();
|
||||
|
||||
setIsExpired(component.isExpired);
|
||||
setFeatureStatus(application.getFeatureStatus(component.identifier));
|
||||
|
||||
const readonlyState = application.componentManager.getReadonlyStateForComponent(component);
|
||||
|
||||
if (!readonlyState.lockReadonly) {
|
||||
application.componentManager.setReadonlyStateForComponent(component, isExpired);
|
||||
application.componentManager.setReadonlyStateForComponent(component, featureStatus !== FeatureStatus.Entitled);
|
||||
}
|
||||
setIsComponentValid(!offlineRestricted && !hasUrlError);
|
||||
|
||||
@@ -104,7 +108,7 @@ export const ComponentView: FunctionalComponent<IProps> = observer(
|
||||
}
|
||||
setIsDeprecated(component.isDeprecated);
|
||||
setDeprecationMessage(component.package_info.deprecation_message);
|
||||
}, [application.componentManager, component, isComponentValid, isExpired]);
|
||||
}, [application, component, isComponentValid, featureStatus]);
|
||||
|
||||
const dismissDeprecationMessage = () => {
|
||||
setTimeout(() => {
|
||||
@@ -214,10 +218,6 @@ export const ComponentView: FunctionalComponent<IProps> = observer(
|
||||
};
|
||||
}, [application.componentManager, component, handleIframeLoad, loadComponent, reloadStatus]);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
const expiredDate = isExpired ? component.dateToLocalizedString(component.valid_until) : '';
|
||||
|
||||
const getUrl = () => {
|
||||
const url = component ? application.componentManager.urlForComponent(component) : '';
|
||||
return url as string;
|
||||
@@ -319,8 +319,14 @@ export const ComponentView: FunctionalComponent<IProps> = observer(
|
||||
reloadIframe={reloadIframe}
|
||||
/>
|
||||
)}
|
||||
{isExpired && (
|
||||
<IsExpired expiredDate={expiredDate} reloadStatus={reloadStatus} />
|
||||
{featureStatus !== FeatureStatus.Entitled && (
|
||||
<IsExpired
|
||||
expiredDate={dateToLocalizedString(component.valid_until)}
|
||||
reloadStatus={reloadStatus}
|
||||
featureStatus={featureStatus!}
|
||||
componentName={component.name}
|
||||
manageSubscription={manageSubscription}
|
||||
/>
|
||||
)}
|
||||
{isDeprecated && !isDeprecationMessageDismissed && (
|
||||
<IsDeprecated
|
||||
|
||||
Reference in New Issue
Block a user