Merge branch 'release/10.0.0'
This commit is contained in:
@@ -57,7 +57,7 @@ export const AutocompleteTagHint = observer(
|
||||
<button
|
||||
ref={hintRef}
|
||||
type="button"
|
||||
className="sn-dropdown-item"
|
||||
className="sn-dropdown-item focus:bg-info focus:color-info-contrast hover:color-foreground"
|
||||
onClick={onTagHintClick}
|
||||
onFocus={onFocus}
|
||||
onBlur={onBlur}
|
||||
|
||||
@@ -77,7 +77,7 @@ export const AutocompleteTagResult = observer(
|
||||
<button
|
||||
ref={tagResultRef}
|
||||
type="button"
|
||||
className="sn-dropdown-item"
|
||||
className="sn-dropdown-item focus:bg-info focus:color-info-contrast"
|
||||
onClick={() => onTagOptionClick(tagResult)}
|
||||
onFocus={onFocus}
|
||||
onBlur={onBlur}
|
||||
|
||||
@@ -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>(FeatureStatus.Entitled);
|
||||
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
|
||||
|
||||
@@ -44,8 +44,11 @@ const countNoteAttributes = (text: string) => {
|
||||
paragraphs: 'N/A',
|
||||
};
|
||||
} catch {
|
||||
const removeTags = text.replace(/<[^>]*>/g," ").replace(/\s+/g, ' ').trim();
|
||||
text = removeTags;
|
||||
|
||||
const characters = text.length;
|
||||
const words = text.match(/[\w’'-]+\b/g)?.length;
|
||||
const words = text.split(" ")?.length;
|
||||
const paragraphs = text.replace(/\n$/gm, '').split(/\n/).length;
|
||||
|
||||
return {
|
||||
@@ -82,8 +85,8 @@ const NoteAttributes: FunctionComponent<{ note: SNNote }> = ({ note }) => {
|
||||
);
|
||||
|
||||
const dateLastModified = useMemo(
|
||||
() => formatDate(note.serverUpdatedAt),
|
||||
[note.serverUpdatedAt]
|
||||
() => formatDate(note.userModifiedDate),
|
||||
[note.userModifiedDate]
|
||||
);
|
||||
|
||||
const dateCreated = useMemo(
|
||||
@@ -229,6 +232,13 @@ export const NotesOptions = observer(
|
||||
if (errored) {
|
||||
return (
|
||||
<>
|
||||
{notes.length === 1 ? (
|
||||
<div className="px-3 pt-1.5 pb-1 text-xs color-neutral font-medium">
|
||||
<div>
|
||||
<span className="font-semibold">Note ID:</span> {notes[0].uuid}
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
<DeletePermanentlyButton
|
||||
closeOnBlur={closeOnBlur}
|
||||
onClick={async () => {
|
||||
|
||||
12
app/assets/javascripts/hooks/manageSubscription.ts
Normal file
12
app/assets/javascripts/hooks/manageSubscription.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { SNApplication } from '@standardnotes/snjs';
|
||||
|
||||
export function openSubscriptionDashboard(application: SNApplication): void {
|
||||
application.getNewSubscriptionToken().then((token) => {
|
||||
if (!token) {
|
||||
return;
|
||||
}
|
||||
window.open(
|
||||
`${window._dashboard_url}?subscription_token=${token}`
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { IconType } from '@/components/Icon';
|
||||
import { action, makeAutoObservable, observable } from 'mobx';
|
||||
import { ExtensionsLatestVersions } from '@/preferences/panes/extensions-segments';
|
||||
import { ContentType, SNComponent } from '@node_modules/@standardnotes/snjs';
|
||||
import { ContentType, SNComponent } from '@standardnotes/snjs';
|
||||
import { WebApplication } from '@/ui_models/application';
|
||||
import { FeatureIdentifier } from '@node_modules/@standardnotes/features/dist/Domain/Feature/FeatureIdentifier';
|
||||
import { ComponentArea } from '@standardnotes/snjs';
|
||||
|
||||
@@ -20,7 +20,13 @@ const loadExtensions = (application: WebApplication) => application.getItems([
|
||||
export const Extensions: FunctionComponent<{
|
||||
application: WebApplication
|
||||
extensionsLatestVersions: ExtensionsLatestVersions,
|
||||
}> = observer(({ application, extensionsLatestVersions }) => {
|
||||
className?: string,
|
||||
}> = observer(
|
||||
({
|
||||
application,
|
||||
extensionsLatestVersions,
|
||||
className = ''
|
||||
}) => {
|
||||
|
||||
const [customUrl, setCustomUrl] = useState('');
|
||||
const [confirmableExtension, setConfirmableExtension] = useState<SNComponent | undefined>(undefined);
|
||||
@@ -84,7 +90,7 @@ export const Extensions: FunctionComponent<{
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={className}>
|
||||
{visibleExtensions.length > 0 &&
|
||||
<div>
|
||||
{
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import { FunctionalComponent } from 'preact';
|
||||
import { PreferencesGroup, PreferencesSegment, Title } from '@/preferences/components';
|
||||
import { PreferencesGroup, PreferencesSegment } from '@/preferences/components';
|
||||
import { OfflineSubscription } from '@/preferences/panes/account/offlineSubscription';
|
||||
import { WebApplication } from '@/ui_models/application';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { AppState } from '@/ui_models/app_state';
|
||||
import { Extensions } from '@/preferences/panes/Extensions';
|
||||
import { ExtensionsLatestVersions } from '@/preferences/panes/extensions-segments';
|
||||
import { HorizontalSeparator } from '@/components/shared/HorizontalSeparator';
|
||||
import { AccordionItem } from '@/components/shared/AccordionItem';
|
||||
|
||||
interface IProps {
|
||||
@@ -24,8 +23,7 @@ export const Advanced: FunctionalComponent<IProps> = observer(
|
||||
<div className='flex flex-row items-center'>
|
||||
<div className='flex-grow flex flex-col'>
|
||||
<OfflineSubscription application={application} appState={appState} />
|
||||
<HorizontalSeparator classes="mt-8 mb-8" />
|
||||
<Extensions application={application} extensionsLatestVersions={extensionsLatestVersions} />
|
||||
<Extensions className={'mt-3'} application={application} extensionsLatestVersions={extensionsLatestVersions} />
|
||||
</div>
|
||||
</div>
|
||||
</AccordionItem>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Button } from '@/components/Button';
|
||||
import { WebApplication } from '@/ui_models/application';
|
||||
import { observer } from '@node_modules/mobx-react-lite';
|
||||
import { HorizontalSeparator } from '@/components/shared/HorizontalSeparator';
|
||||
import { dateToLocalizedString } from '@/utils';
|
||||
import { dateToLocalizedString } from '@standardnotes/snjs';
|
||||
import { useState } from 'preact/hooks';
|
||||
import { ChangeEmail } from '@/preferences/panes/account/changeEmail';
|
||||
import { PasswordWizardType } from '@/types';
|
||||
|
||||
@@ -5,10 +5,9 @@ import {
|
||||
Title,
|
||||
} from '@/preferences/components';
|
||||
import { Button } from '@/components/Button';
|
||||
import { SyncQueueStrategy } from '@node_modules/@standardnotes/snjs';
|
||||
import { SyncQueueStrategy, dateToLocalizedString } from '@standardnotes/snjs';
|
||||
import { STRING_GENERIC_SYNC_ERROR } from '@/strings';
|
||||
import { useState } from '@node_modules/preact/hooks';
|
||||
import { dateToLocalizedString } from '@/utils';
|
||||
import { observer } from '@node_modules/mobx-react-lite';
|
||||
import { WebApplication } from '@/ui_models/application';
|
||||
import { FunctionComponent } from 'preact';
|
||||
|
||||
@@ -10,6 +10,7 @@ import { AppState } from '@/ui_models/app_state';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { STRING_REMOVE_OFFLINE_KEY_CONFIRMATION } from '@/strings';
|
||||
import { ButtonType } from '@standardnotes/snjs';
|
||||
import { HorizontalSeparator } from '@/components/shared/HorizontalSeparator';
|
||||
|
||||
interface IProps {
|
||||
application: WebApplication;
|
||||
@@ -55,10 +56,6 @@ export const OfflineSubscription: FunctionalComponent<IProps> = observer(({ appl
|
||||
setIsSuccessfullyRemoved(true);
|
||||
};
|
||||
|
||||
if (!shouldShowOfflineSubscription()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const handleRemoveClick = async () => {
|
||||
application.alertService.confirm(
|
||||
STRING_REMOVE_OFFLINE_KEY_CONFIRMATION,
|
||||
@@ -77,48 +74,55 @@ export const OfflineSubscription: FunctionalComponent<IProps> = observer(({ appl
|
||||
});
|
||||
};
|
||||
|
||||
if (!shouldShowOfflineSubscription()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='flex items-center justify-between'>
|
||||
<div className='flex flex-col mt-3 w-full'>
|
||||
<Subtitle>{!hasUserPreviouslyStoredCode && 'Activate'} Offline Subscription</Subtitle>
|
||||
<form onSubmit={handleSubscriptionCodeSubmit}>
|
||||
<div className={'mt-2'}>
|
||||
{!hasUserPreviouslyStoredCode && (
|
||||
<DecoratedInput
|
||||
onChange={(code) => setActivationCode(code)}
|
||||
placeholder={'Offline Subscription Code'}
|
||||
text={activationCode}
|
||||
disabled={isSuccessfullyActivated}
|
||||
className={'mb-3'}
|
||||
<>
|
||||
<div className='flex items-center justify-between'>
|
||||
<div className='flex flex-col mt-3 w-full'>
|
||||
<Subtitle>{!hasUserPreviouslyStoredCode && 'Activate'} Offline Subscription</Subtitle>
|
||||
<form onSubmit={handleSubscriptionCodeSubmit}>
|
||||
<div className={'mt-2'}>
|
||||
{!hasUserPreviouslyStoredCode && (
|
||||
<DecoratedInput
|
||||
onChange={(code) => setActivationCode(code)}
|
||||
placeholder={'Offline Subscription Code'}
|
||||
text={activationCode}
|
||||
disabled={isSuccessfullyActivated}
|
||||
className={'mb-3'}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{(isSuccessfullyActivated || isSuccessfullyRemoved) && (
|
||||
<div className={'mt-3 mb-3 info font-bold'}>
|
||||
Successfully {isSuccessfullyActivated ? 'Activated' : 'Removed'}!
|
||||
</div>
|
||||
)}
|
||||
{hasUserPreviouslyStoredCode && (
|
||||
<Button
|
||||
type='danger'
|
||||
label='Remove offline key'
|
||||
onClick={() => {
|
||||
handleRemoveClick();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{(isSuccessfullyActivated || isSuccessfullyRemoved) && (
|
||||
<div className={'mt-3 mb-3 info font-bold'}>
|
||||
Successfully {isSuccessfullyActivated ? 'Activated' : 'Removed'}!
|
||||
</div>
|
||||
)}
|
||||
{hasUserPreviouslyStoredCode && (
|
||||
<Button
|
||||
type='danger'
|
||||
label='Remove offline key'
|
||||
onClick={() => {
|
||||
handleRemoveClick();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{!hasUserPreviouslyStoredCode && !isSuccessfullyActivated && (
|
||||
<Button
|
||||
label={'Submit'}
|
||||
type='primary'
|
||||
disabled={activationCode === ''}
|
||||
onClick={(event) =>
|
||||
handleSubscriptionCodeSubmit(event as TargetedEvent<HTMLFormElement>)
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</form>
|
||||
{!hasUserPreviouslyStoredCode && !isSuccessfullyActivated && (
|
||||
<Button
|
||||
label={'Submit'}
|
||||
type='primary'
|
||||
disabled={activationCode === ''}
|
||||
onClick={(event) =>
|
||||
handleSubscriptionCodeSubmit(event as TargetedEvent<HTMLFormElement>)
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<HorizontalSeparator classes='mt-8 mb-5' />
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Text } from '@/preferences/components';
|
||||
import { Button } from '@/components/Button';
|
||||
import { WebApplication } from '@/ui_models/application';
|
||||
import { convertTimestampToMilliseconds } from '@standardnotes/snjs';
|
||||
import { openSubscriptionDashboard } from '@/hooks/manageSubscription';
|
||||
|
||||
type Props = {
|
||||
subscriptionState: SubscriptionState;
|
||||
@@ -44,14 +45,8 @@ const StatusText = observer(({ subscriptionState }: Props) => {
|
||||
|
||||
export const SubscriptionInformation = observer(
|
||||
({ subscriptionState, application }: Props) => {
|
||||
const openSubscriptionDashboard = async () => {
|
||||
const token = await application?.getNewSubscriptionToken();
|
||||
if (!token) {
|
||||
return;
|
||||
}
|
||||
window.location.assign(
|
||||
`${window._dashboard_url}?subscription_token=${token}`
|
||||
);
|
||||
const manageSubscription = async () => {
|
||||
openSubscriptionDashboard(application!);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -61,7 +56,7 @@ export const SubscriptionInformation = observer(
|
||||
className="min-w-20 mt-3 mr-3"
|
||||
type="normal"
|
||||
label="Manage subscription"
|
||||
onClick={openSubscriptionDashboard}
|
||||
onClick={manageSubscription}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -49,7 +49,7 @@ export const ConfirmCustomExtension: FunctionComponent<{
|
||||
return (
|
||||
<>
|
||||
<Subtitle>{field.label}</Subtitle>
|
||||
<Text>{field.value}</Text>
|
||||
<Text className={'wrap'}>{field.value}</Text>
|
||||
<div className="min-h-2" />
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -26,7 +26,7 @@ export const ScanQRCode: FunctionComponent<{
|
||||
</ModalDialogLabel>
|
||||
<ModalDialogDescription className="h-33">
|
||||
<div className="w-25 h-25 flex items-center justify-center bg-info">
|
||||
<QRCode value={act.qrCode} size={100} />
|
||||
<QRCode className="border-neutral-contrast-bg border-solid border-2" value={act.qrCode} size={100} />
|
||||
</div>
|
||||
<div className="min-w-5" />
|
||||
<div className="flex-grow flex flex-col">
|
||||
|
||||
@@ -34,31 +34,6 @@ export function getPlatform(): Platform {
|
||||
return platformFromString(getPlatformString());
|
||||
}
|
||||
|
||||
let sharedDateFormatter: Intl.DateTimeFormat;
|
||||
export function dateToLocalizedString(date: Date) {
|
||||
if (typeof Intl !== 'undefined' && Intl.DateTimeFormat) {
|
||||
if (!sharedDateFormatter) {
|
||||
const locale =
|
||||
navigator.languages && navigator.languages.length
|
||||
? navigator.languages[0]
|
||||
: navigator.language;
|
||||
sharedDateFormatter = new Intl.DateTimeFormat(locale, {
|
||||
year: 'numeric',
|
||||
month: 'numeric',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
});
|
||||
}
|
||||
return sharedDateFormatter.format(date);
|
||||
} else {
|
||||
// IE < 11, Safari <= 9.0.
|
||||
// In English, this generates the string most similar to
|
||||
// the toLocaleDateString() result above.
|
||||
return date.toDateString() + ' ' + date.toLocaleTimeString();
|
||||
}
|
||||
}
|
||||
|
||||
export function isSameDay(dateA: Date, dateB: Date): boolean {
|
||||
return (
|
||||
dateA.getFullYear() === dateB.getFullYear() &&
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#tags-column.sn-component.section.tags(aria-label='Tags')
|
||||
.component-view-container(ng-if='self.component.active')
|
||||
.component-view-container(ng-if='self.component && self.component.active')
|
||||
component-view.component-view(
|
||||
component-uuid='self.component.uuid',
|
||||
application='self.application'
|
||||
|
||||
@@ -106,9 +106,9 @@ class TagsViewCtrl extends PureViewCtrl<unknown, TagState> {
|
||||
beginStreamingItems() {
|
||||
this.removeFoldersObserver = this.application.streamItems(
|
||||
[ContentType.Component],
|
||||
async (_items) => {
|
||||
async () => {
|
||||
this.component = this.application.componentManager
|
||||
.componentsForArea(ComponentArea.TagsList)[0];
|
||||
.componentsForArea(ComponentArea.TagsList).find((component) => component.active);
|
||||
});
|
||||
|
||||
this.removeTagsObserver = this.application.streamItems(
|
||||
|
||||
@@ -170,9 +170,10 @@
|
||||
@extend .flex;
|
||||
@extend .items-center;
|
||||
@extend .border-0;
|
||||
@extend .focus\:inner-ring-info;
|
||||
@extend .focus\:shadow-none;
|
||||
@extend .cursor-pointer;
|
||||
@extend .hover\:bg-contrast;
|
||||
@extend .hover\:color-foreground;
|
||||
@extend .color-text;
|
||||
@extend .bg-transparent;
|
||||
@extend .px-3;
|
||||
@@ -395,6 +396,14 @@
|
||||
border-color: var(--sn-stylekit-danger-color);
|
||||
}
|
||||
|
||||
.border-neutral-contrast-bg {
|
||||
border-color: var(--sn-stylekit-neutral-contrast-color);
|
||||
}
|
||||
|
||||
.border-2 {
|
||||
border-width: 0.5rem;
|
||||
}
|
||||
|
||||
.bg-inverted-default {
|
||||
background-color: var(--sn-stylekit-contrast-foreground-color);
|
||||
}
|
||||
@@ -676,6 +685,18 @@
|
||||
@extend .rounded-full;
|
||||
}
|
||||
|
||||
.focus\:bg-info:focus {
|
||||
@extend .bg-info;
|
||||
}
|
||||
|
||||
.focus\:color-info-contrast:focus {
|
||||
@extend .color-info-contrast;
|
||||
}
|
||||
|
||||
.hover\:color-foreground:hover {
|
||||
color: var(--sn-stylekit-foreground-color) !important;
|
||||
}
|
||||
|
||||
.focus\:bg-info-backdrop:focus {
|
||||
background-color: var(--sn-stylekit-info-backdrop-color);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "standard-notes-web",
|
||||
"version": "3.9.4",
|
||||
"version": "3.9.5",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -43,7 +43,7 @@
|
||||
"eslint": "^8.0.1",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-react": "^7.26.1",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"eslint-plugin-react-hooks": "^4.2.1-beta-149b420f6-20211119",
|
||||
"file-loader": "^6.2.0",
|
||||
"html-webpack-plugin": "^5.4.0",
|
||||
"lodash": "^4.17.21",
|
||||
@@ -70,9 +70,9 @@
|
||||
"@reach/checkbox": "^0.16.0",
|
||||
"@reach/dialog": "^0.16.2",
|
||||
"@reach/listbox": "^0.16.2",
|
||||
"@standardnotes/features": "1.8.1",
|
||||
"@standardnotes/features": "1.9.0",
|
||||
"@standardnotes/sncrypto-web": "1.5.3",
|
||||
"@standardnotes/snjs": "2.17.9",
|
||||
"@standardnotes/snjs": "2.18.2",
|
||||
"mobx": "^6.3.5",
|
||||
"mobx-react-lite": "^3.2.1",
|
||||
"preact": "^10.5.15",
|
||||
|
||||
41
yarn.lock
41
yarn.lock
@@ -2147,6 +2147,13 @@
|
||||
prop-types "^15.7.2"
|
||||
tslib "^2.3.0"
|
||||
|
||||
"@standardnotes/auth@3.8.3":
|
||||
version "3.8.3"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/auth/-/auth-3.8.3.tgz#6e627c1a1a9ebf91d97f52950d099bf7704382e3"
|
||||
integrity sha512-wz056b3pv8IIX74lYaqjCUvnw3NSow+ex5pn/VlGxg8r7gq19WsmgyXP2BoE7nqKddO1JMlFok+4gdnutYF0Cw==
|
||||
dependencies:
|
||||
"@standardnotes/common" "^1.2.1"
|
||||
|
||||
"@standardnotes/auth@^3.8.1":
|
||||
version "3.8.1"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/auth/-/auth-3.8.1.tgz#4197fb2f7e223c6bd13a870a3feac3c73294fb3c"
|
||||
@@ -2166,18 +2173,12 @@
|
||||
dependencies:
|
||||
"@standardnotes/auth" "^3.8.1"
|
||||
|
||||
"@standardnotes/features@1.8.1":
|
||||
version "1.8.1"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/features/-/features-1.8.1.tgz#addea86a8a4288a0046bc674a8e76f1bac3c2f89"
|
||||
integrity sha512-yDbMLu9SnwR4cmjsIMgpxBOK9kIbWVQ7WezfJ5MN0FAwD2IbfKCMaWBMUJStCtSqkKE4dkL3BC+sIxik/4RwdQ==
|
||||
dependencies:
|
||||
"@standardnotes/common" "^1.2.1"
|
||||
|
||||
"@standardnotes/features@^1.8.2":
|
||||
version "1.8.2"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/features/-/features-1.8.2.tgz#ecc8162ca80ee3740468aa55a64c23b519c13505"
|
||||
integrity sha512-EYeYYT/WBIh7fMVTVhsy0INjVdyHVtnH90pbZQHNPpsUkLdj05DzuyxUKjsc0oxiskmZjPOVSfXt8l3RL5Av4g==
|
||||
"@standardnotes/features@1.9.0", "@standardnotes/features@^1.8.3":
|
||||
version "1.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/features/-/features-1.9.0.tgz#6b563dbc177592a6c0741abc7794b0cd6f1989e0"
|
||||
integrity sha512-fuRfLrnKEq43ti7FpYZhZu9SLk6j8ruuLcDygoS8gDKB3LU3yuAbKfumqnt0NDF/hXg5/y5FhxxjYQX2GxykoA==
|
||||
dependencies:
|
||||
"@standardnotes/auth" "3.8.3"
|
||||
"@standardnotes/common" "^1.2.1"
|
||||
|
||||
"@standardnotes/settings@^1.2.1":
|
||||
@@ -2199,15 +2200,15 @@
|
||||
buffer "^6.0.3"
|
||||
libsodium-wrappers "^0.7.9"
|
||||
|
||||
"@standardnotes/snjs@2.17.9":
|
||||
version "2.17.9"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/snjs/-/snjs-2.17.9.tgz#9e7402252c6acda6471cc9d887516f17b6bd2cb2"
|
||||
integrity sha512-Hi57vIByRD5qEOFfQrq/qILH5qRgngyn57qhHE2yKDsX//CTDsdqKGKVZbG0W54rQ+JxYUfjAuBPR/pxIVTi6w==
|
||||
"@standardnotes/snjs@2.18.2":
|
||||
version "2.18.2"
|
||||
resolved "https://registry.yarnpkg.com/@standardnotes/snjs/-/snjs-2.18.2.tgz#42957cf50b18e2db3f10dacb2c3dfa95c16719e7"
|
||||
integrity sha512-KcYRwxJJmA+b9E0xXJGDsdCC3ptNaDLiGDpB8Lv4lUpjH6Z1VC43oxhFDXVxLLrQxTpQULvhlkW8Mc38Kxtzxg==
|
||||
dependencies:
|
||||
"@standardnotes/auth" "^3.8.1"
|
||||
"@standardnotes/common" "^1.2.1"
|
||||
"@standardnotes/domain-events" "^2.5.1"
|
||||
"@standardnotes/features" "^1.8.2"
|
||||
"@standardnotes/features" "^1.8.3"
|
||||
"@standardnotes/settings" "^1.2.1"
|
||||
"@standardnotes/sncrypto-common" "^1.5.2"
|
||||
|
||||
@@ -4013,10 +4014,10 @@ eslint-config-prettier@^8.3.0:
|
||||
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz#f7471b20b6fe8a9a9254cc684454202886a2dd7a"
|
||||
integrity sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==
|
||||
|
||||
eslint-plugin-react-hooks@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.2.0.tgz#8c229c268d468956334c943bb45fc860280f5556"
|
||||
integrity sha512-623WEiZJqxR7VdxFCKLI6d6LLpwJkGPYKODnkH3D7WpOG5KM8yWueBd8TLsNAetEJNF5iJmolaAKO3F8yzyVBQ==
|
||||
eslint-plugin-react-hooks@^4.2.1-beta-149b420f6-20211119:
|
||||
version "4.2.1-beta-149b420f6-20211119"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.2.1-beta-149b420f6-20211119.tgz#183ee5420a6db51b09b64694826dbeda17b492f8"
|
||||
integrity sha512-wTNvhQhv/c3yyXbHFBhvGJDlOuFSA8F+ih/RAwVlZcJjDll+17u55xCI/1I5CWhaMukoYtizQf+qi52veglSvQ==
|
||||
|
||||
eslint-plugin-react@^7.26.1:
|
||||
version "7.26.1"
|
||||
|
||||
Reference in New Issue
Block a user