mirror of
https://github.com/outline/outline.git
synced 2026-06-13 11:25:03 +03:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f153968b75 | |||
| 042fd0fcb9 | |||
| 53735e25de |
+16
-8
@@ -11,8 +11,11 @@ import {
|
||||
} from "~/types";
|
||||
import Analytics from "~/utils/Analytics";
|
||||
|
||||
function resolve<T>(value: any, context: ActionContext): T {
|
||||
return typeof value === "function" ? value(context) : value;
|
||||
function resolve<T>(
|
||||
value: T | ((context: ActionContext) => T),
|
||||
context: ActionContext
|
||||
): T {
|
||||
return typeof value === "function" ? (value as (context: ActionContext) => T)(context) : value;
|
||||
}
|
||||
|
||||
export function createAction(definition: Optional<Action, "id">): Action {
|
||||
@@ -43,8 +46,12 @@ export function actionToMenuItem(
|
||||
action: Action,
|
||||
context: ActionContext
|
||||
): MenuItemButton | MenuItemWithChildren {
|
||||
const resolvedIcon = resolve<React.ReactElement<any>>(action.icon, context);
|
||||
const resolvedChildren = resolve<Action[]>(action.children, context);
|
||||
const resolvedIcon = action.icon
|
||||
? resolve<React.ReactNode>(action.icon, context)
|
||||
: undefined;
|
||||
const resolvedChildren = action.children
|
||||
? resolve<Action[]>(action.children, context)
|
||||
: undefined;
|
||||
const visible = action.visible ? action.visible(context) : true;
|
||||
const title = resolve<string>(action.name, context);
|
||||
const icon =
|
||||
@@ -86,11 +93,12 @@ export function actionToKBar(
|
||||
return [];
|
||||
}
|
||||
|
||||
const resolvedIcon = resolve<React.ReactElement>(action.icon, context);
|
||||
const resolvedChildren = resolve<Action[]>(action.children, context);
|
||||
const resolvedSection = resolve<string>(action.section, context);
|
||||
const resolvedIcon = action.icon ? resolve<React.ReactNode>(action.icon, context) : undefined;
|
||||
const resolvedChildren = action.children ? resolve<Action[]>(action.children, context) : undefined;
|
||||
const resolvedSection = action.section ? resolve<string>(action.section, context) : undefined;
|
||||
const resolvedName = resolve<string>(action.name, context);
|
||||
const resolvedPlaceholder = resolve<string>(action.placeholder, context);
|
||||
const resolvedPlaceholder = action.placeholder ? resolve<string>(action.placeholder, context) : undefined;
|
||||
|
||||
const children = resolvedChildren
|
||||
? flattenDeep(resolvedChildren.map((a) => actionToKBar(a, context))).filter(
|
||||
(a) => !!a
|
||||
|
||||
@@ -138,11 +138,11 @@ const AvatarPresence = styled.div<AvatarWrapperProps>`
|
||||
border: 2px solid transparent;
|
||||
pointer-events: none;
|
||||
|
||||
${(props) =>
|
||||
props.$isObserving &&
|
||||
${(innerProps) =>
|
||||
innerProps.$isObserving &&
|
||||
css`
|
||||
border: 2px solid ${props.$color};
|
||||
box-shadow: inset 0 0 0 2px ${props.theme.background};
|
||||
border: 2px solid ${innerProps.$color};
|
||||
box-shadow: inset 0 0 0 2px ${innerProps.theme.background};
|
||||
|
||||
&:hover {
|
||||
top: -1px;
|
||||
@@ -154,7 +154,7 @@ const AvatarPresence = styled.div<AvatarWrapperProps>`
|
||||
}
|
||||
|
||||
&:hover:after {
|
||||
border: 2px solid ${(props) => props.$color};
|
||||
border: 2px solid ${(hoverProps) => hoverProps.$color};
|
||||
box-shadow: inset 0 0 0 2px ${s("background")};
|
||||
}
|
||||
`}
|
||||
|
||||
@@ -36,9 +36,10 @@ function Collaborators(props: Props) {
|
||||
const { document } = props;
|
||||
const { observingUserId } = ui;
|
||||
const documentPresence = presence.get(document.id);
|
||||
const documentPresenceArray = documentPresence
|
||||
? Array.from(documentPresence.values())
|
||||
: [];
|
||||
const documentPresenceArray = useMemo(
|
||||
() => (documentPresence ? Array.from(documentPresence.values()) : []),
|
||||
[documentPresence]
|
||||
);
|
||||
|
||||
// Use Set for O(1) lookups and stable references
|
||||
const presentIds = useMemo(
|
||||
|
||||
@@ -94,14 +94,19 @@ const ContentEditable = React.forwardRef(function _ContentEditable(
|
||||
| React.KeyboardEventHandler<HTMLSpanElement>
|
||||
| undefined
|
||||
) =>
|
||||
(event: any) => {
|
||||
(
|
||||
event:
|
||||
| React.FocusEvent<HTMLSpanElement>
|
||||
| React.KeyboardEvent<HTMLSpanElement>
|
||||
| React.FormEvent<HTMLSpanElement>
|
||||
) => {
|
||||
if (readOnly) {
|
||||
return;
|
||||
}
|
||||
|
||||
const text = event.currentTarget.textContent || "";
|
||||
|
||||
if (maxLength && isPrintableKeyEvent(event) && text.length >= maxLength) {
|
||||
if (maxLength && 'key' in event && isPrintableKeyEvent(event as React.KeyboardEvent<HTMLSpanElement>) && text.length >= maxLength) {
|
||||
event?.preventDefault();
|
||||
return;
|
||||
}
|
||||
@@ -111,7 +116,16 @@ const ContentEditable = React.forwardRef(function _ContentEditable(
|
||||
onChange?.(text);
|
||||
}
|
||||
|
||||
callback?.(event);
|
||||
// Type assertion to handle the union type callback
|
||||
if (callback) {
|
||||
if ('key' in event) {
|
||||
(callback as React.KeyboardEventHandler<HTMLSpanElement>)(event as React.KeyboardEvent<HTMLSpanElement>);
|
||||
} else if ('relatedTarget' in event) {
|
||||
(callback as React.FocusEventHandler<HTMLSpanElement>)(event as React.FocusEvent<HTMLSpanElement>);
|
||||
} else {
|
||||
(callback as React.FormEventHandler<HTMLSpanElement>)(event as React.FormEvent<HTMLSpanElement>);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// This is to account for being within a React.Suspense boundary, in this
|
||||
|
||||
@@ -4,11 +4,12 @@ import Document from "~/models/Document";
|
||||
import DocumentListItem from "~/components/DocumentListItem";
|
||||
import Error from "~/components/List/Error";
|
||||
import PaginatedList from "~/components/PaginatedList";
|
||||
import { PaginationParams } from "~/types";
|
||||
|
||||
type Props = {
|
||||
documents: Document[];
|
||||
fetch: (options: any) => Promise<Document[] | undefined>;
|
||||
options?: Record<string, any>;
|
||||
fetch: (options?: PaginationParams) => Promise<Document[] | undefined>;
|
||||
options?: PaginationParams;
|
||||
heading?: React.ReactNode;
|
||||
empty?: JSX.Element;
|
||||
showParentDocuments?: boolean;
|
||||
|
||||
@@ -20,7 +20,9 @@ const defaultOptions: ThrottleSettings = {
|
||||
* @param dependencies The dependencies to watch for changes
|
||||
* @param options The throttle options
|
||||
*/
|
||||
export default function useThrottledCallback<T extends (...args: any[]) => any>(
|
||||
export default function useThrottledCallback<
|
||||
T extends (...args: unknown[]) => unknown
|
||||
>(
|
||||
fn: T,
|
||||
wait = 250,
|
||||
dependencies: React.DependencyList = [],
|
||||
|
||||
+4
-5
@@ -108,11 +108,10 @@ if ("serviceWorker" in navigator && env.ENVIRONMENT !== "development") {
|
||||
if (maybePromise?.then) {
|
||||
maybePromise
|
||||
.then((registration) => {
|
||||
Logger.debug(
|
||||
"lifecycle",
|
||||
"[ServiceWorker] Registered.",
|
||||
registration
|
||||
);
|
||||
Logger.debug("lifecycle", "[ServiceWorker] Registered.", {
|
||||
scope: registration.scope,
|
||||
active: !!registration.active,
|
||||
});
|
||||
})
|
||||
.catch((registrationError) => {
|
||||
Logger.debug(
|
||||
|
||||
@@ -12,8 +12,9 @@ export const getFieldsForModel = <T extends Model>(target: T) =>
|
||||
* @param target
|
||||
* @param propertyKey
|
||||
*/
|
||||
const Field = <T>(target: any, propertyKey: keyof T) => {
|
||||
const className = target.constructor.name;
|
||||
const Field = <T>(target: T, propertyKey: keyof T) => {
|
||||
const className = (target as { constructor: { name: string } }).constructor
|
||||
.name;
|
||||
fields.set(className, [...(fields.get(className) || []), propertyKey]);
|
||||
};
|
||||
|
||||
|
||||
@@ -288,6 +288,9 @@ const CollectionScene = observer(function _CollectionScene() {
|
||||
fetch={documents.fetchAlphabetical}
|
||||
options={{
|
||||
collectionId: collection.id,
|
||||
parentDocumentId: null,
|
||||
sort: collection.sort.field,
|
||||
direction: collection.sort.direction.toUpperCase() as "ASC" | "DESC",
|
||||
}}
|
||||
/>
|
||||
</Route>
|
||||
@@ -302,6 +305,9 @@ const CollectionScene = observer(function _CollectionScene() {
|
||||
fetch={documents.fetchLeastRecentlyUpdated}
|
||||
options={{
|
||||
collectionId: collection.id,
|
||||
parentDocumentId: null,
|
||||
sort: collection.sort.field,
|
||||
direction: collection.sort.direction.toUpperCase() as "ASC" | "DESC",
|
||||
}}
|
||||
/>
|
||||
</Route>
|
||||
@@ -319,8 +325,11 @@ const CollectionScene = observer(function _CollectionScene() {
|
||||
fetch={documents.fetchRecentlyPublished}
|
||||
options={{
|
||||
collectionId: collection.id,
|
||||
parentDocumentId: null,
|
||||
sort: collection.sort.field,
|
||||
direction: collection.sort.direction.toUpperCase() as "ASC" | "DESC",
|
||||
showPublished: true,
|
||||
}}
|
||||
showPublished
|
||||
/>
|
||||
</Route>
|
||||
<Route
|
||||
@@ -337,6 +346,9 @@ const CollectionScene = observer(function _CollectionScene() {
|
||||
fetch={documents.fetchRecentlyUpdated}
|
||||
options={{
|
||||
collectionId: collection.id,
|
||||
parentDocumentId: null,
|
||||
sort: collection.sort.field,
|
||||
direction: collection.sort.direction.toUpperCase() as "ASC" | "DESC",
|
||||
}}
|
||||
/>
|
||||
</Route>
|
||||
@@ -354,7 +366,7 @@ const CollectionScene = observer(function _CollectionScene() {
|
||||
collectionId: collection.id,
|
||||
parentDocumentId: null,
|
||||
sort: collection.sort.field,
|
||||
direction: collection.sort.direction,
|
||||
direction: collection.sort.direction.toUpperCase() as "ASC" | "DESC",
|
||||
}}
|
||||
showParentDocuments
|
||||
/>
|
||||
@@ -372,7 +384,7 @@ const CollectionScene = observer(function _CollectionScene() {
|
||||
collectionId: collection.id,
|
||||
parentDocumentId: null,
|
||||
sort: collection.sort.field,
|
||||
direction: collection.sort.direction,
|
||||
direction: collection.sort.direction.toUpperCase() as "ASC" | "DESC",
|
||||
statusFilter: [StatusFilter.Archived],
|
||||
}}
|
||||
showParentDocuments
|
||||
|
||||
@@ -17,6 +17,7 @@ import Subheading from "~/components/Subheading";
|
||||
import useStores from "~/hooks/useStores";
|
||||
import NewDocumentMenu from "~/menus/NewDocumentMenu";
|
||||
import DateFilter from "./Search/components/DateFilter";
|
||||
import { PaginationParams } from "~/types";
|
||||
|
||||
function Drafts() {
|
||||
const { t } = useTranslation();
|
||||
@@ -43,7 +44,7 @@ function Drafts() {
|
||||
};
|
||||
|
||||
const isFiltered = collectionId || dateFilter;
|
||||
const options = {
|
||||
const options: PaginationParams = {
|
||||
dateFilter,
|
||||
collectionId,
|
||||
};
|
||||
|
||||
+2
-1
@@ -21,6 +21,7 @@ import { usePinnedDocuments } from "~/hooks/usePinnedDocuments";
|
||||
import usePolicy from "~/hooks/usePolicy";
|
||||
import useStores from "~/hooks/useStores";
|
||||
import NewDocumentMenu from "~/menus/NewDocumentMenu";
|
||||
import { PaginationParams } from "~/types";
|
||||
|
||||
function Home() {
|
||||
const { documents, ui } = useStores();
|
||||
@@ -79,7 +80,7 @@ function Home() {
|
||||
fetch={documents.fetchOwned}
|
||||
options={{
|
||||
userId,
|
||||
}}
|
||||
} as PaginationParams}
|
||||
empty={
|
||||
<Empty>{t("You haven’t created any documents yet")}</Empty>
|
||||
}
|
||||
|
||||
+6
-1
@@ -162,7 +162,12 @@ export type PaginationParams = {
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
sort?: string;
|
||||
direction?: "ASC" | "DESC";
|
||||
direction?: "ASC" | "DESC" | "asc" | "desc";
|
||||
collectionId?: string;
|
||||
userId?: string;
|
||||
parentDocumentId?: string | null;
|
||||
statusFilter?: any[];
|
||||
dateFilter?: any;
|
||||
};
|
||||
|
||||
export type SearchResult = {
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ type LogCategory =
|
||||
| "plugins"
|
||||
| "policies";
|
||||
|
||||
type Extra = Record<string, any>;
|
||||
type Extra = Record<string, unknown>;
|
||||
|
||||
class Logger {
|
||||
/**
|
||||
|
||||
@@ -14,14 +14,19 @@ export class OIDCStrategy extends Strategy {
|
||||
}
|
||||
}
|
||||
|
||||
authenticate(req: any, options: any) {
|
||||
options.originalQuery = req.query;
|
||||
super.authenticate(req, options);
|
||||
authenticate(
|
||||
req: { query?: Record<string, unknown> },
|
||||
options?: Record<string, unknown>
|
||||
) {
|
||||
if (options) {
|
||||
(options as any).originalQuery = req.query;
|
||||
}
|
||||
super.authenticate(req as any, options);
|
||||
}
|
||||
|
||||
authorizationParams(options: any) {
|
||||
authorizationParams(options?: Record<string, unknown>) {
|
||||
return {
|
||||
...(options.originalQuery || {}),
|
||||
...((options as any)?.originalQuery || {}),
|
||||
...(super.authorizationParams?.(options) || {}),
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user