mirror of
https://github.com/stonith404/pingvin-share.git
synced 2025-02-05 01:38:56 -05:00
feat(MyShares): show information about own share security options (#720)
* Provide security information about owned shares * Add visitor count display for shares with max views * Add password protection indicator to shares table * Remove validation from MyShareSecurityDTO * center lock icon and add spacing * refactor: run formatter --------- Co-authored-by: Elias Schneider <login@eliasschneider.com>
This commit is contained in:
parent
4d3aa398a2
commit
b58dcdba0b
7 changed files with 60 additions and 9 deletions
|
@ -2,6 +2,7 @@ import { Expose, plainToClass, Type } from "class-transformer";
|
||||||
import { ShareDTO } from "./share.dto";
|
import { ShareDTO } from "./share.dto";
|
||||||
import { FileDTO } from "../../file/dto/file.dto";
|
import { FileDTO } from "../../file/dto/file.dto";
|
||||||
import { OmitType } from "@nestjs/swagger";
|
import { OmitType } from "@nestjs/swagger";
|
||||||
|
import { MyShareSecurityDTO } from "./myShareSecurity.dto";
|
||||||
|
|
||||||
export class MyShareDTO extends OmitType(ShareDTO, [
|
export class MyShareDTO extends OmitType(ShareDTO, [
|
||||||
"files",
|
"files",
|
||||||
|
@ -21,6 +22,9 @@ export class MyShareDTO extends OmitType(ShareDTO, [
|
||||||
@Type(() => OmitType(FileDTO, ["share", "from"] as const))
|
@Type(() => OmitType(FileDTO, ["share", "from"] as const))
|
||||||
files: Omit<FileDTO, "share" | "from">[];
|
files: Omit<FileDTO, "share" | "from">[];
|
||||||
|
|
||||||
|
@Expose()
|
||||||
|
security?: MyShareSecurityDTO;
|
||||||
|
|
||||||
from(partial: Partial<MyShareDTO>) {
|
from(partial: Partial<MyShareDTO>) {
|
||||||
return plainToClass(MyShareDTO, partial, { excludeExtraneousValues: true });
|
return plainToClass(MyShareDTO, partial, { excludeExtraneousValues: true });
|
||||||
}
|
}
|
||||||
|
|
9
backend/src/share/dto/myShareSecurity.dto.ts
Normal file
9
backend/src/share/dto/myShareSecurity.dto.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import { Expose } from "class-transformer";
|
||||||
|
|
||||||
|
export class MyShareSecurityDTO {
|
||||||
|
@Expose()
|
||||||
|
passwordProtected: boolean;
|
||||||
|
|
||||||
|
@Expose()
|
||||||
|
maxViews: number;
|
||||||
|
}
|
|
@ -233,7 +233,7 @@ export class ShareService {
|
||||||
orderBy: {
|
orderBy: {
|
||||||
expiration: "desc",
|
expiration: "desc",
|
||||||
},
|
},
|
||||||
include: { recipients: true, files: true },
|
include: { recipients: true, files: true, security: true },
|
||||||
});
|
});
|
||||||
|
|
||||||
return shares.map((share) => {
|
return shares.map((share) => {
|
||||||
|
@ -241,6 +241,10 @@ export class ShareService {
|
||||||
...share,
|
...share,
|
||||||
size: share.files.reduce((acc, file) => acc + parseInt(file.size), 0),
|
size: share.files.reduce((acc, file) => acc + parseInt(file.size), 0),
|
||||||
recipients: share.recipients.map((recipients) => recipients.email),
|
recipients: share.recipients.map((recipients) => recipients.email),
|
||||||
|
security: {
|
||||||
|
maxViews: share.security?.maxViews,
|
||||||
|
passwordProtected: !!share.security?.password,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,6 +124,8 @@ export default {
|
||||||
"account.shares.table.expiresAt": "Läuft ab am",
|
"account.shares.table.expiresAt": "Läuft ab am",
|
||||||
"account.shares.table.createdAt": "Angelegt am",
|
"account.shares.table.createdAt": "Angelegt am",
|
||||||
"account.shares.table.size": "Größe",
|
"account.shares.table.size": "Größe",
|
||||||
|
"account.shares.table.password-protected": "Passwortgeschützt",
|
||||||
|
"account.shares.table.visitor-count": "{count} von {max}",
|
||||||
"account.shares.table.expiry-never": "nie",
|
"account.shares.table.expiry-never": "nie",
|
||||||
"account.shares.modal.share-informations": "Teile deine Information",
|
"account.shares.modal.share-informations": "Teile deine Information",
|
||||||
"account.shares.modal.share-link": "Freigabe teilen",
|
"account.shares.modal.share-link": "Freigabe teilen",
|
||||||
|
|
|
@ -158,6 +158,8 @@ export default {
|
||||||
"account.shares.table.expiresAt": "Expires on",
|
"account.shares.table.expiresAt": "Expires on",
|
||||||
"account.shares.table.createdAt": "Created on",
|
"account.shares.table.createdAt": "Created on",
|
||||||
"account.shares.table.size": "Size",
|
"account.shares.table.size": "Size",
|
||||||
|
"account.shares.table.password-protected": "Password protected",
|
||||||
|
"account.shares.table.visitor-count": "{count} of {max}",
|
||||||
"account.shares.table.expiry-never": "Never",
|
"account.shares.table.expiry-never": "Never",
|
||||||
|
|
||||||
"account.shares.modal.share-informations": "Share informations",
|
"account.shares.modal.share-informations": "Share informations",
|
||||||
|
|
|
@ -15,7 +15,7 @@ import { useModals } from "@mantine/modals";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { TbEdit, TbInfoCircle, TbLink, TbTrash } from "react-icons/tb";
|
import { TbEdit, TbInfoCircle, TbLink, TbLock, TbTrash } from "react-icons/tb";
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
import Meta from "../../components/Meta";
|
import Meta from "../../components/Meta";
|
||||||
import showShareInformationsModal from "../../components/account/showShareInformationsModal";
|
import showShareInformationsModal from "../../components/account/showShareInformationsModal";
|
||||||
|
@ -85,13 +85,37 @@ const MyShares = () => {
|
||||||
<tbody>
|
<tbody>
|
||||||
{shares.map((share) => (
|
{shares.map((share) => (
|
||||||
<tr key={share.id}>
|
<tr key={share.id}>
|
||||||
<td>{share.id}</td>
|
|
||||||
<td>{share.name}</td>
|
|
||||||
<td>{share.views}</td>
|
|
||||||
<td>
|
<td>
|
||||||
{moment(share.expiration).unix() === 0
|
<Group spacing="xs">
|
||||||
? <FormattedMessage id="account.shares.table.expiry-never" />
|
{share.id}{" "}
|
||||||
: moment(share.expiration).format("LLL")}
|
{share.security.passwordProtected && (
|
||||||
|
<TbLock
|
||||||
|
color="orange"
|
||||||
|
title={t("account.shares.table.password-protected")}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Group>
|
||||||
|
</td>
|
||||||
|
<td>{share.name}</td>
|
||||||
|
<td>
|
||||||
|
{share.security.maxViews ? (
|
||||||
|
<FormattedMessage
|
||||||
|
id="account.shares.table.visitor-count"
|
||||||
|
values={{
|
||||||
|
count: share.views,
|
||||||
|
max: share.security.maxViews,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
share.views
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{moment(share.expiration).unix() === 0 ? (
|
||||||
|
<FormattedMessage id="account.shares.table.expiry-never" />
|
||||||
|
) : (
|
||||||
|
moment(share.expiration).format("LLL")
|
||||||
|
)}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<Group position="right">
|
<Group position="right">
|
||||||
|
|
|
@ -34,9 +34,10 @@ export type ShareMetaData = {
|
||||||
isZipReady: boolean;
|
isZipReady: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type MyShare = Share & {
|
export type MyShare = Omit<Share, "hasPassword"> & {
|
||||||
views: number;
|
views: number;
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
|
security: MyShareSecurity;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type MyReverseShare = {
|
export type MyReverseShare = {
|
||||||
|
@ -52,3 +53,8 @@ export type ShareSecurity = {
|
||||||
maxViews?: number;
|
maxViews?: number;
|
||||||
password?: string;
|
password?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type MyShareSecurity = {
|
||||||
|
passwordProtected: boolean;
|
||||||
|
maxViews: number;
|
||||||
|
};
|
||||||
|
|
Loading…
Add table
Reference in a new issue