Skip to content

Commit

Permalink
feat(server): add database migrations and initial notifications
Browse files Browse the repository at this point in the history
  • Loading branch information
KyleKincer committed Jun 24, 2024
1 parent d24c5cb commit 201b822
Show file tree
Hide file tree
Showing 9 changed files with 132 additions and 33 deletions.
3 changes: 3 additions & 0 deletions server/entity/MediaRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,9 @@ export class MediaRequest {
@Column({ default: false })
public isAutoRequest: boolean;

@Column({ nullable: true, length: 140 })
public adminMessage?: string;

constructor(init?: Partial<MediaRequest>) {
Object.assign(this, init);
}
Expand Down
1 change: 1 addition & 0 deletions server/lib/notifications/agents/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export interface NotificationPayload {
request?: MediaRequest;
issue?: Issue;
comment?: IssueComment;
adminMessage?: string;
}

export abstract class BaseAgent<T extends NotificationAgentConfig> {
Expand Down
8 changes: 8 additions & 0 deletions server/lib/notifications/agents/discord.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,14 @@ class DiscordAgent
inline: true,
});
}

if (payload.request.adminMessage) {
fields.push({
name: 'Admin Message',
value: payload.request.adminMessage,
inline: true,
});
}
} else if (payload.comment) {
fields.push({
name: `Comment from ${payload.comment.user.displayName}`,
Expand Down
7 changes: 6 additions & 1 deletion server/lib/notifications/agents/webpush.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ class WebPushAgent
: 'series'
: undefined;
const is4k = payload.request?.is4k;
const adminMessage = payload.adminMessage
? payload.adminMessage
: undefined;

const issueType = payload.issue
? payload.issue.issueType !== IssueType.OTHER
Expand Down Expand Up @@ -80,7 +83,9 @@ class WebPushAgent
}${mediaType} request is now available!`;
break;
case Notification.MEDIA_DECLINED:
message = `Your ${is4k ? '4K ' : ''}${mediaType} request was declined.`;
message = `Your ${is4k ? '4K ' : ''}${mediaType} request was declined${
adminMessage ? `: ${adminMessage}` : '.'
}.`;
break;
case Notification.MEDIA_FAILED:
message = `Failed to process ${is4k ? '4K ' : ''}${mediaType} request.`;
Expand Down
17 changes: 17 additions & 0 deletions server/migration/1702596723101-AddAdminMessage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { MigrationInterface, QueryRunner } from 'typeorm';

export class AddAdminMessage1702596723101 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
ALTER TABLE media_requests
ADD adminMessage VARCHAR(140)
`);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
ALTER TABLE media_requests
DROP COLUMN adminMessage
`);
}
}
6 changes: 6 additions & 0 deletions server/routes/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,12 @@ requestRoutes.post<{

request.status = newStatus;
request.modifiedBy = req.user;

const adminMessage = req.body.adminMessage;
if (adminMessage) {
request.adminMessage = adminMessage;
}

await requestRepository.save(request);

return res.status(200).json(request);
Expand Down
4 changes: 4 additions & 0 deletions server/templates/email/media-request/html.pug
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ div(style='display: block; background-color: #111827; padding: 2.5rem 0;')
td(style='font-size: .85em; color: #9ca3af; line-height: 1em; vertical-align: bottom; margin-right: 1rem')
span
| #{timestamp}
if adminMessage
tr
td(style='font-size: 1.0em; color: #9ca3af; line-height: 1.5em; vertical-align: top; margin-right: 1rem')
| #{adminMessage}
if actionUrl
tr
td
Expand Down
99 changes: 72 additions & 27 deletions src/components/DeclineRequestModal/index.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,39 @@
import { Transition } from '@headlessui/react';
import { useState } from 'react';
import Modal from '@app/components/Common/Modal';
import useSWR from 'swr';
import globalMessages from '@app/i18n/globalMessages';
import { Transition } from '@headlessui/react';
import type { MovieDetails } from '@server/models/Movie';
import { Field, Form, Formik } from 'formik';
import React from 'react';
import { useIntl } from 'react-intl';
import globalMessages from '@app/i18n/globalMessages';
import useSWR from 'swr';
import * as Yup from 'yup';

interface DeclineRequestModalProps {
show: boolean;
tmdbId: number;
onDecline?: (declineMessage: string) => void;
onDecline: (declineMessage: string) => void;
onCancel?: () => void;
}

const validationSchema = Yup.object().shape({
declineMessage: Yup.string().max(140, 'Message is too long'),
});

const DeclineRequestModal = ({
show,
tmdbId,
onDecline,
onCancel,
}: DeclineRequestModalProps) => {
const intl = useIntl();
const [declineMessage, setDeclineMessage] = useState('');
const { data, error } = useSWR<MovieDetails>(`/api/v1/movie/${tmdbId}`, {
revalidateOnMount: true,
});

const handleDecline = () => {
if (onDecline) {
onDecline(declineMessage);
const [characterCount, setCharacterCount] = React.useState(0);
const handleCancel = () => {
setCharacterCount(0);
if (onCancel) {
onCancel();
}
};

Expand All @@ -42,23 +48,62 @@ const DeclineRequestModal = ({
leaveTo="opacity-0"
show={show}
>
<Modal
loading={!data && !error}
title="Decline Request"
subTitle={data?.title}
onCancel={onCancel}
onOk={handleDecline}
okText={intl.formatMessage(globalMessages.decline)}
okButtonType="danger"
cancelText={intl.formatMessage(globalMessages.cancel)}
backdrop={`https://image.tmdb.org/t/p/w1920_and_h800_multi_faces/${data?.backdropPath}`}
>
<textarea
value={declineMessage}
onChange={(e) => setDeclineMessage(e.target.value)}
placeholder="Optional decline message"
/>
</Modal>
<Formik
initialValues={{ declineMessage: '' }}
validationSchema={validationSchema}
onSubmit={(values, { setSubmitting }) => {
setSubmitting(true);
onDecline(values.declineMessage);
}}
>
{({ errors, touched, handleSubmit, setFieldValue, values }) => {
const handleInputChange = (
event: React.ChangeEvent<HTMLTextAreaElement>
) => {
const { value } = event.target;
setFieldValue('declineMessage', value);
setCharacterCount(value.length);
};

return (
<Modal
loading={!data && !error}
title="Decline Request"
subTitle={data?.title}
onCancel={handleCancel}
onOk={() => {
handleSubmit();
}}
okText={intl.formatMessage(globalMessages.decline)}
okButtonType="danger"
cancelText={intl.formatMessage(globalMessages.cancel)}
backdrop={`https://image.tmdb.org/t/p/w1920_and_h800_multi_faces/${data?.backdropPath}`}
>
<Form>
<Field
name="declineMessage"
as="textarea"
placeholder="Optional decline message"
onChange={handleInputChange}
value={values.declineMessage}
/>
{errors.declineMessage && touched.declineMessage ? (
<div className="pt-2 text-red-500">
{errors.declineMessage}
</div>
) : null}
<div
className={`pt-2 text-xs font-light ${
characterCount > 140 ? 'text-red-500' : 'text-gray-300'
}`}
>
{characterCount}/140
</div>
</Form>
</Modal>
);
}}
</Formik>
</Transition>
);
};
Expand Down
20 changes: 15 additions & 5 deletions src/components/RequestList/RequestItem/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -310,13 +310,13 @@ const RequestItem = ({ request, revalidateList }: RequestItemProps) => {

const declineRequest = async (declineMessage: string) => {
const response = await axios.post(`/api/v1/request/${request.id}/decline`, {
message: declineMessage,
adminMessage: declineMessage,
});

if (response) {
revalidate();
}
}
};

