mirror of
https://github.com/BrenBroZAYT/gameyfin.git
synced 2026-06-15 08:15:37 +00:00
Implement notification templates
Refactor notification providers to be more expandable in the future Minor layout changes
This commit is contained in:
@@ -1,24 +1,38 @@
|
||||
import React from "react";
|
||||
import React, {useState} from "react";
|
||||
import withConfigPage from "Frontend/components/administration/withConfigPage";
|
||||
import * as Yup from 'yup';
|
||||
import ConfigFormField from "Frontend/components/administration/ConfigFormField";
|
||||
import Section from "Frontend/components/general/Section";
|
||||
import {Button, Input, Select, SelectItem} from "@nextui-org/react";
|
||||
import {NotificationEndpoint} from "Frontend/generated/endpoints";
|
||||
import EmailCredentialsDto from "Frontend/generated/de/grimsi/gameyfin/notifications/dto/EmailCredentialsDto";
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
Textarea,
|
||||
useDisclosure
|
||||
} from "@nextui-org/react";
|
||||
import {ConfigEndpoint, NotificationEndpoint} from "Frontend/generated/endpoints";
|
||||
import {toast} from "sonner";
|
||||
import ConfigEntryDto from "Frontend/generated/de/grimsi/gameyfin/config/dto/ConfigEntryDto";
|
||||
import {Pencil} from "@phosphor-icons/react";
|
||||
|
||||
function NotificationManagementLayout({getConfig, formik}: any) {
|
||||
function NotificationManagementLayout({getConfig, getConfigs, formik}: any) {
|
||||
|
||||
async function testMailSettings() {
|
||||
const credentials: EmailCredentialsDto = {
|
||||
host: formik.values.notifications.email.host,
|
||||
port: formik.values.notifications.email.port,
|
||||
username: formik.values.notifications.email.username,
|
||||
password: formik.values.notifications.email.password
|
||||
const {isOpen, onOpen, onOpenChange} = useDisclosure();
|
||||
const [selectedTemplate, setSelectedTemplate] = useState<ConfigEntryDto | null>(null);
|
||||
|
||||
async function verifyCredentials(provider: string) {
|
||||
const credentials: Record<string, any> = {
|
||||
host: formik.values.notifications.providers.email.host,
|
||||
port: formik.values.notifications.providers.email.port,
|
||||
username: formik.values.notifications.providers.email.username,
|
||||
password: formik.values.notifications.providers.email.password
|
||||
}
|
||||
|
||||
const areCredentialsValid = await NotificationEndpoint.verifyEmailCredentials(credentials);
|
||||
const areCredentialsValid = await NotificationEndpoint.verifyCredentials(provider, credentials);
|
||||
|
||||
if (areCredentialsValid) {
|
||||
toast.success("Credentials are valid")
|
||||
@@ -27,6 +41,19 @@ function NotificationManagementLayout({getConfig, formik}: any) {
|
||||
}
|
||||
}
|
||||
|
||||
async function openModal(template: ConfigEntryDto) {
|
||||
let templateContent = await ConfigEndpoint.get(template.key);
|
||||
setSelectedTemplate({
|
||||
...template,
|
||||
value: templateContent
|
||||
});
|
||||
onOpen();
|
||||
}
|
||||
|
||||
async function saveTemplate(template: ConfigEntryDto) {
|
||||
await ConfigEndpoint.set(template.key, template.value);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col">
|
||||
<div className="flex flex-row">
|
||||
@@ -36,36 +63,83 @@ function NotificationManagementLayout({getConfig, formik}: any) {
|
||||
<div className="flex flex-row gap-8">
|
||||
<div className="flex flex-col flex-1">
|
||||
<Section title="E-Mail"/>
|
||||
<ConfigFormField configElement={getConfig("notifications.email.host")}
|
||||
<ConfigFormField configElement={getConfig("notifications.providers.email.host")}
|
||||
isDisabled={!formik.values.notifications.enabled}/>
|
||||
<ConfigFormField configElement={getConfig("notifications.email.port")}
|
||||
<ConfigFormField configElement={getConfig("notifications.providers.email.port")}
|
||||
isDisabled={!formik.values.notifications.enabled}/>
|
||||
<ConfigFormField configElement={getConfig("notifications.email.username")}
|
||||
<ConfigFormField configElement={getConfig("notifications.providers.email.username")}
|
||||
isDisabled={!formik.values.notifications.enabled}/>
|
||||
<ConfigFormField configElement={getConfig("notifications.email.password")}
|
||||
<ConfigFormField configElement={getConfig("notifications.providers.email.password")}
|
||||
type="password"
|
||||
isDisabled={!formik.values.notifications.enabled}/>
|
||||
<Button onPress={testMailSettings}
|
||||
<Button onPress={() => verifyCredentials("email")}
|
||||
isDisabled={!(
|
||||
formik.values.notifications.enabled &&
|
||||
formik.values.notifications.email.host &&
|
||||
formik.values.notifications.email.port &&
|
||||
formik.values.notifications.email.username)}>Test</Button>
|
||||
formik.values.notifications.providers.email.host &&
|
||||
formik.values.notifications.providers.email.port &&
|
||||
formik.values.notifications.providers.email.username)}>Test</Button>
|
||||
</div>
|
||||
<div className="flex flex-col flex-1">
|
||||
<Section title="Push"/>
|
||||
{/* TODO: Evaluate need and options if need is given */}
|
||||
<Select className="mt-2 mb-10"
|
||||
label="Push notification provider" defaultSelectedKeys={["pushbullet"]}
|
||||
isDisabled>
|
||||
<SelectItem key="pushbullet">Pushbullet</SelectItem>
|
||||
</Select>
|
||||
<Input className="mt-2 mb-10" label="Access Token" type="password" isDisabled/>
|
||||
<Input className="mt-2 mb-10" label="Channel tag" type="text" isDisabled/>
|
||||
<Section title="Message Templates"/>
|
||||
<div className="flex flex-col gap-4">
|
||||
{getConfigs("notifications.templates").map((template: ConfigEntryDto) =>
|
||||
<Card className="flex flex-row items-center gap-2 p-4">
|
||||
<Button isIconOnly
|
||||
size="sm"
|
||||
onPress={() => openModal(template)}
|
||||
>
|
||||
<Pencil/>
|
||||
</Button>
|
||||
<p className="text-lg">{template.description}</p>
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Modal isOpen={isOpen} onOpenChange={onOpenChange} size="5xl">
|
||||
<ModalContent>
|
||||
{(onClose) => (
|
||||
<>
|
||||
<ModalHeader
|
||||
className="flex flex-col gap-1">Edit {selectedTemplate?.description.toLowerCase()}</ModalHeader>
|
||||
<ModalBody>
|
||||
<Textarea
|
||||
size="lg"
|
||||
autoFocus
|
||||
disableAutosize
|
||||
value={selectedTemplate?.value}
|
||||
onChange={(e) => {
|
||||
if (selectedTemplate?.key) setSelectedTemplate({
|
||||
...selectedTemplate,
|
||||
value: e.target.value
|
||||
})
|
||||
}}
|
||||
classNames={{
|
||||
input: "resize-y min-h-[500px]"
|
||||
}}
|
||||
/>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button color="danger" variant="light" onPress={onClose}>
|
||||
Close
|
||||
</Button>
|
||||
<Button color="primary" onPress={async () => {
|
||||
if (selectedTemplate) {
|
||||
await saveTemplate(selectedTemplate);
|
||||
toast.success("Template saved")
|
||||
onClose();
|
||||
}
|
||||
}}>
|
||||
Save
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</>
|
||||
)}
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ function SsoManagementLayout({getConfig, formik, setSaveMessage}: any) {
|
||||
<Button
|
||||
isDisabled={isAutoPopulateDisabled()}
|
||||
onPress={autoPopulate}
|
||||
className="h-14 mt-2"><MagicWand className="min-w-5"/> Auto-populate</Button>
|
||||
className="h-14"><MagicWand className="min-w-5"/> Auto-populate</Button>
|
||||
</div>
|
||||
<ConfigFormField configElement={getConfig("sso.oidc.authorize-url")}
|
||||
isDisabled={!formik.values.sso.oidc.enabled}/>
|
||||
|
||||
@@ -44,6 +44,10 @@ export default function withConfigPage(WrappedComponent: React.ComponentType<any
|
||||
return configDtos.find((configDto: ConfigEntryDto) => configDto.key === key);
|
||||
}
|
||||
|
||||
function getConfigs(prefix: string) {
|
||||
return configDtos.filter((configDto: ConfigEntryDto) => configDto.key?.startsWith(prefix));
|
||||
}
|
||||
|
||||
function toNestedConfig(configArray: ConfigEntryDto[]): NestedConfig {
|
||||
const nestedConfig: NestedConfig = {};
|
||||
|
||||
@@ -143,7 +147,10 @@ export default function withConfigPage(WrappedComponent: React.ComponentType<any
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<WrappedComponent {...props} getConfig={getConfig} formik={formik}
|
||||
<WrappedComponent {...props}
|
||||
getConfig={getConfig}
|
||||
getConfigs={getConfigs}
|
||||
formik={formik}
|
||||
setSaveMessage={setSaveMessage}/>
|
||||
</Form>
|
||||
)}
|
||||
|
||||
@@ -7,7 +7,7 @@ const CheckboxInput = ({label, ...props}) => {
|
||||
const [field] = useField(props);
|
||||
|
||||
return (
|
||||
<div className="flex flex-row flex-1 items-center gap-2 my-2">
|
||||
<div className="flex flex-row flex-1 items-center gap-2">
|
||||
<Checkbox
|
||||
{...field}
|
||||
{...props}
|
||||
|
||||
@@ -9,7 +9,7 @@ const Input = ({label, ...props}) => {
|
||||
const [field, meta] = useField(props);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col flex-1 items-start gap-2 my-2">
|
||||
<div className="flex flex-col flex-1 items-start gap-2">
|
||||
<NextUiInput
|
||||
{...props}
|
||||
{...field}
|
||||
|
||||
@@ -7,7 +7,7 @@ const SelectInput = ({label, values, ...props}) => {
|
||||
const [field] = useField(props);
|
||||
|
||||
return (
|
||||
<div className="flex flex-row flex-1 justify-center gap-2 my-2">
|
||||
<div className="flex flex-row flex-1 justify-center gap-2">
|
||||
<Select
|
||||
{...field}
|
||||
{...props}
|
||||
|
||||
Reference in New Issue
Block a user