refactor: modified device list to make it mobile friendly

pull/3421/head
Brandon Cohen 1 year ago
parent e3012708c4
commit a673c62d90

@ -0,0 +1,51 @@
import type { MigrationInterface, QueryRunner } from 'typeorm';
export class datasource1684249347630 implements MigrationInterface {
name = 'datasource1684249347630';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE "temporary_user_push_subscription" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "endpoint" varchar NOT NULL, "p256dh" varchar NOT NULL, "auth" varchar NOT NULL, "userId" integer, "userAgent" varchar, "createdAt" datetime DEFAULT (datetime('now')), CONSTRAINT "UQ_95f313437ec4a8f8148d74a0ed8" UNIQUE ("auth"), CONSTRAINT "FK_03f7958328e311761b0de675fbe" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`
);
await queryRunner.query(
`INSERT INTO "temporary_user_push_subscription"("id", "endpoint", "p256dh", "auth", "userId", "userAgent", "createdAt") SELECT "id", "endpoint", "p256dh", "auth", "userId", "userAgent", "createdAt" FROM "user_push_subscription"`
);
await queryRunner.query(`DROP TABLE "user_push_subscription"`);
await queryRunner.query(
`ALTER TABLE "temporary_user_push_subscription" RENAME TO "user_push_subscription"`
);
await queryRunner.query(
`CREATE TABLE "temporary_user_push_subscription" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "endpoint" varchar NOT NULL, "p256dh" varchar NOT NULL, "auth" varchar NOT NULL, "userId" integer, "userAgent" varchar, "createdAt" datetime DEFAULT (datetime('now')), CONSTRAINT "UQ_95f313437ec4a8f8148d74a0ed8" UNIQUE ("auth"), CONSTRAINT "FK_03f7958328e311761b0de675fbe" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`
);
await queryRunner.query(
`INSERT INTO "temporary_user_push_subscription"("id", "endpoint", "p256dh", "auth", "userId", "userAgent", "createdAt") SELECT "id", "endpoint", "p256dh", "auth", "userId", "userAgent", "createdAt" FROM "user_push_subscription"`
);
await queryRunner.query(`DROP TABLE "user_push_subscription"`);
await queryRunner.query(
`ALTER TABLE "temporary_user_push_subscription" RENAME TO "user_push_subscription"`
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "user_push_subscription" RENAME TO "temporary_user_push_subscription"`
);
await queryRunner.query(
`CREATE TABLE "user_push_subscription" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "endpoint" varchar NOT NULL, "p256dh" varchar NOT NULL, "auth" varchar NOT NULL, "userId" integer, "userAgent" varchar, "createdAt" datetime DEFAULT (datetime('now')), CONSTRAINT "UQ_95f313437ec4a8f8148d74a0ed8" UNIQUE ("auth"), CONSTRAINT "FK_03f7958328e311761b0de675fbe" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`
);
await queryRunner.query(
`INSERT INTO "user_push_subscription"("id", "endpoint", "p256dh", "auth", "userId", "userAgent", "createdAt") SELECT "id", "endpoint", "p256dh", "auth", "userId", "userAgent", "createdAt" FROM "temporary_user_push_subscription"`
);
await queryRunner.query(`DROP TABLE "temporary_user_push_subscription"`);
await queryRunner.query(
`ALTER TABLE "user_push_subscription" RENAME TO "temporary_user_push_subscription"`
);
await queryRunner.query(
`CREATE TABLE "user_push_subscription" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "endpoint" varchar NOT NULL, "p256dh" varchar NOT NULL, "auth" varchar NOT NULL, "userId" integer, "userAgent" varchar, "createdAt" datetime DEFAULT (datetime('now')), CONSTRAINT "UQ_95f313437ec4a8f8148d74a0ed8" UNIQUE ("auth"), CONSTRAINT "FK_03f7958328e311761b0de675fbe" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`
);
await queryRunner.query(
`INSERT INTO "user_push_subscription"("id", "endpoint", "p256dh", "auth", "userId", "userAgent", "createdAt") SELECT "id", "endpoint", "p256dh", "auth", "userId", "userAgent", "createdAt" FROM "temporary_user_push_subscription"`
);
await queryRunner.query(`DROP TABLE "temporary_user_push_subscription"`);
}
}