const modifyRequest = async (type: 'approve' | 'decline') => {
const response = await axios.post(`/api/v1/request/${request.id}/${type}`);
Expand Down Expand Up @@ -393,11 +393,10 @@ const RequestItem = ({ request, revalidateList }: RequestItemProps) => {
onDecline={(declineMessage) => {
declineRequest(declineMessage);
setShowDeclineModal(false);
}
}
}}
onCancel={() => setShowDeclineModal(false)}
/>
<div className="relative flex w-full flex-col justify-between overflow-hidden rounded-xl bg-gray-800 py-4 text-gray-400 shadow-md ring-1 ring-gray-700 xl:h-28 xl:flex-row">
<div className="relative flex w-full flex-col justify-between overflow-hidden rounded-xl bg-gray-800 py-4 text-gray-400 shadow-md ring-1 ring-gray-700 xl:flex-row">
{title.backdropPath && (
<div className="absolute inset-0 z-0 w-full bg-cover bg-center xl:w-2/3">
<CachedImage
Expand Down Expand Up @@ -620,6 +619,17 @@ const RequestItem = ({ request, revalidateList }: RequestItemProps) => {
</span>
</div>
)}
{requestData.adminMessage && (
<div className="card-field">
<span className="card-field-name">
{requestData.status === MediaRequestStatus.DECLINED && (
<span className="flex truncate whitespace-normal text-sm font-light italic text-gray-300">
&quot;{requestData.adminMessage}&quot;
</span>
)}
</span>
</div>
)}
</div>
</div>
<div className="z-10 mt-4 flex w-full flex-col justify-center space-y-2 pl-4 pr-4 xl:mt-0 xl:w-96 xl:items-end xl:pl-0">
Expand Down

0 comments on commit 201b822

Please sign in to comment.