Web: Files: added external link api

This commit is contained in:
Nikita Gopienko 2023-05-11 11:37:32 +03:00
parent 426616c217
commit ea45c5803b
18 changed files with 338 additions and 85 deletions

View File

@ -120,14 +120,16 @@
"AddNewLink": "Add new link",
"EmbeddingSettings": "Embedding settings",
"DisableLink": "Disable link",
"EnableLink": "Enable link",
"PasswordAccess": "Password access",
"LimitByTimePeriod": "Limit by time period",
"DenyDownload": "Deny download",
"Clean": "Clean",
"CopyPassword": "Copy password",
"LinkEditedSuccessfully": "Link edited successfully",
"LinkDisabledSuccessfully": "Link disabled successfully",
"LinkEnabledSuccessfully": "Link enable successfully",
"MarkAsVersion": "Mark as version",
"MarkAsRevision": "Mark as revision",
"TableSettingsTitle": "Manage displayed columns",
"DocumentEdited": "Cannot perform the action because the document is being edited."
"TableSettingsTitle": "Manage displayed columns"
}

View File

@ -39,5 +39,5 @@
"SystemProperties": "System properties",
"UsersInRoom": "Users in room",
"Versions": "Versions",
"LinksToViewingIcon": "Links to Viewing Icon"
"LinksToViewingIcon": "Links to Viewing"
}

View File

@ -120,6 +120,5 @@
"ChooseExpirationDate": "Вы можете выбрать срок действия для этой ссылки",
"PreventDownloadFilesAndFolders": "Вы можете запретить скачивать файлы и папки в этой комнате",
"MarkAsVersion": "Отметить как версию",
"MarkAsRevision": "Отметить как ревизию",
"DocumentEdited": "Невозможно выполнить действие, так как документ редактируется."
"MarkAsRevision": "Отметить как ревизию"
}

View File

@ -2,13 +2,13 @@ import React from "react";
import ToggleBlock from "./ToggleBlock";
const LimitTimeBlock = (props) => {
const { isChecked } = props;
const { isChecked, isLoading } = props;
const DateTimePicker = () => <></>;
return (
<ToggleBlock {...props}>
{isChecked ? <DateTimePicker /> : <></>}
{isChecked ? <DateTimePicker isDisabled={isLoading} /> : <></>}
</ToggleBlock>
);
};

View File