@ -0,0 +1,107 @@
import ConfirmButton from '@app/components/Common/ConfirmButton';
import globalMessages from '@app/i18n/globalMessages';
import {
ComputerDesktopIcon,
DevicePhoneMobileIcon,
TrashIcon,
} from '@heroicons/react/24/solid';
import { defineMessages, useIntl } from 'react-intl';
import { UAParser } from 'ua-parser-js';
interface DeviceItemProps {
disablePushNotifications: (p256dh: string) => void;
device: {
endpoint: string;
p256dh: string;
auth: string;
userAgent: string;
createdAt: Date;
};
}
const messages = defineMessages({
operatingsystem: 'Operating System',
browser: 'Browser',
engine: 'Engine',
deletesubscription: 'Delete Subscription',
});
const DeviceItem = ({ disablePushNotifications, device }: DeviceItemProps) => {
const intl = useIntl();
return (
<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 sm:flex-row">
<div className="relative z-10 flex w-full items-center overflow-hidden pl-4 pr-4 sm:pr-0 xl:w-7/12 2xl:w-2/3">
<div className="relative h-auto w-12 flex-shrink-0 scale-100 transform-gpu overflow-hidden rounded-md transition duration-300 hover:scale-105">
{UAParser(device.userAgent).device.type === 'mobile' ? (
<DevicePhoneMobileIcon />
) : (
<ComputerDesktopIcon />
)}
</div>
<div className="flex flex-col justify-center overflow-hidden pl-2 xl:pl-4">
<div className="pt-0.5 text-xs font-medium text-white sm:pt-1">
{device.createdAt
? intl.formatDate(device.createdAt, {
year: 'numeric',
month: 'long',
day: 'numeric',
})
: 'Unknown'}
</div>
<div className="mr-2 min-w-0 truncate text-lg font-bold text-white hover:underline xl:text-xl">
{device.userAgent
? UAParser(device.userAgent).device.model
: 'Unknown'}
</div>
</div>
</div>
<div className="z-10 mt-4 ml-4 flex w-full flex-col justify-center overflow-hidden pr-4 text-sm sm:ml-2 sm:mt-0 xl:flex-1 xl:pr-0">
<div className="card-field">
<span className="card-field-name">
{intl.formatMessage(messages.operatingsystem)}
</span>
<span className="flex truncate text-sm text-gray-300">
{device.userAgent
? UAParser(device.userAgent).os.name
: 'Unknown'}
</span>
</div>
<div className="card-field">
<span className="card-field-name">
{intl.formatMessage(messages.browser)}
</span>
<span className="flex truncate text-sm text-gray-300">
{device.userAgent
? UAParser(device.userAgent).browser.name
: 'Unknown'}
</span>
</div>
<div className="card-field">
<span className="card-field-name">
{intl.formatMessage(messages.engine)}
</span>
<span className="flex truncate text-sm text-gray-300">
{device.userAgent
? UAParser(device.userAgent).engine.name
: 'Unknown'}
</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">
<ConfirmButton
onClick={() => disablePushNotifications(device.p256dh)}
confirmText={intl.formatMessage(globalMessages.areyousure)}
className="w-full"
>
<TrashIcon />
<span>{intl.formatMessage(messages.deletesubscription)}</span>
</ConfirmButton>
</div>
</div>
);
};
export default DeviceItem;

