diff --git a/apps/chat/src/components/Chat/Migration/Migration.tsx b/apps/chat/src/components/Chat/Migration/Migration.tsx
index 345858fd90..05fe31a009 100644
--- a/apps/chat/src/components/Chat/Migration/Migration.tsx
+++ b/apps/chat/src/components/Chat/Migration/Migration.tsx
@@ -17,11 +17,12 @@ export const Migration = ({ total, uploaded }: Props) => {
return (
-
+
+ {t('Migration')}
+
+
{uploaded} {t('out of')} {total}
-
- {t('conversations and prompts are loaded')}
-
+ {t('conversations and prompts are loaded')}
diff --git a/apps/chat/src/components/Chat/Migration/MigrationFailedModal.tsx b/apps/chat/src/components/Chat/Migration/MigrationFailedModal.tsx
index f8097e1bd5..845ab4777d 100644
--- a/apps/chat/src/components/Chat/Migration/MigrationFailedModal.tsx
+++ b/apps/chat/src/components/Chat/Migration/MigrationFailedModal.tsx
@@ -46,19 +46,21 @@ const ItemsList = ({
setEntitiesToRetryIds(entitiesToRetryIds.filter((id) => id !== entityId));
};
- return failedMigratedEntities.length ? (
+ if (!failedMigratedEntities.length) return null;
+
+ return (
{failedMigratedEntities.map((entity) => (
-
-
+
{getModelIcon(entity)}
-
{entity.name}
+
{entity.name}
-
+
handleSelect(entity.id)}
className="relative flex size-[18px] group-hover/file-item:flex"
@@ -94,7 +96,7 @@ const ItemsList = ({
))}
- ) : null;
+ );
};
interface Props {
@@ -102,6 +104,40 @@ interface Props {
failedMigratedPrompts: Prompt[];
}
+interface AllItemsCheckboxesProps {
+ isChecked: boolean;
+ isCheckIcon: boolean;
+ isMinusIcon: boolean;
+ onSelectHandler: () => void;
+}
+
+const AllItemsCheckboxes = ({
+ isChecked,
+ isCheckIcon,
+ isMinusIcon,
+ onSelectHandler,
+}: AllItemsCheckboxesProps) => {
+ const Icon = isCheckIcon ? IconCheck : isMinusIcon ? IconMinus : null;
+
+ return (
+
+
+ {Icon && (
+
+ )}
+
+ );
+};
+
export const MigrationFailedWindow = ({
failedMigratedConversations,
failedMigratedPrompts,
@@ -134,20 +170,7 @@ export const MigrationFailedWindow = ({
setPromptsToRetryIds(failedMigratedPrompts.map((prompt) => prompt.id));
}, [failedMigratedPrompts]);
- const onSkipAll = useCallback(() => {
- dispatch(
- ConversationsActions.skipFailedMigratedConversations({
- idsToMarkAsMigrated: failedMigratedConversations.map((conv) => conv.id),
- }),
- );
- dispatch(
- PromptsActions.skipFailedMigratedPrompts({
- idsToMarkAsMigrated: failedMigratedPrompts.map((prompt) => prompt.id),
- }),
- );
- }, [dispatch, failedMigratedConversations, failedMigratedPrompts]);
-
- const onRetry = useCallback(() => {
+ const retryMigration = useCallback(() => {
const failedMigratedConversationIds = failedMigratedConversations.map(
(conv) => conv.id,
);
@@ -155,7 +178,6 @@ export const MigrationFailedWindow = ({
(conv) => conv.id,
);
- dispatch(ImportExportActions.exportLocalStorageEntities());
dispatch(
ConversationsActions.skipFailedMigratedConversations({
idsToMarkAsMigrated: failedMigratedConversationIds.filter(
@@ -170,8 +192,9 @@ export const MigrationFailedWindow = ({
),
}),
);
- dispatch(ConversationsActions.migrateConversations());
- dispatch(PromptsActions.migratePrompts());
+
+ dispatch(ConversationsActions.migrateConversationsIfRequired());
+ dispatch(PromptsActions.migratePromptsIfRequired());
}, [
conversationsToRetryIds,
dispatch,
@@ -180,6 +203,15 @@ export const MigrationFailedWindow = ({
promptsToRetryIds,
]);
+ const onRetryWithoutBackup = useCallback(() => {
+ retryMigration();
+ }, [retryMigration]);
+
+ const onRetryWithBackup = useCallback(() => {
+ dispatch(ImportExportActions.exportLocalStorageEntities());
+ retryMigration();
+ }, [dispatch, retryMigration]);
+
const onSelectAll = useCallback(() => {
setConversationsToRetryIds(
failedMigratedConversations.map((conv) => conv.id),
@@ -192,6 +224,15 @@ export const MigrationFailedWindow = ({
setPromptsToRetryIds([]);
};
+ const isAllItemsSelected =
+ conversationsToRetryIds.length + promptsToRetryIds.length ===
+ failedMigratedPrompts.length + failedMigratedConversations.length;
+ const isSomeItemsSelected =
+ !!conversationsToRetryIds.length || !!promptsToRetryIds.length;
+ const isNothingSelected =
+ conversationsToRetryIds.length === 0 &&
+ conversationsToRetryIds.length === 0;
+
return (
@@ -220,40 +261,19 @@ export const MigrationFailedWindow = ({
{t('All items')}
-
@@ -290,14 +310,14 @@ export const MigrationFailedWindow = ({
diff --git a/apps/chat/src/store/conversations/conversations.epics.ts b/apps/chat/src/store/conversations/conversations.epics.ts
index c95b7b11d0..d8f8379980 100644
--- a/apps/chat/src/store/conversations/conversations.epics.ts
+++ b/apps/chat/src/store/conversations/conversations.epics.ts
@@ -705,18 +705,18 @@ const deleteConversationsEpic: AppEpic = (action$, state$) =>
}),
);
-const migrateConversationsEpic: AppEpic = (action$, state$) => {
+const migrateConversationsIfRequiredEpic: AppEpic = (action$, state$) => {
const browserStorage = new BrowserStorage();
return action$.pipe(
- filter(ConversationsActions.migrateConversations.match),
+ filter(ConversationsActions.migrateConversationsIfRequired.match),
switchMap(() =>
forkJoin({
conversations: browserStorage
.getConversations()
.pipe(map(filterOnlyMyEntities)),
conversationsFolders: browserStorage
- .getConversationsFolders()
+ .getConversationsFolders(undefined, true)
.pipe(map(filterOnlyMyEntities)),
migratedConversationIds: BrowserStorage.getMigratedEntityIds(
MigrationStorageKeys.MigratedConversationIds,
@@ -769,6 +769,7 @@ const migrateConversationsEpic: AppEpic = (action$, state$) => {
const { path } = getPathToFolderById(
conversationsFolders,
conv.folderId,
+ true,
);
const newName = conv.name.replace(notAllowedSymbolsRegex, '');
@@ -776,7 +777,7 @@ const migrateConversationsEpic: AppEpic = (action$, state$) => {
...conv,
id: constructPath(...[path, newName]),
name: newName,
- folderId: path.replace(notAllowedSymbolsRegex, ''),
+ folderId: path,
};
}); // to send conversation with proper parentPath and lastActivityDate order
@@ -2238,7 +2239,7 @@ const openFolderEpic: AppEpic = (action$, state$) =>
);
export const ConversationsEpics = combineEpics(
- migrateConversationsEpic,
+ migrateConversationsIfRequiredEpic,
skipFailedMigratedConversationsEpic,
// init
initEpic,
diff --git a/apps/chat/src/store/conversations/conversations.reducers.ts b/apps/chat/src/store/conversations/conversations.reducers.ts
index d6cba5062d..8bd33b1a56 100644
--- a/apps/chat/src/store/conversations/conversations.reducers.ts
+++ b/apps/chat/src/store/conversations/conversations.reducers.ts
@@ -55,7 +55,7 @@ export const conversationsSlice = createSlice({
initialState,
reducers: {
init: (state) => state,
- migrateConversations: (state) => state,
+ migrateConversationsIfRequired: (state) => state,
initConversationsMigration: (
state,
{
diff --git a/apps/chat/src/store/prompts/prompts.epics.ts b/apps/chat/src/store/prompts/prompts.epics.ts
index 69382ed097..8a246cc69d 100644
--- a/apps/chat/src/store/prompts/prompts.epics.ts
+++ b/apps/chat/src/store/prompts/prompts.epics.ts
@@ -402,16 +402,16 @@ const importPromptsEpic: AppEpic = (action$, state$) =>
// ),
// );
-const migratePromptsEpic: AppEpic = (action$, state$) => {
+const migratePromptsIfRequiredEpic: AppEpic = (action$, state$) => {
const browserStorage = new BrowserStorage();
return action$.pipe(
- filter(PromptsActions.migratePrompts.match),
+ filter(PromptsActions.migratePromptsIfRequired.match),
switchMap(() =>
forkJoin({
prompts: browserStorage.getPrompts().pipe(map(filterOnlyMyEntities)),
promptsFolders: browserStorage
- .getPromptsFolders()
+ .getPromptsFolders(undefined, true)
.pipe(map(filterOnlyMyEntities)),
migratedPromptIds: BrowserStorage.getMigratedEntityIds(
MigrationStorageKeys.MigratedPromptIds,
@@ -453,14 +453,18 @@ const migratePromptsEpic: AppEpic = (action$, state$) => {
}
const preparedPrompts: Prompt[] = notMigratedPrompts.map((prompt) => {
- const { path } = getPathToFolderById(promptsFolders, prompt.folderId);
+ const { path } = getPathToFolderById(
+ promptsFolders,
+ prompt.folderId,
+ true,
+ );
const newName = prompt.name.replace(notAllowedSymbolsRegex, '');
return {
...prompt,
id: constructPath(...[path, newName]),
name: newName,
- folderId: path.replace(notAllowedSymbolsRegex, ''),
+ folderId: path,
};
}); // to send prompts with proper parentPath
let migratedPromptsCount = 0;
@@ -834,7 +838,7 @@ export const uploadPromptEpic: AppEpic = (action$, state$) =>
);
export const PromptsEpics = combineEpics(
- migratePromptsEpic,
+ migratePromptsIfRequiredEpic,
skipFailedMigratedPromptsEpic,
// init
initEpic,
diff --git a/apps/chat/src/store/prompts/prompts.reducers.ts b/apps/chat/src/store/prompts/prompts.reducers.ts
index 181acddbeb..e59c672e48 100644
--- a/apps/chat/src/store/prompts/prompts.reducers.ts
+++ b/apps/chat/src/store/prompts/prompts.reducers.ts
@@ -46,7 +46,7 @@ export const promptsSlice = createSlice({
reducers: {
init: (state) => state,
initPrompts: (state) => state,
- migratePrompts: (state) => state,
+ migratePromptsIfRequired: (state) => state,
skipFailedMigratedPrompts: (
state,
{ payload: _ }: PayloadAction<{ idsToMarkAsMigrated: string[] }>,
diff --git a/apps/chat/src/store/settings/settings.epic.ts b/apps/chat/src/store/settings/settings.epic.ts
index d0be1c2670..6d75d236cb 100644
--- a/apps/chat/src/store/settings/settings.epic.ts
+++ b/apps/chat/src/store/settings/settings.epic.ts
@@ -63,8 +63,8 @@ const initEpic: AppEpic = (action$, state$) =>
),
switchMap(() =>
concat(
- of(ConversationsActions.migrateConversations()),
- of(PromptsActions.migratePrompts()),
+ of(ConversationsActions.migrateConversationsIfRequired()),
+ of(PromptsActions.migratePromptsIfRequired()),
of(UIActions.init()),
of(ModelsActions.init()),
of(AddonsActions.init()),
diff --git a/apps/chat/src/utils/app/data/storages/browser-storage.ts b/apps/chat/src/utils/app/data/storages/browser-storage.ts
index 7539f6b3d9..1d1828dcbe 100644
--- a/apps/chat/src/utils/app/data/storages/browser-storage.ts
+++ b/apps/chat/src/utils/app/data/storages/browser-storage.ts
@@ -172,23 +172,25 @@ export class BrowserStorage implements DialStorage {
return BrowserStorage.setData(UIStorageKeys.Prompts, prompts);
}
- getConversationsFolders(path?: string) {
+ getConversationsFolders(path?: string, recursive?: boolean) {
return BrowserStorage.getData(UIStorageKeys.Folders, []).pipe(
map((folders: FolderInterface[]) => {
return folders.filter(
(folder) =>
- folder.type === FolderType.Chat && folder.folderId === path,
+ folder.type === FolderType.Chat &&
+ (recursive || folder.folderId === path),
);
}),
);
}
- getPromptsFolders(path?: string) {
+ getPromptsFolders(path?: string, recursive?: boolean) {
return BrowserStorage.getData(UIStorageKeys.Folders, []).pipe(
map((folders: FolderInterface[]) => {
return folders.filter(
(folder) =>
- folder.type === FolderType.Prompt && folder.folderId === path,
+ folder.type === FolderType.Prompt &&
+ (recursive || folder.folderId === path),
);
}),
);
diff --git a/apps/chat/src/utils/app/folders.ts b/apps/chat/src/utils/app/folders.ts
index 9e667bcf43..d21b64fad0 100644
--- a/apps/chat/src/utils/app/folders.ts
+++ b/apps/chat/src/utils/app/folders.ts
@@ -12,6 +12,8 @@ import { FolderInterface, FolderType } from '@/src/types/folder';
import { Prompt, PromptInfo } from '@/src/types/prompt';
import { EntityFilters } from '@/src/types/search';
+import { DEFAULT_FOLDER_NAME } from '@/src/constants/default-settings';
+
import escapeStringRegexp from 'escape-string-regexp';
export const getFoldersDepth = (
@@ -193,13 +195,18 @@ export const getFolderIdByPath = (path: string, folders: FolderInterface[]) => {
export const getPathToFolderById = (
folders: FolderInterface[],
starterId: string | undefined,
+ removeNotAllowedSymbols = false,
) => {
const path: string[] = [];
const createPath = (folderId: string) => {
const folder = folders.find((folder) => folder.id === folderId);
if (!folder) return;
- path.unshift(folder.name);
+ path.unshift(
+ removeNotAllowedSymbols
+ ? folder.name.replace(notAllowedSymbolsRegex, '') || DEFAULT_FOLDER_NAME
+ : folder.name,
+ );
if (folder.folderId) {
createPath(folder.folderId);