mirror of
https://github.com/outline/outline.git
synced 2026-06-13 11:25:03 +03:00
chore: Refactor of activeDocumentId (#11144)
* wip: Refactor of activeDocumentId * Remove legacy usage from definitions/collections
This commit is contained in:
@@ -20,7 +20,7 @@ import {
|
||||
UnsubscribeIcon,
|
||||
} from "outline-icons";
|
||||
import { toast } from "sonner";
|
||||
import type Collection from "~/models/Collection";
|
||||
import Collection from "~/models/Collection";
|
||||
import { CollectionEdit } from "~/components/Collection/CollectionEdit";
|
||||
import { CollectionNew } from "~/components/Collection/CollectionNew";
|
||||
import CollectionDeleteDialog from "~/components/CollectionDeleteDialog";
|
||||
@@ -96,11 +96,11 @@ export const editCollection = createAction({
|
||||
analyticsName: "Edit collection",
|
||||
section: ActiveCollectionSection,
|
||||
icon: <EditIcon />,
|
||||
visible: ({ activeCollectionId, stores }) =>
|
||||
!!activeCollectionId &&
|
||||
stores.policies.abilities(activeCollectionId).update,
|
||||
perform: ({ t, activeCollectionId, stores }) => {
|
||||
if (!activeCollectionId) {
|
||||
visible: ({ getActivePolicies }) =>
|
||||
getActivePolicies(Collection).some((policy) => policy.abilities.update),
|
||||
perform: ({ t, getActiveModel, stores }) => {
|
||||
const collection = getActiveModel(Collection);
|
||||
if (!collection) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ export const editCollection = createAction({
|
||||
content: (
|
||||
<CollectionEdit
|
||||
onSubmit={stores.dialogs.closeAllModals}
|
||||
collectionId={activeCollectionId}
|
||||
collectionId={collection.id}
|
||||
/>
|
||||
),
|
||||
});
|
||||
@@ -122,14 +122,10 @@ export const editCollectionPermissions = createAction({
|
||||
analyticsName: "Collection permissions",
|
||||
section: ActiveCollectionSection,
|
||||
icon: <PadlockIcon />,
|
||||
visible: ({ activeCollectionId, stores }) =>
|
||||
!!activeCollectionId &&
|
||||
stores.policies.abilities(activeCollectionId).update,
|
||||
perform: ({ t, activeCollectionId, stores }) => {
|
||||
if (!activeCollectionId) {
|
||||
return;
|
||||
}
|
||||
const collection = stores.collections.get(activeCollectionId);
|
||||
visible: ({ getActivePolicies }) =>
|
||||
getActivePolicies(Collection).some((policy) => policy.abilities.update),
|
||||
perform: ({ t, getActiveModel, stores }) => {
|
||||
const collection = getActiveModel(Collection);
|
||||
if (!collection) {
|
||||
return;
|
||||
}
|
||||
@@ -152,15 +148,16 @@ export const importDocument = createAction({
|
||||
analyticsName: "Import document",
|
||||
section: ActiveCollectionSection,
|
||||
icon: <ImportIcon />,
|
||||
visible: ({ activeCollectionId, stores }) => {
|
||||
if (activeCollectionId) {
|
||||
return !!stores.policies.abilities(activeCollectionId).createDocument;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
perform: ({ activeCollectionId, stores }) => {
|
||||
visible: ({ getActivePolicies }) =>
|
||||
getActivePolicies(Collection).some(
|
||||
(policy) => policy.abilities.createDocument
|
||||
),
|
||||
perform: ({ getActiveModel, stores }) => {
|
||||
const { documents } = stores;
|
||||
const collection = getActiveModel(Collection);
|
||||
if (!collection) {
|
||||
return;
|
||||
}
|
||||
const input = document.createElement("input");
|
||||
input.type = "file";
|
||||
input.accept = documents.importFileTypesString;
|
||||
@@ -170,15 +167,10 @@ export const importDocument = createAction({
|
||||
const file = files[0];
|
||||
|
||||
try {
|
||||
const document = await documents.import(
|
||||
file,
|
||||
null,
|
||||
activeCollectionId,
|
||||
{
|
||||
publish: true,
|
||||
}
|
||||
);
|
||||
history.push(document.url);
|
||||
const document = await documents.import(file, null, collection.id, {
|
||||
publish: true,
|
||||
});
|
||||
history.push(document.path);
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
}
|
||||
@@ -191,11 +183,10 @@ export const importDocument = createAction({
|
||||
export const sortCollection = createActionWithChildren({
|
||||
name: ({ t }) => t("Sort in sidebar"),
|
||||
section: ActiveCollectionSection,
|
||||
visible: ({ activeCollectionId, stores }) =>
|
||||
!!activeCollectionId &&
|
||||
!!stores.policies.abilities(activeCollectionId).update,
|
||||
icon: ({ activeCollectionId, stores }) => {
|
||||
const collection = stores.collections.get(activeCollectionId);
|
||||
visible: ({ getActivePolicies }) =>
|
||||
getActivePolicies(Collection).some((policy) => policy.abilities.update),
|
||||
icon: ({ getActiveModel }) => {
|
||||
const collection = getActiveModel(Collection);
|
||||
const sortAlphabetical = collection?.sort.field === "title";
|
||||
const sortDir = collection?.sort.direction;
|
||||
|
||||
@@ -213,15 +204,15 @@ export const sortCollection = createActionWithChildren({
|
||||
createAction({
|
||||
name: ({ t }) => t("A-Z sort"),
|
||||
section: ActiveCollectionSection,
|
||||
selected: ({ activeCollectionId, stores }) => {
|
||||
const collection = stores.collections.get(activeCollectionId);
|
||||
selected: ({ getActiveModel }) => {
|
||||
const collection = getActiveModel(Collection);
|
||||
return (
|
||||
collection?.sort.field === "title" &&
|
||||
collection?.sort.direction === "asc"
|
||||
);
|
||||
},
|
||||
perform: ({ activeCollectionId, stores }) => {
|
||||
const collection = stores.collections.get(activeCollectionId);
|
||||
perform: ({ getActiveModel }) => {
|
||||
const collection = getActiveModel(Collection);
|
||||
return collection?.save({
|
||||
sort: {
|
||||
field: "title",
|
||||
@@ -233,15 +224,15 @@ export const sortCollection = createActionWithChildren({
|
||||
createAction({
|
||||
name: ({ t }) => t("Z-A sort"),
|
||||
section: ActiveCollectionSection,
|
||||
selected: ({ activeCollectionId, stores }) => {
|
||||
const collection = stores.collections.get(activeCollectionId);
|
||||
selected: ({ getActiveModel }) => {
|
||||
const collection = getActiveModel(Collection);
|
||||
return (
|
||||
collection?.sort.field === "title" &&
|
||||
collection?.sort.direction === "desc"
|
||||
);
|
||||
},
|
||||
perform: ({ activeCollectionId, stores }) => {
|
||||
const collection = stores.collections.get(activeCollectionId);
|
||||
perform: ({ getActiveModel }) => {
|
||||
const collection = getActiveModel(Collection);
|
||||
return collection?.save({
|
||||
sort: {
|
||||
field: "title",
|
||||
@@ -253,12 +244,12 @@ export const sortCollection = createActionWithChildren({
|
||||
createAction({
|
||||
name: ({ t }) => t("Manual sort"),
|
||||
section: ActiveCollectionSection,
|
||||
selected: ({ activeCollectionId, stores }) => {
|
||||
const collection = stores.collections.get(activeCollectionId);
|
||||
selected: ({ getActiveModel }) => {
|
||||
const collection = getActiveModel(Collection);
|
||||
return collection?.sort.field !== "title";
|
||||
},
|
||||
perform: ({ activeCollectionId, stores }) => {
|
||||
const collection = stores.collections.get(activeCollectionId);
|
||||
perform: ({ getActiveModel }) => {
|
||||
const collection = getActiveModel(Collection);
|
||||
return collection?.save({
|
||||
sort: {
|
||||
field: "index",
|
||||
@@ -275,22 +266,19 @@ export const searchInCollection = createInternalLinkAction({
|
||||
analyticsName: "Search collection",
|
||||
section: ActiveCollectionSection,
|
||||
icon: <SearchIcon />,
|
||||
visible: ({ activeCollectionId, stores }) => {
|
||||
if (!activeCollectionId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const collection = stores.collections.get(activeCollectionId);
|
||||
|
||||
visible: ({ getActiveModel, stores }) => {
|
||||
const collection = getActiveModel(Collection);
|
||||
if (!collection?.isActive) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return stores.policies.abilities(activeCollectionId).readDocument;
|
||||
return stores.policies.abilities(collection.id).readDocument;
|
||||
},
|
||||
to: ({ activeCollectionId, sidebarContext }) => {
|
||||
to: ({ getActiveModel, sidebarContext }) => {
|
||||
const collection = getActiveModel(Collection);
|
||||
|
||||
const [pathname, search] = searchPath({
|
||||
collectionId: activeCollectionId,
|
||||
collectionId: collection?.id,
|
||||
}).split("?");
|
||||
|
||||
return {
|
||||
@@ -307,23 +295,22 @@ export const starCollection = createAction({
|
||||
section: ActiveCollectionSection,
|
||||
icon: <StarredIcon />,
|
||||
keywords: "favorite bookmark",
|
||||
visible: ({ activeCollectionId, stores }) => {
|
||||
if (!activeCollectionId) {
|
||||
visible: ({ getActiveModel, stores }) => {
|
||||
const collection = getActiveModel(Collection);
|
||||
if (!collection) {
|
||||
return false;
|
||||
}
|
||||
const collection = stores.collections.get(activeCollectionId);
|
||||
|
||||
return (
|
||||
!collection?.isStarred &&
|
||||
stores.policies.abilities(activeCollectionId).star
|
||||
!collection.isStarred && stores.policies.abilities(collection.id).star
|
||||
);
|
||||
},
|
||||
perform: async ({ activeCollectionId, stores }) => {
|
||||
if (!activeCollectionId) {
|
||||
perform: async ({ getActiveModel }) => {
|
||||
const collection = getActiveModel(Collection);
|
||||
if (!collection) {
|
||||
return;
|
||||
}
|
||||
|
||||
const collection = stores.collections.get(activeCollectionId);
|
||||
await collection?.star();
|
||||
await collection.star();
|
||||
setPersistedState(getHeaderExpandedKey("starred"), true);
|
||||
},
|
||||
});
|
||||
@@ -334,22 +321,18 @@ export const unstarCollection = createAction({
|
||||
section: ActiveCollectionSection,
|
||||
icon: <UnstarredIcon />,
|
||||
keywords: "unfavorite unbookmark",
|
||||
visible: ({ activeCollectionId, stores }) => {
|
||||
if (!activeCollectionId) {
|
||||
visible: ({ getActiveModel, stores }) => {
|
||||
const collection = getActiveModel(Collection);
|
||||
if (!collection) {
|
||||
return false;
|
||||
}
|
||||
const collection = stores.collections.get(activeCollectionId);
|
||||
|
||||
return (
|
||||
!!collection?.isStarred &&
|
||||
stores.policies.abilities(activeCollectionId).unstar
|
||||
!!collection.isStarred && stores.policies.abilities(collection.id).unstar
|
||||
);
|
||||
},
|
||||
perform: async ({ activeCollectionId, stores }) => {
|
||||
if (!activeCollectionId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const collection = stores.collections.get(activeCollectionId);
|
||||
perform: async ({ getActiveModel }) => {
|
||||
const collection = getActiveModel(Collection);
|
||||
await collection?.unstar();
|
||||
},
|
||||
});
|
||||
@@ -359,28 +342,25 @@ export const subscribeCollection = createAction({
|
||||
analyticsName: "Subscribe to collection",
|
||||
section: ActiveCollectionSection,
|
||||
icon: <SubscribeIcon />,
|
||||
visible: ({ activeCollectionId, stores }) => {
|
||||
if (!activeCollectionId) {
|
||||
visible: ({ getActiveModel, stores }) => {
|
||||
const collection = getActiveModel(Collection);
|
||||
if (!collection) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const collection = stores.collections.get(activeCollectionId);
|
||||
|
||||
return (
|
||||
!!collection?.isActive &&
|
||||
!collection?.isSubscribed &&
|
||||
stores.policies.abilities(activeCollectionId).subscribe
|
||||
!!collection.isActive &&
|
||||
!collection.isSubscribed &&
|
||||
stores.policies.abilities(collection.id).subscribe
|
||||
);
|
||||
},
|
||||
perform: async ({ activeCollectionId, stores, t }) => {
|
||||
if (!activeCollectionId) {
|
||||
perform: async ({ getActiveModel, t }) => {
|
||||
const collection = getActiveModel(Collection);
|
||||
if (!collection) {
|
||||
return;
|
||||
}
|
||||
|
||||
const collection = stores.collections.get(activeCollectionId);
|
||||
|
||||
await collection?.subscribe();
|
||||
|
||||
await collection.subscribe();
|
||||
toast.success(t("Subscribed to document notifications"));
|
||||
},
|
||||
});
|
||||
@@ -390,28 +370,25 @@ export const unsubscribeCollection = createAction({
|
||||
analyticsName: "Unsubscribe from collection",
|
||||
section: ActiveCollectionSection,
|
||||
icon: <UnsubscribeIcon />,
|
||||
visible: ({ activeCollectionId, stores }) => {
|
||||
if (!activeCollectionId) {
|
||||
visible: ({ getActiveModel, stores }) => {
|
||||
const collection = getActiveModel(Collection);
|
||||
if (!collection) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const collection = stores.collections.get(activeCollectionId);
|
||||
|
||||
return (
|
||||
!!collection?.isActive &&
|
||||
!!collection?.isSubscribed &&
|
||||
stores.policies.abilities(activeCollectionId).unsubscribe
|
||||
!!collection.isActive &&
|
||||
!!collection.isSubscribed &&
|
||||
stores.policies.abilities(collection.id).unsubscribe
|
||||
);
|
||||
},
|
||||
perform: async ({ activeCollectionId, currentUserId, stores, t }) => {
|
||||
if (!activeCollectionId || !currentUserId) {
|
||||
perform: async ({ getActiveModel, t }) => {
|
||||
const collection = getActiveModel(Collection);
|
||||
if (!collection) {
|
||||
return;
|
||||
}
|
||||
|
||||
const collection = stores.collections.get(activeCollectionId);
|
||||
|
||||
await collection?.unsubscribe();
|
||||
|
||||
await collection.unsubscribe();
|
||||
toast.success(t("Unsubscribed from document notifications"));
|
||||
},
|
||||
});
|
||||
@@ -421,23 +398,15 @@ export const archiveCollection = createAction({
|
||||
analyticsName: "Archive collection",
|
||||
section: ActiveCollectionSection,
|
||||
icon: <ArchiveIcon />,
|
||||
visible: ({ activeCollectionId, stores }) => {
|
||||
if (!activeCollectionId) {
|
||||
return false;
|
||||
}
|
||||
return !!stores.policies.abilities(activeCollectionId).archive;
|
||||
},
|
||||
perform: async ({ activeCollectionId, stores, t }) => {
|
||||
const { dialogs, collections } = stores;
|
||||
if (!activeCollectionId) {
|
||||
return;
|
||||
}
|
||||
const collection = collections.get(activeCollectionId);
|
||||
visible: ({ getActivePolicies }) =>
|
||||
getActivePolicies(Collection).some((policy) => policy.abilities.archive),
|
||||
perform: async ({ getActiveModel, stores, t }) => {
|
||||
const collection = getActiveModel(Collection);
|
||||
if (!collection) {
|
||||
return;
|
||||
}
|
||||
|
||||
dialogs.openModal({
|
||||
stores.dialogs.openModal({
|
||||
title: t("Archive collection"),
|
||||
content: (
|
||||
<ConfirmationDialog
|
||||
@@ -462,17 +431,10 @@ export const restoreCollection = createAction({
|
||||
analyticsName: "Restore collection",
|
||||
section: CollectionSection,
|
||||
icon: <RestoreIcon />,
|
||||
visible: ({ activeCollectionId, stores }) => {
|
||||
if (!activeCollectionId) {
|
||||
return false;
|
||||
}
|
||||
return !!stores.policies.abilities(activeCollectionId).restore;
|
||||
},
|
||||
perform: async ({ activeCollectionId, stores, t }) => {
|
||||
if (!activeCollectionId) {
|
||||
return;
|
||||
}
|
||||
const collection = stores.collections.get(activeCollectionId);
|
||||
visible: ({ getActivePolicies }) =>
|
||||
getActivePolicies(Collection).some((policy) => policy.abilities.restore),
|
||||
perform: async ({ getActiveModel, t }) => {
|
||||
const collection = getActiveModel(Collection);
|
||||
if (!collection) {
|
||||
return;
|
||||
}
|
||||
@@ -488,18 +450,10 @@ export const deleteCollection = createAction({
|
||||
section: ActiveCollectionSection,
|
||||
dangerous: true,
|
||||
icon: <TrashIcon />,
|
||||
visible: ({ activeCollectionId, stores }) => {
|
||||
if (!activeCollectionId) {
|
||||
return false;
|
||||
}
|
||||
return stores.policies.abilities(activeCollectionId).delete;
|
||||
},
|
||||
perform: ({ activeCollectionId, t, stores }) => {
|
||||
if (!activeCollectionId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const collection = stores.collections.get(activeCollectionId);
|
||||
visible: ({ getActivePolicies }) =>
|
||||
getActivePolicies(Collection).some((policy) => policy.abilities.delete),
|
||||
perform: ({ getActiveModel, t, stores }) => {
|
||||
const collection = getActiveModel(Collection);
|
||||
if (!collection) {
|
||||
return;
|
||||
}
|
||||
@@ -521,18 +475,10 @@ export const exportCollection = createAction({
|
||||
analyticsName: "Export collection",
|
||||
section: ActiveCollectionSection,
|
||||
icon: <ExportIcon />,
|
||||
visible: ({ currentTeamId, activeCollectionId, stores }) => {
|
||||
if (!currentTeamId || !activeCollectionId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !!stores.policies.abilities(activeCollectionId).export;
|
||||
},
|
||||
perform: async ({ activeCollectionId, stores, t }) => {
|
||||
if (!activeCollectionId) {
|
||||
return;
|
||||
}
|
||||
const collection = stores.collections.get(activeCollectionId);
|
||||
visible: ({ getActivePolicies }) =>
|
||||
getActivePolicies(Collection).some((policy) => policy.abilities.export),
|
||||
perform: async ({ getActiveModel, stores, t }) => {
|
||||
const collection = getActiveModel(Collection);
|
||||
if (!collection) {
|
||||
return;
|
||||
}
|
||||
@@ -555,13 +501,13 @@ export const createDocument = createInternalLinkAction({
|
||||
section: ActiveCollectionSection,
|
||||
icon: <NewDocumentIcon />,
|
||||
keywords: "new create document",
|
||||
visible: ({ activeCollectionId, stores }) =>
|
||||
!!(
|
||||
!!activeCollectionId &&
|
||||
stores.policies.abilities(activeCollectionId).createDocument
|
||||
visible: ({ getActivePolicies }) =>
|
||||
getActivePolicies(Collection).some(
|
||||
(policy) => policy.abilities.createDocument
|
||||
),
|
||||
to: ({ activeCollectionId, sidebarContext }) => {
|
||||
const [pathname, search] = newDocumentPath(activeCollectionId).split("?");
|
||||
to: ({ getActiveModel, sidebarContext }) => {
|
||||
const collection = getActiveModel(Collection);
|
||||
const [pathname, search] = newDocumentPath(collection?.id).split("?");
|
||||
|
||||
return {
|
||||
pathname,
|
||||
@@ -577,13 +523,13 @@ export const createTemplate = createInternalLinkAction({
|
||||
section: ActiveCollectionSection,
|
||||
icon: <ShapesIcon />,
|
||||
keywords: "new create template",
|
||||
visible: ({ activeCollectionId, stores }) =>
|
||||
!!(
|
||||
!!activeCollectionId &&
|
||||
stores.policies.abilities(activeCollectionId).createDocument
|
||||
visible: ({ getActivePolicies }) =>
|
||||
getActivePolicies(Collection).some(
|
||||
(policy) => policy.abilities.createDocument
|
||||
),
|
||||
to: ({ activeCollectionId, sidebarContext }) => {
|
||||
const [pathname, search] = newTemplatePath(activeCollectionId).split("?");
|
||||
to: ({ getActiveModel, sidebarContext }) => {
|
||||
const collection = getActiveModel(Collection);
|
||||
const [pathname, search] = newTemplatePath(collection?.id).split("?");
|
||||
|
||||
return {
|
||||
pathname,
|
||||
|
||||
@@ -4,6 +4,8 @@ import React, { createContext, useContext } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useLocation } from "react-router";
|
||||
import useStores from "~/hooks/useStores";
|
||||
import type Model from "~/models/base/Model";
|
||||
import type Policy from "~/models/Policy";
|
||||
import type { ActionContext as ActionContextType } from "~/types";
|
||||
|
||||
export const ActionContext = createContext<ActionContextType | undefined>(
|
||||
@@ -49,8 +51,31 @@ export const ActionContextProvider = observer(function ActionContextProvider_({
|
||||
isMenu: false,
|
||||
isCommandBar: false,
|
||||
isButton: false,
|
||||
|
||||
// Legacy (backward compatibility)
|
||||
activeCollectionId: stores.ui.activeCollectionId ?? undefined,
|
||||
activeDocumentId: stores.ui.activeDocumentId ?? undefined,
|
||||
|
||||
// New API
|
||||
getActiveModels: <T extends Model>(
|
||||
modelClass: new (...args: any[]) => T
|
||||
): T[] => stores.ui.getActiveModels<T>(modelClass),
|
||||
|
||||
getActiveModel: <T extends Model>(
|
||||
modelClass: new (...args: any[]) => T
|
||||
): T | undefined => stores.ui.getActiveModels<T>(modelClass)[0],
|
||||
|
||||
getActivePolicies: <T extends Model>(
|
||||
modelClass: new (...args: any[]) => T
|
||||
): Policy[] =>
|
||||
stores.ui
|
||||
.getActiveModels<T>(modelClass)
|
||||
.map((node) => stores.policies.get(node.id))
|
||||
.filter((policy): policy is Policy => policy !== undefined),
|
||||
|
||||
isModelActive: (model: Model): boolean => stores.ui.isModelActive(model),
|
||||
activeModels: stores.ui.activeModels,
|
||||
|
||||
currentUserId: stores.auth.user?.id,
|
||||
currentTeamId: stores.auth.team?.id,
|
||||
location,
|
||||
|
||||
+112
-13
@@ -2,7 +2,9 @@ import { action, computed, observable } from "mobx";
|
||||
import { flushSync } from "react-dom";
|
||||
import { light as defaultTheme } from "@shared/styles/theme";
|
||||
import Storage from "@shared/utils/Storage";
|
||||
import type Document from "~/models/Document";
|
||||
import type Model from "~/models/base/Model";
|
||||
import Collection from "~/models/Collection";
|
||||
import Document from "~/models/Document";
|
||||
import type { ConnectionStatus } from "~/scenes/Document/components/MultiplayerEditor";
|
||||
import { startViewTransition } from "~/utils/viewTransition";
|
||||
import type RootStore from "./RootStore";
|
||||
@@ -52,10 +54,7 @@ class UiStore {
|
||||
systemTheme: SystemTheme;
|
||||
|
||||
@observable
|
||||
activeDocumentId: string | undefined;
|
||||
|
||||
@observable
|
||||
activeCollectionId?: string | null;
|
||||
activeModels = new Set<Model>();
|
||||
|
||||
@observable
|
||||
observingUserId: string | undefined;
|
||||
@@ -150,6 +149,86 @@ class UiStore {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a model instance to the active set.
|
||||
*
|
||||
* @param model the model instance to add.
|
||||
*/
|
||||
@action
|
||||
addActiveModel = (model: Model): void => {
|
||||
this.activeModels.add(model);
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove a model instance from the active set.
|
||||
*
|
||||
* @param model the model instance to remove.
|
||||
*/
|
||||
@action
|
||||
removeActiveModel = (model: Model): void => {
|
||||
this.activeModels.delete(model);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get all active models of a specific type.
|
||||
*
|
||||
* @param modelClass the model class to filter by.
|
||||
* @returns array of active models of the specified type.
|
||||
*/
|
||||
getActiveModels<T extends Model>(modelClass: new (...args: any[]) => T): T[] {
|
||||
return Array.from(this.activeModels).filter(
|
||||
(model) => model.constructor === modelClass
|
||||
) as T[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a model instance is in the active set.
|
||||
*
|
||||
* @param model the model instance to check.
|
||||
* @returns true if the model is active.
|
||||
*/
|
||||
isModelActive(model: Model): boolean {
|
||||
return this.activeModels.has(model);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all active models, or only models of a specific type.
|
||||
*
|
||||
* @param modelClass optional model class to filter by.
|
||||
*/
|
||||
@action
|
||||
clearActiveModels(modelClass?: new (...args: any[]) => Model): void {
|
||||
if (modelClass) {
|
||||
const modelsToRemove = this.getActiveModels(modelClass);
|
||||
modelsToRemove.forEach((model) => this.activeModels.delete(model));
|
||||
} else {
|
||||
this.activeModels.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the most recently added model of a specific type (primary).
|
||||
*
|
||||
* @param modelClass the model class to filter by.
|
||||
* @returns the most recently added model of the specified type.
|
||||
*/
|
||||
getPrimaryActiveModel<T extends Model>(
|
||||
modelClass: new (...args: any[]) => T
|
||||
): T | undefined {
|
||||
const models = this.getActiveModels<T>(modelClass);
|
||||
return models[models.length - 1];
|
||||
}
|
||||
|
||||
@computed
|
||||
get activeDocumentId(): string | undefined {
|
||||
return this.getPrimaryActiveModel<Document>(Document)?.id;
|
||||
}
|
||||
|
||||
@computed
|
||||
get activeCollectionId(): string | undefined {
|
||||
return this.getPrimaryActiveModel<Collection>(Collection)?.id;
|
||||
}
|
||||
|
||||
@action
|
||||
setTheme = (theme: Theme) => {
|
||||
startViewTransition(() => {
|
||||
@@ -173,17 +252,28 @@ class UiStore {
|
||||
|
||||
@action
|
||||
setActiveDocument = (document: Document | string): void => {
|
||||
let model: Document | undefined;
|
||||
|
||||
if (typeof document === "string") {
|
||||
this.activeDocumentId = document;
|
||||
this.observingUserId = undefined;
|
||||
model = this.rootStore.documents.get(document);
|
||||
} else {
|
||||
model = document;
|
||||
}
|
||||
|
||||
if (!model) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.activeDocumentId = document.id;
|
||||
this.clearActiveModels(Document);
|
||||
this.addActiveModel(model);
|
||||
this.observingUserId = undefined;
|
||||
|
||||
if (document.isActive) {
|
||||
this.activeCollectionId = document.collectionId;
|
||||
if (model.isActive && model.collectionId) {
|
||||
const collection = this.rootStore.collections.get(model.collectionId);
|
||||
if (collection) {
|
||||
this.clearActiveModels(Collection);
|
||||
this.addActiveModel(collection);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -203,7 +293,16 @@ class UiStore {
|
||||
|
||||
@action
|
||||
setActiveCollection = (collectionId: string | undefined): void => {
|
||||
this.activeCollectionId = collectionId;
|
||||
if (collectionId === undefined || collectionId === null) {
|
||||
this.clearActiveModels(Collection);
|
||||
return;
|
||||
}
|
||||
|
||||
const model = this.rootStore.collections.get(collectionId);
|
||||
if (model) {
|
||||
this.clearActiveModels(Collection);
|
||||
this.addActiveModel(model);
|
||||
}
|
||||
};
|
||||
|
||||
@action
|
||||
@@ -213,12 +312,12 @@ class UiStore {
|
||||
|
||||
@action
|
||||
clearActiveDocument = (): void => {
|
||||
this.activeDocumentId = undefined;
|
||||
this.clearActiveModels(Document);
|
||||
this.observingUserId = undefined;
|
||||
|
||||
// Unset when navigating away from a document (e.g. to another document, home, settings, etc.)
|
||||
// Next document's onMount will set the right activeCollectionId.
|
||||
this.activeCollectionId = undefined;
|
||||
this.clearActiveModels(Collection);
|
||||
};
|
||||
|
||||
@action
|
||||
|
||||
@@ -8,12 +8,14 @@ import type {
|
||||
} from "@shared/types";
|
||||
import type RootStore from "~/stores/RootStore";
|
||||
import type { SidebarContextType } from "./components/Sidebar/components/SidebarContext";
|
||||
import type Model from "./models/base/Model";
|
||||
import type Document from "./models/Document";
|
||||
import type FileOperation from "./models/FileOperation";
|
||||
import type Pin from "./models/Pin";
|
||||
import type Star from "./models/Star";
|
||||
import type User from "./models/User";
|
||||
import type UserMembership from "./models/UserMembership";
|
||||
import type Policy from "./models/Policy";
|
||||
|
||||
export type PartialExcept<T, K extends keyof T> = Partial<Omit<T, K>> &
|
||||
Required<Pick<T, K>>;
|
||||
@@ -104,8 +106,24 @@ export type ActionContext = {
|
||||
isCommandBar: boolean;
|
||||
isButton: boolean;
|
||||
sidebarContext?: SidebarContextType;
|
||||
|
||||
// Legacy (backward compatibility) - returns primary active model's ID
|
||||
activeCollectionId?: string | undefined;
|
||||
activeDocumentId: string | undefined;
|
||||
|
||||
// New API - work directly with Model instances
|
||||
getActiveModels: <T extends Model>(
|
||||
modelClass: new (...args: any[]) => T
|
||||
) => T[];
|
||||
getActiveModel: <T extends Model>(
|
||||
modelClass: new (...args: any[]) => T
|
||||
) => T | undefined;
|
||||
getActivePolicies: <T extends Model>(
|
||||
modelClass: new (...args: any[]) => T
|
||||
) => Policy[];
|
||||
isModelActive: (model: Model) => boolean;
|
||||
activeModels: ReadonlySet<Model>;
|
||||
|
||||
currentUserId: string | undefined;
|
||||
currentTeamId: string | undefined;
|
||||
location: Location;
|
||||
|
||||
Reference in New Issue
Block a user