diff --git a/app/scenes/GroupDelete.js b/app/scenes/GroupDelete.js index 7ae579de3..0f1581c7e 100644 --- a/app/scenes/GroupDelete.js +++ b/app/scenes/GroupDelete.js @@ -1,59 +1,57 @@ // @flow -import { observable } from "mobx"; -import { inject, observer } from "mobx-react"; +import { observer } from "mobx-react"; import * as React from "react"; -import { withRouter, type RouterHistory } from "react-router-dom"; +import { useTranslation, Trans } from "react-i18next"; +import { useHistory } from "react-router-dom"; import { groupSettings } from "shared/utils/routeHelpers"; -import UiStore from "stores/UiStore"; import Group from "models/Group"; import Button from "components/Button"; import Flex from "components/Flex"; import HelpText from "components/HelpText"; +import useStores from "hooks/useStores"; -type Props = { - history: RouterHistory, +type Props = {| group: Group, - ui: UiStore, onSubmit: () => void, -}; +|}; -@observer -class GroupDelete extends React.Component { - @observable isDeleting: boolean; +function GroupDelete({ group, onSubmit }: Props) { + const { ui } = useStores(); + const { t } = useTranslation(); + const history = useHistory(); + const [isDeleting, setIsDeleting] = React.useState(); - handleSubmit = async (ev: SyntheticEvent<>) => { + const handleSubmit = async (ev: SyntheticEvent<>) => { ev.preventDefault(); - this.isDeleting = true; + setIsDeleting(true); try { - await this.props.group.delete(); - this.props.history.push(groupSettings()); - this.props.onSubmit(); + await group.delete(); + history.push(groupSettings()); + onSubmit(); } catch (err) { - this.props.ui.showToast(err.message, { type: "error" }); + ui.showToast(err.message, { type: "error" }); } finally { - this.isDeleting = false; + setIsDeleting(false); } }; - render() { - const { group } = this.props; - - return ( - -
- - Are you sure about that? Deleting the {group.name}{" "} - group will cause its members to lose access to collections and - documents that it is associated with. - - -
-
- ); - } + return ( + +
+ + }} + /> + + +
+
+ ); } -export default inject("ui")(withRouter(GroupDelete)); +export default observer(GroupDelete); diff --git a/app/scenes/GroupEdit.js b/app/scenes/GroupEdit.js index 5a28a4174..7f24ecc45 100644 --- a/app/scenes/GroupEdit.js +++ b/app/scenes/GroupEdit.js @@ -1,70 +1,71 @@ // @flow -import { observable } from "mobx"; -import { inject, observer } from "mobx-react"; +import { observer } from "mobx-react"; import * as React from "react"; -import { withRouter, type RouterHistory } from "react-router-dom"; -import UiStore from "stores/UiStore"; +import { useTranslation, Trans } from "react-i18next"; import Group from "models/Group"; import Button from "components/Button"; import Flex from "components/Flex"; import HelpText from "components/HelpText"; import Input from "components/Input"; +import useStores from "hooks/useStores"; type Props = { - history: RouterHistory, - ui: UiStore, group: Group, onSubmit: () => void, }; -@observer -class GroupEdit extends React.Component { - @observable name: string = this.props.group.name; - @observable isSaving: boolean; +function GroupEdit({ group, onSubmit }: Props) { + const { ui } = useStores(); + const { t } = useTranslation(); + const [name, setName] = React.useState(group.name); + const [isSaving, setIsSaving] = React.useState(); - handleSubmit = async (ev: SyntheticEvent<>) => { - ev.preventDefault(); - this.isSaving = true; + const handleSubmit = React.useCallback( + async (ev: SyntheticEvent<>) => { + ev.preventDefault(); + setIsSaving(true); - try { - await this.props.group.save({ name: this.name }); - this.props.onSubmit(); - } catch (err) { - this.props.ui.showToast(err.message, { type: "error" }); - } finally { - this.isSaving = false; - } - }; + try { + await group.save({ name: name }); + onSubmit(); + } catch (err) { + ui.showToast(err.message, { type: "error" }); + } finally { + setIsSaving(false); + } + }, + [group, onSubmit, ui, name] + ); - handleNameChange = (ev: SyntheticInputEvent<*>) => { - this.name = ev.target.value; - }; + const handleNameChange = React.useCallback((ev: SyntheticInputEvent<*>) => { + setName(ev.target.value); + }, []); - render() { - return ( -
- + return ( + + + You can edit the name of this group at any time, however doing so too often might confuse your team mates. - - - - + + + + + - -
- ); - } + + + ); } -export default inject("ui")(withRouter(GroupEdit)); +export default observer(GroupEdit); diff --git a/app/scenes/GroupMembers/GroupMembers.js b/app/scenes/GroupMembers/GroupMembers.js index 3938ef9f2..8dd378b18 100644 --- a/app/scenes/GroupMembers/GroupMembers.js +++ b/app/scenes/GroupMembers/GroupMembers.js @@ -1,14 +1,8 @@ // @flow -import { observable } from "mobx"; -import { inject, observer } from "mobx-react"; +import { observer } from "mobx-react"; import { PlusIcon } from "outline-icons"; import * as React from "react"; -import { withTranslation, type TFunction } from "react-i18next"; -import AuthStore from "stores/AuthStore"; -import GroupMembershipsStore from "stores/GroupMembershipsStore"; -import PoliciesStore from "stores/PoliciesStore"; -import UiStore from "stores/UiStore"; -import UsersStore from "stores/UsersStore"; +import { useTranslation, Trans } from "react-i18next"; import Group from "models/Group"; import User from "models/User"; import Button from "components/Button"; @@ -20,112 +14,99 @@ import PaginatedList from "components/PaginatedList"; import Subheading from "components/Subheading"; import AddPeopleToGroup from "./AddPeopleToGroup"; import GroupMemberListItem from "./components/GroupMemberListItem"; +import useStores from "hooks/useStores"; type Props = { - ui: UiStore, - auth: AuthStore, group: Group, - users: UsersStore, - policies: PoliciesStore, - groupMemberships: GroupMembershipsStore, - t: TFunction, }; -@observer -class GroupMembers extends React.Component { - @observable addModalOpen: boolean = false; +function GroupMembers({ group }: Props) { + const [addModalOpen, setAddModalOpen] = React.useState(); + const { users, groupMemberships, policies, ui } = useStores(); + const { t } = useTranslation(); + const can = policies.abilities(group.id); - handleAddModalOpen = () => { - this.addModalOpen = true; + const handleAddModal = (state) => { + setAddModalOpen(state); }; - handleAddModalClose = () => { - this.addModalOpen = false; - }; - - handleRemoveUser = async (user: User) => { - const { t } = this.props; - + const handleRemoveUser = async (user: User) => { try { - await this.props.groupMemberships.delete({ - groupId: this.props.group.id, + await groupMemberships.delete({ + groupId: group.id, userId: user.id, }); - this.props.ui.showToast( + ui.showToast( t(`{{userName}} was removed from the group`, { userName: user.name }), { type: "success" } ); } catch (err) { - this.props.ui.showToast(t("Could not remove user"), { type: "error" }); + ui.showToast(t("Could not remove user"), { type: "error" }); } }; - render() { - const { group, users, groupMemberships, policies, t, auth } = this.props; - const { user } = auth; - if (!user) return null; - - const can = policies.abilities(group.id); - - return ( - - {can.update ? ( - <> - - Add and remove team members in the {group.name}{" "} - group. Adding people to the group will give them access to any - collections this group has been added to. - - - - - - ) : ( + return ( + + {can.update ? ( + <> - Listing team members in the {group.name} group. + }} + /> - )} + + + + + ) : ( + + }} + /> + + )} - Members - {t("This group has no members.")}} - renderItem={(item) => ( - this.handleRemoveUser(item) : undefined - } - /> - )} - /> - {can.update && ( - - - + + Members + + {t("This group has no members.")}} + renderItem={(item) => ( + handleRemoveUser(item) : undefined} + /> )} - - ); - } + /> + {can.update && ( + handleAddModal(false)} + isOpen={addModalOpen} + > + handleAddModal(false)} + /> + + )} + + ); } -export default withTranslation()( - inject("auth", "users", "policies", "groupMemberships", "ui")(GroupMembers) -); +export default observer(GroupMembers); diff --git a/app/scenes/GroupNew.js b/app/scenes/GroupNew.js index f6131a989..6ed74813d 100644 --- a/app/scenes/GroupNew.js +++ b/app/scenes/GroupNew.js @@ -1,10 +1,7 @@ // @flow -import { observable } from "mobx"; -import { inject, observer } from "mobx-react"; +import { observer } from "mobx-react"; import * as React from "react"; -import { withRouter, type RouterHistory } from "react-router-dom"; -import GroupsStore from "stores/GroupsStore"; -import UiStore from "stores/UiStore"; +import { useTranslation, Trans } from "react-i18next"; import Group from "models/Group"; import GroupMembers from "scenes/GroupMembers"; import Button from "components/Button"; @@ -12,79 +9,80 @@ import Flex from "components/Flex"; import HelpText from "components/HelpText"; import Input from "components/Input"; import Modal from "components/Modal"; +import useStores from "hooks/useStores"; type Props = { - history: RouterHistory, - ui: UiStore, - groups: GroupsStore, onSubmit: () => void, }; -@observer -class GroupNew extends React.Component { - @observable name: string = ""; - @observable isSaving: boolean; - @observable group: Group; +function GroupNew({ onSubmit }: Props) { + const { ui, groups } = useStores(); + const { t } = useTranslation(); + const [name, setName] = React.useState(); + const [isSaving, setIsSaving] = React.useState(); + const [group, setGroup] = React.useState(); - handleSubmit = async (ev: SyntheticEvent<>) => { + const handleSubmit = async (ev: SyntheticEvent<>) => { ev.preventDefault(); - this.isSaving = true; + setIsSaving(true); const group = new Group( { - name: this.name, + name: name, }, - this.props.groups + groups ); try { - this.group = await group.save(); + setGroup(await group.save()); } catch (err) { - this.props.ui.showToast(err.message, { type: "error" }); + ui.showToast(err.message, { type: "error" }); } finally { - this.isSaving = false; + setIsSaving(false); } }; - handleNameChange = (ev: SyntheticInputEvent<*>) => { - this.name = ev.target.value; + const handleNameChange = (ev: SyntheticInputEvent<*>) => { + setName(ev.target.value); }; - render() { - return ( - <> -
- + return ( + <> + + + Groups are for organizing your team. They work best when centered around a function or a responsibility — Support or Engineering for example. - - - - - You’ll be able to add people to the group next. + + + + + + + You’ll be able to add people to the group next. + - -
- - - - - ); - } + + + + + + + ); } -export default inject("groups", "ui")(withRouter(GroupNew)); +export default observer(GroupNew); diff --git a/shared/i18n/locales/en_US/translation.json b/shared/i18n/locales/en_US/translation.json index d934b8aa7..45093506c 100644 --- a/shared/i18n/locales/en_US/translation.json +++ b/shared/i18n/locales/en_US/translation.json @@ -209,6 +209,7 @@ "Contents": "Contents", "Headings you add to the document will appear here": "Headings you add to the document will appear here", "Table of contents": "Table of contents", + "Contents": "Contents", "By {{ author }}": "By {{ author }}", "Are you sure you want to make {{ userName }} an admin? Admins can modify team and billing information.": "Are you sure you want to make {{ userName }} an admin? Admins can modify team and billing information.", "Are you sure you want to make {{ userName }} a member?": "Are you sure you want to make {{ userName }} a member?", @@ -328,11 +329,20 @@ "We were unable to load the document while offline.": "We were unable to load the document while offline.", "Your account has been suspended": "Your account has been suspended", "A team admin ({{ suspendedContactEmail }}) has suspended your account. To re-activate your account, please reach out to them directly.": "A team admin ({{ suspendedContactEmail }}) has suspended your account. To re-activate your account, please reach out to them directly.", + "Are you sure about that? Deleting the {{groupName}} group will cause its members to lose access to collections and documents that it is associated with.": "Are you sure about that? Deleting the {{groupName}} group will cause its members to lose access to collections and documents that it is associated with.", + "You can edit the name of this group at any time, however doing so too often might confuse your team mates.": "You can edit the name of this group at any time, however doing so too often might confuse your team mates.", "{{userName}} was added to the group": "{{userName}} was added to the group", "Add team members below to give them access to the group. Need to add someone who’s not yet on the team yet?": "Add team members below to give them access to the group. Need to add someone who’s not yet on the team yet?", "Invite them to {{teamName}}": "Invite them to {{teamName}}", "{{userName}} was removed from the group": "{{userName}} was removed from the group", + "Add and remove team members in the {{groupName}} group. Adding people to the group will give them access to any collections this group has been added to.": "Add and remove team members in the {{groupName}} group. Adding people to the group will give them access to any collections this group has been added to.", + "Listing team members in the {{groupName}} group.": "Listing team members in the {{groupName}} group.", "This group has no members.": "This group has no members.", + "Add people to {{groupName}}": "Add people to {{groupName}}", + "Groups are for organizing your team. They work best when centered around a function or a responsibility — Support or Engineering for example.": "Groups are for organizing your team. They work best when centered around a function or a responsibility — Support or Engineering for example.", + "You’ll be able to add people to the group next.": "You’ll be able to add people to the group next.", + "Continue": "Continue", + "Group members": "Group members", "Recently viewed": "Recently viewed", "Created by me": "Created by me", "Navigation": "Navigation",