fix: legacy history (#879)
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import { WebApplication } from '@/ui_models/application';
|
import { WebApplication } from '@/ui_models/application';
|
||||||
import {
|
import {
|
||||||
|
Action,
|
||||||
ActionVerb,
|
ActionVerb,
|
||||||
HistoryEntry,
|
HistoryEntry,
|
||||||
NoteHistoryEntry,
|
NoteHistoryEntry,
|
||||||
@@ -8,14 +9,13 @@ import {
|
|||||||
} from '@standardnotes/snjs';
|
} from '@standardnotes/snjs';
|
||||||
import { observer } from 'mobx-react-lite';
|
import { observer } from 'mobx-react-lite';
|
||||||
import { FunctionComponent } from 'preact';
|
import { FunctionComponent } from 'preact';
|
||||||
import { StateUpdater, useCallback, useMemo, useState } from 'preact/hooks';
|
import { StateUpdater, useCallback, useState } from 'preact/hooks';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { LegacyHistoryList } from './LegacyHistoryList';
|
import { LegacyHistoryList } from './LegacyHistoryList';
|
||||||
import { RemoteHistoryList } from './RemoteHistoryList';
|
import { RemoteHistoryList } from './RemoteHistoryList';
|
||||||
import { SessionHistoryList } from './SessionHistoryList';
|
import { SessionHistoryList } from './SessionHistoryList';
|
||||||
import {
|
import {
|
||||||
LegacyHistoryEntry,
|
LegacyHistoryEntry,
|
||||||
ListGroup,
|
|
||||||
RemoteRevisionListGroup,
|
RemoteRevisionListGroup,
|
||||||
sortRevisionListIntoGroups,
|
sortRevisionListIntoGroups,
|
||||||
} from './utils';
|
} from './utils';
|
||||||
@@ -55,19 +55,45 @@ export const HistoryListContainer: FunctionComponent<Props> = observer(
|
|||||||
note
|
note
|
||||||
) as NoteHistoryEntry[]
|
) as NoteHistoryEntry[]
|
||||||
);
|
);
|
||||||
const [isFetchingLegacyHistory, setIsFetchingLegacyHistory] =
|
const [legacyHistory, setLegacyHistory] = useState<Action[]>();
|
||||||
useState(false);
|
|
||||||
const [legacyHistory, setLegacyHistory] =
|
|
||||||
useState<ListGroup<LegacyHistoryEntry>[]>();
|
|
||||||
const legacyHistoryLength = useMemo(
|
|
||||||
() => legacyHistory?.map((group) => group.entries).flat().length ?? 0,
|
|
||||||
[legacyHistory]
|
|
||||||
);
|
|
||||||
|
|
||||||
const [selectedTab, setSelectedTab] = useState<RevisionListTabType>(
|
const [selectedTab, setSelectedTab] = useState<RevisionListTabType>(
|
||||||
RevisionListTabType.Remote
|
RevisionListTabType.Remote
|
||||||
);
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchLegacyHistory = async () => {
|
||||||
|
const actionExtensions = application.actionsManager.getExtensions();
|
||||||
|
actionExtensions.forEach(async (ext) => {
|
||||||
|
const actionExtension =
|
||||||
|
await application.actionsManager.loadExtensionInContextOfItem(
|
||||||
|
ext,
|
||||||
|
note
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!actionExtension) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isLegacyNoteHistoryExt = actionExtension?.actions.some(
|
||||||
|
(action) => action.verb === ActionVerb.Nested
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!isLegacyNoteHistoryExt) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const legacyHistoryEntries = actionExtension.actions.filter(
|
||||||
|
(action) => action.subactions?.[0]
|
||||||
|
);
|
||||||
|
|
||||||
|
setLegacyHistory(legacyHistoryEntries);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchLegacyHistory();
|
||||||
|
}, [application.actionsManager, note]);
|
||||||
|
|
||||||
const TabButton: FunctionComponent<{
|
const TabButton: FunctionComponent<{
|
||||||
type: RevisionListTabType;
|
type: RevisionListTabType;
|
||||||
}> = ({ type }) => {
|
}> = ({ type }) => {
|
||||||
@@ -88,6 +114,43 @@ export const HistoryListContainer: FunctionComponent<Props> = observer(
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const fetchAndSetLegacyRevision = useCallback(
|
||||||
|
async (revisionListEntry: Action) => {
|
||||||
|
setSelectedRemoteEntry(undefined);
|
||||||
|
setSelectedRevision(undefined);
|
||||||
|
setIsFetchingSelectedRevision(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!revisionListEntry.subactions?.[0]) {
|
||||||
|
throw new Error('Could not find revision action url');
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await application.actionsManager.runAction(
|
||||||
|
revisionListEntry.subactions[0],
|
||||||
|
note
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!response) {
|
||||||
|
throw new Error('Could not fetch revision');
|
||||||
|
}
|
||||||
|
|
||||||
|
setSelectedRevision(response.item as HistoryEntry);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
setSelectedRevision(undefined);
|
||||||
|
} finally {
|
||||||
|
setIsFetchingSelectedRevision(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[
|
||||||
|
application.actionsManager,
|
||||||
|
note,
|
||||||
|
setIsFetchingSelectedRevision,
|
||||||
|
setSelectedRemoteEntry,
|
||||||
|
setSelectedRevision,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
const fetchAndSetRemoteRevision = useCallback(
|
const fetchAndSetRemoteRevision = useCallback(
|
||||||
async (revisionListEntry: RevisionListEntry) => {
|
async (revisionListEntry: RevisionListEntry) => {
|
||||||
setShowContentLockedScreen(false);
|
setShowContentLockedScreen(false);
|
||||||
@@ -132,12 +195,7 @@ export const HistoryListContainer: FunctionComponent<Props> = observer(
|
|||||||
<div className="flex border-0 border-b-1 border-solid border-main">
|
<div className="flex border-0 border-b-1 border-solid border-main">
|
||||||
<TabButton type={RevisionListTabType.Remote} />
|
<TabButton type={RevisionListTabType.Remote} />
|
||||||
<TabButton type={RevisionListTabType.Session} />
|
<TabButton type={RevisionListTabType.Session} />
|
||||||
{isFetchingLegacyHistory && (
|
{legacyHistory && legacyHistory.length > 0 && (
|
||||||
<div className="flex items-center justify-center px-3 py-2.5">
|
|
||||||
<div className="sk-spinner w-3 h-3 spinner-info" />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{legacyHistory && legacyHistoryLength > 0 && (
|
|
||||||
<TabButton type={RevisionListTabType.Legacy} />
|
<TabButton type={RevisionListTabType.Legacy} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -165,6 +223,7 @@ export const HistoryListContainer: FunctionComponent<Props> = observer(
|
|||||||
legacyHistory={legacyHistory}
|
legacyHistory={legacyHistory}
|
||||||
setSelectedRevision={setSelectedRevision}
|
setSelectedRevision={setSelectedRevision}
|
||||||
setSelectedRemoteEntry={setSelectedRemoteEntry}
|
setSelectedRemoteEntry={setSelectedRemoteEntry}
|
||||||
|
fetchAndSetLegacyRevision={fetchAndSetLegacyRevision}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { HistoryEntry, RevisionListEntry } from '@standardnotes/snjs';
|
import { Action, HistoryEntry, RevisionListEntry } from '@standardnotes/snjs';
|
||||||
import { Fragment, FunctionComponent } from 'preact';
|
import { FunctionComponent } from 'preact';
|
||||||
import {
|
import {
|
||||||
StateUpdater,
|
StateUpdater,
|
||||||
useCallback,
|
useCallback,
|
||||||
@@ -11,19 +11,16 @@ import {
|
|||||||
import { useListKeyboardNavigation } from '../utils';
|
import { useListKeyboardNavigation } from '../utils';
|
||||||
import { RevisionListTabType } from './HistoryListContainer';
|
import { RevisionListTabType } from './HistoryListContainer';
|
||||||
import { HistoryListItem } from './HistoryListItem';
|
import { HistoryListItem } from './HistoryListItem';
|
||||||
import {
|
import { LegacyHistoryEntry } from './utils';
|
||||||
LegacyHistoryEntry,
|
|
||||||
ListGroup,
|
|
||||||
previewHistoryEntryTitle,
|
|
||||||
} from './utils';
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
selectedTab: RevisionListTabType;
|
selectedTab: RevisionListTabType;
|
||||||
legacyHistory: ListGroup<LegacyHistoryEntry>[] | undefined;
|
legacyHistory: Action[] | undefined;
|
||||||
setSelectedRevision: StateUpdater<
|
setSelectedRevision: StateUpdater<
|
||||||
HistoryEntry | LegacyHistoryEntry | undefined
|
HistoryEntry | LegacyHistoryEntry | undefined
|
||||||
>;
|
>;
|
||||||
setSelectedRemoteEntry: StateUpdater<RevisionListEntry | undefined>;
|
setSelectedRemoteEntry: StateUpdater<RevisionListEntry | undefined>;
|
||||||
|
fetchAndSetLegacyRevision: (revisionListEntry: Action) => Promise<void>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const LegacyHistoryList: FunctionComponent<Props> = ({
|
export const LegacyHistoryList: FunctionComponent<Props> = ({
|
||||||
@@ -31,44 +28,36 @@ export const LegacyHistoryList: FunctionComponent<Props> = ({
|
|||||||
selectedTab,
|
selectedTab,
|
||||||
setSelectedRevision,
|
setSelectedRevision,
|
||||||
setSelectedRemoteEntry,
|
setSelectedRemoteEntry,
|
||||||
|
fetchAndSetLegacyRevision,
|
||||||
}) => {
|
}) => {
|
||||||
const legacyHistoryListRef = useRef<HTMLDivElement>(null);
|
const legacyHistoryListRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
useListKeyboardNavigation(legacyHistoryListRef);
|
useListKeyboardNavigation(legacyHistoryListRef);
|
||||||
|
|
||||||
const legacyHistoryLength = useMemo(
|
const [selectedItemUrl, setSelectedItemUrl] = useState<string>();
|
||||||
() => legacyHistory?.map((group) => group.entries).flat().length,
|
|
||||||
[legacyHistory]
|
|
||||||
);
|
|
||||||
|
|
||||||
const [selectedItemCreatedAt, setSelectedItemCreatedAt] = useState<Date>();
|
|
||||||
|
|
||||||
const firstEntry = useMemo(() => {
|
const firstEntry = useMemo(() => {
|
||||||
return legacyHistory?.find((group) => group.entries?.length)?.entries?.[0];
|
return legacyHistory?.[0];
|
||||||
}, [legacyHistory]);
|
}, [legacyHistory]);
|
||||||
|
|
||||||
const selectFirstEntry = useCallback(() => {
|
const selectFirstEntry = useCallback(() => {
|
||||||
if (firstEntry) {
|
if (firstEntry) {
|
||||||
setSelectedItemCreatedAt(firstEntry.payload?.created_at);
|
setSelectedItemUrl(firstEntry.subactions?.[0].url);
|
||||||
setSelectedRevision(firstEntry);
|
setSelectedRevision(undefined);
|
||||||
|
fetchAndSetLegacyRevision(firstEntry);
|
||||||
}
|
}
|
||||||
}, [firstEntry, setSelectedRevision]);
|
}, [fetchAndSetLegacyRevision, firstEntry, setSelectedRevision]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (firstEntry && !selectedItemCreatedAt) {
|
if (firstEntry && !selectedItemUrl) {
|
||||||
selectFirstEntry();
|
selectFirstEntry();
|
||||||
} else if (!firstEntry) {
|
} else if (!firstEntry) {
|
||||||
setSelectedRevision(undefined);
|
setSelectedRevision(undefined);
|
||||||
}
|
}
|
||||||
}, [
|
}, [firstEntry, selectFirstEntry, selectedItemUrl, setSelectedRevision]);
|
||||||
firstEntry,
|
|
||||||
selectFirstEntry,
|
|
||||||
selectedItemCreatedAt,
|
|
||||||
setSelectedRevision,
|
|
||||||
]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (selectedTab === RevisionListTabType.Session) {
|
if (selectedTab === RevisionListTabType.Legacy) {
|
||||||
selectFirstEntry();
|
selectFirstEntry();
|
||||||
legacyHistoryListRef.current?.focus();
|
legacyHistoryListRef.current?.focus();
|
||||||
}
|
}
|
||||||
@@ -77,33 +66,28 @@ export const LegacyHistoryList: FunctionComponent<Props> = ({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`flex flex-col w-full h-full focus:shadow-none ${
|
className={`flex flex-col w-full h-full focus:shadow-none ${
|
||||||
!legacyHistoryLength ? 'items-center justify-center' : ''
|
!legacyHistory?.length ? 'items-center justify-center' : ''
|
||||||
}`}
|
}`}
|
||||||
ref={legacyHistoryListRef}
|
ref={legacyHistoryListRef}
|
||||||
>
|
>
|
||||||
{legacyHistory?.map((group) =>
|
{legacyHistory?.map((entry) => {
|
||||||
group.entries && group.entries.length ? (
|
const url = entry.subactions?.[0].url;
|
||||||
<Fragment key={group.title}>
|
|
||||||
<div className="px-3 mt-2.5 mb-1 font-semibold color-text uppercase color-grey-0 select-none">
|
return (
|
||||||
{group.title}
|
<HistoryListItem
|
||||||
</div>
|
key={url}
|
||||||
{group.entries.map((entry, index) => (
|
isSelected={selectedItemUrl === url}
|
||||||
<HistoryListItem
|
onClick={() => {
|
||||||
key={index}
|
setSelectedItemUrl(url);
|
||||||
isSelected={selectedItemCreatedAt === entry.payload.created_at}
|
setSelectedRemoteEntry(undefined);
|
||||||
onClick={() => {
|
fetchAndSetLegacyRevision(entry);
|
||||||
setSelectedItemCreatedAt(entry.payload.created_at);
|
}}
|
||||||
setSelectedRevision(entry);
|
>
|
||||||
setSelectedRemoteEntry(undefined);
|
{entry.label}
|
||||||
}}
|
</HistoryListItem>
|
||||||
>
|
);
|
||||||
{previewHistoryEntryTitle(entry)}
|
})}
|
||||||
</HistoryListItem>
|
{!legacyHistory?.length && (
|
||||||
))}
|
|
||||||
</Fragment>
|
|
||||||
) : null
|
|
||||||
)}
|
|
||||||
{!legacyHistoryLength && (
|
|
||||||
<div className="color-grey-0 select-none">No legacy history found</div>
|
<div className="color-grey-0 select-none">No legacy history found</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user