mirror of
https://github.com/outline/outline.git
synced 2026-06-13 19:35:02 +03:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3187619328 |
@@ -125,8 +125,8 @@ function Collaborators(props: Props) {
|
||||
|
||||
return (
|
||||
<AvatarWithPresence
|
||||
key={collaborator.id}
|
||||
{...rest}
|
||||
key={collaborator.id}
|
||||
user={collaborator}
|
||||
isPresent={isPresent}
|
||||
isEditing={isEditing}
|
||||
|
||||
@@ -31,7 +31,7 @@ const HoverPreviewIssue = React.forwardRef(function _HoverPreviewIssue(
|
||||
const authorName = author.name;
|
||||
const urlObj = new URL(url);
|
||||
let service;
|
||||
|
||||
|
||||
if (urlObj.hostname === "github.com") {
|
||||
service = IntegrationService.GitHub;
|
||||
} else if (urlObj.hostname === "gitlab.com") {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as React from "react";
|
||||
import lazyWithRetry from "~/utils/lazyWithRetry";
|
||||
|
||||
export interface LazyComponent<T extends React.ComponentType<unknown>> {
|
||||
export interface LazyComponent<T extends React.ComponentType<any>> {
|
||||
Component: React.LazyExoticComponent<T>;
|
||||
preload: () => Promise<{ default: T }>;
|
||||
}
|
||||
@@ -34,7 +34,7 @@ interface LazyLoadOptions {
|
||||
* MyComponent.preload();
|
||||
* ```
|
||||
*/
|
||||
export function createLazyComponent<T extends React.ComponentType<unknown>>(
|
||||
export function createLazyComponent<T extends React.ComponentType<any>>(
|
||||
factory: () => Promise<{ default: T }>,
|
||||
options: LazyLoadOptions = {}
|
||||
): LazyComponent<T> {
|
||||
|
||||
@@ -14,7 +14,7 @@ describe("PaginatedList", () => {
|
||||
i18n,
|
||||
tReady: true,
|
||||
t: ((key: string) => key) as TFunction,
|
||||
} as unknown;
|
||||
} as any;
|
||||
|
||||
it("with no items renders nothing", () => {
|
||||
const result = render(
|
||||
|
||||
@@ -34,11 +34,11 @@ interface Props<T extends PaginatedItem>
|
||||
* @param options Pagination and other query options
|
||||
*/
|
||||
fetch?: (
|
||||
options: Record<string, unknown> | undefined
|
||||
options: Record<string, any> | undefined
|
||||
) => Promise<unknown[] | undefined> | undefined;
|
||||
|
||||
/** Additional options to pass to the fetch function */
|
||||
options?: Record<string, unknown>;
|
||||
options?: Record<string, any>;
|
||||
|
||||
/** Optional header content to display above the list */
|
||||
heading?: React.ReactNode;
|
||||
@@ -77,9 +77,7 @@ interface Props<T extends PaginatedItem>
|
||||
* Function to render section headings (typically date-based)
|
||||
* @param name The heading text or element to render
|
||||
*/
|
||||
renderHeading?: (
|
||||
name: React.ReactElement<unknown> | string
|
||||
) => React.ReactNode;
|
||||
renderHeading?: (name: React.ReactElement<any> | string) => React.ReactNode;
|
||||
|
||||
/**
|
||||
* Handler for escape key press
|
||||
@@ -208,7 +206,7 @@ const PaginatedList = <T extends PaginatedItem>({
|
||||
if (fetch) {
|
||||
void fetchResults();
|
||||
}
|
||||
}, [fetch, fetchResults]);
|
||||
}, [fetch]);
|
||||
|
||||
// Handle updates to fetch or options
|
||||
React.useEffect(() => {
|
||||
|
||||
@@ -9,7 +9,7 @@ function Toasts() {
|
||||
|
||||
return (
|
||||
<StyledToaster
|
||||
theme={ui.resolvedTheme as unknown}
|
||||
theme={ui.resolvedTheme as any}
|
||||
closeButton
|
||||
toastOptions={{
|
||||
duration: 5000,
|
||||
|
||||
@@ -70,7 +70,7 @@ const LinkEditor: React.FC<Props> = ({
|
||||
React.useCallback(async () => {
|
||||
const res = await client.post("/suggestions.mention", { query });
|
||||
res.data.documents.map(documents.add);
|
||||
}, [query, documents.add])
|
||||
}, [query])
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -79,22 +79,6 @@ const LinkEditor: React.FC<Props> = ({
|
||||
}
|
||||
}, [trimmedQuery, request]);
|
||||
|
||||
const save = React.useCallback(
|
||||
(href: string, title?: string) => {
|
||||
href = href.trim();
|
||||
|
||||
if (href.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
discardRef.current = true;
|
||||
href = sanitizeUrl(href) ?? "";
|
||||
|
||||
onSelectLink({ href, title, from, to });
|
||||
},
|
||||
[onSelectLink, from, to]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const handleGlobalKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.key === "k" && event.metaKey) {
|
||||
@@ -123,7 +107,20 @@ const LinkEditor: React.FC<Props> = ({
|
||||
|
||||
save(trimmedQuery, trimmedQuery);
|
||||
};
|
||||
}, [trimmedQuery, initialValue, handleRemoveLink, save]);
|
||||
}, [trimmedQuery, initialValue]);
|
||||
|
||||
const save = (href: string, title?: string) => {
|
||||
href = href.trim();
|
||||
|
||||
if (href.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
discardRef.current = true;
|
||||
href = sanitizeUrl(href) ?? "";
|
||||
|
||||
onSelectLink({ href, title, from, to });
|
||||
};
|
||||
|
||||
const moveSelectionToEnd = () => {
|
||||
const { state, dispatch } = view;
|
||||
@@ -198,7 +195,7 @@ const LinkEditor: React.FC<Props> = ({
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemoveLink = React.useCallback(() => {
|
||||
const handleRemoveLink = () => {
|
||||
discardRef.current = true;
|
||||
|
||||
const { state, dispatch } = view;
|
||||
@@ -206,12 +203,9 @@ const LinkEditor: React.FC<Props> = ({
|
||||
dispatch(state.tr.removeMark(from, to, mark));
|
||||
}
|
||||
|
||||
if (onRemoveLink) {
|
||||
onRemoveLink();
|
||||
}
|
||||
|
||||
onRemoveLink?.();
|
||||
view.focus();
|
||||
}, [view, mark, from, to, onRemoveLink]);
|
||||
};
|
||||
|
||||
const isInternal = isInternalUrl(query);
|
||||
const hasResults = !!results.length;
|
||||
|
||||
@@ -184,16 +184,7 @@ function MentionMenu({ search, isActive, ...rest }: Props) {
|
||||
setItems(items);
|
||||
setLoaded(true);
|
||||
}
|
||||
}, [
|
||||
t,
|
||||
actorId,
|
||||
loading,
|
||||
search,
|
||||
users,
|
||||
documents,
|
||||
maxResultsInSection,
|
||||
collections,
|
||||
]);
|
||||
}, [t, actorId, loading, search, users, documents, maxResultsInSection]);
|
||||
|
||||
const handleSelect = useCallback(
|
||||
async (item: MentionItem) => {
|
||||
|
||||
@@ -87,7 +87,7 @@ function useIsActive(state: EditorState) {
|
||||
|
||||
const slice = selection.content();
|
||||
const fragment = slice.content;
|
||||
const nodes = (fragment as unknown).content;
|
||||
const nodes = (fragment as any).content;
|
||||
|
||||
return some(nodes, (n) => n.content.size);
|
||||
}
|
||||
|
||||
+2
-2
@@ -125,7 +125,7 @@ export type Action = {
|
||||
* Perform the action – note this should generally not be called directly, use `performAction`
|
||||
* instead. Errors will be caught and displayed to the user as a toast message.
|
||||
*/
|
||||
perform?: (context: ActionContext) => unknown;
|
||||
perform?: (context: ActionContext) => any;
|
||||
to?: string | { url: string; target?: string };
|
||||
children?: ((context: ActionContext) => Action[]) | Action[];
|
||||
};
|
||||
@@ -154,7 +154,7 @@ export type ActionV2 = BaseActionV2 & {
|
||||
tooltip?:
|
||||
| ((context: ActionContext) => React.ReactChild | undefined)
|
||||
| React.ReactChild;
|
||||
perform: (context: ActionContext) => unknown;
|
||||
perform: (context: ActionContext) => any;
|
||||
};
|
||||
|
||||
export type InternalLinkActionV2 = BaseActionV2 & {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as React from "react";
|
||||
|
||||
type ComponentPromise<T extends React.ComponentType<unknown>> = Promise<{
|
||||
type ComponentPromise<T extends React.ComponentType<any>> = Promise<{
|
||||
default: T;
|
||||
}>;
|
||||
|
||||
@@ -12,7 +12,7 @@ type ComponentPromise<T extends React.ComponentType<unknown>> = Promise<{
|
||||
* @param interval The interval between retries in milliseconds, defaults to 1000.
|
||||
* @returns A lazy component.
|
||||
*/
|
||||
export default function lazyWithRetry<T extends React.ComponentType<unknown>>(
|
||||
export default function lazyWithRetry<T extends React.ComponentType<any>>(
|
||||
component: () => ComponentPromise<T>,
|
||||
retries?: number,
|
||||
interval?: number
|
||||
@@ -20,7 +20,7 @@ export default function lazyWithRetry<T extends React.ComponentType<unknown>>(
|
||||
return React.lazy(() => retry(component, retries, interval));
|
||||
}
|
||||
|
||||
function retry<T extends React.ComponentType<unknown>>(
|
||||
function retry<T extends React.ComponentType<any>>(
|
||||
fn: () => ComponentPromise<T>,
|
||||
retriesLeft = 3,
|
||||
interval = 1000
|
||||
|
||||
@@ -43,8 +43,7 @@ export const isURLMentionable = ({
|
||||
|
||||
return (
|
||||
hostname === "gitlab.com" &&
|
||||
settings.gitlab?.project.path_with_namespace ===
|
||||
pathParts.slice(1, -2).join("/") // ensure installed project path matches with the provided url.
|
||||
settings.gitlab?.project.path_with_namespace === pathParts.slice(1, -2).join("/") // ensure installed project path matches with the provided url.
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -48,3 +48,4 @@ export default function Icon({ size = 24, fill = "currentColor" }: Props) {
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -59,8 +59,8 @@ function GitLab() {
|
||||
<>
|
||||
<Text as="p">
|
||||
<Trans>
|
||||
Enable previews of GitLab issues and merge requests in documents
|
||||
by connecting a GitLab project to {appName}.
|
||||
Enable previews of GitLab issues and merge requests in documents by connecting a
|
||||
GitLab project to {appName}.
|
||||
</Trans>
|
||||
</Text>
|
||||
{integrations.gitlab.length ? (
|
||||
@@ -73,7 +73,8 @@ function GitLab() {
|
||||
</Heading>
|
||||
<List>
|
||||
{integrations.gitlab.map((integration) => {
|
||||
const gitlabProject = integration.settings?.gitlab?.project;
|
||||
const gitlabProject =
|
||||
integration.settings?.gitlab?.project;
|
||||
const integrationCreatedBy = integration.user
|
||||
? integration.user.name
|
||||
: undefined;
|
||||
@@ -137,3 +138,4 @@ function GitLab() {
|
||||
}
|
||||
|
||||
export default observer(GitLab);
|
||||
|
||||
|
||||
@@ -21,3 +21,4 @@ export function GitLabConnectButton(props: Props<HTMLButtonElement>) {
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,3 +14,4 @@ PluginManager.add([
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
|
||||
@@ -5,3 +5,4 @@
|
||||
"description": "Adds a GitLab integration for link unfurling and converting links to mentions.",
|
||||
"after": "linear"
|
||||
}
|
||||
|
||||
|
||||
@@ -97,3 +97,4 @@ router.get(
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
||||
|
||||
@@ -15,3 +15,4 @@ export const GitLabCallbackSchema = BaseSchema.extend({
|
||||
});
|
||||
|
||||
export type GitLabCallbackReq = z.infer<typeof GitLabCallbackSchema>;
|
||||
|
||||
|
||||
@@ -4,3 +4,4 @@ export default {
|
||||
GITLAB_CLIENT_ID: env.GITLAB_CLIENT_ID,
|
||||
GITLAB_CLIENT_SECRET: env.GITLAB_CLIENT_SECRET,
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as queryString from "query-string";
|
||||
import queryString from "query-string";
|
||||
import env from "@shared/env";
|
||||
import { integrationSettingsPath } from "@shared/utils/routeHelpers";
|
||||
|
||||
@@ -49,3 +49,4 @@ export class GitLabUtils {
|
||||
return `${this.authBaseUrl}?${queryString.stringify(params)}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -55,15 +55,7 @@ export const Notion = observer(() => {
|
||||
onClose: clearQueryParams,
|
||||
});
|
||||
}
|
||||
}, [
|
||||
t,
|
||||
dialogs,
|
||||
oauthSuccess,
|
||||
service,
|
||||
clearQueryParams,
|
||||
handleSubmit,
|
||||
integrationId,
|
||||
]);
|
||||
}, [t, dialogs, oauthSuccess, service, clearQueryParams]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!oauthError) {
|
||||
|
||||
@@ -52,15 +52,7 @@ export function ImportDialog({ integrationId, onSubmit }: Props) {
|
||||
toast.error(err.message);
|
||||
resetSubmitting();
|
||||
}
|
||||
}, [
|
||||
permission,
|
||||
onSubmit,
|
||||
integrationId,
|
||||
t,
|
||||
imports,
|
||||
resetSubmitting,
|
||||
setSubmitting,
|
||||
]);
|
||||
}, [permission, onSubmit]);
|
||||
|
||||
return (
|
||||
<Flex column gap={12}>
|
||||
|
||||
@@ -15,13 +15,12 @@ export class OIDCStrategy extends Strategy {
|
||||
}
|
||||
}
|
||||
|
||||
authenticate(req: Request, options?: any) {
|
||||
options = options || {};
|
||||
authenticate(req: Request, options: Record<string, unknown>) {
|
||||
options.originalQuery = req.query;
|
||||
super.authenticate(req, options);
|
||||
}
|
||||
|
||||
authorizationParams(options: any) {
|
||||
authorizationParams(options: Record<string, unknown>) {
|
||||
return {
|
||||
...options.originalQuery,
|
||||
...super.authorizationParams?.(options),
|
||||
|
||||
@@ -573,7 +573,7 @@ router.post(
|
||||
});
|
||||
|
||||
let document: Document | null;
|
||||
let serializedDocument: Record<string, unknown> | undefined;
|
||||
let serializedDocument: Record<string, any> | undefined;
|
||||
let isPublic = false;
|
||||
|
||||
if (shareId) {
|
||||
|
||||
@@ -227,22 +227,20 @@ describe("#groups.list", () => {
|
||||
},
|
||||
});
|
||||
const body = await res.json();
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
expect(body.data.groups.length).toEqual(2);
|
||||
expect(body.data.groups[0].id).toEqual(anotherGroup.id);
|
||||
expect(body.data.groups[1].id).toEqual(group.id);
|
||||
|
||||
expect(body.data.groupMemberships.length).toEqual(2);
|
||||
expect(body.data.groupMemberships[0].groupId).toEqual(group.id);
|
||||
expect(body.data.groupMemberships[1].groupId).toEqual(group.id);
|
||||
expect(
|
||||
body.data.groupMemberships
|
||||
.map((u: { user: { id: string }; groupId: string }) => u.user.id)
|
||||
.includes(user.id)
|
||||
body.data.groupMemberships.map((u: any) => u.user.id).includes(user.id)
|
||||
).toBe(true);
|
||||
expect(
|
||||
body.data.groupMemberships
|
||||
.map((u: { user: { id: string }; groupId: string }) => u.user.id)
|
||||
.map((u: any) => u.user.id)
|
||||
.includes(anotherUser.id)
|
||||
).toBe(true);
|
||||
expect(body.policies.length).toEqual(2);
|
||||
@@ -261,13 +259,11 @@ describe("#groups.list", () => {
|
||||
expect(anotherBody.data.groupMemberships[0].groupId).toEqual(group.id);
|
||||
expect(anotherBody.data.groupMemberships[1].groupId).toEqual(group.id);
|
||||
expect(
|
||||
body.data.groupMemberships
|
||||
.map((u: { user: { id: string }; groupId: string }) => u.user.id)
|
||||
.includes(user.id)
|
||||
body.data.groupMemberships.map((u: any) => u.user.id).includes(user.id)
|
||||
).toBe(true);
|
||||
expect(
|
||||
body.data.groupMemberships
|
||||
.map((u: { user: { id: string }; groupId: string }) => u.user.id)
|
||||
.map((u: any) => u.user.id)
|
||||
.includes(anotherUser.id)
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
@@ -14,5 +14,5 @@ export class MutexLock {
|
||||
};
|
||||
}
|
||||
|
||||
private static redlock: unknown;
|
||||
private static redlock: any;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import { BaseIconProps } from ".";
|
||||
|
||||
/** Renders an icon for a specific GitLab issue state */
|
||||
export function GitLabIssueStatusIcon(props: BaseIconProps) {
|
||||
// No theme needed for this component
|
||||
const { state } = props;
|
||||
const isOpen = state.name === "opened";
|
||||
const color = state.color || (isOpen ? "#1aaa55" : "#db3b21"); // Green for open, red for closed
|
||||
|
||||
@@ -10,10 +10,10 @@ export type CommandFactory = (attrs?: Record<string, Primitive>) => Command;
|
||||
export type WidgetProps = { rtl: boolean; readOnly: boolean | undefined };
|
||||
|
||||
export default class Extension {
|
||||
options: Record<string, unknown>;
|
||||
options: any;
|
||||
editor: Editor;
|
||||
|
||||
constructor(options: Record<string, unknown> = {}) {
|
||||
constructor(options: Record<string, any> = {}) {
|
||||
this.options = {
|
||||
...this.defaultOptions,
|
||||
...options,
|
||||
|
||||
+2
-9
@@ -141,9 +141,7 @@ export const ImportableIntegrationService = {
|
||||
|
||||
export type IssueTrackerIntegrationService = Extract<
|
||||
IntegrationService,
|
||||
| IntegrationService.GitHub
|
||||
| IntegrationService.Linear
|
||||
| IntegrationService.GitLab
|
||||
IntegrationService.GitHub | IntegrationService.Linear | IntegrationService.GitLab
|
||||
>;
|
||||
|
||||
export const IssueTrackerIntegrationService = {
|
||||
@@ -194,12 +192,7 @@ export type IntegrationSettings<T> = T extends IntegrationType.Embed
|
||||
workspace: { id: string; name: string; key: string; logoUrl?: string };
|
||||
};
|
||||
gitlab?: {
|
||||
project: {
|
||||
id: string;
|
||||
name: string;
|
||||
path_with_namespace: string;
|
||||
avatar_url?: string;
|
||||
};
|
||||
project: { id: string; name: string; path_with_namespace: string; avatar_url?: string };
|
||||
};
|
||||
}
|
||||
: T extends IntegrationType.Analytics
|
||||
|
||||
Reference in New Issue
Block a user