chore: Upgrade flow (#1854)
* wip: upgrade flow * chore: More sealed props improvements * Final fixes
This commit is contained in:
parent
ce2b246e60
commit
32f0589190
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -1,6 +1,7 @@
|
||||
{
|
||||
"javascript.validate.enable": false,
|
||||
"javascript.format.enable": false,
|
||||
"typescript.validate.enable": false,
|
||||
"typescript.format.enable": false,
|
||||
"editor.formatOnSave": true,
|
||||
"typescript.format.enable": false
|
||||
}
|
@ -3,13 +3,17 @@ import { observable } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import * as React from "react";
|
||||
import styled from "styled-components";
|
||||
import User from "models/User";
|
||||
import placeholder from "./placeholder.png";
|
||||
|
||||
type Props = {
|
||||
type Props = {|
|
||||
src: string,
|
||||
size: number,
|
||||
icon?: React.Node,
|
||||
};
|
||||
user?: User,
|
||||
onClick?: () => void,
|
||||
className?: string,
|
||||
|};
|
||||
|
||||
@observer
|
||||
class Avatar extends React.Component<Props> {
|
||||
|
@ -108,8 +108,8 @@ export const Inner = styled.span`
|
||||
${(props) => props.hasIcon && !props.hasText && "padding: 0 4px;"};
|
||||
`;
|
||||
|
||||
export type Props = {
|
||||
type?: string,
|
||||
export type Props = {|
|
||||
type?: "button" | "submit",
|
||||
value?: string,
|
||||
icon?: React.Node,
|
||||
iconColor?: string,
|
||||
@ -118,9 +118,21 @@ export type Props = {
|
||||
innerRef?: React.ElementRef<any>,
|
||||
disclosure?: boolean,
|
||||
neutral?: boolean,
|
||||
danger?: boolean,
|
||||
primary?: boolean,
|
||||
disabled?: boolean,
|
||||
fullwidth?: boolean,
|
||||
autoFocus?: boolean,
|
||||
style?: Object,
|
||||
as?: React.ComponentType<any>,
|
||||
to?: string,
|
||||
onClick?: (event: SyntheticEvent<>) => mixed,
|
||||
borderOnHover?: boolean,
|
||||
};
|
||||
|
||||
"data-on"?: string,
|
||||
"data-event-category"?: string,
|
||||
"data-event-action"?: string,
|
||||
|};
|
||||
|
||||
function Button({
|
||||
type = "text",
|
||||
|
@ -4,15 +4,18 @@ import { VisuallyHidden } from "reakit/VisuallyHidden";
|
||||
import styled from "styled-components";
|
||||
import HelpText from "components/HelpText";
|
||||
|
||||
export type Props = {
|
||||
export type Props = {|
|
||||
checked?: boolean,
|
||||
label?: string,
|
||||
labelHidden?: boolean,
|
||||
className?: string,
|
||||
name?: string,
|
||||
disabled?: boolean,
|
||||
onChange: (event: SyntheticInputEvent<HTMLInputElement>) => mixed,
|
||||
note?: string,
|
||||
short?: boolean,
|
||||
small?: boolean,
|
||||
};
|
||||
|};
|
||||
|
||||
const LabelText = styled.span`
|
||||
font-weight: 500;
|
||||
|
@ -4,13 +4,16 @@ import * as React from "react";
|
||||
import { MenuItem as BaseMenuItem } from "reakit/Menu";
|
||||
import styled from "styled-components";
|
||||
|
||||
type Props = {
|
||||
type Props = {|
|
||||
onClick?: (SyntheticEvent<>) => void | Promise<void>,
|
||||
children?: React.Node,
|
||||
selected?: boolean,
|
||||
disabled?: boolean,
|
||||
to?: string,
|
||||
href?: string,
|
||||
target?: "_blank",
|
||||
as?: string | React.ComponentType<*>,
|
||||
};
|
||||
|};
|
||||
|
||||
const MenuItem = ({
|
||||
onClick,
|
||||
|
@ -4,10 +4,15 @@ import * as React from "react";
|
||||
import Document from "models/Document";
|
||||
import DocumentListItem from "components/DocumentListItem";
|
||||
|
||||
type Props = {
|
||||
type Props = {|
|
||||
documents: Document[],
|
||||
limit?: number,
|
||||
};
|
||||
showCollection?: boolean,
|
||||
showPublished?: boolean,
|
||||
showPin?: boolean,
|
||||
showDraft?: boolean,
|
||||
showTemplate?: boolean,
|
||||
|};
|
||||
|
||||
export default function DocumentList({ limit, documents, ...rest }: Props) {
|
||||
const items = limit ? documents.splice(0, limit) : documents;
|
||||
|
@ -23,14 +23,14 @@ const Modified = styled.span`
|
||||
font-weight: ${(props) => (props.highlight ? "600" : "400")};
|
||||
`;
|
||||
|
||||
type Props = {
|
||||
type Props = {|
|
||||
showCollection?: boolean,
|
||||
showPublished?: boolean,
|
||||
showLastViewed?: boolean,
|
||||
document: Document,
|
||||
children: React.Node,
|
||||
to?: string,
|
||||
};
|
||||
|};
|
||||
|
||||
function DocumentMeta({
|
||||
showPublished,
|
||||
|
@ -16,14 +16,31 @@ const RichMarkdownEditor = React.lazy(() => import("rich-markdown-editor"));
|
||||
|
||||
const EMPTY_ARRAY = [];
|
||||
|
||||
type Props = {
|
||||
export type Props = {|
|
||||
id?: string,
|
||||
value?: string,
|
||||
defaultValue?: string,
|
||||
readOnly?: boolean,
|
||||
grow?: boolean,
|
||||
disableEmbeds?: boolean,
|
||||
ui?: UiStore,
|
||||
};
|
||||
autoFocus?: boolean,
|
||||
template?: boolean,
|
||||
placeholder?: string,
|
||||
scrollTo?: string,
|
||||
readOnlyWriteCheckboxes?: boolean,
|
||||
onBlur?: (event: SyntheticEvent<>) => any,
|
||||
onFocus?: (event: SyntheticEvent<>) => any,
|
||||
onPublish?: (event: SyntheticEvent<>) => any,
|
||||
onSave?: ({ done?: boolean, autosave?: boolean, publish?: boolean }) => any,
|
||||
onCancel?: () => any,
|
||||
onChange?: (getValue: () => string) => any,
|
||||
onSearchLink?: (title: string) => any,
|
||||
onHoverLink?: (event: MouseEvent) => any,
|
||||
onCreateLink?: (title: string) => Promise<string>,
|
||||
onImageUploadStart?: () => any,
|
||||
onImageUploadStop?: () => any,
|
||||
|};
|
||||
|
||||
type PropsWithRef = Props & {
|
||||
forwardedRef: React.Ref<any>,
|
||||
|
@ -2,10 +2,13 @@
|
||||
import * as React from "react";
|
||||
import { cdnPath } from "utils/urls";
|
||||
|
||||
type Props = {
|
||||
type Props = {|
|
||||
alt: string,
|
||||
src: string,
|
||||
};
|
||||
title?: string,
|
||||
width?: number,
|
||||
height?: number,
|
||||
|};
|
||||
|
||||
export default function Image({ src, alt, ...rest }: Props) {
|
||||
return <img src={cdnPath(src)} alt={alt} {...rest} />;
|
||||
|
@ -75,8 +75,8 @@ export const LabelText = styled.div`
|
||||
display: inline-block;
|
||||
`;
|
||||
|
||||
export type Props = {
|
||||
type?: string,
|
||||
export type Props = {|
|
||||
type?: "text" | "email" | "checkbox" | "search",
|
||||
value?: string,
|
||||
label?: string,
|
||||
className?: string,
|
||||
@ -85,9 +85,18 @@ export type Props = {
|
||||
short?: boolean,
|
||||
margin?: string | number,
|
||||
icon?: React.Node,
|
||||
name?: string,
|
||||
minLength?: number,
|
||||
maxLength?: number,
|
||||
autoFocus?: boolean,
|
||||
autoComplete?: boolean | string,
|
||||
readOnly?: boolean,
|
||||
required?: boolean,
|
||||
placeholder?: string,
|
||||
onChange?: (ev: SyntheticInputEvent<HTMLInputElement>) => mixed,
|
||||
onFocus?: (ev: SyntheticEvent<>) => void,
|
||||
onBlur?: (ev: SyntheticEvent<>) => void,
|
||||
};
|
||||
|};
|
||||
|
||||
@observer
|
||||
class Input extends React.Component<Props> {
|
||||
|
@ -8,13 +8,13 @@ import Editor from "components/Editor";
|
||||
import HelpText from "components/HelpText";
|
||||
import { LabelText, Outline } from "components/Input";
|
||||
|
||||
type Props = {
|
||||
type Props = {|
|
||||
label: string,
|
||||
minHeight?: number,
|
||||
maxHeight?: number,
|
||||
readOnly?: boolean,
|
||||
ui: UiStore,
|
||||
};
|
||||
|};
|
||||
|
||||
@observer
|
||||
class InputRich extends React.Component<Props> {
|
||||
|
@ -36,6 +36,8 @@ export type Props = {
|
||||
className?: string,
|
||||
labelHidden?: boolean,
|
||||
options: Option[],
|
||||
onBlur?: () => void,
|
||||
onFocus?: () => void,
|
||||
};
|
||||
|
||||
@observer
|
||||
|
@ -4,10 +4,10 @@ import * as React from "react";
|
||||
import styled from "styled-components";
|
||||
import Flex from "components/Flex";
|
||||
|
||||
type Props = {
|
||||
type Props = {|
|
||||
label: React.Node | string,
|
||||
children: React.Node,
|
||||
};
|
||||
|};
|
||||
|
||||
const Labeled = ({ label, children, ...props }: Props) => (
|
||||
<Flex column {...props}>
|
||||
|
@ -5,10 +5,10 @@ import { randomInteger } from "shared/random";
|
||||
import { pulsate } from "shared/styles/animations";
|
||||
import Flex from "components/Flex";
|
||||
|
||||
type Props = {
|
||||
type Props = {|
|
||||
header?: boolean,
|
||||
height?: number,
|
||||
};
|
||||
|};
|
||||
|
||||
class Mask extends React.Component<Props> {
|
||||
width: number;
|
||||
@ -23,7 +23,7 @@ class Mask extends React.Component<Props> {
|
||||
}
|
||||
|
||||
render() {
|
||||
return <Redacted width={this.width} {...this.props} />;
|
||||
return <Redacted width={this.width} />;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,12 +13,12 @@ import Scrollable from "components/Scrollable";
|
||||
|
||||
ReactModal.setAppElement("#root");
|
||||
|
||||
type Props = {
|
||||
type Props = {|
|
||||
children?: React.Node,
|
||||
isOpen: boolean,
|
||||
title?: string,
|
||||
onRequestClose: () => void,
|
||||
};
|
||||
|};
|
||||
|
||||
const GlobalStyles = createGlobalStyle`
|
||||
.ReactModal__Overlay {
|
||||
|
@ -5,13 +5,18 @@ import Document from "models/Document";
|
||||
import DocumentListItem from "components/DocumentListItem";
|
||||
import PaginatedList from "components/PaginatedList";
|
||||
|
||||
type Props = {
|
||||
type Props = {|
|
||||
documents: Document[],
|
||||
fetch: (options: ?Object) => Promise<void>,
|
||||
options?: Object,
|
||||
heading?: React.Node,
|
||||
empty?: React.Node,
|
||||
};
|
||||
showCollection?: boolean,
|
||||
showPublished?: boolean,
|
||||
showPin?: boolean,
|
||||
showDraft?: boolean,
|
||||
showTemplate?: boolean,
|
||||
|};
|
||||
|
||||
@observer
|
||||
class PaginatedDocumentList extends React.Component<Props> {
|
||||
|
@ -5,12 +5,13 @@ import styled from "styled-components";
|
||||
import Flex from "components/Flex";
|
||||
import TeamLogo from "components/TeamLogo";
|
||||
|
||||
type Props = {
|
||||
type Props = {|
|
||||
teamName: string,
|
||||
subheading: React.Node,
|
||||
showDisclosure?: boolean,
|
||||
onClick: (event: SyntheticEvent<>) => void,
|
||||
logoUrl: string,
|
||||
};
|
||||
|};
|
||||
|
||||
const HeaderBlock = React.forwardRef<Props, any>(
|
||||
({ showDisclosure, teamName, subheading, logoUrl, ...rest }: Props, ref) => (
|
||||
|
@ -3,12 +3,15 @@ import * as React from "react";
|
||||
import styled from "styled-components";
|
||||
import { LabelText } from "components/Input";
|
||||
|
||||
type Props = {
|
||||
type Props = {|
|
||||
width?: number,
|
||||
height?: number,
|
||||
label?: string,
|
||||
checked?: boolean,
|
||||
disabled?: boolean,
|
||||
onChange: (event: SyntheticInputEvent<HTMLInputElement>) => mixed,
|
||||
id?: string,
|
||||
};
|
||||
|};
|
||||
|
||||
function Switch({ width = 38, height = 20, label, ...props }: Props) {
|
||||
const component = (
|
||||
|
@ -3,14 +3,14 @@ import Tippy from "@tippy.js/react";
|
||||
import * as React from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
type Props = {
|
||||
type Props = {|
|
||||
tooltip: React.Node,
|
||||
shortcut?: React.Node,
|
||||
placement?: "top" | "bottom" | "left" | "right",
|
||||
children: React.Node,
|
||||
delay?: number,
|
||||
className?: string,
|
||||
};
|
||||
|};
|
||||
|
||||
class Tooltip extends React.Component<Props> {
|
||||
render() {
|
||||
|
@ -27,7 +27,6 @@ function NewDocumentMenu() {
|
||||
as={Link}
|
||||
to={newDocumentUrl(collections.orderedData[0].id)}
|
||||
icon={<PlusIcon />}
|
||||
small
|
||||
>
|
||||
{t("New doc")}
|
||||
</Button>
|
||||
|
@ -11,7 +11,7 @@ export default class BaseModel {
|
||||
this.store = store;
|
||||
}
|
||||
|
||||
save = async (params: ?Object) => {
|
||||
save = async (params: Object = {}) => {
|
||||
this.isSaving = true;
|
||||
|
||||
try {
|
||||
|
@ -142,7 +142,7 @@ export default class Document extends BaseModel {
|
||||
};
|
||||
|
||||
@action
|
||||
updateFromJson = (data) => {
|
||||
updateFromJson = (data: Object) => {
|
||||
set(this, data);
|
||||
};
|
||||
|
||||
@ -150,7 +150,7 @@ export default class Document extends BaseModel {
|
||||
return this.store.archive(this);
|
||||
};
|
||||
|
||||
restore = (options) => {
|
||||
restore = (options: { revisionId?: string, collectionId?: string }) => {
|
||||
return this.store.restore(this, options);
|
||||
};
|
||||
|
||||
@ -233,7 +233,7 @@ export default class Document extends BaseModel {
|
||||
};
|
||||
|
||||
@action
|
||||
save = async (options: SaveOptions) => {
|
||||
save = async (options: SaveOptions = {}) => {
|
||||
if (this.isSaving) return this;
|
||||
|
||||
const isCreating = !this.id;
|
||||
@ -246,7 +246,9 @@ export default class Document extends BaseModel {
|
||||
collectionId: this.collectionId,
|
||||
title: this.title,
|
||||
text: this.text,
|
||||
...options,
|
||||
publish: options.publish,
|
||||
done: options.done,
|
||||
autosave: options.autosave,
|
||||
});
|
||||
}
|
||||
|
||||
@ -257,7 +259,9 @@ export default class Document extends BaseModel {
|
||||
text: this.text,
|
||||
templateId: this.templateId,
|
||||
lastRevision: options.lastRevision,
|
||||
...options,
|
||||
publish: options.publish,
|
||||
done: options.done,
|
||||
autosave: options.autosave,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -61,7 +61,7 @@ type Props = {
|
||||
document: Document,
|
||||
revision: Revision,
|
||||
readOnly: boolean,
|
||||
onCreateLink: (title: string) => string,
|
||||
onCreateLink: (title: string) => Promise<string>,
|
||||
onSearchLink: (term: string) => any,
|
||||
theme: Theme,
|
||||
auth: AuthStore,
|
||||
|
@ -9,24 +9,24 @@ import parseTitle from "shared/utils/parseTitle";
|
||||
import Document from "models/Document";
|
||||
import ClickablePadding from "components/ClickablePadding";
|
||||
import DocumentMetaWithViews from "components/DocumentMetaWithViews";
|
||||
import Editor from "components/Editor";
|
||||
import Editor, { type Props as EditorProps } from "components/Editor";
|
||||
import Flex from "components/Flex";
|
||||
import HoverPreview from "components/HoverPreview";
|
||||
import Star, { AnimatedStar } from "components/Star";
|
||||
import { isModKey } from "utils/keyboard";
|
||||
import { documentHistoryUrl } from "utils/routeHelpers";
|
||||
|
||||
type Props = {
|
||||
type Props = {|
|
||||
...EditorProps,
|
||||
onChangeTitle: (event: SyntheticInputEvent<>) => void,
|
||||
title: string,
|
||||
defaultValue: string,
|
||||
document: Document,
|
||||
isDraft: boolean,
|
||||
isShare: boolean,
|
||||
readOnly?: boolean,
|
||||
onSave: ({ publish?: boolean, done?: boolean, autosave?: boolean }) => mixed,
|
||||
grow?: boolean,
|
||||
onSave: ({ done?: boolean, autosave?: boolean, publish?: boolean }) => any,
|
||||
innerRef: { current: any },
|
||||
};
|
||||
|};
|
||||
|
||||
@observer
|
||||
class DocumentEditor extends React.Component<Props> {
|
||||
@ -98,6 +98,7 @@ class DocumentEditor extends React.Component<Props> {
|
||||
isShare,
|
||||
readOnly,
|
||||
innerRef,
|
||||
...rest
|
||||
} = this.props;
|
||||
|
||||
const { emoji } = parseTitle(title);
|
||||
@ -135,12 +136,12 @@ class DocumentEditor extends React.Component<Props> {
|
||||
/>
|
||||
<Editor
|
||||
ref={innerRef}
|
||||
autoFocus={title && !this.props.defaultValue}
|
||||
autoFocus={!!title && !this.props.defaultValue}
|
||||
placeholder="…the rest is up to you"
|
||||
onHoverLink={this.handleLinkActive}
|
||||
scrollTo={window.location.hash}
|
||||
grow
|
||||
{...this.props}
|
||||
{...rest}
|
||||
/>
|
||||
{!readOnly && <ClickablePadding onClick={this.focusAtEnd} grow />}
|
||||
{this.activeLinkEvent && !isShare && readOnly && (
|
||||
|
@ -240,7 +240,6 @@ class Header extends React.Component<Props> {
|
||||
<Button
|
||||
onClick={this.handleSave}
|
||||
disabled={savingIsDisabled}
|
||||
isSaving={isSaving}
|
||||
neutral={isDraft}
|
||||
>
|
||||
{isDraft ? t("Save Draft") : t("Done Editing")}
|
||||
@ -311,7 +310,6 @@ class Header extends React.Component<Props> {
|
||||
>
|
||||
<Button
|
||||
onClick={this.handlePublish}
|
||||
title={t("Publish document")}
|
||||
disabled={publishingIsDisabled}
|
||||
>
|
||||
{isPublishing ? `${t("Publishing")}…` : t("Publish")}
|
||||
|
@ -7,11 +7,11 @@ import Document from "models/Document";
|
||||
import DocumentMeta from "components/DocumentMeta";
|
||||
import type { NavigationNode } from "types";
|
||||
|
||||
type Props = {
|
||||
type Props = {|
|
||||
document: Document | NavigationNode,
|
||||
anchor?: string,
|
||||
showCollection?: boolean,
|
||||
};
|
||||
|};
|
||||
|
||||
const DocumentLink = styled(Link)`
|
||||
display: block;
|
||||
|
@ -43,7 +43,7 @@ class DocumentShare extends React.Component<Props> {
|
||||
this.isSaving = true;
|
||||
|
||||
try {
|
||||
await share.save({ published: event.target.checked });
|
||||
await share.save({ published: event.currentTarget.checked });
|
||||
} catch (err) {
|
||||
this.props.ui.showToast(err.message, { type: "error" });
|
||||
} finally {
|
||||
|
@ -87,7 +87,6 @@ class AddPeopleToGroup extends React.Component<Props> {
|
||||
</ButtonLink>
|
||||
.
|
||||
</HelpText>
|
||||
|
||||
<Input
|
||||
type="search"
|
||||
placeholder={`${t("Search by name")}…`}
|
||||
|
@ -17,10 +17,9 @@ function Zapier() {
|
||||
</HelpText>
|
||||
<p>
|
||||
<Button
|
||||
as="a"
|
||||
href="https://zapier.com/apps/outline"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
onClick={() =>
|
||||
(window.location.href = "https://zapier.com/apps/outline")
|
||||
}
|
||||
>
|
||||
Open Zapier →
|
||||
</Button>
|
||||
|
@ -34,10 +34,10 @@ class UserDelete extends React.Component<Props> {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { auth, ...rest } = this.props;
|
||||
const { onRequestClose } = this.props;
|
||||
|
||||
return (
|
||||
<Modal isOpen title="Delete Account" {...rest}>
|
||||
<Modal isOpen title="Delete Account" onRequestClose={onRequestClose}>
|
||||
<Flex column>
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<HelpText>
|
||||
|
@ -19,11 +19,11 @@ import Subheading from "components/Subheading";
|
||||
import useCurrentUser from "hooks/useCurrentUser";
|
||||
import useStores from "hooks/useStores";
|
||||
|
||||
type Props = {
|
||||
type Props = {|
|
||||
user: User,
|
||||
history: RouterHistory,
|
||||
onRequestClose: () => void,
|
||||
};
|
||||
|};
|
||||
|
||||
function UserProfile(props: Props) {
|
||||
const { t } = useTranslation();
|
||||
|
@ -4,6 +4,7 @@ import invariant from "invariant";
|
||||
import { observable, action, computed, autorun, runInAction } from "mobx";
|
||||
import { getCookie, setCookie, removeCookie } from "tiny-cookie";
|
||||
import RootStore from "stores/RootStore";
|
||||
import Policy from "models/Policy";
|
||||
import Team from "models/Team";
|
||||
import User from "models/User";
|
||||
import env from "env";
|
||||
@ -13,17 +14,17 @@ import { getCookieDomain } from "utils/domains";
|
||||
const AUTH_STORE = "AUTH_STORE";
|
||||
const NO_REDIRECT_PATHS = ["/", "/create", "/home"];
|
||||
|
||||
type Service = {
|
||||
type Service = {|
|
||||
id: string,
|
||||
name: string,
|
||||
authUrl: string,
|
||||
};
|
||||
|};
|
||||
|
||||
type Config = {
|
||||
type Config = {|
|
||||
name?: string,
|
||||
hostname?: string,
|
||||
services: Service[],
|
||||
};
|
||||
|};
|
||||
|
||||
export default class AuthStore {
|
||||
@observable user: ?User;
|
||||
@ -88,7 +89,7 @@ export default class AuthStore {
|
||||
}
|
||||
}
|
||||
|
||||
addPolicies = (policies) => {
|
||||
addPolicies = (policies: Policy[]) => {
|
||||
if (policies) {
|
||||
policies.forEach((policy) => this.rootStore.policies.add(policy));
|
||||
}
|
||||
|
@ -174,7 +174,13 @@ export default class DocumentsStore extends BaseStore<Document> {
|
||||
return this.drafts().length;
|
||||
}
|
||||
|
||||
drafts = (options = {}): Document[] => {
|
||||
drafts = (
|
||||
options: {
|
||||
...PaginationParams,
|
||||
dateFilter?: "day" | "week" | "month" | "year",
|
||||
collectionId?: string,
|
||||
} = {}
|
||||
): Document[] => {
|
||||
let drafts = filter(
|
||||
orderBy(this.all, "updatedAt", "desc"),
|
||||
(doc) => !doc.publishedAt
|
||||
@ -185,7 +191,7 @@ export default class DocumentsStore extends BaseStore<Document> {
|
||||
drafts,
|
||||
(draft) =>
|
||||
new Date(draft.updatedAt) >=
|
||||
subtractDate(new Date(), options.dateFilter)
|
||||
subtractDate(new Date(), options.dateFilter || "year")
|
||||
);
|
||||
}
|
||||
|
||||
@ -245,7 +251,7 @@ export default class DocumentsStore extends BaseStore<Document> {
|
||||
@action
|
||||
fetchNamedPage = async (
|
||||
request: string = "list",
|
||||
options: ?PaginationParams
|
||||
options: ?Object
|
||||
): Promise<?(Document[])> => {
|
||||
this.isFetching = true;
|
||||
|
||||
@ -338,10 +344,9 @@ export default class DocumentsStore extends BaseStore<Document> {
|
||||
};
|
||||
|
||||
@action
|
||||
searchTitles = async (query: string, options: PaginationParams = {}) => {
|
||||
searchTitles = async (query: string) => {
|
||||
const res = await client.get("/documents.search_titles", {
|
||||
query,
|
||||
...options,
|
||||
});
|
||||
invariant(res && res.data, "Search response should be available");
|
||||
|
||||
@ -354,7 +359,15 @@ export default class DocumentsStore extends BaseStore<Document> {
|
||||
@action
|
||||
search = async (
|
||||
query: string,
|
||||
options: PaginationParams = {}
|
||||
options: {
|
||||
offset?: number,
|
||||
limit?: number,
|
||||
dateFilter?: "day" | "week" | "month" | "year",
|
||||
includeArchived?: boolean,
|
||||
includeDrafts?: boolean,
|
||||
collectionId?: string,
|
||||
userId?: string,
|
||||
}
|
||||
): Promise<SearchResult[]> => {
|
||||
const compactedOptions = omitBy(options, (o) => !o);
|
||||
const res = await client.get("/documents.search", {
|
||||
@ -601,10 +614,14 @@ export default class DocumentsStore extends BaseStore<Document> {
|
||||
};
|
||||
|
||||
@action
|
||||
restore = async (document: Document, options = {}) => {
|
||||
restore = async (
|
||||
document: Document,
|
||||
options: { revisionId?: string, collectionId?: string } = {}
|
||||
) => {
|
||||
const res = await client.post("/documents.restore", {
|
||||
id: document.id,
|
||||
...options,
|
||||
revisionId: options.revisionId,
|
||||
collectionId: options.collectionId,
|
||||
});
|
||||
runInAction("Document#restore", () => {
|
||||
invariant(res && res.data, "Data should be available");
|
||||
|
@ -182,13 +182,15 @@ class UiStore {
|
||||
@action
|
||||
showToast = (
|
||||
message: string,
|
||||
options?: {
|
||||
type?: "warning" | "error" | "info" | "success",
|
||||
options: {
|
||||
type: "warning" | "error" | "info" | "success",
|
||||
timeout?: number,
|
||||
action?: {
|
||||
text: string,
|
||||
onClick: () => void,
|
||||
},
|
||||
} = {
|
||||
type: "info",
|
||||
}
|
||||
) => {
|
||||
if (!message) return;
|
||||
@ -204,7 +206,14 @@ class UiStore {
|
||||
|
||||
const id = v4();
|
||||
const createdAt = new Date().toISOString();
|
||||
this.toasts.set(id, { message, createdAt, id, ...options });
|
||||
this.toasts.set(id, {
|
||||
id,
|
||||
message,
|
||||
createdAt,
|
||||
type: options.type,
|
||||
timeout: options.timeout,
|
||||
action: options.action,
|
||||
});
|
||||
this.lastToastId = id;
|
||||
return id;
|
||||
};
|
||||
|
@ -11,7 +11,7 @@ export type LocationWithState = Location & {
|
||||
},
|
||||
};
|
||||
|
||||
export type Toast = {
|
||||
export type Toast = {|
|
||||
id: string,
|
||||
createdAt: string,
|
||||
message: string,
|
||||
@ -22,7 +22,7 @@ export type Toast = {
|
||||
text: string,
|
||||
onClick: () => void,
|
||||
},
|
||||
};
|
||||
|};
|
||||
|
||||
export type FetchOptions = {
|
||||
prefetch?: boolean,
|
||||
@ -31,12 +31,12 @@ export type FetchOptions = {
|
||||
force?: boolean,
|
||||
};
|
||||
|
||||
export type NavigationNode = {
|
||||
export type NavigationNode = {|
|
||||
id: string,
|
||||
title: string,
|
||||
url: string,
|
||||
children: NavigationNode[],
|
||||
};
|
||||
|};
|
||||
|
||||
// Pagination response in an API call
|
||||
export type Pagination = {
|
||||
@ -46,12 +46,12 @@ export type Pagination = {
|
||||
};
|
||||
|
||||
// Pagination request params
|
||||
export type PaginationParams = {
|
||||
export type PaginationParams = {|
|
||||
limit?: number,
|
||||
offset?: number,
|
||||
sort?: string,
|
||||
direction?: "ASC" | "DESC",
|
||||
};
|
||||
|};
|
||||
|
||||
export type SearchResult = {
|
||||
ranking: number,
|
||||
|
@ -110,6 +110,7 @@ export default function download(
|
||||
// $FlowIssue
|
||||
if (navigator.msSaveBlob) {
|
||||
// IE10+ : (has Blob, but not a[download] or URL)
|
||||
// $FlowIssue
|
||||
return navigator.msSaveBlob(blob, fn);
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,6 @@
|
||||
"start": "node ./build/server/index.js",
|
||||
"dev": "nodemon --exec \"yarn build:server && yarn build:i18n && node build/server/index.js\" -e js --ignore build/ --ignore app/",
|
||||
"lint": "eslint app server shared",
|
||||
"flow": "flow",
|
||||
"deploy": "git push heroku master",
|
||||
"heroku-postbuild": "yarn build && yarn sequelize:migrate",
|
||||
"sequelize:create-migration": "sequelize migration:create",
|
||||
@ -192,7 +191,7 @@
|
||||
"eslint-plugin-react": "^7.20.0",
|
||||
"eslint-plugin-react-hooks": "^4.1.0",
|
||||
"fetch-test-server": "^1.1.0",
|
||||
"flow-bin": "^0.104.0",
|
||||
"flow-bin": "^0.124.0",
|
||||
"html-webpack-plugin": "3.2.0",
|
||||
"i18next-parser": "^3.3.0",
|
||||
"jest-cli": "^26.0.0",
|
||||
|
@ -5181,10 +5181,10 @@ flatted@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138"
|
||||
integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==
|
||||
|
||||
flow-bin@^0.104.0:
|
||||
version "0.104.0"
|
||||
resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.104.0.tgz#ef5b3600dfd36abe191a87d19f66e481bad2e235"
|
||||
integrity sha512-EZXRRmf7m7ET5Lcnwm/I/T8G3d427Bq34vmO3qIlRcPIYloGuVoqRCwjaeezLRDntHkdciagAKbhJ+NTbDjnkw==
|
||||
flow-bin@^0.124.0:
|
||||
version "0.124.0"
|
||||
resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.124.0.tgz#24b2e55874e1e2041f9247f42473b3db2ef32758"
|
||||
integrity sha512-KEtDJ7CFUjcuhw6N52FTZshDd1krf1fxpp4APSIrwhVm+IrlcKJ+EMXpeXKM1kKNSZ347dYGh8wEvXQl4pHZEA==
|
||||
|
||||
flow-typed@^2.6.2:
|
||||
version "2.6.2"
|
||||
|
Reference in New Issue
Block a user