1
0
mirror of https://github.com/stonith404/pingvin-share.git synced 2024-06-30 14:40:10 +02:00

feat: add name property to share (#462)

* add name property to share

* refactor: run formatter

* tests: adapt system tests

* tests: adapt second system test
This commit is contained in:
Elias Schneider 2024-05-03 18:12:26 +03:00 committed by GitHub
parent 0e12ba87bc
commit b717663b5c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 59 additions and 39 deletions

View File

@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Share" ADD COLUMN "name" TEXT;

View File

@ -75,6 +75,7 @@ model Share {
id String @id @default(uuid())
createdAt DateTime @default(now())
name String?
uploadLocked Boolean @default(false)
isZipReady Boolean @default(false)
views Int @default(0)

View File

@ -13,7 +13,7 @@ export class ReverseShareTokenWithShares extends OmitType(ReverseShareDTO, [
@Type(() => OmitType(MyShareDTO, ["recipients", "hasPassword"] as const))
shares: Omit<
MyShareDTO,
"recipients" | "files" | "from" | "fromList" | "hasPassword"
"recipients" | "files" | "from" | "fromList" | "hasPassword" | "size"
>[];
@Expose()

View File

@ -18,6 +18,10 @@ export class CreateShareDTO {
@Length(3, 50)
id: string;
@Length(3, 30)
@IsOptional()
name: string;
@IsString()
expiration: string;

View File

@ -6,6 +6,9 @@ export class ShareDTO {
@Expose()
id: string;
@Expose()
name?: string;
@Expose()
expiration: Date;
@ -23,6 +26,9 @@ export class ShareDTO {
@Expose()
hasPassword: boolean;
@Expose()
size: number;
from(partial: Partial<ShareDTO>) {
return plainToClass(ShareDTO, partial, { excludeExtraneousValues: true });
}

View File

@ -214,6 +214,7 @@ export class ShareService {
return shares.map((share) => {
return {
...share,
size: share.files.reduce((acc, file) => acc + parseInt(file.size), 0),
recipients: share.recipients.map((recipients) => recipients.email),
};
});

View File

@ -432,7 +432,7 @@
" const responseBody = pm.response.json();",
" pm.expect(responseBody).to.have.property(\"id\")",
" pm.expect(responseBody).to.have.property(\"expiration\")",
" pm.expect(Object.keys(responseBody).length).be.equal(3)",
" pm.expect(Object.keys(responseBody).length).be.equal(4)",
"});",
""
],
@ -626,7 +626,7 @@
" const responseBody = pm.response.json();",
" pm.expect(responseBody).to.have.property(\"id\")",
" pm.expect(responseBody).to.have.property(\"expiration\")",
" pm.expect(Object.keys(responseBody).length).be.equal(3)",
" pm.expect(Object.keys(responseBody).length).be.equal(4)",
"});",
""
],

View File

@ -17,13 +17,9 @@ const showShareInformationsModal = (
const t = translateOutsideContext();
const link = `${appUrl}/s/${share.id}`;
let shareSize: number = 0;
for (let file of share.files as FileMetaData[])
shareSize += parseInt(file.size);
const formattedShareSize = byteToHumanSizeString(shareSize);
const formattedShareSize = byteToHumanSizeString(share.size);
const formattedMaxShareSize = byteToHumanSizeString(maxShareSize);
const shareSizeProgress = (shareSize / maxShareSize) * 100;
const shareSizeProgress = (share.size / maxShareSize) * 100;
const formattedCreatedAt = moment(share.createdAt).format("LLL");
const formattedExpiration =
@ -42,12 +38,18 @@ const showShareInformationsModal = (
</b>
{share.id}
</Text>
<Text size="sm">
<b>
<FormattedMessage id="account.shares.table.name" />:{" "}
</b>
{share.name || "-"}
</Text>
<Text size="sm">
<b>
<FormattedMessage id="account.shares.table.description" />:{" "}
</b>
{share.description || "No description"}
{share.description || "-"}
</Text>
<Text size="sm">
@ -75,15 +77,15 @@ const showShareInformationsModal = (
</Text>
<Flex align="center" justify="center">
{shareSize / maxShareSize < 0.1 && (
{share.size / maxShareSize < 0.1 && (
<Text size="xs" style={{ marginRight: "4px" }}>
{formattedShareSize}
</Text>
)}
<Progress
value={shareSizeProgress}
label={shareSize / maxShareSize >= 0.1 ? formattedShareSize : ""}
style={{ width: shareSize / maxShareSize < 0.1 ? "70%" : "80%" }}
label={share.size / maxShareSize >= 0.1 ? formattedShareSize : ""}
style={{ width: share.size / maxShareSize < 0.1 ? "70%" : "80%" }}
size="xl"
radius="xl"
/>

View File

@ -92,11 +92,16 @@ const CreateUploadModalBody = ({
.matches(new RegExp("^[a-zA-Z0-9_-]*$"), {
message: t("upload.modal.link.error.invalid"),
}),
name: yup
.string()
.transform((value) => value || undefined)
.min(3, t("common.error.too-short", { length: 3 }))
.max(30, t("common.error.too-long", { length: 30 })),
password: yup
.string()
.transform((value) => value || undefined)
.min(3)
.max(30),
.min(3, t("common.error.too-short", { length: 3 }))
.max(30, t("common.error.too-long", { length: 30 })),
maxViews: yup
.number()
.transform((value) => value || undefined)
@ -105,6 +110,7 @@ const CreateUploadModalBody = ({
const form = useForm({
initialValues: {
name: undefined,
link: generatedLink,
recipients: [] as string[],
password: undefined,
@ -154,6 +160,7 @@ const CreateUploadModalBody = ({
uploadCallback(
{
id: values.link,
name: values.name,
expiration: expirationString,
recipients: values.recipients,
description: values.description,
@ -308,14 +315,21 @@ const CreateUploadModalBody = ({
<Accordion>
<Accordion.Item value="description" sx={{ borderBottom: "none" }}>
<Accordion.Control>
<FormattedMessage id="upload.modal.accordion.description.title" />
<FormattedMessage id="upload.modal.accordion.name-and-description.title" />
</Accordion.Control>
<Accordion.Panel>
<Stack align="stretch">
<TextInput
variant="filled"
placeholder={t(
"upload.modal.accordion.name-and-description.name.placeholder",
)}
{...form.getInputProps("name")}
/>
<Textarea
variant="filled"
placeholder={t(
"upload.modal.accordion.description.placeholder",
"upload.modal.accordion.name-and-description.description.placeholder",
)}
{...form.getInputProps("description")}
/>

View File

@ -307,8 +307,9 @@ export default {
"upload.modal.expires.year-singular": "Year",
"upload.modal.expires.year-plural": "Years",
"upload.modal.accordion.description.title": "Description",
"upload.modal.accordion.description.placeholder":
"upload.modal.accordion.name-and-description.title": "Name and description",
"upload.modal.accordion.name-and-description.name.placeholder": "Name",
"upload.modal.accordion.name-and-description.description.placeholder":
"Note for the recipients of this share",
"upload.modal.accordion.email.title": "Email recipients",

View File

@ -68,15 +68,12 @@ const MyShares = () => {
<Table>
<thead>
<tr>
<th>
<FormattedMessage id="account.shares.table.id" />
</th>
<th>
<FormattedMessage id="account.shares.table.name" />
</th>
<MediaQuery smallerThan="md" styles={{ display: "none" }}>
<th>
<FormattedMessage id="account.shares.table.description" />
</th>
</MediaQuery>
<th>
<FormattedMessage id="account.shares.table.visitors" />
</th>
@ -90,18 +87,7 @@ const MyShares = () => {
{shares.map((share) => (
<tr key={share.id}>
<td>{share.id}</td>
<MediaQuery smallerThan="sm" styles={{ display: "none" }}>
<td
style={{
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
maxWidth: "300px",
}}
>
{share.description || ""}
</td>
</MediaQuery>
<td>{share.name}</td>
<td>{share.views}</td>
<td>
{moment(share.expiration).unix() === 0

View File

@ -91,13 +91,13 @@ const Share = ({ shareId }: { shareId: string }) => {
return (
<>
<Meta
title={t("share.title", { shareId })}
title={t("share.title", { shareId: share?.name || shareId })}
description={t("share.description")}
/>
<Group position="apart" mb="lg">
<Box style={{ maxWidth: "70%" }}>
<Title order={3}>{share?.id}</Title>
<Title order={3}>{share?.name || share?.id}</Title>
<Text size="sm">{share?.description}</Text>
</Box>
{share?.files.length > 1 && <DownloadAllButton shareId={shareId} />}

View File

@ -2,15 +2,18 @@ import User from "./user.type";
export type Share = {
id: string;
name?: string;
files: any;
creator: User;
description?: string;
expiration: Date;
size: number;
hasPassword: boolean;
};
export type CreateShare = {
id: string;
name?: string;
description?: string;
recipients: string[];
expiration: string;