mirror of
https://github.com/sct/overseerr.git
synced 2025-12-27 08:45:06 +01:00
chore: merge upstream (#2024)
This commit is contained in:
@@ -9,17 +9,22 @@ import useSettings from '@app/hooks/useSettings';
|
||||
import { useUser } from '@app/hooks/useUser';
|
||||
import globalMessages from '@app/i18n/globalMessages';
|
||||
import defineMessages from '@app/utils/defineMessages';
|
||||
import {
|
||||
getPushSubscription,
|
||||
subscribeToPushNotifications,
|
||||
unsubscribeToPushNotifications,
|
||||
verifyPushSubscription,
|
||||
} from '@app/utils/pushSubscriptionHelpers';
|
||||
import { ArrowDownOnSquareIcon } from '@heroicons/react/24/outline';
|
||||
import {
|
||||
CloudArrowDownIcon,
|
||||
CloudArrowUpIcon,
|
||||
} from '@heroicons/react/24/solid';
|
||||
import type { UserPushSubscription } from '@server/entity/UserPushSubscription';
|
||||
import type { UserSettingsNotificationsResponse } from '@server/interfaces/api/userSettingsInterfaces';
|
||||
import axios from 'axios';
|
||||
import { Form, Formik } from 'formik';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { useToasts } from 'react-toast-notifications';
|
||||
import useSWR, { mutate } from 'swr';
|
||||
@@ -53,6 +58,7 @@ const UserWebPushSettings = () => {
|
||||
const { user } = useUser({ id: Number(router.query.userId) });
|
||||
const { currentSettings } = useSettings();
|
||||
const [webPushEnabled, setWebPushEnabled] = useState(false);
|
||||
const [subEndpoint, setSubEndpoint] = useState<string | null>(null);
|
||||
const {
|
||||
data,
|
||||
error,
|
||||
@@ -72,141 +78,122 @@ const UserWebPushSettings = () => {
|
||||
|
||||
// Subscribes to the push manager
|
||||
// Will only add to the database if subscribing for the first time
|
||||
const enablePushNotifications = () => {
|
||||
if ('serviceWorker' in navigator && user?.id) {
|
||||
navigator.serviceWorker
|
||||
.getRegistration('/sw.js')
|
||||
.then(async (registration) => {
|
||||
if (currentSettings.enablePushRegistration) {
|
||||
const sub = await registration?.pushManager.subscribe({
|
||||
userVisibleOnly: true,
|
||||
applicationServerKey: currentSettings.vapidPublic,
|
||||
});
|
||||
const parsedSub = JSON.parse(JSON.stringify(sub));
|
||||
const enablePushNotifications = async () => {
|
||||
try {
|
||||
const isSubscribed = await subscribeToPushNotifications(
|
||||
user?.id,
|
||||
currentSettings
|
||||
);
|
||||
|
||||
if (parsedSub.keys.p256dh && parsedSub.keys.auth) {
|
||||
await axios.post('/api/v1/user/registerPushSubscription', {
|
||||
endpoint: parsedSub.endpoint,
|
||||
p256dh: parsedSub.keys.p256dh,
|
||||
auth: parsedSub.keys.auth,
|
||||
userAgent: navigator.userAgent,
|
||||
});
|
||||
setWebPushEnabled(true);
|
||||
addToast(intl.formatMessage(messages.webpushhasbeenenabled), {
|
||||
appearance: 'success',
|
||||
autoDismiss: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(function () {
|
||||
addToast(intl.formatMessage(messages.enablingwebpusherror), {
|
||||
autoDismiss: true,
|
||||
appearance: 'error',
|
||||
});
|
||||
})
|
||||
.finally(function () {
|
||||
revalidateDevices();
|
||||
if (isSubscribed) {
|
||||
localStorage.setItem('pushNotificationsEnabled', 'true');
|
||||
setWebPushEnabled(true);
|
||||
addToast(intl.formatMessage(messages.webpushhasbeenenabled), {
|
||||
appearance: 'success',
|
||||
autoDismiss: true,
|
||||
});
|
||||
} else {
|
||||
throw new Error('Subscription failed');
|
||||
}
|
||||
} catch (error) {
|
||||
addToast(intl.formatMessage(messages.enablingwebpusherror), {
|
||||
appearance: 'error',
|
||||
autoDismiss: true,
|
||||
});
|
||||
} finally {
|
||||
revalidateDevices();
|
||||
}
|
||||
};
|
||||
|
||||
// Unsubscribes from the push manager
|
||||
// Deletes/disables corresponding push subscription from database
|
||||
const disablePushNotifications = async (endpoint?: string) => {
|
||||
if ('serviceWorker' in navigator && user?.id) {
|
||||
navigator.serviceWorker.getRegistration('/sw.js').then((registration) => {
|
||||
registration?.pushManager
|
||||
.getSubscription()
|
||||
.then(async (subscription) => {
|
||||
const parsedSub = JSON.parse(JSON.stringify(subscription));
|
||||
try {
|
||||
await unsubscribeToPushNotifications(user?.id, endpoint);
|
||||
|
||||
await axios.delete(
|
||||
`/api/v1/user/${user.id}/pushSubscription/${encodeURIComponent(
|
||||
endpoint ?? parsedSub.endpoint
|
||||
)}`
|
||||
);
|
||||
|
||||
if (
|
||||
subscription &&
|
||||
(endpoint === parsedSub.endpoint || !endpoint)
|
||||
) {
|
||||
subscription.unsubscribe();
|
||||
setWebPushEnabled(false);
|
||||
}
|
||||
addToast(
|
||||
intl.formatMessage(
|
||||
endpoint
|
||||
? messages.subscriptiondeleted
|
||||
: messages.webpushhasbeendisabled
|
||||
),
|
||||
{
|
||||
autoDismiss: true,
|
||||
appearance: 'success',
|
||||
}
|
||||
);
|
||||
})
|
||||
.catch(function () {
|
||||
addToast(
|
||||
intl.formatMessage(
|
||||
endpoint
|
||||
? messages.subscriptiondeleteerror
|
||||
: messages.disablingwebpusherror
|
||||
),
|
||||
{
|
||||
autoDismiss: true,
|
||||
appearance: 'error',
|
||||
}
|
||||
);
|
||||
})
|
||||
.finally(function () {
|
||||
revalidateDevices();
|
||||
});
|
||||
localStorage.setItem('pushNotificationsEnabled', 'false');
|
||||
setWebPushEnabled(false);
|
||||
addToast(intl.formatMessage(messages.webpushhasbeendisabled), {
|
||||
autoDismiss: true,
|
||||
appearance: 'success',
|
||||
});
|
||||
} catch (error) {
|
||||
addToast(intl.formatMessage(messages.disablingwebpusherror), {
|
||||
autoDismiss: true,
|
||||
appearance: 'error',
|
||||
});
|
||||
} finally {
|
||||
revalidateDevices();
|
||||
}
|
||||
};
|
||||
|
||||
// Checks our current subscription on page load
|
||||
// Will set the web push state to true if subscribed
|
||||
useEffect(() => {
|
||||
if ('serviceWorker' in navigator && user?.id) {
|
||||
navigator.serviceWorker
|
||||
.getRegistration('/sw.js')
|
||||
.then(async (registration) => {
|
||||
await registration?.pushManager
|
||||
.getSubscription()
|
||||
.then(async (subscription) => {
|
||||
if (subscription) {
|
||||
const parsedKey = JSON.parse(JSON.stringify(subscription));
|
||||
const currentUserPushSub =
|
||||
await axios.get<UserPushSubscription>(
|
||||
`/api/v1/user/${
|
||||
user.id
|
||||
}/pushSubscription/${encodeURIComponent(
|
||||
parsedKey.endpoint
|
||||
)}`
|
||||
);
|
||||
const deletePushSubscriptionFromBackend = async (endpoint: string) => {
|
||||
try {
|
||||
await axios.delete(
|
||||
`/api/v1/user/${user?.id}/pushSubscription/${encodeURIComponent(
|
||||
endpoint
|
||||
)}`
|
||||
);
|
||||
|
||||
if (currentUserPushSub.data.endpoint !== parsedKey.endpoint) {
|
||||
return;
|
||||
}
|
||||
|
||||
setWebPushEnabled(true);
|
||||
} else {
|
||||
setWebPushEnabled(false);
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(function (error) {
|
||||
setWebPushEnabled(false);
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(
|
||||
'[SW] Failure retrieving push manager subscription, error:',
|
||||
error
|
||||
);
|
||||
});
|
||||
addToast(intl.formatMessage(messages.subscriptiondeleted), {
|
||||
autoDismiss: true,
|
||||
appearance: 'success',
|
||||
});
|
||||
} catch (error) {
|
||||
addToast(intl.formatMessage(messages.subscriptiondeleteerror), {
|
||||
autoDismiss: true,
|
||||
appearance: 'error',
|
||||
});
|
||||
} finally {
|
||||
revalidateDevices();
|
||||
}
|
||||
}, [user?.id]);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const verifyWebPush = async () => {
|
||||
const enabled = await verifyPushSubscription(user?.id, currentSettings);
|
||||
setWebPushEnabled(enabled);
|
||||
};
|
||||
|
||||
if (user?.id) {
|
||||
verifyWebPush();
|
||||
}
|
||||
}, [user?.id, currentSettings]);
|
||||
|
||||
useEffect(() => {
|
||||
const getSubscriptionEndpoint = async () => {
|
||||
if ('serviceWorker' in navigator && 'PushManager' in window) {
|
||||
const { subscription } = await getPushSubscription();
|
||||
|
||||
if (subscription) {
|
||||
setSubEndpoint(subscription.endpoint);
|
||||
} else {
|
||||
setSubEndpoint(null);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
getSubscriptionEndpoint();
|
||||
}, [webPushEnabled]);
|
||||
|
||||
const sortedDevices = useMemo(() => {
|
||||
if (!dataDevices || !subEndpoint) {
|
||||
return dataDevices;
|
||||
}
|
||||
|
||||
return [...dataDevices].sort((a, b) => {
|
||||
if (a.endpoint === subEndpoint) {
|
||||
return -1;
|
||||
}
|
||||
if (b.endpoint === subEndpoint) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
const dateA = a.createdAt ? new Date(a.createdAt).getTime() : 0;
|
||||
const dateB = b.createdAt ? new Date(b.createdAt).getTime() : 0;
|
||||
return dateB - dateA;
|
||||
});
|
||||
}, [dataDevices, subEndpoint]);
|
||||
|
||||
if (!data && !error) {
|
||||
return <LoadingSpinner />;
|
||||
@@ -324,22 +311,18 @@ const UserWebPushSettings = () => {
|
||||
{intl.formatMessage(messages.managedevices)}
|
||||
</h3>
|
||||
<div className="section">
|
||||
{dataDevices?.length ? (
|
||||
dataDevices
|
||||
?.sort((a, b) => {
|
||||
const dateA = a.createdAt ? new Date(a.createdAt).getTime() : 0;
|
||||
const dateB = b.createdAt ? new Date(b.createdAt).getTime() : 0;
|
||||
return dateB - dateA;
|
||||
})
|
||||
.map((device, index) => (
|
||||
<div className="py-2" key={`device-list-${index}`}>
|
||||
<DeviceItem
|
||||
key={index}
|
||||
disablePushNotifications={disablePushNotifications}
|
||||
device={device}
|
||||
/>
|
||||
</div>
|
||||
))
|
||||
{sortedDevices?.length ? (
|
||||
sortedDevices.map((device) => (
|
||||
<div className="py-2" key={`device-list-${device.endpoint}`}>
|
||||
<DeviceItem
|
||||
deletePushSubscriptionFromBackend={
|
||||
deletePushSubscriptionFromBackend
|
||||
}
|
||||
device={device}
|
||||
subEndpoint={subEndpoint}
|
||||
/>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<>
|
||||
<Alert
|
||||
|
||||
Reference in New Issue
Block a user