mirror of
https://github.com/outline/outline.git
synced 2026-06-13 03:14:59 +03:00
wip: Refactor of activeDocumentId
This commit is contained in:
@@ -4,6 +4,7 @@ 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 { ActionContext as ActionContextType } from "~/types";
|
||||
|
||||
export const ActionContext = createContext<ActionContextType | undefined>(
|
||||
@@ -49,8 +50,18 @@ 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: typeof Model): T[] =>
|
||||
stores.ui.getActiveModels<T>(modelClass),
|
||||
isModelActive: (model: Model): boolean =>
|
||||
stores.ui.isModelActive(model),
|
||||
activeModels: stores.ui.activeModels,
|
||||
|
||||
currentUserId: stores.auth.user?.id,
|
||||
currentTeamId: stores.auth.team?.id,
|
||||
location,
|
||||
|
||||
+110
-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";
|
||||
@@ -48,10 +50,7 @@ class UiStore {
|
||||
systemTheme: SystemTheme;
|
||||
|
||||
@observable
|
||||
activeDocumentId: string | undefined;
|
||||
|
||||
@observable
|
||||
activeCollectionId?: string | null;
|
||||
activeModels = new Set<Model>();
|
||||
|
||||
@observable
|
||||
observingUserId: string | undefined;
|
||||
@@ -140,6 +139,84 @@ 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: typeof Model): 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?: typeof 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: typeof Model): 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(() => {
|
||||
@@ -152,17 +229,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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -182,7 +270,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
|
||||
@@ -192,12 +289,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,6 +8,7 @@ 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";
|
||||
@@ -96,8 +97,16 @@ 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: typeof Model) => T[];
|
||||
isModelActive: (model: Model) => boolean;
|
||||
activeModels: ReadonlySet<Model>;
|
||||
|
||||
currentUserId: string | undefined;
|
||||
currentTeamId: string | undefined;
|
||||
location: Location;
|
||||
|
||||
Reference in New Issue
Block a user