mirror of
https://github.com/outline/outline.git
synced 2026-06-13 11:25:03 +03:00
chore: resolve no-explicit-any and no-base-to-string lint warnings (#12217)
This commit is contained in:
@@ -17,8 +17,12 @@ import Analytics from "~/utils/Analytics";
|
|||||||
import history from "~/utils/history";
|
import history from "~/utils/history";
|
||||||
import type { Action as KbarAction } from "kbar";
|
import type { Action as KbarAction } from "kbar";
|
||||||
|
|
||||||
export function resolve<T>(value: any, context: ActionContext): T {
|
export function resolve<T>(value: unknown, context: ActionContext): T {
|
||||||
return typeof value === "function" ? value(context) : value;
|
return (
|
||||||
|
typeof value === "function"
|
||||||
|
? (value as (context: ActionContext) => T)(context)
|
||||||
|
: value
|
||||||
|
) as T;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ActionSeparator: TActionSeparator = {
|
export const ActionSeparator: TActionSeparator = {
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ import { useDialogContext } from "~/components/DialogContext";
|
|||||||
|
|
||||||
const IconPicker = createLazyComponent(() => import("~/components/IconPicker"));
|
const IconPicker = createLazyComponent(() => import("~/components/IconPicker"));
|
||||||
|
|
||||||
export interface FormData {
|
export type FormData = {
|
||||||
name: string;
|
name: string;
|
||||||
icon: string;
|
icon: string;
|
||||||
color: string | null;
|
color: string | null;
|
||||||
@@ -37,7 +37,7 @@ export interface FormData {
|
|||||||
permission: CollectionPermission | undefined;
|
permission: CollectionPermission | undefined;
|
||||||
commenting?: boolean | null;
|
commenting?: boolean | null;
|
||||||
templateManagement: CollectionPermission;
|
templateManagement: CollectionPermission;
|
||||||
}
|
};
|
||||||
|
|
||||||
const useIconColor = (collection?: Collection) => {
|
const useIconColor = (collection?: Collection) => {
|
||||||
const { collections } = useStores();
|
const { collections } = useStores();
|
||||||
|
|||||||
@@ -87,22 +87,23 @@ const ContentEditable = React.forwardRef(function ContentEditable_(
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
const wrappedEvent =
|
const wrappedEvent =
|
||||||
(
|
<E extends React.SyntheticEvent<HTMLSpanElement>>(
|
||||||
callback:
|
callback: ((event: E) => void) | undefined
|
||||||
| React.FocusEventHandler<HTMLSpanElement>
|
|
||||||
| React.FormEventHandler<HTMLSpanElement>
|
|
||||||
| React.KeyboardEventHandler<HTMLSpanElement>
|
|
||||||
| undefined
|
|
||||||
) =>
|
) =>
|
||||||
(event: any) => {
|
(event: E) => {
|
||||||
if (readOnly) {
|
if (readOnly) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const text = event.currentTarget.textContent || "";
|
const text = event.currentTarget.textContent || "";
|
||||||
|
|
||||||
if (maxLength && isPrintableKeyEvent(event) && text.length >= maxLength) {
|
if (
|
||||||
event?.preventDefault();
|
maxLength &&
|
||||||
|
event.nativeEvent instanceof KeyboardEvent &&
|
||||||
|
isPrintableKeyEvent(event.nativeEvent) &&
|
||||||
|
text.length >= maxLength
|
||||||
|
) {
|
||||||
|
event.preventDefault();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import lazyWithRetry from "~/utils/lazyWithRetry";
|
import lazyWithRetry from "~/utils/lazyWithRetry";
|
||||||
|
|
||||||
|
// oxlint-disable no-explicit-any -- ComponentType<any> is the standard React pattern for generic component constraints
|
||||||
export interface LazyComponent<T extends React.ComponentType<any>> {
|
export interface LazyComponent<T extends React.ComponentType<any>> {
|
||||||
Component: React.LazyExoticComponent<T>;
|
Component: React.LazyExoticComponent<T>;
|
||||||
preload: () => Promise<{ default: T }>;
|
preload: () => Promise<{ default: T }>;
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import Switch from "../Switch";
|
|||||||
import EventBoundary from "@shared/components/EventBoundary";
|
import EventBoundary from "@shared/components/EventBoundary";
|
||||||
import { InputClientType } from "./InputClientType";
|
import { InputClientType } from "./InputClientType";
|
||||||
|
|
||||||
export interface FormData {
|
export type FormData = {
|
||||||
name: string;
|
name: string;
|
||||||
developerName: string;
|
developerName: string;
|
||||||
developerUrl: string;
|
developerUrl: string;
|
||||||
@@ -22,7 +22,7 @@ export interface FormData {
|
|||||||
redirectUris: string[];
|
redirectUris: string[];
|
||||||
published: boolean;
|
published: boolean;
|
||||||
clientType: "confidential" | "public";
|
clientType: "confidential" | "public";
|
||||||
}
|
};
|
||||||
|
|
||||||
export const OAuthClientForm = observer(function OAuthClientForm_({
|
export const OAuthClientForm = observer(function OAuthClientForm_({
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ import PaginatedList from "~/components/PaginatedList";
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
documents: Document[];
|
documents: Document[];
|
||||||
fetch: (options: any) => Promise<Document[] | undefined>;
|
// oxlint-disable-next-line no-explicit-any
|
||||||
|
fetch: (options: Record<string, any>) => Promise<Document[] | undefined>;
|
||||||
|
// oxlint-disable-next-line no-explicit-any
|
||||||
options?: Record<string, any>;
|
options?: Record<string, any>;
|
||||||
heading?: React.ReactNode;
|
heading?: React.ReactNode;
|
||||||
empty?: JSX.Element;
|
empty?: JSX.Element;
|
||||||
|
|||||||
@@ -35,10 +35,12 @@ interface Props<
|
|||||||
* @param options Pagination and other query options
|
* @param options Pagination and other query options
|
||||||
*/
|
*/
|
||||||
fetch?: (
|
fetch?: (
|
||||||
|
// oxlint-disable-next-line no-explicit-any
|
||||||
options: Record<string, any> | undefined
|
options: Record<string, any> | undefined
|
||||||
) => Promise<unknown[] | undefined> | undefined;
|
) => Promise<unknown[] | undefined> | undefined;
|
||||||
|
|
||||||
/** Additional options to pass to the fetch function */
|
/** Additional options to pass to the fetch function */
|
||||||
|
// oxlint-disable-next-line no-explicit-any
|
||||||
options?: Record<string, any>;
|
options?: Record<string, any>;
|
||||||
|
|
||||||
/** Optional header content to display above the list */
|
/** Optional header content to display above the list */
|
||||||
@@ -78,7 +80,7 @@ interface Props<
|
|||||||
* Function to render section headings (typically date-based)
|
* Function to render section headings (typically date-based)
|
||||||
* @param name The heading text or element to render
|
* @param name The heading text or element to render
|
||||||
*/
|
*/
|
||||||
renderHeading?: (name: React.ReactElement<any> | string) => React.ReactNode;
|
renderHeading?: (name: React.ReactElement | string) => React.ReactNode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function to determine if an item is a duplicate of the previous item.
|
* Function to determine if an item is a duplicate of the previous item.
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const Tabs: React.FC = ({ children }: Props) => {
|
const Tabs: React.FC = ({ children }: Props) => {
|
||||||
const ref = React.useRef<any>();
|
const ref = React.useRef<HTMLElement>(null);
|
||||||
const [shadowVisible, setShadow] = React.useState(false);
|
const [shadowVisible, setShadow] = React.useState(false);
|
||||||
const { width } = useWindowSize();
|
const { width } = useWindowSize();
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { Toaster, useSonner } from "sonner";
|
|||||||
import styled, { useTheme } from "styled-components";
|
import styled, { useTheme } from "styled-components";
|
||||||
import { useWebHaptics } from "web-haptics/react";
|
import { useWebHaptics } from "web-haptics/react";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
|
import type { ResolvedTheme } from "~/stores/UiStore";
|
||||||
|
|
||||||
function Toasts() {
|
function Toasts() {
|
||||||
const { ui } = useStores();
|
const { ui } = useStores();
|
||||||
@@ -26,7 +27,8 @@ function Toasts() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledToaster
|
<StyledToaster
|
||||||
theme={ui.resolvedTheme as any}
|
// @ts-expect-error styled-components overrides sonner's theme prop with DefaultTheme
|
||||||
|
theme={ui.resolvedTheme as ResolvedTheme}
|
||||||
closeButton
|
closeButton
|
||||||
toastOptions={{
|
toastOptions={{
|
||||||
duration: 5000,
|
duration: 5000,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// oxlint-disable no-explicit-any -- window.env is a server-injected boundary with mixed value types
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
env: Record<string, any>;
|
env: Record<string, any>;
|
||||||
|
|||||||
@@ -69,15 +69,15 @@ export const ActionContextProvider = observer(function ActionContextProvider_({
|
|||||||
activeDocumentId: stores.ui.activeDocumentId ?? undefined,
|
activeDocumentId: stores.ui.activeDocumentId ?? undefined,
|
||||||
|
|
||||||
getActiveModels: <T extends Model>(
|
getActiveModels: <T extends Model>(
|
||||||
modelClass: new (...args: any[]) => T
|
modelClass: new (...args: never[]) => T
|
||||||
): T[] => stores.ui.getActiveModels<T>(modelClass),
|
): T[] => stores.ui.getActiveModels<T>(modelClass),
|
||||||
|
|
||||||
getActiveModel: <T extends Model>(
|
getActiveModel: <T extends Model>(
|
||||||
modelClass: new (...args: any[]) => T
|
modelClass: new (...args: never[]) => T
|
||||||
): T | undefined => stores.ui.getActiveModels<T>(modelClass)[0],
|
): T | undefined => stores.ui.getActiveModels<T>(modelClass)[0],
|
||||||
|
|
||||||
getActivePolicies: <T extends Model>(
|
getActivePolicies: <T extends Model>(
|
||||||
modelClass: new (...args: any[]) => T
|
modelClass: new (...args: never[]) => T
|
||||||
): Policy[] =>
|
): Policy[] =>
|
||||||
stores.ui
|
stores.ui
|
||||||
.getActiveModels<T>(modelClass)
|
.getActiveModels<T>(modelClass)
|
||||||
@@ -97,7 +97,7 @@ export const ActionContextProvider = observer(function ActionContextProvider_({
|
|||||||
// Override model accessors when models are provided in value
|
// Override model accessors when models are provided in value
|
||||||
const getActiveModels =
|
const getActiveModels =
|
||||||
valueModels && valueModels.length > 0
|
valueModels && valueModels.length > 0
|
||||||
? <T extends Model>(modelClass: new (...args: any[]) => T): T[] => {
|
? <T extends Model>(modelClass: new (...args: never[]) => T): T[] => {
|
||||||
const matching = valueModels.filter(
|
const matching = valueModels.filter(
|
||||||
(model): model is T => model instanceof modelClass
|
(model): model is T => model instanceof modelClass
|
||||||
);
|
);
|
||||||
@@ -108,11 +108,11 @@ export const ActionContextProvider = observer(function ActionContextProvider_({
|
|||||||
: baseContext.getActiveModels;
|
: baseContext.getActiveModels;
|
||||||
|
|
||||||
const getActiveModel = <T extends Model>(
|
const getActiveModel = <T extends Model>(
|
||||||
modelClass: new (...args: any[]) => T
|
modelClass: new (...args: never[]) => T
|
||||||
): T | undefined => getActiveModels(modelClass)[0];
|
): T | undefined => getActiveModels(modelClass)[0];
|
||||||
|
|
||||||
const getActivePolicies = <T extends Model>(
|
const getActivePolicies = <T extends Model>(
|
||||||
modelClass: new (...args: any[]) => T
|
modelClass: new (...args: never[]) => T
|
||||||
): Policy[] =>
|
): Policy[] =>
|
||||||
getActiveModels(modelClass)
|
getActiveModels(modelClass)
|
||||||
.map((node) => stores.policies.get(node.id))
|
.map((node) => stores.policies.get(node.id))
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
import { MobXProviderContext } from "mobx-react";
|
import { MobXProviderContext } from "mobx-react";
|
||||||
import { useContext } from "react";
|
import { useContext } from "react";
|
||||||
import type RootStore from "~/stores";
|
import type RootStore from "~/stores/RootStore";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hook to access the MobX stores from the React context.
|
* Hook to access the MobX stores from the React context.
|
||||||
*
|
*
|
||||||
* @returns The root store containing all application stores
|
* @returns The root store containing all application stores.
|
||||||
*/
|
*/
|
||||||
export default function useStores() {
|
export default function useStores(): RootStore {
|
||||||
return useContext(MobXProviderContext) as typeof RootStore;
|
const { rootStore } = useContext(MobXProviderContext) as {
|
||||||
|
rootStore: RootStore;
|
||||||
|
};
|
||||||
|
return rootStore;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,9 @@ const defaultOptions: ThrottleSettings = {
|
|||||||
* @param dependencies The dependencies to watch for changes
|
* @param dependencies The dependencies to watch for changes
|
||||||
* @param options The throttle options
|
* @param options The throttle options
|
||||||
*/
|
*/
|
||||||
export default function useThrottledCallback<T extends (...args: any[]) => any>(
|
export default function useThrottledCallback<
|
||||||
|
T extends (...args: never[]) => unknown,
|
||||||
|
>(
|
||||||
fn: T,
|
fn: T,
|
||||||
wait = 250,
|
wait = 250,
|
||||||
dependencies: React.DependencyList = [],
|
dependencies: React.DependencyList = [],
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { useRef, useEffect } from "react";
|
|||||||
*
|
*
|
||||||
* @param callback Function to be called on component unmount
|
* @param callback Function to be called on component unmount
|
||||||
*/
|
*/
|
||||||
const useUnmount = (callback: (...args: Array<any>) => any) => {
|
const useUnmount = (callback: () => void) => {
|
||||||
const ref = useRef(callback);
|
const ref = useRef(callback);
|
||||||
ref.current = callback;
|
ref.current = callback;
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -57,7 +57,7 @@ if (element) {
|
|||||||
const App = () => (
|
const App = () => (
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
<HelmetProvider>
|
<HelmetProvider>
|
||||||
<Provider {...stores}>
|
<Provider rootStore={stores}>
|
||||||
<Analytics>
|
<Analytics>
|
||||||
<Router history={history}>
|
<Router history={history}>
|
||||||
<Theme>
|
<Theme>
|
||||||
|
|||||||
@@ -37,11 +37,12 @@ class AuthenticationProvider extends Model {
|
|||||||
@AfterDelete
|
@AfterDelete
|
||||||
static afterDelete(model: AuthenticationProvider) {
|
static afterDelete(model: AuthenticationProvider) {
|
||||||
// Restore a placeholder record to allow re-connection
|
// Restore a placeholder record to allow re-connection
|
||||||
return (model.store as AuthenticationProvidersStore).add({
|
return (model.store as AuthenticationProvidersStore).add(
|
||||||
...model,
|
Object.assign({}, model, {
|
||||||
isEnabled: false,
|
isEnabled: false,
|
||||||
isConnected: false,
|
isConnected: false,
|
||||||
});
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ type SaveOptions = JSONObject & {
|
|||||||
export default class Document extends ArchivableModel implements Searchable {
|
export default class Document extends ArchivableModel implements Searchable {
|
||||||
static modelName = "Document";
|
static modelName = "Document";
|
||||||
|
|
||||||
constructor(fields: Record<string, any>, store: DocumentsStore) {
|
constructor(fields: Record<string, unknown>, store: DocumentsStore) {
|
||||||
super(fields, store);
|
super(fields, store);
|
||||||
|
|
||||||
this.embedsDisabled = Storage.get(`embedsDisabled-${this.id}`) ?? false;
|
this.embedsDisabled = Storage.get(`embedsDisabled-${this.id}`) ?? false;
|
||||||
@@ -570,7 +570,7 @@ export default class Document extends ArchivableModel implements Searchable {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// if saving is successful set the new values on the model itself
|
// if saving is successful set the new values on the model itself
|
||||||
set(this, { ...params, ...model });
|
set(this, Object.assign({}, params, model));
|
||||||
|
|
||||||
this.persistedAttributes = this.toAPI();
|
this.persistedAttributes = this.toAPI();
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export default abstract class Model {
|
|||||||
|
|
||||||
store: Store<Model>;
|
store: Store<Model>;
|
||||||
|
|
||||||
constructor(fields: Record<string, any>, store: Store<Model>) {
|
constructor(fields: Record<string, unknown>, store: Store<Model>) {
|
||||||
this.store = store;
|
this.store = store;
|
||||||
this.updateData(fields);
|
this.updateData(fields);
|
||||||
this.isNew = !this.id;
|
this.isNew = !this.id;
|
||||||
@@ -43,7 +43,7 @@ export default abstract class Model {
|
|||||||
async loadRelations(
|
async loadRelations(
|
||||||
this: Model,
|
this: Model,
|
||||||
options: { withoutPolicies?: boolean } = {}
|
options: { withoutPolicies?: boolean } = {}
|
||||||
): Promise<any> {
|
): Promise<unknown> {
|
||||||
// this is to ensure that multiple loads don’t happen in parallel
|
// this is to ensure that multiple loads don’t happen in parallel
|
||||||
if (this.loadingRelations) {
|
if (this.loadingRelations) {
|
||||||
return this.loadingRelations;
|
return this.loadingRelations;
|
||||||
@@ -90,7 +90,7 @@ export default abstract class Model {
|
|||||||
* @returns A promise that resolves with the updated model
|
* @returns A promise that resolves with the updated model
|
||||||
*/
|
*/
|
||||||
save = async (
|
save = async (
|
||||||
params?: Record<string, any>,
|
params?: Record<string, unknown>,
|
||||||
options?: Record<string, string | boolean | number | undefined>
|
options?: Record<string, string | boolean | number | undefined>
|
||||||
): Promise<Model> => {
|
): Promise<Model> => {
|
||||||
const isNew = this.isNew;
|
const isNew = this.isNew;
|
||||||
@@ -120,7 +120,7 @@ export default abstract class Model {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// if saving is successful set the new values on the model itself
|
// if saving is successful set the new values on the model itself
|
||||||
this.updateData({ ...params, ...model });
|
this.updateData(Object.assign({}, params, model));
|
||||||
|
|
||||||
if (isNew) {
|
if (isNew) {
|
||||||
LifecycleManager.executeHooks(this.constructor, "afterCreate", this);
|
LifecycleManager.executeHooks(this.constructor, "afterCreate", this);
|
||||||
@@ -134,7 +134,7 @@ export default abstract class Model {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
updateData = action((data: Partial<Model>) => {
|
updateData = action((data: Record<string, unknown>) => {
|
||||||
if (this.initialized) {
|
if (this.initialized) {
|
||||||
LifecycleManager.executeHooks(this.constructor, "beforeChange", this);
|
LifecycleManager.executeHooks(this.constructor, "beforeChange", this);
|
||||||
}
|
}
|
||||||
@@ -197,7 +197,7 @@ export default abstract class Model {
|
|||||||
*
|
*
|
||||||
* @returns A plain object representation of the model
|
* @returns A plain object representation of the model
|
||||||
*/
|
*/
|
||||||
toAPI = (): Record<string, any> => {
|
toAPI = (): Partial<Model> => {
|
||||||
const fields = getFieldsForModel(this);
|
const fields = getFieldsForModel(this);
|
||||||
return pick(this, fields);
|
return pick(this, fields);
|
||||||
};
|
};
|
||||||
@@ -247,7 +247,7 @@ export default abstract class Model {
|
|||||||
protected persistedAttributes: Partial<Model> = {};
|
protected persistedAttributes: Partial<Model> = {};
|
||||||
|
|
||||||
/** A promise that resolves when all relations have been loaded. */
|
/** A promise that resolves when all relations have been loaded. */
|
||||||
private loadingRelations: Promise<any[]> | undefined;
|
private loadingRelations: Promise<unknown[]> | undefined;
|
||||||
|
|
||||||
/** A boolean representing if the constructor has been called. */
|
/** A boolean representing if the constructor has been called. */
|
||||||
private initialized = false;
|
private initialized = false;
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ export const getFieldsForModel = <T extends Model>(target: T) =>
|
|||||||
* @param target
|
* @param target
|
||||||
* @param propertyKey
|
* @param propertyKey
|
||||||
*/
|
*/
|
||||||
const Field = <T>(target: any, propertyKey: keyof T) => {
|
const Field = (target: Model, propertyKey: string | symbol) => {
|
||||||
const className = target.constructor.name;
|
const className = target.constructor.name;
|
||||||
fields.set(className, [...(fields.get(className) || []), propertyKey]);
|
fields.set(className, [...(fields.get(className) ?? []), propertyKey]);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Field;
|
export default Field;
|
||||||
|
|||||||
@@ -1,24 +1,32 @@
|
|||||||
export class LifecycleManager {
|
type ModelClass = { readonly name: string };
|
||||||
private static hooks = new Map();
|
type Hook = (...args: unknown[]) => unknown;
|
||||||
|
|
||||||
public static getHooks(target: any, lifecycle: string) {
|
export class LifecycleManager {
|
||||||
|
private static hooks = new Map<string, Map<string, string[]>>();
|
||||||
|
|
||||||
|
public static getHooks(target: ModelClass, lifecycle: string): string[] {
|
||||||
const key = `lifecycle:${lifecycle}`;
|
const key = `lifecycle:${lifecycle}`;
|
||||||
const modelHooks = this.hooks.get(target.name);
|
const modelHooks = this.hooks.get(target.name);
|
||||||
return modelHooks?.get(key) || [];
|
return modelHooks?.get(key) ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static executeHooks(target: any, lifecycle: string, ...args: any[]) {
|
public static executeHooks(
|
||||||
|
target: ModelClass,
|
||||||
|
lifecycle: string,
|
||||||
|
...args: unknown[]
|
||||||
|
): void {
|
||||||
const hooks = this.getHooks(target, lifecycle);
|
const hooks = this.getHooks(target, lifecycle);
|
||||||
hooks.forEach((hook: keyof typeof target) => {
|
hooks.forEach((hook) => {
|
||||||
target[hook](...args);
|
const fn = (target as unknown as Record<string, Hook>)[hook];
|
||||||
|
fn(...args);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static registerHook(
|
public static registerHook(
|
||||||
target: any,
|
target: ModelClass,
|
||||||
propertyKey: string,
|
propertyKey: string,
|
||||||
lifecycle: string
|
lifecycle: string
|
||||||
) {
|
): void {
|
||||||
const key = `lifecycle:${lifecycle}`;
|
const key = `lifecycle:${lifecycle}`;
|
||||||
let modelHooks = this.hooks.get(target.name);
|
let modelHooks = this.hooks.get(target.name);
|
||||||
|
|
||||||
@@ -37,42 +45,42 @@ export class LifecycleManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function BeforeCreate(target: any, propertyKey: string) {
|
export function BeforeCreate(target: ModelClass, propertyKey: string) {
|
||||||
LifecycleManager.registerHook(target, propertyKey, "beforeCreate");
|
LifecycleManager.registerHook(target, propertyKey, "beforeCreate");
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AfterCreate(target: any, propertyKey: string) {
|
export function AfterCreate(target: ModelClass, propertyKey: string) {
|
||||||
LifecycleManager.registerHook(target, propertyKey, "afterCreate");
|
LifecycleManager.registerHook(target, propertyKey, "afterCreate");
|
||||||
}
|
}
|
||||||
|
|
||||||
export function BeforeUpdate(target: any, propertyKey: string) {
|
export function BeforeUpdate(target: ModelClass, propertyKey: string) {
|
||||||
LifecycleManager.registerHook(target, propertyKey, "beforeUpdate");
|
LifecycleManager.registerHook(target, propertyKey, "beforeUpdate");
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AfterUpdate(target: any, propertyKey: string) {
|
export function AfterUpdate(target: ModelClass, propertyKey: string) {
|
||||||
LifecycleManager.registerHook(target, propertyKey, "afterUpdate");
|
LifecycleManager.registerHook(target, propertyKey, "afterUpdate");
|
||||||
}
|
}
|
||||||
|
|
||||||
export function BeforeChange(target: any, propertyKey: string) {
|
export function BeforeChange(target: ModelClass, propertyKey: string) {
|
||||||
LifecycleManager.registerHook(target, propertyKey, "beforeChange");
|
LifecycleManager.registerHook(target, propertyKey, "beforeChange");
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AfterChange(target: any, propertyKey: string) {
|
export function AfterChange(target: ModelClass, propertyKey: string) {
|
||||||
LifecycleManager.registerHook(target, propertyKey, "afterChange");
|
LifecycleManager.registerHook(target, propertyKey, "afterChange");
|
||||||
}
|
}
|
||||||
|
|
||||||
export function BeforeRemove(target: any, propertyKey: string) {
|
export function BeforeRemove(target: ModelClass, propertyKey: string) {
|
||||||
LifecycleManager.registerHook(target, propertyKey, "beforeRemove");
|
LifecycleManager.registerHook(target, propertyKey, "beforeRemove");
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AfterRemove(target: any, propertyKey: string) {
|
export function AfterRemove(target: ModelClass, propertyKey: string) {
|
||||||
LifecycleManager.registerHook(target, propertyKey, "afterRemove");
|
LifecycleManager.registerHook(target, propertyKey, "afterRemove");
|
||||||
}
|
}
|
||||||
|
|
||||||
export function BeforeDelete(target: any, propertyKey: string) {
|
export function BeforeDelete(target: ModelClass, propertyKey: string) {
|
||||||
LifecycleManager.registerHook(target, propertyKey, "beforeDelete");
|
LifecycleManager.registerHook(target, propertyKey, "beforeDelete");
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AfterDelete(target: any, propertyKey: string) {
|
export function AfterDelete(target: ModelClass, propertyKey: string) {
|
||||||
LifecycleManager.registerHook(target, propertyKey, "afterDelete");
|
LifecycleManager.registerHook(target, propertyKey, "afterDelete");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ export default function Relation<T extends typeof Model>(
|
|||||||
classResolver: () => T,
|
classResolver: () => T,
|
||||||
options?: RelationOptions
|
options?: RelationOptions
|
||||||
) {
|
) {
|
||||||
return function (target: any, propertyKey: string) {
|
return function (target: Model, propertyKey: string) {
|
||||||
const idKey = options?.multiple
|
const idKey = options?.multiple
|
||||||
? `${String(singular(propertyKey))}Ids`
|
? `${String(singular(propertyKey))}Ids`
|
||||||
: `${String(propertyKey)}Id`;
|
: `${String(propertyKey)}Id`;
|
||||||
@@ -96,16 +96,16 @@ export default function Relation<T extends typeof Model>(
|
|||||||
// TODO: requestAnimationFrame is a temporary solution to a bug in rolldown compiled code that
|
// TODO: requestAnimationFrame is a temporary solution to a bug in rolldown compiled code that
|
||||||
// will place static methods _after_ decorators. Temporary fix is to delay the registration until
|
// will place static methods _after_ decorators. Temporary fix is to delay the registration until
|
||||||
// the next frame.
|
// the next frame.
|
||||||
|
const modelName = (target.constructor as typeof Model).modelName;
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
if (options) {
|
if (options) {
|
||||||
const configForClass =
|
const configForClass = relations.get(modelName) ?? new Map();
|
||||||
relations.get(target.constructor.modelName) || new Map();
|
|
||||||
configForClass.set(propertyKey, {
|
configForClass.set(propertyKey, {
|
||||||
options,
|
options,
|
||||||
relationClassResolver: classResolver,
|
relationClassResolver: classResolver,
|
||||||
idKey,
|
idKey,
|
||||||
});
|
});
|
||||||
relations.set(target.constructor.modelName, configForClass);
|
relations.set(modelName, configForClass);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ function Login({ children, onBack }: Props) {
|
|||||||
const handleGoSubdomain = React.useCallback(async (event) => {
|
const handleGoSubdomain = React.useCallback(async (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const data = Object.fromEntries(new FormData(event.target));
|
const data = Object.fromEntries(new FormData(event.target));
|
||||||
await navigateToSubdomain(data.subdomain.toString());
|
await navigateToSubdomain(data.subdomain as string);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ function AuthenticationProvider(props: Props) {
|
|||||||
const input = document.createElement("input");
|
const input = document.createElement("input");
|
||||||
input.type = "hidden";
|
input.type = "hidden";
|
||||||
input.name = fieldName;
|
input.name = fieldName;
|
||||||
input.value = String(value);
|
input.value = String(value as string | number | boolean);
|
||||||
formRef.current?.appendChild(input);
|
formRef.current?.appendChild(input);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import Event from "~/models/Event";
|
|||||||
import type RootStore from "./RootStore";
|
import type RootStore from "./RootStore";
|
||||||
import Store, { RPCAction } from "./base/Store";
|
import Store, { RPCAction } from "./base/Store";
|
||||||
|
|
||||||
|
// oxlint-disable no-explicit-any -- Event generic must be `any` because the store holds events for all model types
|
||||||
export default class EventsStore extends Store<Event<any>> {
|
export default class EventsStore extends Store<Event<any>> {
|
||||||
actions = [RPCAction.List];
|
actions = [RPCAction.List];
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ export enum SystemTheme {
|
|||||||
Dark = "dark",
|
Dark = "dark",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ResolvedTheme = "light" | "dark" | "system";
|
||||||
|
|
||||||
type PersistedData = Pick<
|
type PersistedData = Pick<
|
||||||
UiStore,
|
UiStore,
|
||||||
| "languagePromptDismissed"
|
| "languagePromptDismissed"
|
||||||
@@ -209,7 +211,9 @@ class UiStore {
|
|||||||
* @param modelClass the model class to filter by.
|
* @param modelClass the model class to filter by.
|
||||||
* @returns array of active models of the specified type.
|
* @returns array of active models of the specified type.
|
||||||
*/
|
*/
|
||||||
getActiveModels<T extends Model>(modelClass: new (...args: any[]) => T): T[] {
|
getActiveModels<T extends Model>(
|
||||||
|
modelClass: new (...args: never[]) => T
|
||||||
|
): T[] {
|
||||||
return Array.from(this.activeModels.values()).filter(
|
return Array.from(this.activeModels.values()).filter(
|
||||||
(model) => model.constructor === modelClass
|
(model) => model.constructor === modelClass
|
||||||
) as T[];
|
) as T[];
|
||||||
@@ -231,7 +235,7 @@ class UiStore {
|
|||||||
* @param modelClass optional model class to filter by.
|
* @param modelClass optional model class to filter by.
|
||||||
*/
|
*/
|
||||||
@action
|
@action
|
||||||
clearActiveModels(modelClass?: new (...args: any[]) => Model): void {
|
clearActiveModels(modelClass?: new (...args: never[]) => Model): void {
|
||||||
if (modelClass) {
|
if (modelClass) {
|
||||||
const modelsToRemove = this.getActiveModels(modelClass);
|
const modelsToRemove = this.getActiveModels(modelClass);
|
||||||
modelsToRemove.forEach((model) => this.activeModels.delete(model.id));
|
modelsToRemove.forEach((model) => this.activeModels.delete(model.id));
|
||||||
@@ -247,7 +251,7 @@ class UiStore {
|
|||||||
* @returns the most recently added model of the specified type.
|
* @returns the most recently added model of the specified type.
|
||||||
*/
|
*/
|
||||||
getPrimaryActiveModel<T extends Model>(
|
getPrimaryActiveModel<T extends Model>(
|
||||||
modelClass: new (...args: any[]) => T
|
modelClass: new (...args: never[]) => T
|
||||||
): T | undefined {
|
): T | undefined {
|
||||||
const models = this.getActiveModels<T>(modelClass);
|
const models = this.getActiveModels<T>(modelClass);
|
||||||
return models[models.length - 1];
|
return models[models.length - 1];
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ export type PaginatedResponse<T> = T[] & {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// oxlint-disable-next-line no-explicit-any
|
||||||
export type FetchPageParams = PaginationParams & Record<string, any>;
|
export type FetchPageParams = PaginationParams & Record<string, any>;
|
||||||
|
|
||||||
export default abstract class Store<T extends Model> {
|
export default abstract class Store<T extends Model> {
|
||||||
@@ -59,6 +60,7 @@ export default abstract class Store<T extends Model> {
|
|||||||
@observable
|
@observable
|
||||||
isLoaded = false;
|
isLoaded = false;
|
||||||
|
|
||||||
|
// oxlint-disable-next-line no-explicit-any
|
||||||
requests: Map<string, Promise<any>> = new Map();
|
requests: Map<string, Promise<any>> = new Map();
|
||||||
|
|
||||||
model: typeof Model;
|
model: typeof Model;
|
||||||
@@ -430,6 +432,7 @@ export default abstract class Store<T extends Model> {
|
|||||||
|
|
||||||
@action
|
@action
|
||||||
fetchAll = async (
|
fetchAll = async (
|
||||||
|
// oxlint-disable-next-line no-explicit-any
|
||||||
params?: Record<string, any>
|
params?: Record<string, any>
|
||||||
): Promise<PaginatedResponse<T>> => {
|
): Promise<PaginatedResponse<T>> => {
|
||||||
const limit = params?.limit ?? Pagination.defaultLimit;
|
const limit = params?.limit ?? Pagination.defaultLimit;
|
||||||
|
|||||||
+4
-4
@@ -116,13 +116,13 @@ export type ActionContext = {
|
|||||||
|
|
||||||
// New API - work directly with Model instances
|
// New API - work directly with Model instances
|
||||||
getActiveModels: <T extends Model>(
|
getActiveModels: <T extends Model>(
|
||||||
modelClass: new (...args: any[]) => T
|
modelClass: new (...args: never[]) => T
|
||||||
) => T[];
|
) => T[];
|
||||||
getActiveModel: <T extends Model>(
|
getActiveModel: <T extends Model>(
|
||||||
modelClass: new (...args: any[]) => T
|
modelClass: new (...args: never[]) => T
|
||||||
) => T | undefined;
|
) => T | undefined;
|
||||||
getActivePolicies: <T extends Model>(
|
getActivePolicies: <T extends Model>(
|
||||||
modelClass: new (...args: any[]) => T
|
modelClass: new (...args: never[]) => T
|
||||||
) => Policy[];
|
) => Policy[];
|
||||||
isModelActive: (model: Model) => boolean;
|
isModelActive: (model: Model) => boolean;
|
||||||
activeModels: ReadonlySet<Model>;
|
activeModels: ReadonlySet<Model>;
|
||||||
@@ -160,7 +160,7 @@ export type Action = BaseAction & {
|
|||||||
tooltip?:
|
tooltip?:
|
||||||
| ((context: ActionContext) => React.ReactChild | undefined)
|
| ((context: ActionContext) => React.ReactChild | undefined)
|
||||||
| React.ReactChild;
|
| React.ReactChild;
|
||||||
perform: (context: ActionContext) => any;
|
perform: (context: ActionContext) => unknown;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type InternalLinkAction = BaseAction & {
|
export type InternalLinkAction = BaseAction & {
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ class ApiClient {
|
|||||||
shareId?: string;
|
shareId?: string;
|
||||||
|
|
||||||
/** Map of in-flight POST requests for deduplication, keyed by path + body. */
|
/** Map of in-flight POST requests for deduplication, keyed by path + body. */
|
||||||
|
// oxlint-disable-next-line no-explicit-any
|
||||||
private inflightRequests = new Map<string, Promise<any>>();
|
private inflightRequests = new Map<string, Promise<any>>();
|
||||||
|
|
||||||
constructor(options: Options = {}) {
|
constructor(options: Options = {}) {
|
||||||
@@ -57,6 +58,7 @@ class ApiClient {
|
|||||||
this.shareId = shareId;
|
this.shareId = shareId;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// oxlint-disable-next-line no-explicit-any
|
||||||
fetch = async <T = any>(
|
fetch = async <T = any>(
|
||||||
path: string,
|
path: string,
|
||||||
method: string,
|
method: string,
|
||||||
@@ -93,7 +95,7 @@ class ApiClient {
|
|||||||
// toggling Content-Type to application/json
|
// toggling Content-Type to application/json
|
||||||
if (
|
if (
|
||||||
typeof data === "object" &&
|
typeof data === "object" &&
|
||||||
(data || "").toString() === "[object Object]"
|
Object.prototype.toString.call(data) === "[object Object]"
|
||||||
) {
|
) {
|
||||||
body = JSON.stringify(data);
|
body = JSON.stringify(data);
|
||||||
}
|
}
|
||||||
@@ -206,7 +208,7 @@ class ApiClient {
|
|||||||
const error: {
|
const error: {
|
||||||
message?: string;
|
message?: string;
|
||||||
error?: string;
|
error?: string;
|
||||||
data?: Record<string, any>;
|
data?: Record<string, unknown>;
|
||||||
} = {};
|
} = {};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -277,12 +279,14 @@ class ApiClient {
|
|||||||
throw err;
|
throw err;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// oxlint-disable-next-line no-explicit-any
|
||||||
get = <T = any>(
|
get = <T = any>(
|
||||||
path: string,
|
path: string,
|
||||||
data: JSONObject | undefined,
|
data: JSONObject | undefined,
|
||||||
options?: FetchOptions
|
options?: FetchOptions
|
||||||
) => this.fetch<T>(path, "GET", data, options);
|
) => this.fetch<T>(path, "GET", data, options);
|
||||||
|
|
||||||
|
// oxlint-disable-next-line no-explicit-any
|
||||||
post = <T = any>(
|
post = <T = any>(
|
||||||
path: string,
|
path: string,
|
||||||
data?: JSONObject | FormData,
|
data?: JSONObject | FormData,
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ type LogCategory =
|
|||||||
| "plugins"
|
| "plugins"
|
||||||
| "policies";
|
| "policies";
|
||||||
|
|
||||||
|
// oxlint-disable-next-line no-explicit-any
|
||||||
type Extra = Record<string, any>;
|
type Extra = Record<string, any>;
|
||||||
|
|
||||||
class Logger {
|
class Logger {
|
||||||
|
|||||||
@@ -41,9 +41,8 @@ export default function download(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// go ahead and download dataURLs right away
|
// go ahead and download dataURLs right away
|
||||||
if (String(x).match(/^data:[\w+-]+\/[\w+-]+[,;]/)) {
|
if (typeof x === "string" && x.match(/^data:[\w+-]+\/[\w+-]+[,;]/)) {
|
||||||
// @ts-expect-error ts-migrate(2554) FIXME: Expected 2 arguments, but got 1.
|
return saver(x);
|
||||||
return saver(x); // everyone else can save dataURLs un-processed
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// end if dataURL passed?
|
// end if dataURL passed?
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
|
// oxlint-disable no-explicit-any -- ComponentType<any> is the standard React pattern for generic component constraints
|
||||||
type ComponentPromise<T extends React.ComponentType<any>> = Promise<{
|
type ComponentPromise<T extends React.ComponentType<any>> = Promise<{
|
||||||
default: T;
|
default: T;
|
||||||
}>;
|
}>;
|
||||||
|
|||||||
@@ -60,8 +60,8 @@ class Logger {
|
|||||||
winston.format.printf(
|
winston.format.printf(
|
||||||
({ message, level, label, ...extra }) =>
|
({ message, level, label, ...extra }) =>
|
||||||
`${level}: ${
|
`${level}: ${
|
||||||
label ? styleText("bold", `[${String(label)}] `) : ""
|
label ? styleText("bold", `[${label as string}] `) : ""
|
||||||
}${String(message)} ${isEmpty(extra) ? "" : JSON.stringify(extra)}`
|
}${message as string} ${isEmpty(extra) ? "" : JSON.stringify(extra)}`
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -371,13 +371,13 @@ export class ProsemirrorHelper extends SharedProsemirrorHelper {
|
|||||||
|
|
||||||
function replaceAttachmentUrls(node: ProsemirrorData) {
|
function replaceAttachmentUrls(node: ProsemirrorData) {
|
||||||
if (node.attrs?.src) {
|
if (node.attrs?.src) {
|
||||||
node.attrs.src = getMapping(String(node.attrs.src));
|
node.attrs.src = getMapping(node.attrs.src as string);
|
||||||
} else if (node.attrs?.href) {
|
} else if (node.attrs?.href) {
|
||||||
node.attrs.href = getMapping(String(node.attrs.href));
|
node.attrs.href = getMapping(node.attrs.href as string);
|
||||||
} else if (node.marks) {
|
} else if (node.marks) {
|
||||||
node.marks.forEach((mark) => {
|
node.marks.forEach((mark) => {
|
||||||
if (mark.attrs?.href) {
|
if (mark.attrs?.href) {
|
||||||
mark.attrs.href = getMapping(String(mark.attrs.href));
|
mark.attrs.href = getMapping(mark.attrs.href as string);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,9 +63,14 @@ export function withAPIContext<T>(
|
|||||||
* @param obj Object to convert to form-urlencoded string
|
* @param obj Object to convert to form-urlencoded string
|
||||||
* @returns Form-urlencoded string representation of the object
|
* @returns Form-urlencoded string representation of the object
|
||||||
*/
|
*/
|
||||||
export function toFormData(obj: Record<string, any>): string {
|
export function toFormData(
|
||||||
|
obj: Record<string, string | number | boolean | null | undefined>
|
||||||
|
): string {
|
||||||
return Object.entries(obj)
|
return Object.entries(obj)
|
||||||
.filter(([_, value]) => value !== undefined)
|
.filter(
|
||||||
|
(entry): entry is [string, string | number | boolean] =>
|
||||||
|
entry[1] !== undefined && entry[1] !== null
|
||||||
|
)
|
||||||
.map(
|
.map(
|
||||||
([key, value]) =>
|
([key, value]) =>
|
||||||
`${encodeURIComponent(key)}=${encodeURIComponent(value)}`
|
`${encodeURIComponent(key)}=${encodeURIComponent(value)}`
|
||||||
|
|||||||
@@ -356,7 +356,7 @@ export function documentTools(server: McpServer, scopes: string[]) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "text" as const,
|
type: "text" as const,
|
||||||
text: String(text ?? ""),
|
text: typeof text === "string" ? text : "",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
} satisfies CallToolResult;
|
} satisfies CallToolResult;
|
||||||
@@ -606,7 +606,7 @@ export function documentTools(server: McpServer, scopes: string[]) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "text" as const,
|
type: "text" as const,
|
||||||
text: String(text ?? ""),
|
text: typeof text === "string" ? text : "",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
} satisfies CallToolResult;
|
} satisfies CallToolResult;
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ export function fetchTool(server: McpServer, scopes: string[]) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "text" as const,
|
type: "text" as const,
|
||||||
text: String(text ?? ""),
|
text: typeof text === "string" ? text : "",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
} satisfies CallToolResult;
|
} satisfies CallToolResult;
|
||||||
|
|||||||
@@ -299,9 +299,9 @@ export class DocumentConverter {
|
|||||||
|
|
||||||
// Replace the content-location with a data URI for each attachment.
|
// Replace the content-location with a data URI for each attachment.
|
||||||
for (const attachment of parsed.attachments) {
|
for (const attachment of parsed.attachments) {
|
||||||
const contentLocation = String(
|
const contentLocation =
|
||||||
attachment.headers.get("content-location") ?? ""
|
(attachment.headers.get("content-location") as string | undefined) ??
|
||||||
);
|
"";
|
||||||
|
|
||||||
const id = contentLocation.split("/").pop();
|
const id = contentLocation.split("/").pop();
|
||||||
if (!id) {
|
if (!id) {
|
||||||
|
|||||||
@@ -13,6 +13,4 @@ export class MutexLock {
|
|||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static redlock: any;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -195,10 +195,15 @@ export const MentionURL = (props: IssueUrlProps) => {
|
|||||||
...attrs
|
...attrs
|
||||||
} = getAttributesFromNode(node);
|
} = getAttributesFromNode(node);
|
||||||
|
|
||||||
const url = String(attrs.href);
|
const url = typeof attrs.href === "string" ? attrs.href : undefined;
|
||||||
const unfurl = unfurls.get(url)?.data ?? unfurlAttr;
|
const unfurl = url ? (unfurls.get(url)?.data ?? unfurlAttr) : undefined;
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
|
if (!url) {
|
||||||
|
setLoaded(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const fetchUnfurl = async () => {
|
const fetchUnfurl = async () => {
|
||||||
try {
|
try {
|
||||||
const unfurlModel = await unfurls.fetchUnfurl({ url });
|
const unfurlModel = await unfurls.fetchUnfurl({ url });
|
||||||
|
|||||||
@@ -142,7 +142,8 @@ export function setCellAttrs(node: Node): Attrs {
|
|||||||
attrs["data-colwidth"] = node.attrs.colwidth.map(Number).join(",");
|
attrs["data-colwidth"] = node.attrs.colwidth.map(Number).join(",");
|
||||||
} else {
|
} else {
|
||||||
attrs.style =
|
attrs.style =
|
||||||
(attrs.style ?? "") + `min-width: ${Number(node.attrs.colwidth[0])}px;`;
|
((attrs.style as string) ?? "") +
|
||||||
|
`min-width: ${Number(node.attrs.colwidth[0])}px;`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Array.isArray(node.attrs.marks)) {
|
if (Array.isArray(node.attrs.marks)) {
|
||||||
@@ -156,7 +157,7 @@ export function setCellAttrs(node: Node): Attrs {
|
|||||||
const color = backgroundMark.attrs!.color as string;
|
const color = backgroundMark.attrs!.color as string;
|
||||||
attrs["data-bgcolor"] = color;
|
attrs["data-bgcolor"] = color;
|
||||||
attrs.style =
|
attrs.style =
|
||||||
(attrs.style ?? "") +
|
((attrs.style as string) ?? "") +
|
||||||
`--cell-bg-color: ${color}; --cell-text-color: ${readableColor(color)};`;
|
`--cell-bg-color: ${color}; --cell-text-color: ${readableColor(color)};`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,5 +2,5 @@ import { MobXProviderContext } from "mobx-react";
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
export default function useStores() {
|
export default function useStores() {
|
||||||
return React.useContext(MobXProviderContext);
|
return React.useContext(MobXProviderContext).rootStore;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export const ellipsis = () => `
|
|||||||
*/
|
*/
|
||||||
export const s =
|
export const s =
|
||||||
(key: keyof DefaultTheme) => (props: { theme: DefaultTheme }) =>
|
(key: keyof DefaultTheme) => (props: { theme: DefaultTheme }) =>
|
||||||
String(props.theme[key]);
|
props.theme[key] as string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mixin to hide scrollbars.
|
* Mixin to hide scrollbars.
|
||||||
|
|||||||
@@ -506,19 +506,19 @@ export class ProsemirrorHelper {
|
|||||||
if (
|
if (
|
||||||
node.type === "image" &&
|
node.type === "image" &&
|
||||||
node.attrs?.src &&
|
node.attrs?.src &&
|
||||||
regex.test(String(node.attrs.src))
|
regex.test(node.attrs.src as string)
|
||||||
) {
|
) {
|
||||||
node.attrs.src = env.URL + node.attrs.src;
|
node.attrs.src = env.URL + node.attrs.src;
|
||||||
} else if (
|
} else if (
|
||||||
node.type === "video" &&
|
node.type === "video" &&
|
||||||
node.attrs?.src &&
|
node.attrs?.src &&
|
||||||
regex.test(String(node.attrs.src))
|
regex.test(node.attrs.src as string)
|
||||||
) {
|
) {
|
||||||
node.attrs.src = env.URL + node.attrs.src;
|
node.attrs.src = env.URL + node.attrs.src;
|
||||||
} else if (
|
} else if (
|
||||||
node.type === "attachment" &&
|
node.type === "attachment" &&
|
||||||
node.attrs?.href &&
|
node.attrs?.href &&
|
||||||
regex.test(String(node.attrs.href))
|
regex.test(node.attrs.href as string)
|
||||||
) {
|
) {
|
||||||
node.attrs.href = env.URL + node.attrs.href;
|
node.attrs.href = env.URL + node.attrs.href;
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-1
@@ -40,7 +40,10 @@ export class CSVHelper {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
const stringValue = String(value);
|
const stringValue =
|
||||||
|
typeof value === "object"
|
||||||
|
? JSON.stringify(value)
|
||||||
|
: String(value as string | number | boolean);
|
||||||
|
|
||||||
// If the value contains comma, quote, or newline, wrap it in quotes and escape internal quotes
|
// If the value contains comma, quote, or newline, wrap it in quotes and escape internal quotes
|
||||||
if (
|
if (
|
||||||
|
|||||||
Reference in New Issue
Block a user