Web: Files: InfoPanel: added infinite-loader to members list
This commit is contained in:
parent
e28d1b8704
commit
5dab788680
@ -39,5 +39,9 @@
|
||||
"SubmenuHistory": "History",
|
||||
"SystemProperties": "System properties",
|
||||
"UsersInRoom": "Users in room",
|
||||
"Versions": "Versions"
|
||||
"Versions": "Versions",
|
||||
"Administration": "Administration",
|
||||
"Users": "Users",
|
||||
"ExpectUsers": "Expect users",
|
||||
"InfoBanner": "The list of invited users includes the owner and/or admins of this DocSpace with full access to all rooms. The owner and/or administrator cannot be assigned other access rights. Once added to the room, they will be notified of all changes."
|
||||
}
|
||||
|
@ -181,6 +181,13 @@ const StyledRow = styled.div`
|
||||
margin-right: 0;
|
||||
`}
|
||||
}
|
||||
|
||||
.combo-button-label {
|
||||
color: ${(props) => props.theme.text.disableColor};
|
||||
}
|
||||
.combo-buttons_expander-icon path {
|
||||
fill: ${(props) => props.theme.text.disableColor};
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledInviteInput = styled.div`
|
||||
|
@ -24,6 +24,9 @@ import InviteInput from "./sub-components/InviteInput";
|
||||
import ExternalLinks from "./sub-components/ExternalLinks";
|
||||
import Scrollbar from "@docspace/components/scrollbar";
|
||||
import { LinkType } from "../../../helpers/constants";
|
||||
|
||||
import InfoBar from "./sub-components/InfoBar";
|
||||
|
||||
const InvitePanel = ({
|
||||
folders,
|
||||
getFolderInfo,
|
||||
@ -57,9 +60,12 @@ const InvitePanel = ({
|
||||
const [externalLinksVisible, setExternalLinksVisible] = useState(false);
|
||||
const [scrollAllPanelContent, setScrollAllPanelContent] = useState(false);
|
||||
const [activeLink, setActiveLink] = useState({});
|
||||
const [infoBarIsVisible, setInfoBarIsVisible] = useState(true);
|
||||
const [addUsersPanelVisible, setAddUsersPanelVisible] = useState(false);
|
||||
const [isMobileView, setIsMobileView] = useState(isMobileOnly);
|
||||
|
||||
const onCloseBar = () => setInfoBarIsVisible(false);
|
||||
|
||||
const inputsRef = useRef();
|
||||
const invitePanelBodyRef = useRef();
|
||||
|
||||
@ -67,10 +73,6 @@ const InvitePanel = ({
|
||||
setExternalLinksVisible(visible);
|
||||
};
|
||||
|
||||
const onChangeActiveLink = (activeLink) => {
|
||||
setActiveLink(activeLink);
|
||||
};
|
||||
|
||||
const selectRoom = () => {
|
||||
const room = folders.find((folder) => folder.id === roomId);
|
||||
|
||||
@ -84,26 +86,26 @@ const InvitePanel = ({
|
||||
};
|
||||
|
||||
const getInfo = () => {
|
||||
getRoomSecurityInfo(roomId).then((users) => {
|
||||
let links = [];
|
||||
getRoomSecurityInfo(roomId).then((links) => {
|
||||
const link = links[0];
|
||||
if (link) {
|
||||
const { shareLink, id, title, expirationDate } = link.sharedTo;
|
||||
|
||||
users.map((user) => {
|
||||
const { shareLink, id, title, expirationDate, linkType } =
|
||||
user.sharedTo;
|
||||
|
||||
if (!!shareLink && linkType === LinkType.Invite) {
|
||||
links.push({
|
||||
const activeLink = {
|
||||
id,
|
||||
title,
|
||||
shareLink,
|
||||
expirationDate,
|
||||
access: user.access || defaultAccess,
|
||||
});
|
||||
}
|
||||
});
|
||||
access: link.access || defaultAccess,
|
||||
};
|
||||
|
||||
setShareLinks(links);
|
||||
setRoomUsers(users);
|
||||
onChangeExternalLinksVisible(!!links.length);
|
||||
|
||||
setShareLinks([activeLink]);
|
||||
setActiveLink(activeLink);
|
||||
}
|
||||
|
||||
// setRoomUsers(users); // TODO:
|
||||
});
|
||||
};
|
||||
|
||||
@ -241,6 +243,7 @@ const InvitePanel = ({
|
||||
|
||||
const roomType = selectedRoom ? selectedRoom.roomType : -1;
|
||||
const hasInvitedUsers = !!inviteItems.length;
|
||||
const hasAdmins = inviteItems.findIndex((u) => u.isAdmin || u.isOwner) > -1;
|
||||
|
||||
const bodyInvitePanel = useMemo(() => {
|
||||
return (
|
||||
@ -248,11 +251,12 @@ const InvitePanel = ({
|
||||
<ExternalLinks
|
||||
t={t}
|
||||
shareLinks={shareLinks}
|
||||
setShareLinks={setShareLinks}
|
||||
getInfo={getInfo}
|
||||
roomType={roomType}
|
||||
onChangeExternalLinksVisible={onChangeExternalLinksVisible}
|
||||
externalLinksVisible={externalLinksVisible}
|
||||
onChangeActiveLink={onChangeActiveLink}
|
||||
setActiveLink={setActiveLink}
|
||||
activeLink={activeLink}
|
||||
isMobileView={isMobileView}
|
||||
/>
|
||||
@ -267,6 +271,9 @@ const InvitePanel = ({
|
||||
setAddUsersPanelVisible={setAddUsersPanelVisible}
|
||||
isMobileView={isMobileView}
|
||||
/>
|
||||
{infoBarIsVisible && hasAdmins && (
|
||||
<InfoBar t={t} onClose={onCloseBar} />
|
||||
)}
|
||||
{hasInvitedUsers && (
|
||||
<ItemsList
|
||||
t={t}
|
||||
|
@ -21,6 +21,7 @@ const AccessSelector = ({
|
||||
className,
|
||||
standalone,
|
||||
isMobileView,
|
||||
noBorder = false,
|
||||
}) => {
|
||||
const [horizontalOrientation, setHorizontalOrientation] = useState(false);
|
||||
const width = containerRef?.current?.offsetWidth - 32;
|
||||
@ -64,7 +65,7 @@ const AccessSelector = ({
|
||||
selectedOption={selectedOption}
|
||||
onSelect={onSelectAccess}
|
||||
accessOptions={filteredAccesses ? filteredAccesses : accessOptions}
|
||||
noBorder={false}
|
||||
noBorder={noBorder}
|
||||
directionX="right"
|
||||
directionY="bottom"
|
||||
fixedDirection={true}
|
||||
@ -82,7 +83,7 @@ const AccessSelector = ({
|
||||
selectedOption={selectedOption}
|
||||
onSelect={onSelectAccess}
|
||||
accessOptions={filteredAccesses ? filteredAccesses : accessOptions}
|
||||
noBorder={false}
|
||||
noBorder={noBorder}
|
||||
directionX="right"
|
||||
directionY="top"
|
||||
fixedDirection={true}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import MediaDownloadReactSvgUrl from "PUBLIC_DIR/images/media.download.react.svg?url";
|
||||
import CopyReactSvgUrl from "PUBLIC_DIR/images/copy.react.svg?url";
|
||||
import React, { useState, useEffect, useRef, useCallback } from "react";
|
||||
import React, { useState, useRef, useCallback } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import copy from "copy-to-clipboard";
|
||||
|
||||
@ -29,12 +29,12 @@ const ExternalLinks = ({
|
||||
roomType,
|
||||
defaultAccess,
|
||||
shareLinks,
|
||||
setShareLinks,
|
||||
setInvitationLinks,
|
||||
isOwner,
|
||||
getInfo,
|
||||
onChangeExternalLinksVisible,
|
||||
externalLinksVisible,
|
||||
onChangeActiveLink,
|
||||
setActiveLink,
|
||||
activeLink,
|
||||
isMobileView,
|
||||
}) => {
|
||||
@ -42,44 +42,32 @@ const ExternalLinks = ({
|
||||
|
||||
const inputsRef = useRef();
|
||||
|
||||
useEffect(() => {
|
||||
if (shareLinks[0]?.expirationDate) toggleLinks(false);
|
||||
}, [shareLinks]);
|
||||
|
||||
const toggleLinks = (withCopy = true) => {
|
||||
let link = null;
|
||||
if (!shareLinks.length) return;
|
||||
|
||||
if (roomId === -1) {
|
||||
link = shareLinks.find((l) => l.access === +defaultAccess);
|
||||
|
||||
onChangeActiveLink(link);
|
||||
} else {
|
||||
link = shareLinks[0];
|
||||
|
||||
const toggleLinks = () => {
|
||||
!externalLinksVisible ? editLink() : disableLink();
|
||||
}
|
||||
|
||||
onChangeExternalLinksVisible(!externalLinksVisible);
|
||||
|
||||
if (!externalLinksVisible && withCopy) copyLink(link?.shareLink);
|
||||
};
|
||||
|
||||
const disableLink = () => {
|
||||
setInvitationLinks(roomId, shareLinks[0].id, "Invite", 0);
|
||||
setTimeout(() => getInfo(), 100);
|
||||
setInvitationLinks(roomId, "Invite", 0, shareLinks[0].id);
|
||||
setShareLinks([]);
|
||||
};
|
||||
|
||||
const editLink = () => {
|
||||
if (!shareLinks[0].expirationDate) {
|
||||
setInvitationLinks(
|
||||
roomId,
|
||||
shareLinks[0].id,
|
||||
"Invite",
|
||||
shareLinks[0].access
|
||||
);
|
||||
}
|
||||
onChangeActiveLink(shareLinks[0]);
|
||||
const editLink = async () => {
|
||||
const link = await setInvitationLinks(roomId, "Invite", 2);
|
||||
|
||||
const { shareLink, id, title, expirationDate } = link.sharedTo;
|
||||
|
||||
const activeLink = {
|
||||
id,
|
||||
title,
|
||||
shareLink,
|
||||
expirationDate,
|
||||
access: link.access || defaultAccess,
|
||||
};
|
||||
|
||||
copyLink(shareLink);
|
||||
setShareLinks([activeLink]);
|
||||
setActiveLink(activeLink);
|
||||
};
|
||||
|
||||
const onSelectAccess = (access) => {
|
||||
@ -87,12 +75,12 @@ const ExternalLinks = ({
|
||||
if (roomId === -1) {
|
||||
link = shareLinks.find((l) => l.access === access.access);
|
||||
|
||||
onChangeActiveLink(link);
|
||||
setActiveLink(link);
|
||||
} else {
|
||||
setInvitationLinks(roomId, shareLinks[0].id, "Invite", +access.access);
|
||||
setInvitationLinks(roomId, "Invite", +access.access, shareLinks[0].id);
|
||||
|
||||
link = shareLinks[0];
|
||||
onChangeActiveLink(shareLinks[0]);
|
||||
setActiveLink(shareLinks[0]);
|
||||
}
|
||||
|
||||
copyLink(link.shareLink);
|
||||
|
@ -0,0 +1,80 @@
|
||||
import React from "react";
|
||||
import { ReactSVG } from "react-svg";
|
||||
import styled from "styled-components";
|
||||
import InfoIcon from "PUBLIC_DIR/images/info.outline.react.svg?url";
|
||||
import CrossReactSvg from "PUBLIC_DIR/images/cross.react.svg?url";
|
||||
import IconButton from "@docspace/components/icon-button";
|
||||
import Text from "@docspace/components/text";
|
||||
|
||||
const StyledInfoBar = styled.div`
|
||||
display: flex;
|
||||
background-color: #f8f9f9;
|
||||
color: #333;
|
||||
font-size: 12px;
|
||||
padding: 12px 16px;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 10px;
|
||||
margin: -4px 16px 20px;
|
||||
.text-container {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.header-body {
|
||||
display: flex;
|
||||
height: fit-content;
|
||||
width: 100%;
|
||||
gap: 8px;
|
||||
font-weight: 600;
|
||||
.header-icon {
|
||||
svg {
|
||||
path {
|
||||
fill: #ed7309;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.body-container {
|
||||
color: #555f65;
|
||||
font-weight: 400;
|
||||
}
|
||||
.close-icon {
|
||||
margin: 3px 1px 0px 0px;
|
||||
path {
|
||||
fill: ${({ theme }) => theme.iconButton.color};
|
||||
}
|
||||
svg {
|
||||
weight: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const InfoBar = (props) => {
|
||||
const { t, iconName, onClose, ...rest } = props;
|
||||
|
||||
return (
|
||||
<StyledInfoBar {...rest}>
|
||||
<div className="text-container">
|
||||
<div className="header-body">
|
||||
<div className="header-icon">
|
||||
<ReactSVG src={InfoIcon} />
|
||||
</div>
|
||||
<Text fontSize="12px" fontWeight={600}>
|
||||
{t("Common:Info")}
|
||||
</Text>
|
||||
</div>
|
||||
<div className="body-container">{t("InfoPanel:InfoBanner")}</div>
|
||||
</div>
|
||||
|
||||
<IconButton
|
||||
className="close-icon"
|
||||
size={10}
|
||||
iconName={CrossReactSvg}
|
||||
onClick={onClose}
|
||||
/>
|
||||
</StyledInfoBar>
|
||||
);
|
||||
};
|
||||
|
||||
export default InfoBar;
|
@ -149,6 +149,7 @@ const Item = ({
|
||||
filteredAccesses={filteredAccesses}
|
||||
setIsOpenItemAccess={setIsOpenItemAccess}
|
||||
isMobileView={isMobileView}
|
||||
noBorder
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
@ -63,6 +63,25 @@ const StyledTitle = styled.div`
|
||||
align-items: center;
|
||||
height: 32px;
|
||||
|
||||
.info_title-icons {
|
||||
display: flex;
|
||||
margin-left: auto;
|
||||
gap: 14px;
|
||||
.icon {
|
||||
cursor: pointer;
|
||||
path,
|
||||
rect {
|
||||
fill: ${(props) => props.theme.infoPanel.members.iconColor};
|
||||
}
|
||||
&:hover {
|
||||
path,
|
||||
rect {
|
||||
fill: ${(props) => props.theme.infoPanel.members.iconHoverColor};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
&.icon {
|
||||
display: flex;
|
||||
|
@ -6,30 +6,18 @@ const StyledUserTypeHeader = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding-top: ${props => (props.isExpect ? "20px" : "8px")};
|
||||
padding-top: ${(props) => (props.isExpect ? "20px" : "16px")};
|
||||
padding-bottom: 12px;
|
||||
|
||||
.title {
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: ${props => props.theme.infoPanel.members.subtitleColor};
|
||||
color: ${(props) => props.theme.infoPanel.members.subtitleColor};
|
||||
}
|
||||
|
||||
.icon {
|
||||
cursor: pointer;
|
||||
|
||||
path,
|
||||
rect {
|
||||
fill: ${props => props.theme.infoPanel.members.iconColor};
|
||||
}
|
||||
|
||||
&:hover {
|
||||
path,
|
||||
rect {
|
||||
fill: ${props => props.theme.infoPanel.members.iconHoverColor};
|
||||
}
|
||||
}
|
||||
margin-right: 8px;
|
||||
}
|
||||
`;
|
||||
|
||||
@ -56,7 +44,7 @@ const StyledUser = styled.div`
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.isExpect && `color: ${props.theme.infoPanel.members.isExpectName}`};
|
||||
}
|
||||
|
||||
@ -64,8 +52,8 @@ const StyledUser = styled.div`
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
line-height: 16px;
|
||||
color: ${props => props.theme.infoPanel.members.meLabelColor};
|
||||
${props =>
|
||||
color: ${(props) => props.theme.infoPanel.members.meLabelColor};
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
margin-right: -8px;
|
||||
@ -76,7 +64,7 @@ const StyledUser = styled.div`
|
||||
}
|
||||
|
||||
.role-wrapper {
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
padding-right: 8px;
|
||||
@ -93,8 +81,10 @@ const StyledUser = styled.div`
|
||||
white-space: nowrap;
|
||||
|
||||
.disabled-role-combobox {
|
||||
color: ${props =>
|
||||
color: ${(props) =>
|
||||
props.theme.infoPanel.members.disabledRoleSelectorColor};
|
||||
|
||||
margin-right: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -102,14 +92,14 @@ const StyledUser = styled.div`
|
||||
cursor: pointer;
|
||||
svg {
|
||||
path {
|
||||
fill: ${props => props.theme.iconButton.color};
|
||||
fill: ${(props) => props.theme.iconButton.color};
|
||||
}
|
||||
}
|
||||
|
||||
:hover {
|
||||
svg {
|
||||
path {
|
||||
fill: ${props => props.theme.iconButton.hoverColor};
|
||||
fill: ${(props) => props.theme.iconButton.hoverColor};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,26 @@
|
||||
import React, { useRef } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import { ReactSVG } from "react-svg";
|
||||
|
||||
import { Text } from "@docspace/components";
|
||||
|
||||
import PersonPlusReactSvgUrl from "PUBLIC_DIR/images/person+.react.svg?url";
|
||||
import IconButton from "@docspace/components/icon-button";
|
||||
import Text from "@docspace/components/text";
|
||||
import ItemContextOptions from "./ItemContextOptions";
|
||||
|
||||
import { StyledTitle } from "../../styles/common";
|
||||
import RoomIcon from "@docspace/client/src/components/RoomIcon";
|
||||
|
||||
const FilesItemTitle = ({ t, selection, isSeveralItems }) => {
|
||||
import { RoomsType, ShareAccessRights } from "@docspace/common/constants";
|
||||
|
||||
const FilesItemTitle = ({
|
||||
t,
|
||||
selection,
|
||||
isSeveralItems,
|
||||
selectionParentRoom,
|
||||
setIsMobileHidden,
|
||||
isGracePeriod,
|
||||
setInvitePanelOptions,
|
||||
setInviteUsersWarningDialogVisible,
|
||||
isPublicRoomType,
|
||||
}) => {
|
||||
const itemTitleRef = useRef();
|
||||
|
||||
if (isSeveralItems) return <></>;
|
||||
@ -18,6 +28,27 @@ const FilesItemTitle = ({ t, selection, isSeveralItems }) => {
|
||||
const icon = selection.icon;
|
||||
const isLoadedRoomIcon = !!selection.logo?.medium;
|
||||
const showDefaultRoomIcon = !isLoadedRoomIcon && selection.isRoom;
|
||||
const security = selectionParentRoom ? selectionParentRoom.security : {};
|
||||
const canInviteUserInRoomAbility = security?.EditAccess;
|
||||
|
||||
const onClickInviteUsers = () => {
|
||||
setIsMobileHidden(true);
|
||||
const parentRoomId = selectionParentRoom.id;
|
||||
|
||||
if (isGracePeriod) {
|
||||
setInviteUsersWarningDialogVisible(true);
|
||||
return;
|
||||
}
|
||||
|
||||
setInvitePanelOptions({
|
||||
visible: true,
|
||||
roomId: parentRoomId,
|
||||
hideSelector: false,
|
||||
defaultAccess: isPublicRoomType
|
||||
? ShareAccessRights.RoomManager
|
||||
: ShareAccessRights.ReadOnly,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledTitle ref={itemTitleRef}>
|
||||
@ -37,6 +68,18 @@ const FilesItemTitle = ({ t, selection, isSeveralItems }) => {
|
||||
)}
|
||||
</div>
|
||||
<Text className="text">{selection.title}</Text>
|
||||
<div className="info_title-icons">
|
||||
{canInviteUserInRoomAbility && (
|
||||
<IconButton
|
||||
id="info_add-user"
|
||||
className={"icon"}
|
||||
title={t("Common:AddUsers")}
|
||||
iconName={PersonPlusReactSvgUrl}
|
||||
isFill={true}
|
||||
onClick={onClickInviteUsers}
|
||||
size={16}
|
||||
/>
|
||||
)}
|
||||
{selection && (
|
||||
<ItemContextOptions
|
||||
t={t}
|
||||
@ -44,14 +87,38 @@ const FilesItemTitle = ({ t, selection, isSeveralItems }) => {
|
||||
selection={selection}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</StyledTitle>
|
||||
);
|
||||
};
|
||||
|
||||
export default withTranslation([
|
||||
export default inject(({ auth, dialogsStore, selectedFolderStore }) => {
|
||||
const { selectionParentRoom, setIsMobileHidden } = auth.infoPanelStore;
|
||||
const { isGracePeriod } = auth.currentTariffStatusStore;
|
||||
|
||||
const { setInvitePanelOptions, setInviteUsersWarningDialogVisible } =
|
||||
dialogsStore;
|
||||
|
||||
const roomType =
|
||||
selectedFolderStore.roomType ?? selectionParentRoom?.roomType;
|
||||
|
||||
const isPublicRoomType =
|
||||
roomType === RoomsType.PublicRoom || roomType === RoomsType.CustomRoom;
|
||||
|
||||
return {
|
||||
selectionParentRoom,
|
||||
setIsMobileHidden,
|
||||
isGracePeriod,
|
||||
setInvitePanelOptions,
|
||||
setInviteUsersWarningDialogVisible,
|
||||
isPublicRoomType,
|
||||
};
|
||||
})(
|
||||
withTranslation([
|
||||
"Files",
|
||||
"Common",
|
||||
"Translations",
|
||||
"InfoPanel",
|
||||
"SharingPanel",
|
||||
])(observer(FilesItemTitle));
|
||||
])(observer(FilesItemTitle))
|
||||
);
|
||||
|
@ -6,17 +6,6 @@ import { ContextMenu, ContextMenuButton } from "@docspace/components";
|
||||
|
||||
import ContextHelper from "../../helpers/ContextHelper";
|
||||
|
||||
const StyledItemContextOptions = styled.div`
|
||||
${props =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
margin-right: auto;
|
||||
`
|
||||
: css`
|
||||
margin-left: auto;
|
||||
`}
|
||||
`;
|
||||
|
||||
const ItemContextOptions = ({
|
||||
t,
|
||||
selection,
|
||||
@ -70,7 +59,7 @@ const ItemContextOptions = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledItemContextOptions>
|
||||
<>
|
||||
<ContextMenu
|
||||
ref={contextMenuRef}
|
||||
getContextModel={getData}
|
||||
@ -91,7 +80,7 @@ const ItemContextOptions = ({
|
||||
displayType="toggle"
|
||||
/>
|
||||
)}
|
||||
</StyledItemContextOptions>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -49,7 +49,6 @@ const ItemTitle = ({
|
||||
selectionLength={selectionLength}
|
||||
selection={filesItemSelection}
|
||||
isSeveralItems={isSeveralItems}
|
||||
getIcon={getIcon}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -0,0 +1,139 @@
|
||||
import React, { useState, useCallback, memo } from "react";
|
||||
import styled from "styled-components";
|
||||
import { FixedSizeList as List, areEqual } from "react-window";
|
||||
import AutoSizer from "react-virtualized-auto-sizer";
|
||||
import CustomScrollbarsVirtualList from "@docspace/components/scrollbar/custom-scrollbars-virtual-list";
|
||||
import InfiniteLoader from "react-window-infinite-loader";
|
||||
import User from "./User";
|
||||
|
||||
const StyledMembersList = styled.div`
|
||||
height: calc(100vh - 266px);
|
||||
`;
|
||||
|
||||
const Item = memo(({ data, index, style }) => {
|
||||
const {
|
||||
t,
|
||||
members,
|
||||
security,
|
||||
membersHelper,
|
||||
currentMember,
|
||||
updateRoomMemberRole,
|
||||
selectionParentRoom,
|
||||
setSelectionParentRoom,
|
||||
changeUserType,
|
||||
setIsScrollLocked,
|
||||
canInviteUserInRoomAbility,
|
||||
onRepeatInvitation,
|
||||
} = data;
|
||||
|
||||
const user = members[index];
|
||||
|
||||
return (
|
||||
<div key={user.id} style={{ ...style, width: "calc(100% - 30px)" }}>
|
||||
<User
|
||||
t={t}
|
||||
user={user}
|
||||
key={user.id}
|
||||
security={security}
|
||||
membersHelper={membersHelper}
|
||||
currentMember={currentMember}
|
||||
updateRoomMemberRole={updateRoomMemberRole}
|
||||
roomId={selectionParentRoom.id}
|
||||
roomType={selectionParentRoom.roomType}
|
||||
selectionParentRoom={selectionParentRoom}
|
||||
setSelectionParentRoom={setSelectionParentRoom}
|
||||
changeUserType={changeUserType}
|
||||
setIsScrollLocked={setIsScrollLocked}
|
||||
isTitle={user.isTitle}
|
||||
isExpect={user.isExpect}
|
||||
showInviteIcon={canInviteUserInRoomAbility && user.isExpect}
|
||||
onRepeatInvitation={onRepeatInvitation}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}, areEqual);
|
||||
|
||||
const MembersList = (props) => {
|
||||
const {
|
||||
t,
|
||||
security,
|
||||
membersHelper,
|
||||
currentMember,
|
||||
updateRoomMemberRole,
|
||||
selectionParentRoom,
|
||||
setSelectionParentRoom,
|
||||
changeUserType,
|
||||
setIsScrollLocked,
|
||||
members,
|
||||
hasNextPage,
|
||||
itemCount,
|
||||
onRepeatInvitation,
|
||||
loadNextPage,
|
||||
} = props;
|
||||
|
||||
const itemsCount = members.length;
|
||||
const canInviteUserInRoomAbility = security?.EditAccess;
|
||||
const [isNextPageLoading, setIsNextPageLoading] = useState(false);
|
||||
|
||||
const isItemLoaded = useCallback(
|
||||
(index) => {
|
||||
return !hasNextPage || index < itemsCount;
|
||||
},
|
||||
[hasNextPage, itemsCount]
|
||||
);
|
||||
|
||||
const loadMoreItems = useCallback(
|
||||
async (startIndex) => {
|
||||
setIsNextPageLoading(true);
|
||||
if (!isNextPageLoading) {
|
||||
await loadNextPage(startIndex - 1);
|
||||
}
|
||||
setIsNextPageLoading(false);
|
||||
},
|
||||
[isNextPageLoading, loadNextPage]
|
||||
);
|
||||
|
||||
return (
|
||||
<StyledMembersList className="aaaaaaaaaw">
|
||||
<AutoSizer>
|
||||
{({ height, width }) => (
|
||||
<InfiniteLoader
|
||||
isItemLoaded={isItemLoaded}
|
||||
itemCount={itemCount}
|
||||
loadMoreItems={loadMoreItems}
|
||||
>
|
||||
{({ onItemsRendered, ref }) => (
|
||||
<List
|
||||
ref={ref}
|
||||
width={width + 20}
|
||||
height={height}
|
||||
itemCount={itemsCount}
|
||||
itemSize={48}
|
||||
itemData={{
|
||||
t,
|
||||
security,
|
||||
membersHelper,
|
||||
currentMember,
|
||||
updateRoomMemberRole,
|
||||
selectionParentRoom,
|
||||
setSelectionParentRoom,
|
||||
changeUserType,
|
||||
setIsScrollLocked,
|
||||
members,
|
||||
canInviteUserInRoomAbility,
|
||||
onRepeatInvitation,
|
||||
}}
|
||||
outerElementType={CustomScrollbarsVirtualList}
|
||||
onItemsRendered={onItemsRendered}
|
||||
>
|
||||
{Item}
|
||||
</List>
|
||||
)}
|
||||
</InfiniteLoader>
|
||||
)}
|
||||
</AutoSizer>
|
||||
</StyledMembersList>
|
||||
);
|
||||
};
|
||||
|
||||
export default MembersList;
|
@ -9,6 +9,10 @@ import { isMobileOnly } from "react-device-detect";
|
||||
import { decode } from "he";
|
||||
import { filterUserRoleOptions } from "SRC_DIR/helpers/utils";
|
||||
import { getUserRole } from "@docspace/common/utils";
|
||||
import Text from "@docspace/components/text";
|
||||
import EmailPlusReactSvgUrl from "PUBLIC_DIR/images/e-mail+.react.svg?url";
|
||||
import { StyledUserTypeHeader } from "../../styles/members";
|
||||
import IconButton from "@docspace/components/icon-button";
|
||||
|
||||
const User = ({
|
||||
t,
|
||||
@ -21,6 +25,9 @@ const User = ({
|
||||
setSelectionParentRoom,
|
||||
changeUserType,
|
||||
setIsScrollLocked,
|
||||
isTitle,
|
||||
onRepeatInvitation,
|
||||
showInviteIcon,
|
||||
}) => {
|
||||
if (!selectionParentRoom) return null;
|
||||
if (!user.displayName && !user.email) return null;
|
||||
@ -126,7 +133,22 @@ const User = ({
|
||||
user.isOwner ? t("Common:DocSpaceOwner") : t("Common:DocSpaceAdmin")
|
||||
}. ${t("Common:HasFullAccess")}`;
|
||||
|
||||
return (
|
||||
return isTitle ? (
|
||||
<StyledUserTypeHeader isExpect={isExpect}>
|
||||
<Text className="title">{user.displayName}</Text>
|
||||
|
||||
{showInviteIcon && (
|
||||
<IconButton
|
||||
className={"icon"}
|
||||
title={t("Common:RepeatInvitation")}
|
||||
iconName={EmailPlusReactSvgUrl}
|
||||
isFill={true}
|
||||
onClick={onRepeatInvitation}
|
||||
size={16}
|
||||
/>
|
||||
)}
|
||||
</StyledUserTypeHeader>
|
||||
) : (
|
||||
<StyledUser isExpect={isExpect} key={user.id}>
|
||||
<Avatar
|
||||
role={role}
|
||||
|
@ -2,29 +2,22 @@ import React, { useState, useEffect, useCallback } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import toastr from "@docspace/components/toast/toastr";
|
||||
import { FolderType } from "@docspace/common/constants";
|
||||
import Loaders from "@docspace/common/components/Loaders";
|
||||
|
||||
import PersonPlusReactSvgUrl from "PUBLIC_DIR/images/person+.react.svg?url";
|
||||
import EmailPlusReactSvgUrl from "PUBLIC_DIR/images/e-mail+.react.svg?url";
|
||||
import {
|
||||
EmployeeActivationStatus,
|
||||
RoomsType,
|
||||
ShareAccessRights,
|
||||
} from "@docspace/common/constants";
|
||||
|
||||
import { StyledUserList, StyledUserTypeHeader } from "../../styles/members";
|
||||
|
||||
import { RoomsType, ShareAccessRights } from "@docspace/common/constants";
|
||||
|
||||
import IconButton from "@docspace/components/icon-button";
|
||||
import Text from "@docspace/components/text";
|
||||
import User from "./User";
|
||||
import MembersHelper from "../../helpers/MembersHelper";
|
||||
|
||||
import PublicRoomBlock from "./sub-components/PublicRoomBlock";
|
||||
import MembersList from "./MembersList";
|
||||
|
||||
const Members = ({
|
||||
t,
|
||||
selfId,
|
||||
selection,
|
||||
|
||||
setIsMobileHidden,
|
||||
updateRoomMembers,
|
||||
setUpdateRoomMembers,
|
||||
|
||||
@ -34,17 +27,16 @@ const Members = ({
|
||||
setIsScrollLocked,
|
||||
|
||||
getRoomMembers,
|
||||
getRoomLinks,
|
||||
updateRoomMemberRole,
|
||||
setView,
|
||||
roomsView,
|
||||
resendEmailInvitations,
|
||||
setInvitePanelOptions,
|
||||
setInviteUsersWarningDialogVisible,
|
||||
changeUserType,
|
||||
isGracePeriod,
|
||||
isPublicRoomType,
|
||||
|
||||
setExternalLinks,
|
||||
membersFilter,
|
||||
}) => {
|
||||
const membersHelper = new MembersHelper({ t });
|
||||
|
||||
@ -53,34 +45,66 @@ const Members = ({
|
||||
|
||||
const security = selectionParentRoom ? selectionParentRoom.security : {};
|
||||
|
||||
const canInviteUserInRoomAbility = security?.EditAccess;
|
||||
|
||||
const fetchMembers = async (roomId) => {
|
||||
const fetchMembers = async (roomId, clearFilter = true) => {
|
||||
let timerId;
|
||||
if (members) timerId = setTimeout(() => setShowLoader(true), 1000);
|
||||
let data = await getRoomMembers(roomId);
|
||||
// if (members) timerId = setTimeout(() => setShowLoader(true), 1000);
|
||||
|
||||
setExternalLinks(data);
|
||||
const data = await getRoomMembers(roomId, clearFilter);
|
||||
// const links = await getRoomLinks(roomId);
|
||||
|
||||
// console.log("links", links);
|
||||
// setExternalLinks(data); TODO:
|
||||
|
||||
data = data.filter((m) => m.sharedTo.email || m.sharedTo.displayName);
|
||||
clearTimeout(timerId);
|
||||
|
||||
let inRoomMembers = [];
|
||||
let expectedMembers = [];
|
||||
const users = [];
|
||||
const administrators = [];
|
||||
const expectedMembers = [];
|
||||
data.map((fetchedMember) => {
|
||||
const member = {
|
||||
access: fetchedMember.access,
|
||||
canEditAccess: fetchedMember.canEditAccess,
|
||||
...fetchedMember.sharedTo,
|
||||
};
|
||||
if (member.activationStatus !== 2) inRoomMembers.push(member);
|
||||
else expectedMembers.push(member);
|
||||
|
||||
if (member.activationStatus === EmployeeActivationStatus.Pending) {
|
||||
expectedMembers.push(member);
|
||||
} else if (
|
||||
member.access === ShareAccessRights.FullAccess ||
|
||||
member.access === ShareAccessRights.RoomManager
|
||||
) {
|
||||
administrators.push(member);
|
||||
} else {
|
||||
users.push(member);
|
||||
}
|
||||
});
|
||||
|
||||
if (administrators.length && !members?.administrators?.length) {
|
||||
administrators.unshift({
|
||||
id: "administration",
|
||||
displayName: t("Administration"),
|
||||
isTitle: true,
|
||||
});
|
||||
}
|
||||
|
||||
if (users.length && !members?.users?.length) {
|
||||
users.unshift({ id: "user", displayName: t("Users"), isTitle: true });
|
||||
}
|
||||
|
||||
if (expectedMembers.length && !members?.expected?.length) {
|
||||
expectedMembers.unshift({
|
||||
id: "expected",
|
||||
displayName: t("ExpectUsers"),
|
||||
isTitle: true,
|
||||
isExpect: true,
|
||||
});
|
||||
}
|
||||
|
||||
setShowLoader(false);
|
||||
setUpdateRoomMembers(false);
|
||||
return {
|
||||
inRoom: inRoomMembers,
|
||||
users,
|
||||
administrators,
|
||||
expected: expectedMembers,
|
||||
};
|
||||
};
|
||||
@ -129,6 +153,7 @@ const Members = ({
|
||||
...selectionParentRoom,
|
||||
members: fetchedMembers,
|
||||
});
|
||||
|
||||
setMembers(fetchedMembers);
|
||||
}, [selectionParentRoom, selection?.id, updateRoomMembers]);
|
||||
|
||||
@ -141,25 +166,6 @@ const Members = ({
|
||||
updateMembersAction,
|
||||
]);
|
||||
|
||||
const onClickInviteUsers = () => {
|
||||
setIsMobileHidden(true);
|
||||
const parentRoomId = selectionParentRoom.id;
|
||||
|
||||
if (isGracePeriod) {
|
||||
setInviteUsersWarningDialogVisible(true);
|
||||
return;
|
||||
}
|
||||
|
||||
setInvitePanelOptions({
|
||||
visible: true,
|
||||
roomId: parentRoomId,
|
||||
hideSelector: false,
|
||||
defaultAccess: isPublicRoomType
|
||||
? ShareAccessRights.RoomManager
|
||||
: ShareAccessRights.ReadOnly,
|
||||
});
|
||||
};
|
||||
|
||||
const onRepeatInvitation = async () => {
|
||||
const userIds = members.expected.map((user) => user.id);
|
||||
resendEmailInvitations(selectionParentRoom.id, userIds)
|
||||
@ -169,105 +175,55 @@ const Members = ({
|
||||
.catch((err) => toastr.error(err));
|
||||
};
|
||||
|
||||
if (showLoader) return <Loaders.InfoPanelViewLoader view="members" />;
|
||||
if (!selectionParentRoom || !members) return null;
|
||||
|
||||
const [currentMember] = members.inRoom.filter(
|
||||
const [currentMember] = members.administrators.filter(
|
||||
(member) => member.id === selfId
|
||||
);
|
||||
|
||||
const loadNextPage = async () => {
|
||||
const roomId = selectionParentRoom.id;
|
||||
const fetchedMembers = await fetchMembers(roomId, false);
|
||||
const { users, administrators, expected } = fetchedMembers;
|
||||
|
||||
const newMembers = {
|
||||
administrators: [...members.administrators, administrators],
|
||||
users: [...members.users, ...users],
|
||||
expected: [...members.expected, ...expected],
|
||||
};
|
||||
|
||||
setMembers(newMembers);
|
||||
};
|
||||
|
||||
const { administrators, users, expected } = members;
|
||||
const membersList = [...administrators, ...users, ...expected];
|
||||
|
||||
return (
|
||||
<>
|
||||
{isPublicRoomType && <PublicRoomBlock t={t} />}
|
||||
|
||||
<StyledUserTypeHeader>
|
||||
<Text className="title">
|
||||
{t("UsersInRoom")} : {members.inRoom.length}
|
||||
</Text>
|
||||
{canInviteUserInRoomAbility && (
|
||||
<IconButton
|
||||
id="info_add-user"
|
||||
className={"icon"}
|
||||
title={t("Common:AddUsers")}
|
||||
iconName={PersonPlusReactSvgUrl}
|
||||
isFill={true}
|
||||
onClick={onClickInviteUsers}
|
||||
size={16}
|
||||
/>
|
||||
)}
|
||||
</StyledUserTypeHeader>
|
||||
|
||||
<StyledUserList>
|
||||
{Object.values(members.inRoom).map((user) => (
|
||||
<User
|
||||
security={security}
|
||||
key={user.id}
|
||||
<MembersList
|
||||
loadNextPage={loadNextPage}
|
||||
t={t}
|
||||
user={user}
|
||||
security={security}
|
||||
members={membersList}
|
||||
membersHelper={membersHelper}
|
||||
currentMember={currentMember}
|
||||
updateRoomMemberRole={updateRoomMemberRole}
|
||||
roomId={selectionParentRoom.id}
|
||||
roomType={selectionParentRoom.roomType}
|
||||
selectionParentRoom={selectionParentRoom}
|
||||
setSelectionParentRoom={setSelectionParentRoom}
|
||||
changeUserType={changeUserType}
|
||||
setIsScrollLocked={setIsScrollLocked}
|
||||
hasNextPage={membersList.length < membersFilter.total}
|
||||
itemCount={membersFilter.total}
|
||||
onRepeatInvitation={onRepeatInvitation}
|
||||
/>
|
||||
))}
|
||||
</StyledUserList>
|
||||
|
||||
{!!members.expected.length && (
|
||||
<StyledUserTypeHeader isExpect>
|
||||
<Text className="title">{t("PendingInvitations")}</Text>
|
||||
{canInviteUserInRoomAbility && (
|
||||
<IconButton
|
||||
className={"icon"}
|
||||
title={t("Common:RepeatInvitation")}
|
||||
iconName={EmailPlusReactSvgUrl}
|
||||
isFill={true}
|
||||
onClick={onRepeatInvitation}
|
||||
size={16}
|
||||
/>
|
||||
)}
|
||||
</StyledUserTypeHeader>
|
||||
)}
|
||||
|
||||
<StyledUserList>
|
||||
{Object.values(members.expected).map((user, i) => (
|
||||
<User
|
||||
security={security}
|
||||
isExpect
|
||||
key={i}
|
||||
t={t}
|
||||
user={user}
|
||||
membersHelper={membersHelper}
|
||||
currentMember={currentMember}
|
||||
updateRoomMemberRole={updateRoomMemberRole}
|
||||
roomId={selectionParentRoom.id}
|
||||
roomType={selectionParentRoom.roomType}
|
||||
selectionParentRoom={selectionParentRoom}
|
||||
setSelectionParentRoom={setSelectionParentRoom}
|
||||
changeUserType={changeUserType}
|
||||
setIsScrollLocked={setIsScrollLocked}
|
||||
/>
|
||||
))}
|
||||
</StyledUserList>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(
|
||||
({
|
||||
auth,
|
||||
filesStore,
|
||||
peopleStore,
|
||||
dialogsStore,
|
||||
selectedFolderStore,
|
||||
publicRoomStore,
|
||||
}) => {
|
||||
({ auth, filesStore, peopleStore, selectedFolderStore, publicRoomStore }) => {
|
||||
const {
|
||||
setIsMobileHidden,
|
||||
selectionParentRoom,
|
||||
|
||||
setSelectionParentRoom,
|
||||
@ -279,12 +235,14 @@ export default inject(
|
||||
|
||||
setIsScrollLocked,
|
||||
} = auth.infoPanelStore;
|
||||
const { getRoomMembers, updateRoomMemberRole, resendEmailInvitations } =
|
||||
filesStore;
|
||||
const {
|
||||
getRoomMembers,
|
||||
getRoomLinks,
|
||||
updateRoomMemberRole,
|
||||
resendEmailInvitations,
|
||||
membersFilter,
|
||||
} = filesStore;
|
||||
const { id: selfId } = auth.userStore.user;
|
||||
const { isGracePeriod } = auth.currentTariffStatusStore;
|
||||
const { setInvitePanelOptions, setInviteUsersWarningDialogVisible } =
|
||||
dialogsStore;
|
||||
|
||||
const { changeType: changeUserType } = peopleStore;
|
||||
const { setExternalLinks } = publicRoomStore;
|
||||
@ -298,13 +256,13 @@ export default inject(
|
||||
return {
|
||||
setView,
|
||||
roomsView,
|
||||
setIsMobileHidden,
|
||||
selectionParentRoom,
|
||||
setSelectionParentRoom,
|
||||
|
||||
setIsScrollLocked,
|
||||
|
||||
getRoomMembers,
|
||||
getRoomLinks,
|
||||
updateRoomMemberRole,
|
||||
|
||||
updateRoomMembers,
|
||||
@ -312,13 +270,11 @@ export default inject(
|
||||
|
||||
selfId,
|
||||
|
||||
setInvitePanelOptions,
|
||||
setInviteUsersWarningDialogVisible,
|
||||
resendEmailInvitations,
|
||||
changeUserType,
|
||||
isGracePeriod,
|
||||
isPublicRoomType,
|
||||
setExternalLinks,
|
||||
membersFilter,
|
||||
};
|
||||
}
|
||||
)(
|
||||
|
@ -30,7 +30,6 @@ const PublicRoomBar = (props) => {
|
||||
size={8}
|
||||
iconName={CrossReactSvg}
|
||||
onClick={onClose}
|
||||
color="#657077"
|
||||
/> */}
|
||||
</StyledPublicRoomBar>
|
||||
);
|
||||
|
@ -36,6 +36,10 @@ const StyledPublicRoomBar = styled.div`
|
||||
.close-icon {
|
||||
margin: -5px -17px 0 0;
|
||||
|
||||
path {
|
||||
fill: ${({ theme }) => theme.iconButton.color};
|
||||
}
|
||||
|
||||
svg {
|
||||
weight: 8px;
|
||||
height: 8px;
|
||||
|
@ -126,12 +126,14 @@ const InfoPanelHeaderContent = (props) => {
|
||||
style={{ width: "100%" }}
|
||||
data={roomsSubmenu}
|
||||
forsedActiveItemId={roomsView}
|
||||
size="scale"
|
||||
/>
|
||||
) : (
|
||||
<Submenu
|
||||
style={{ width: "100%" }}
|
||||
data={personalSubmenu}
|
||||
forsedActiveItemId={fileView}
|
||||
size="scale"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
@ -10,7 +10,7 @@ import InfiniteGrid from "./InfiniteGrid";
|
||||
|
||||
const paddingCss = css`
|
||||
@media ${desktop} {
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
margin-right: 1px;
|
||||
@ -23,7 +23,7 @@ const paddingCss = css`
|
||||
}
|
||||
|
||||
@media ${tablet} {
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
margin-right: -1px;
|
||||
@ -36,12 +36,12 @@ const paddingCss = css`
|
||||
|
||||
const StyledGridWrapper = styled.div`
|
||||
display: grid;
|
||||
grid-template-columns: ${props =>
|
||||
grid-template-columns: ${(props) =>
|
||||
props.isRooms
|
||||
? "repeat(auto-fill, minmax(274px, 1fr))"
|
||||
: "repeat(auto-fill, minmax(216px, 1fr))"};
|
||||
width: 100%;
|
||||
margin-bottom: ${props => (props.isFolders || props.isRooms ? "23px" : 0)};
|
||||
margin-bottom: ${(props) => (props.isFolders || props.isRooms ? "23px" : 0)};
|
||||
box-sizing: border-box;
|
||||
${paddingCss};
|
||||
|
||||
@ -90,7 +90,7 @@ const StyledTileContainer = styled.div`
|
||||
cursor: pointer !important;
|
||||
|
||||
.sort-combo-box {
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
margin-left: 3px;
|
||||
@ -119,14 +119,14 @@ const StyledTileContainer = styled.div`
|
||||
.option-item__icon {
|
||||
display: none;
|
||||
cursor: pointer;
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.isDesc &&
|
||||
css`
|
||||
transform: rotate(180deg);
|
||||
`}
|
||||
|
||||
path {
|
||||
fill: ${props => props.theme.filterInput.sort.sortFill};
|
||||
fill: ${(props) => props.theme.filterInput.sort.sortFill};
|
||||
}
|
||||
}
|
||||
|
||||
@ -138,7 +138,7 @@ const StyledTileContainer = styled.div`
|
||||
}
|
||||
|
||||
.selected-option-item {
|
||||
background: ${props =>
|
||||
background: ${(props) =>
|
||||
props.theme.filterInput.sort.hoverBackground};
|
||||
cursor: auto;
|
||||
|
||||
@ -156,10 +156,10 @@ const StyledTileContainer = styled.div`
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
|
||||
color: ${props => props.theme.filterInput.sort.tileSortColor};
|
||||
color: ${(props) => props.theme.filterInput.sort.tileSortColor};
|
||||
|
||||
.sort-icon {
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
margin-left: 8px;
|
||||
@ -169,7 +169,7 @@ const StyledTileContainer = styled.div`
|
||||
`}
|
||||
svg {
|
||||
path {
|
||||
fill: ${props => props.theme.filterInput.sort.tileSortFill};
|
||||
fill: ${(props) => props.theme.filterInput.sort.tileSortFill};
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -183,7 +183,7 @@ const StyledTileContainer = styled.div`
|
||||
}
|
||||
|
||||
@media ${tablet} {
|
||||
${props =>
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
margin-left: -3px;
|
||||
@ -217,7 +217,7 @@ class TileContainer extends React.PureComponent {
|
||||
const Folders = [];
|
||||
const Files = [];
|
||||
|
||||
React.Children.map(children, item => {
|
||||
React.Children.map(children, (item) => {
|
||||
const { isFolder, isRoom, fileExst, id } = item.props.item;
|
||||
if ((isFolder || id === -1) && !fileExst && !isRoom) {
|
||||
Folders.push(
|
||||
@ -254,7 +254,8 @@ class TileContainer extends React.PureComponent {
|
||||
<Heading
|
||||
size="xsmall"
|
||||
id={"folder-tile-heading"}
|
||||
className="tile-items-heading">
|
||||
className="tile-items-heading"
|
||||
>
|
||||
{headingFolders}
|
||||
</Heading>
|
||||
)}
|
||||
@ -287,7 +288,8 @@ class TileContainer extends React.PureComponent {
|
||||
className={`${className} files-tile-container`}
|
||||
style={style}
|
||||
useReactWindow={useReactWindow}
|
||||
isDesc={isDesc}>
|
||||
isDesc={isDesc}
|
||||
>
|
||||
{useReactWindow ? (
|
||||
<InfiniteGrid>{renderTile}</InfiniteGrid>
|
||||
) : (
|
||||
|
@ -80,8 +80,9 @@ class FilesStore {
|
||||
bufferSelection = null;
|
||||
selected = "close";
|
||||
|
||||
filter = FilesFilter.getDefault(); //TODO: FILTER
|
||||
filter = FilesFilter.getDefault();
|
||||
roomsFilter = RoomsFilter.getDefault();
|
||||
membersFilter = { page: 0, pageCount: 100, total: 0 };
|
||||
|
||||
categoryType = getCategoryType(window.location);
|
||||
|
||||
@ -2425,9 +2426,48 @@ class FilesStore {
|
||||
return api.rooms.removeLogoFromRoom(id);
|
||||
}
|
||||
|
||||
getRoomMembers(id) {
|
||||
return api.rooms.getRoomMembers(id);
|
||||
getDefaultMembersFilter = () => {
|
||||
return { page: 0, pageCount: 100, total: 0 };
|
||||
};
|
||||
|
||||
setRoomMembersFilter = (roomMembersFilter) => {
|
||||
this.roomMembersFilter = roomMembersFilter;
|
||||
};
|
||||
|
||||
getRoomMembers = (id, clearFilter = true) => {
|
||||
let newFilter = this.membersFilter;
|
||||
|
||||
if (clearFilter) {
|
||||
newFilter = this.getDefaultMembersFilter();
|
||||
} else {
|
||||
newFilter.page += 1;
|
||||
}
|
||||
this.setRoomMembersFilter(newFilter);
|
||||
|
||||
const membersFilters = {
|
||||
startIndex: newFilter.page * newFilter.pageCount,
|
||||
count: newFilter.pageCount,
|
||||
filterType: 0, // 0 (Members)
|
||||
};
|
||||
|
||||
return api.rooms.getRoomMembers(id, membersFilters).then((res) => {
|
||||
const newFilter = this.membersFilter;
|
||||
newFilter.total = res.total;
|
||||
this.membersFilter = newFilter;
|
||||
|
||||
return res.items;
|
||||
});
|
||||
};
|
||||
|
||||
// 2 (External link), 3 (All links);
|
||||
|
||||
getRoomLinks = (id) => {
|
||||
// 1 (Invitation link)
|
||||
|
||||
return api.rooms
|
||||
.getRoomMembers(id, { filterType: 1 })
|
||||
.then((res) => res.items);
|
||||
};
|
||||
|
||||
updateRoomMemberRole(id, data) {
|
||||
return api.rooms.updateRoomMemberRole(id, data);
|
||||
@ -3625,7 +3665,7 @@ class FilesStore {
|
||||
return Math.floor(sectionWidth / minTileWidth);
|
||||
};
|
||||
|
||||
setInvitationLinks = async (roomId, linkId, title, access) => {
|
||||
setInvitationLinks = async (roomId, title, access, linkId) => {
|
||||
return await api.rooms.setInvitationLinks(roomId, linkId, title, access);
|
||||
};
|
||||
|
||||
@ -3634,7 +3674,7 @@ class FilesStore {
|
||||
};
|
||||
|
||||
getRoomSecurityInfo = async (id) => {
|
||||
return await api.rooms.getRoomSecurityInfo(id);
|
||||
return await api.rooms.getRoomSecurityInfo(id).then((res) => res.items);
|
||||
};
|
||||
|
||||
setRoomSecurity = async (id, data) => {
|
||||
|
@ -1,5 +1,9 @@
|
||||
import { request } from "../client";
|
||||
import { checkFilterInstance, decodeDisplayName } from "../../utils";
|
||||
import {
|
||||
checkFilterInstance,
|
||||
decodeDisplayName,
|
||||
toUrlParams,
|
||||
} from "../../utils";
|
||||
import { FolderType } from "../../constants";
|
||||
import RoomsFilter from "./filter";
|
||||
|
||||
@ -45,10 +49,17 @@ export function getRoomInfo(id) {
|
||||
});
|
||||
}
|
||||
|
||||
export function getRoomMembers(id) {
|
||||
export function getRoomMembers(id, filter) {
|
||||
let params = "";
|
||||
const str = toUrlParams(filter);
|
||||
|
||||
if (str) {
|
||||
params = `?${str}`;
|
||||
}
|
||||
|
||||
const options = {
|
||||
method: "get",
|
||||
url: `/files/rooms/${id}/share`,
|
||||
url: `/files/rooms/${id}/share${params}`,
|
||||
};
|
||||
|
||||
return request(options).then((res) => {
|
||||
@ -302,10 +313,11 @@ export const resendEmailInvitations = async (id, usersIds) => {
|
||||
return res;
|
||||
};
|
||||
|
||||
//// 1 (Invitation link)
|
||||
export const getRoomSecurityInfo = async (id) => {
|
||||
const options = {
|
||||
method: "get",
|
||||
url: `/files/rooms/${id}/share`,
|
||||
url: `/files/rooms/${id}/share?filterType=1`,
|
||||
};
|
||||
|
||||
const res = await request(options);
|
||||
|
@ -21,6 +21,7 @@ const Submenu = (props) => {
|
||||
startSelect = 0,
|
||||
forsedActiveItemId,
|
||||
onSelect,
|
||||
size,
|
||||
...rest
|
||||
} = props;
|
||||
if (!data) return null;
|
||||
@ -105,7 +106,7 @@ const Submenu = (props) => {
|
||||
<div className="sticky">
|
||||
<SubmenuRoot>
|
||||
<SubmenuScrollbarSize />
|
||||
<SubmenuScroller>
|
||||
<SubmenuScroller size={size}>
|
||||
<StyledSubmenuItems ref={submenuItemsRef} role="list">
|
||||
{data.map((d) => {
|
||||
const isActive =
|
||||
@ -144,9 +145,14 @@ const Submenu = (props) => {
|
||||
);
|
||||
})}
|
||||
</StyledSubmenuItems>
|
||||
{size !== "scale" && (
|
||||
<StyledSubmenuBottomLine className="bottom-line" />
|
||||
)}
|
||||
</SubmenuScroller>
|
||||
</SubmenuRoot>
|
||||
{size === "scale" && (
|
||||
<StyledSubmenuBottomLine className="bottom-line" />
|
||||
)}
|
||||
</div>
|
||||
<div className="sticky-indent"></div>
|
||||
|
||||
@ -164,6 +170,8 @@ Submenu.propTypes = {
|
||||
startSelect: PropTypes.oneOfType([PropTypes.object, PropTypes.number]),
|
||||
/** Property that allows explicitly selecting content passed through an external operation */
|
||||
forsedActiveItemId: PropTypes.any,
|
||||
/** Scales the width of the bottom line to 100%. */
|
||||
size: PropTypes.string,
|
||||
/** Sets a callback function that is triggered when the submenu item is selected */
|
||||
onSelect: PropTypes.func,
|
||||
};
|
||||
|
@ -128,6 +128,13 @@ export const SubmenuScroller = styled.div`
|
||||
}
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
|
||||
${(props) =>
|
||||
props.size !== "scale" &&
|
||||
css`
|
||||
display: grid;
|
||||
flex: 0 1 auto;
|
||||
`}
|
||||
`;
|
||||
|
||||
export const SubmenuRoot = styled.div`
|
||||
|
@ -1,12 +1,10 @@
|
||||
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_21120_56765)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.5 2C5.18629 2 2.5 4.68629 2.5 8C2.5 11.3137 5.18629 14 8.5 14C11.8137 14 14.5 11.3137 14.5 8C14.5 4.68629 11.8137 2 8.5 2ZM0.5 8C0.5 3.58172 4.08172 0 8.5 0C12.9183 0 16.5 3.58172 16.5 8C16.5 12.4183 12.9183 16 8.5 16C4.08172 16 0.5 12.4183 0.5 8Z" fill="#333333"/>
|
||||
<circle cx="8.5" cy="5" r="1" fill="#333333"/>
|
||||
<rect x="7.5" y="7" width="2" height="5" rx="1" fill="#333333"/>
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_460_8224)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 2C4.68629 2 2 4.68629 2 8C2 11.3137 4.68629 14 8 14C11.3137 14 14 11.3137 14 8C14 4.68629 11.3137 2 8 2ZM0 8C0 3.58172 3.58172 0 8 0C12.4183 0 16 3.58172 16 8C16 12.4183 12.4183 16 8 16C3.58172 16 0 12.4183 0 8ZM9 5C9 5.55228 8.55228 6 8 6C7.44772 6 7 5.55228 7 5C7 4.44772 7.44772 4 8 4C8.55228 4 9 4.44772 9 5ZM8 7C7.44772 7 7 7.44772 7 8V11C7 11.5523 7.44772 12 8 12C8.55228 12 9 11.5523 9 11V8C9 7.44772 8.55228 7 8 7Z" fill="#657077"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_21120_56765">
|
||||
<rect width="16" height="16" fill="white" transform="translate(0.5)"/>
|
||||
<clipPath id="clip0_460_8224">
|
||||
<rect width="16" height="16" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
Before Width: | Height: | Size: 710 B After Width: | Height: | Size: 739 B |
15
yarn.lock
15
yarn.lock
@ -3147,7 +3147,7 @@ __metadata:
|
||||
react-player: ^1.15.3
|
||||
react-router: ^6.10.0
|
||||
react-router-dom: ^6.10.0
|
||||
react-tooltip: 5.21.1
|
||||
react-tooltip: ^5.21.1
|
||||
react-viewer: ^3.2.2
|
||||
react-virtualized-auto-sizer: ^1.0.7
|
||||
react-window: ^1.8.8
|
||||
@ -22258,19 +22258,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-tooltip@npm:5.21.1":
|
||||
version: 5.21.1
|
||||
resolution: "react-tooltip@npm:5.21.1"
|
||||
dependencies:
|
||||
"@floating-ui/dom": ^1.0.0
|
||||
classnames: ^2.3.0
|
||||
peerDependencies:
|
||||
react: ">=16.14.0"
|
||||
react-dom: ">=16.14.0"
|
||||
checksum: 44fbe22169eecc12b32e401460997c3b26bae96f70c527260ad41b5015c2ead37b8f713e01737be2fb748ffbb7b6ee8c461a9f46b064c36e6b39a542aab1b852
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-tooltip@npm:^5.21.1":
|
||||
version: 5.21.4
|
||||
resolution: "react-tooltip@npm:5.21.4"
|
||||
|
Loading…
Reference in New Issue
Block a user