@ -1,10 +1,10 @@
import Alert from '@app/components/Common/Alert';
import Button from '@app/components/Common/Button';
import LoadingSpinner from '@app/components/Common/LoadingSpinner';
import Table from '@app/components/Common/Table';
import NotificationTypeSelector, {
ALL_NOTIFICATIONS,
} from '@app/components/NotificationTypeSelector';
import DeviceItem from '@app/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsWebPush/DeviceItem';
import useSettings from '@app/hooks/useSettings';
import { useUser } from '@app/hooks/useUser';
import globalMessages from '@app/i18n/globalMessages';
@ -12,9 +12,6 @@ import { ArrowDownOnSquareIcon } from '@heroicons/react/24/outline';
import {
CloudArrowDownIcon,
CloudArrowUpIcon,
ComputerDesktopIcon,
DevicePhoneMobileIcon,
TrashIcon,
} from '@heroicons/react/24/solid';
import type { UserPushSubscription } from '@server/entity/UserPushSubscription';
import type { UserSettingsNotificationsResponse } from '@server/interfaces/api/userSettingsInterfaces';
@ -25,14 +22,13 @@ import { useEffect, useState } from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { useToasts } from 'react-toast-notifications';
import useSWR, { mutate } from 'swr';
import { UAParser } from 'ua-parser-js';
const messages = defineMessages({
webpushsettingssaved: 'Web push notification settings saved successfully!',
webpushsettingsfailed: 'Web push notification settings failed to save.',
enablewebpush: 'Enable web push',
disablewebpush: 'Disable web push',
managedevices: 'Manage devices',
managedevices: 'Manage Devices',
type: 'type',
created: 'Created',
device: 'Device',
@ -314,68 +310,26 @@ const UserWebPushSettings = () => {
<h3 className="heading">
{intl.formatMessage(messages.managedevices)}
</h3>
{dataDevices?.length ? (
<Table>
<thead>
<tr>
<Table.TH>{intl.formatMessage(messages.type)}</Table.TH>
<Table.TH>{intl.formatMessage(messages.device)}</Table.TH>
<Table.TH>{intl.formatMessage(messages.created)}</Table.TH>
<Table.TH />
</tr>
</thead>
<Table.TBody>
{dataDevices?.map((device) => (
<tr
key={`device-list-${device.p256dh}`}
className="bg-gray-700"
>
<Table.TD>
<div className="flex items-center">
<div className="h-8 w-8 flex-shrink-0">
{UAParser(device.userAgent).device.type === 'mobile' ? (
<DevicePhoneMobileIcon />
) : (
<ComputerDesktopIcon />
)}
</div>
</div>
</Table.TD>
<Table.TD>
{device.userAgent
? UAParser(device.userAgent).device.model
: 'Unknown'}
</Table.TD>
<Table.TD>
{device.createdAt
? intl.formatDate(device.createdAt, {
year: 'numeric',
month: 'long',
day: 'numeric',
})
: 'Unknown'}
</Table.TD>
<Table.TD alignText="right">
<Button
buttonType="danger"
onClick={() => disablePushNotifications(device.p256dh)}
>
<TrashIcon />
</Button>
</Table.TD>
</tr>
))}
</Table.TBody>
</Table>
) : (
<>
<div className="mt-5" />
<Alert
title={intl.formatMessage(messages.nodevicestoshow)}
type="info"
/>
</>
)}
<div className="section">
{dataDevices?.length ? (
dataDevices?.map((device, index) => (
<div className="py-2" key={`device-list-${index}`}>
<DeviceItem
key={index}
disablePushNotifications={disablePushNotifications}
device={device}
/>
</div>
))
) : (
<>
<Alert
title={intl.formatMessage(messages.nodevicestoshow)}
type="info"
/>
</>
)}
</div>
</div>
</>
);

@ -1101,7 +1101,25 @@
"components.UserProfile.UserSettings.UserGeneralSettings.toastSettingsSuccess": "Settings saved successfully!",
"components.UserProfile.UserSettings.UserGeneralSettings.user": "User",
"components.UserProfile.UserSettings.UserGeneralSettings.validationDiscordId": "You must provide a valid Discord user ID",
"components.UserProfile.UserSettings.UserNotificationSettings.disablewebpush": "Disable web push",
"components.UserProfile.UserSettings.UserNotificationSettings.UserNotificationsWebPush.browser": "Browser",
"components.UserProfile.UserSettings.UserNotificationSettings.UserNotificationsWebPush.created": "Created",
"components.UserProfile.UserSettings.UserNotificationSettings.UserNotificationsWebPush.deletesubscription": "Delete Subscription",
"components.UserProfile.UserSettings.UserNotificationSettings.UserNotificationsWebPush.device": "Device",
"components.UserProfile.UserSettings.UserNotificationSettings.UserNotificationsWebPush.disablewebpush": "Disable web push",
"components.UserProfile.UserSettings.UserNotificationSettings.UserNotificationsWebPush.disablingwebpusherror": "Something went wrong while disabling web push.",
"components.UserProfile.UserSettings.UserNotificationSettings.UserNotificationsWebPush.enablewebpush": "Enable web push",
"components.UserProfile.UserSettings.UserNotificationSettings.UserNotificationsWebPush.enablingwebpusherror": "Something went wrong while enable web push.",
"components.UserProfile.UserSettings.UserNotificationSettings.UserNotificationsWebPush.engine": "Engine",
"components.UserProfile.UserSettings.UserNotificationSettings.UserNotificationsWebPush.managedevices": "Manage Devices",
"components.UserProfile.UserSettings.UserNotificationSettings.UserNotificationsWebPush.nodevicestoshow": "You have no web push subscriptions to show.",
"components.UserProfile.UserSettings.UserNotificationSettings.UserNotificationsWebPush.operatingsystem": "Operating System",
"components.UserProfile.UserSettings.UserNotificationSettings.UserNotificationsWebPush.subscriptiondeleted": "Subscription deleted.",
"components.UserProfile.UserSettings.UserNotificationSettings.UserNotificationsWebPush.subscriptiondeleteerror": "Something went wrong while deleting the user subscription.",
"components.UserProfile.UserSettings.UserNotificationSettings.UserNotificationsWebPush.type": "type",
"components.UserProfile.UserSettings.UserNotificationSettings.UserNotificationsWebPush.webpushhasbeendisabled": "Web push has been disabled.",
"components.UserProfile.UserSettings.UserNotificationSettings.UserNotificationsWebPush.webpushhasbeenenabled": "Web push has been enabled.",
"components.UserProfile.UserSettings.UserNotificationSettings.UserNotificationsWebPush.webpushsettingsfailed": "Web push notification settings failed to save.",
"components.UserProfile.UserSettings.UserNotificationSettings.UserNotificationsWebPush.webpushsettingssaved": "Web push notification settings saved successfully!",
"components.UserProfile.UserSettings.UserNotificationSettings.discordId": "User ID",
"components.UserProfile.UserSettings.UserNotificationSettings.discordIdTip": "The <FindDiscordIdLink>multi-digit ID number</FindDiscordIdLink> associated with your user account",
"components.UserProfile.UserSettings.UserNotificationSettings.discordsettingsfailed": "Discord notification settings failed to save.",
@ -1109,7 +1127,6 @@
"components.UserProfile.UserSettings.UserNotificationSettings.email": "Email",
"components.UserProfile.UserSettings.UserNotificationSettings.emailsettingsfailed": "Email notification settings failed to save.",
"components.UserProfile.UserSettings.UserNotificationSettings.emailsettingssaved": "Email notification settings saved successfully!",
"components.UserProfile.UserSettings.UserNotificationSettings.enablewebpush": "Enable web push",
"components.UserProfile.UserSettings.UserNotificationSettings.notifications": "Notifications",
"components.UserProfile.UserSettings.UserNotificationSettings.notificationsettings": "Notification Settings",
"components.UserProfile.UserSettings.UserNotificationSettings.pgpPublicKey": "PGP Public Key",
@ -1137,8 +1154,6 @@
"components.UserProfile.UserSettings.UserNotificationSettings.validationPushoverUserKey": "You must provide a valid user or group key",
"components.UserProfile.UserSettings.UserNotificationSettings.validationTelegramChatId": "You must provide a valid chat ID",
"components.UserProfile.UserSettings.UserNotificationSettings.webpush": "Web Push",
"components.UserProfile.UserSettings.UserNotificationSettings.webpushsettingsfailed": "Web push notification settings failed to save.",
"components.UserProfile.UserSettings.UserNotificationSettings.webpushsettingssaved": "Web push notification settings saved successfully!",
"components.UserProfile.UserSettings.UserPasswordChange.confirmpassword": "Confirm Password",
"components.UserProfile.UserSettings.UserPasswordChange.currentpassword": "Current Password",
"components.UserProfile.UserSettings.UserPasswordChange.newpassword": "New Password",

Loading…
Cancel
Save