@ -4,9 +4,8 @@ import Link from "@docspace/components/link";
import TextInput from "@docspace/components/text-input";
const LinkBlock = (props) => {
const { t } = props;
const { t, isLoading, linkNameValue, setLinkNameValue } = props;
const [linkNameValue, setLinkNameValue] = useState("");
const [linkValue, setLinkValue] = useState("");
const onChangeLinkName = (e) => {
@ -20,7 +19,7 @@ const LinkBlock = (props) => {
};
const onShortenClick = () => {
alert("onShortenClick");
alert("api in progress");
};
return (
@ -37,6 +36,7 @@ const LinkBlock = (props) => {
value={linkNameValue}
onChange={onChangeLinkName}
placeholder={t("ExternalLink")}
isDisabled={isLoading}
/>
<TextInput
@ -55,6 +55,7 @@ const LinkBlock = (props) => {
fontWeight={600}
isHovered
type="action"
isDisabled={isLoading}
onClick={onShortenClick}
>
{t("Shorten")}

View File

@ -1,4 +1,4 @@
import React, { useRef } from "react";
import React, { useRef, startTransition } from "react";
import ToggleBlock from "./ToggleBlock";
import PasswordInput from "@docspace/components/password-input";
import IconButton from "@docspace/components/icon-button";
@ -6,7 +6,7 @@ import Link from "@docspace/components/link";
import RefreshReactSvgUrl from "PUBLIC_DIR/images/refresh.react.svg?url";
const PasswordAccessBlock = (props) => {
const { t, isChecked } = props;
const { t, isLoading, isChecked, passwordValue, setPasswordValue } = props;
const passwordInputRef = useRef(null);
@ -22,6 +22,12 @@ const PasswordAccessBlock = (props) => {
alert("onCopyClick");
};
const onChangePassword = (e) => {
startTransition(() => {
setPasswordValue(e.target.value);
});
};
return (
<ToggleBlock {...props}>
{isChecked ? (
@ -32,19 +38,19 @@ const PasswordAccessBlock = (props) => {
ref={passwordInputRef}
// scale //doesn't work
simpleView
// hasError={!isPasswordValid}
// isDisabled={isLoading}
isDisabled={isLoading}
// tabIndex={3}
// simpleView
// passwordSettings={{ minLength: 0 }}
// value={passwordValue}
// onChange={onChangePassword}
value={passwordValue}
onChange={onChangePassword}
/>
<IconButton
className="edit-link_generate-icon"
size="16"
isDisabled={isLoading}
iconName={RefreshReactSvgUrl}
onClick={onGeneratePasswordClick}
/>
@ -55,6 +61,7 @@ const PasswordAccessBlock = (props) => {
fontWeight={600}
isHovered
type="action"
isDisabled={isLoading}
onClick={onCleanClick}
>
{t("Clean")}
@ -64,6 +71,7 @@ const PasswordAccessBlock = (props) => {
fontWeight={600}
isHovered
type="action"
isDisabled={isLoading}
onClick={onCopyClick}
>
{t("CopyPassword")}

View File

@ -3,6 +3,7 @@ import Text from "@docspace/components/text";
import ToggleButton from "@docspace/components/toggle-button";
const ToggleBlock = ({
isLoading,
headerText,
bodyText,
isChecked,
@ -16,6 +17,7 @@ const ToggleBlock = ({
{headerText}
</Text>
<ToggleButton
isDisabled={isLoading}
isChecked={isChecked}
onChange={onChange}
className="edit-link-toggle"

View File

@ -5,7 +5,7 @@ import Heading from "@docspace/components/heading";
import Backdrop from "@docspace/components/backdrop";
import Aside from "@docspace/components/aside";
import Button from "@docspace/components/button";
import toastr from "@docspace/components/toast";
import toastr from "@docspace/components/toast/toastr";
import {
StyledEditLinkPanel,
@ -18,8 +18,25 @@ import ToggleBlock from "./ToggleBlock";
import PasswordAccessBlock from "./PasswordAccessBlock";
import LimitTimeBlock from "./LimitTimeBlock";
const EditLinkPanel = ({ t, visible, setIsVisible, isEdit }) => {
const [passwordAccessIsChecked, setPasswordAccessIsChecked] = useState(true);
const EditLinkPanel = (props) => {
const {
t,
visible,
roomId,
setIsVisible,
isEdit,
editExternalLink,
linkId,
title,
} = props;
const [isLoading, setIsLoading] = useState(false);
const [linkNameValue, setLinkNameValue] = useState(title); //t("ExternalLink")
const [passwordValue, setPasswordValue] = useState("");
const [expirationDate, setExpirationDate] = useState("");
const [passwordAccessIsChecked, setPasswordAccessIsChecked] = useState(false);
const [limitByTimeIsChecked, setLimitByTimeIsChecked] = useState(false);
const [denyDownload, setDenyDownload] = useState(false);
@ -33,8 +50,24 @@ const EditLinkPanel = ({ t, visible, setIsVisible, isEdit }) => {
const onClose = () => setIsVisible(false);
const onSave = () => {
toastr(t("LinkEditedSuccessfully"));
onClose();
setIsLoading(true);
const options = {
linkId,
roomId,
title: linkNameValue,
expirationDate,
password: passwordValue,
denyDownload,
};
editExternalLink(options)
.then(() => toastr.success(t("LinkEditedSuccessfully")))
.catch((err) => toastr.error(err?.message))
.finally(() => {
setIsLoading(false);
onClose();
});
};
return (
@ -53,21 +86,31 @@ const EditLinkPanel = ({ t, visible, setIsVisible, isEdit }) => {
</div>
<StyledScrollbar stype="mediumBlack">
<div className="edit-link_body">
<LinkBlock t={t} />
<LinkBlock
t={t}
isLoading={isLoading}
linkNameValue={linkNameValue}
setLinkNameValue={setLinkNameValue}
/>
<PasswordAccessBlock
t={t}
isLoading={isLoading}
headerText={t("Files:PasswordAccess")}
bodyText={t("Files:PasswordLink")}
isChecked={passwordAccessIsChecked}
passwordValue={passwordValue}
setPasswordValue={setPasswordValue}
onChange={onPasswordAccessChange}
/>
<LimitTimeBlock
isLoading={isLoading}
headerText={t("Files:LimitByTimePeriod")}
bodyText={t("Files:ChooseExpirationDate")}
isChecked={limitByTimeIsChecked}
onChange={onLimitByTimeChange}
/>
<ToggleBlock
isLoading={isLoading}
headerText={t("Files:DenyDownload")}
bodyText={t("Files:PreventDownloadFilesAndFolders")}
isChecked={denyDownload}
@ -82,12 +125,14 @@ const EditLinkPanel = ({ t, visible, setIsVisible, isEdit }) => {
primary
size="normal"
label={t("Common:SaveButton")}
isDisabled={isLoading}
onClick={onSave}
/>
<Button
scale
size="normal"
label={t("Common:CancelButton")}
isDisabled={isLoading}
onClick={onClose}
/>
</StyledButtons>
@ -96,14 +141,25 @@ const EditLinkPanel = ({ t, visible, setIsVisible, isEdit }) => {
);
};
export default inject(({ dialogsStore }) => {
const { editLinkPanelIsVisible, setEditLinkPanelIsVisible, linkIsEdit } =
export default inject(({ auth, dialogsStore, publicRoomStore }) => {
const { selectionParentRoom } = auth.infoPanelStore;
const { editLinkPanelIsVisible, setEditLinkPanelIsVisible, linkParams } =
dialogsStore;
const { externalLinks, editExternalLink } = publicRoomStore;
const { isEdit, linkId } = linkParams;
const link = externalLinks.find((l) => l?.sharedTo?.id === linkId);
const template = externalLinks.find((t) => t?.sharedTo?.isTemplate);
return {
visible: editLinkPanelIsVisible,
setIsVisible: setEditLinkPanelIsVisible,
isEdit: linkIsEdit,
isEdit,
linkId: link?.sharedTo?.id ?? template?.sharedTo?.id,
title: link?.sharedTo?.title,
editExternalLink,
roomId: selectionParentRoom.id,
};
})(
withTranslation(["SharingPanel", "Common", "Files"])(observer(EditLinkPanel))

View File

@ -43,7 +43,9 @@ const Members = ({
isGracePeriod,
isPublicRoom,
setEditLinkPanelIsVisible,
setLinkIsEdit,
setLinkParams,
setExternalLinks,
}) => {
const membersHelper = new MembersHelper({ t });
@ -59,6 +61,9 @@ const Members = ({
if (members) timerId = setTimeout(() => setShowLoader(true), 1000);
let data = await getRoomMembers(roomId);
const links = data.filter((t) => t.sharedTo.shareLink);
setExternalLinks(links);
data = data.filter((m) => m.sharedTo.email || m.sharedTo.displayName);
clearTimeout(timerId);
@ -172,7 +177,7 @@ const Members = ({
);
const onCopyLink = () => {
setLinkIsEdit(false);
setLinkParams({ isEdit: false });
setEditLinkPanelIsVisible(true);
};
@ -257,7 +262,14 @@ const Members = ({
};
export default inject(
({ auth, filesStore, peopleStore, dialogsStore, selectedFolderStore }) => {
({
auth,
filesStore,
peopleStore,
dialogsStore,
selectedFolderStore,
publicRoomStore,
}) => {
const {
setIsMobileHidden,
selectionParentRoom,
@ -271,22 +283,22 @@ export default inject(
isScrollLocked,
setIsScrollLocked,
} = auth.infoPanelStore;
const {
getRoomMembers,
updateRoomMemberRole,
resendEmailInvitations,
} = filesStore;
const { getRoomMembers, updateRoomMemberRole, resendEmailInvitations } =
filesStore;
const { id: selfId } = auth.userStore.user;
const { isGracePeriod } = auth.currentTariffStatusStore;
const {
setInvitePanelOptions,
setInviteUsersWarningDialogVisible,
setEditLinkPanelIsVisible,
setLinkIsEdit,
setLinkParams,
} = dialogsStore;
const { changeType: changeUserType } = peopleStore;
const { roomType } = selectedFolderStore;
const { setExternalLinks } = publicRoomStore;
const roomType =
selectedFolderStore.roomType ?? selectionParentRoom?.roomType;
const isPublicRoom = roomType === RoomsType.PublicRoom;
@ -314,7 +326,8 @@ export default inject(
isGracePeriod,
isPublicRoom,
setEditLinkPanelIsVisible,
setLinkIsEdit,
setLinkParams,
setExternalLinks,
};
}
)(

View File

@ -1,8 +1,9 @@
import React from "react";
import React, { useState } from "react";
import { observer, inject } from "mobx-react";
import { withTranslation } from "react-i18next";
import Avatar from "@docspace/components/avatar";
import Link from "@docspace/components/link";
import Text from "@docspace/components/text";
import IconButton from "@docspace/components/icon-button";
import ContextMenuButton from "@docspace/components/context-menu-button";
import { toastr } from "@docspace/components";
@ -13,26 +14,78 @@ import ShareReactSvgUrl from "PUBLIC_DIR/images/share.react.svg?url";
import CodeReactSvgUrl from "PUBLIC_DIR/images/code.react.svg?url";
import OutlineReactSvgUrl from "PUBLIC_DIR/images/outline-true.react.svg?url";
import LockedReactSvgUrl from "PUBLIC_DIR/images/locked.react.svg?url";
import LoadedReactSvgUrl from "PUBLIC_DIR/images/loaded.react.svg?url";
import TrashReactSvgUrl from "PUBLIC_DIR/images/trash.react.svg?url";
import ClockReactSvg from "PUBLIC_DIR/images/clock.react.svg";
import { StyledLinkRow } from "./StyledPublicRoom";
const LinkRow = (props) => {
const { t, setEditLinkPanelIsVisible, setLinkIsEdit, link, ...rest } = props;
const { expiryDate } = link;
const {
t,
roomId,
setEditLinkPanelIsVisible,
setLinkParams,
link,
editExternalLink,
setExternalLink,
canLinkDelete,
...rest
} = props;
const { sharedTo } = link;
const [isLoading, setIsLoading] = useState(false);
const {
title,
/* isLocked, */ shareLink,
id,
password,
disabled,
expirationDate,
} = sharedTo;
const isLocked = !!password;
const expiryDate = !!expirationDate;
const date = "October 5, 2023 11:00 AM.";
const tooltipContent = `This link is valid until ${date} Once it expires, it will be impossible to access the room via this link.`;
const onEditLink = () => {
setEditLinkPanelIsVisible(true);
setLinkIsEdit(true);
setLinkParams({ linkId: id, isEdit: true });
};
const onDisableLink = () => {
setIsLoading(true);
editExternalLink({ roomId, linkId: id, title, disabled: !disabled })
.then((res) => {
setExternalLink(id, res);
disabled
? toastr.success(t("Files:LinkEnabledSuccessfully"))
: toastr.success(t("Files:LinkDisabledSuccessfully"));
})
.catch((err) => toastr.error(err?.message))
.finally(() => setIsLoading(false));
};
const onLockClick = () => {
alert("onLockClick");
};
const onDeleteLink = () => {
alert("show delete dialog");
setIsLoading(true);
editExternalLink({ roomId, linkId: id, title, access: 0 })
.then((res) => {
console.log("res", res);
// setExternalLink(id, res);
})
.catch((err) => toastr.error(err?.message))
.finally(() => setIsLoading(false));
};
const getData = () => {
return [
{
@ -57,17 +110,35 @@ const LinkRow = (props) => {
icon: CodeReactSvgUrl,
// onClick: () => args.onClickLabel("label2"),
},
{
key: "disable-link-key",
label: t("Files:DisableLink"),
icon: OutlineReactSvgUrl,
// onClick: () => args.onClickLabel("label2"),
disabled
? {
key: "enable-link-key",
label: t("Files:EnableLink"),
icon: LoadedReactSvgUrl,
onClick: onDisableLink,
}
: {
key: "disable-link-key",
label: t("Files:DisableLink"),
icon: OutlineReactSvgUrl,
onClick: onDisableLink,
},
canLinkDelete && {
key: "delete-link-separator",
isSeparator: true,
},
canLinkDelete && {
key: "delete-link-key",
label: t("Common:Delete"),
icon: TrashReactSvgUrl,
onClick: onDeleteLink,
},
];
};
const onCopyExternalLink = () => {
toastr.success("onCopyExternalLink");
toastr.success("onCopyExternalLink"); // shareLink
};
return (
@ -87,12 +158,18 @@ const LinkRow = (props) => {
fontSize="14px"
fontWeight={600}
onClick={onEditLink}
isDisabled={disabled}
color={disabled ? "#A3A9AE" : ""}
>
{link.label}
{title}
</Link>
{disabled && (
<Text color={disabled ? "#A3A9AE" : ""}>{t("Settings:Disabled")}</Text>
)}
<div className="external-row-icons">
{link.locked && (
{isLocked && (
<IconButton
className="locked-icon"
size={16}
@ -113,11 +190,17 @@ const LinkRow = (props) => {
);
};
export default inject(({ dialogsStore }) => {
const { setEditLinkPanelIsVisible, setLinkIsEdit } = dialogsStore;
export default inject(({ auth, dialogsStore, publicRoomStore }) => {
const { selectionParentRoom } = auth.infoPanelStore;
const { setEditLinkPanelIsVisible, setLinkParams } = dialogsStore;
const { editExternalLink, externalLinks, setExternalLink } = publicRoomStore;
return {
setEditLinkPanelIsVisible,
setLinkIsEdit,
setLinkParams,
editExternalLink,
roomId: selectionParentRoom.id,
setExternalLink,
canLinkDelete: externalLinks.length >= 1,
};
})(withTranslation(["SharingPanel", "Files"])(observer(LinkRow)));
})(withTranslation(["SharingPanel", "Files", "Settings"])(observer(LinkRow)));

View File

@ -1,4 +1,5 @@
import React, { useState } from "react";
import { inject, observer } from "mobx-react";
import Text from "@docspace/components/text";
import IconButton from "@docspace/components/icon-button";
import toastr from "@docspace/components/toast/toastr";
@ -7,7 +8,7 @@ import PublicRoomBar from "./PublicRoomBar";
import { LinksBlock } from "./StyledPublicRoom";
import LinkRow from "./LinkRow";
const PublicRoomBlock = ({ t, onCopyLink }) => {
const PublicRoomBlock = ({ t, externalLinks, onCopyLink }) => {
const [barIsVisible, setBarVisible] = useState(true);
const onClose = () => {
@ -15,23 +16,7 @@ const PublicRoomBlock = ({ t, onCopyLink }) => {
toastr.success("onCloseBar");
};
const link = {
id: 1,
label: "link1",
isDisabled: false,
expiryDate: true,
locked: true,
};
const link2 = {
id: 2,
label: "link2",
isDisabled: true,
expiryDate: false,
locked: false,
};
const links = [link, link2];
const defaultLink = { id: 0, label: t("SharingPanel:ExternalLink") };
// const defaultLink = { id: 0, label: t("SharingPanel:ExternalLink") };
return (
<>
@ -54,13 +39,24 @@ const PublicRoomBlock = ({ t, onCopyLink }) => {
/>
</LinksBlock>
{links.length ? (
links.map((link) => <LinkRow link={link} key={link.id} />)
{externalLinks.length ? (
externalLinks.map(
(link) =>
!link.sharedTo.isTemplate && (
<LinkRow link={link} key={link?.sharedTo?.id} />
)
)
) : (
<LinkRow link={defaultLink} />
<>{/* <LinkRow link={defaultLink} /> */}</>
)}
</>
);
};
export default PublicRoomBlock;
export default inject(({ publicRoomStore }) => {
const { externalLinks } = publicRoomStore;
return {
externalLinks,
};
})(observer(PublicRoomBlock));

View File

@ -28,7 +28,6 @@ const InfoPanelHeaderContent = (props) => {
getIsAccounts,
getIsTrash,
isRootFolder,
isPublicRoom,
} = props;
const [isTablet, setIsTablet] = useState(false);
@ -66,7 +65,7 @@ const InfoPanelHeaderContent = (props) => {
const submenuData = [
{
id: "info_members",
name: isPublicRoom ? t("Common:LinksAndMembers") : t("Common:Members"),
name: t("Common:Members"),
onClick: setMembers,
content: null,
},
@ -146,10 +145,6 @@ export default inject(({ auth, selectedFolderStore }) => {
} = auth.infoPanelStore;
const { isRootFolder, roomType } = selectedFolderStore;
const isPublicRoom =
selection?.roomType === RoomsType.PublicRoom ||
roomType === RoomsType.PublicRoom;
return {
selection,
setIsVisible,
@ -162,6 +157,5 @@ export default inject(({ auth, selectedFolderStore }) => {
getIsAccounts,
getIsTrash,
isRootFolder,
isPublicRoom,
};
})(withTranslation(["Common", "InfoPanel"])(InfoPanelHeaderContent));

View File

@ -61,7 +61,7 @@ class DialogsStore {
createRoomConfirmDialogVisible = false;
changeUserTypeDialogVisible = false;
editLinkPanelIsVisible = false;
linkIsEdit = false;
linkParams = null;
constructor(
authStore,
@ -320,8 +320,8 @@ class DialogsStore {
this.editLinkPanelIsVisible = editLinkPanelIsVisible;
};
setLinkIsEdit = (linkIsEdit) => {
this.linkIsEdit = linkIsEdit;
setLinkParams = (linkParams) => {
this.linkParams = linkParams;
};
setUnsavedChangesDialog = (unsavedChangesDialogVisible) => {

View File

@ -0,0 +1,56 @@
import { makeAutoObservable } from "mobx";
import api from "@docspace/common/api";
class PublicRoomStore {
externalLinks = [];
constructor() {
makeAutoObservable(this);
}
getExternalLinks = async (roomId) => {
const type = 1;
const externalLinks = await api.rooms.getExternalLinks(roomId, type);
// this.externalLinks = externalLinks;
};
setExternalLink = (linkId, data) => {
const linkIndex = this.externalLinks.findIndex(
(l) => l.sharedTo.id === linkId
);
const dataLink = data.find((l) => l.sharedTo.id === linkId);
this.externalLinks[linkIndex] = dataLink;
};
setExternalLinks = (externalLinks) => {
this.externalLinks = externalLinks;
};
editExternalLink = (options) => {
const {
roomId,
linkId,
title,
access = 2,
expirationDate,
linkType = 1,
password,
disabled,
denyDownload,
} = options;
return api.rooms.editExternalLink(
roomId,
linkId,
title,
access,
expirationDate,
linkType,
password,
disabled,
denyDownload
);
};
}
export default PublicRoomStore;

View File

@ -34,6 +34,7 @@ import OformsStore from "./OformsStore";
import AccessRightsStore from "./AccessRightsStore";
import TableStore from "./TableStore";
import CreateEditRoomStore from "./CreateEditRoomStore";
import PublicRoomStore from "./PublicRoomStore";
const oformsStore = new OformsStore(authStore);
@ -160,6 +161,8 @@ const createEditRoomStore = new CreateEditRoomStore(
authStore.currentQuotaStore
);
const publicRoomStore = new PublicRoomStore();
const store = {
auth: authStore,
payments: paymentStore,
@ -195,6 +198,7 @@ const store = {
accessRightsStore,
createEditRoomStore,
publicRoomStore,
};
export default store;

View File

@ -323,3 +323,40 @@ export const acceptInvitationByLink = async () => {
return await request(options);
};
export function editExternalLink(
roomId,
linkId,
title,
access,
expirationDate,
linkType,
password,
disabled,
denyDownload
) {
return request({
method: "put",
url: `/files/rooms/${roomId}/links`,
data: {
linkId,
title,
access,
expirationDate,
linkType,
password,
disabled,
denyDownload,
},
});
}
export function getExternalLinks(roomId, type) {
const linkType = `?type=${type}`;
return request({
method: "get",
url: `files/rooms/${roomId}/links${linkType}`,
});
}

View File

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M2 8.00029C2 4.6863 4.68608 2 7.99857 2C11.3139 2 14 4.68685 14 8.00029C14 11.3126 11.3139 14 7.99857 14C6.28313 14 4.73839 13.2822 3.64247 12.1249C2.62256 11.0478 2 9.59875 2 8.00029ZM7.99857 0C3.58105 0 0 3.58219 0 8.00029C0 10.1304 0.83265 12.0663 2.19023 13.5C3.64805 15.0395 5.71117 16 7.99857 16C12.4184 16 16 12.4172 16 8.00029C16 3.58219 12.4184 0 7.99857 0ZM7.42453 11.2527L7.5358 11.3632L7.64758 11.2527L12.4849 6.41401L11.0705 5L7.53622 8.53504L5.41533 6.41473L4 7.82841L7.42453 11.2527Z" fill="#333333"/>
</svg>

After

Width:  |  Height:  |  Size: 669 B

View File

@ -146,7 +146,6 @@
"Megabyte": "MB",
"Member": "Member",
"Members": "Members",
"LinksAndMembers": "Links & Members",
"Name": "Name",
"NewDocument": "New document",
"NewFolder": "New folder",