diff --git a/api/ApiHelper.tsx b/api/ApiHelper.tsx
index 3f5f421eb..b2bddcfe3 100644
--- a/api/ApiHelper.tsx
+++ b/api/ApiHelper.tsx
@@ -559,7 +559,7 @@ export function initAPI(returnSSRResponse: boolean = false): API {
return {
id: t.id || 0,
name: t.name || '',
- isDisabled: false,
+ isDisabled: t.isDisabled ?? false,
priority: 0
}
})
@@ -568,6 +568,9 @@ export function initAPI(returnSSRResponse: boolean = false): API {
resolve()
})
.catch(e => {
+ // The listener was already created but linking the subscription failed.
+ // Roll it back so it doesn't linger invisibly and count against the limit.
+ unsubscribe(listener).catch(() => {})
let errorMessage = typeof e === 'string' ? e : e?.message || ''
if (errorMessage.includes('subscription_limit_reached')) {
toast.error(
diff --git a/components/ArchivedAuctions.tsx/ExportArchivedData/ExportArchivedData.tsx b/components/ArchivedAuctions.tsx/ExportArchivedData/ExportArchivedData.tsx
index 3f7d85d8d..8585b9bd9 100644
--- a/components/ArchivedAuctions.tsx/ExportArchivedData/ExportArchivedData.tsx
+++ b/components/ArchivedAuctions.tsx/ExportArchivedData/ExportArchivedData.tsx
@@ -6,6 +6,7 @@ import styles from './ExportArchivedData.module.css'
import HelpIcon from '@mui/icons-material/Help'
import Tooltip from '../../Tooltip/Tooltip'
import NumberElement from '../../Number/Number'
+import { isValidDiscordWebhookUrl } from '../../../utils/NotificationChannelUtils'
interface Props {
itemTag: string
@@ -23,7 +24,7 @@ function ExportArchivedData(props: Props) {
let [showConfirmDialog, setShowConfirmDialog] = useState(false)
let [isExportRunning, setIsExportRunning] = useState(false)
- let isDiscordWebhookUrlValid = discordWebhookUrl && (discordWebhookUrl.startsWith('https://discord.com/api/webhooks/') || discordWebhookUrl.startsWith('https://discordapp.com/api/webhooks/'))
+ let isDiscordWebhookUrlValid = !!discordWebhookUrl && isValidDiscordWebhookUrl(discordWebhookUrl)
useEffect(() => {
setShowModal(props.show)
@@ -80,7 +81,7 @@ function ExportArchivedData(props: Props) {
onChange={e => setDiscordWebhookUrl(e.target.value)}
placeholder="Discord Webhook Url (https://discord.com/api/... or https://discordapp.com/api/...)"
/>
- {discordWebhookUrl && !discordWebhookUrl?.startsWith('https://discord.com/api/') && !discordWebhookUrl?.startsWith('https://discordapp.com/api/webhooks/') ? (
+ {discordWebhookUrl && !isValidDiscordWebhookUrl(discordWebhookUrl) ? (
The Discord Webhook URL has to start with "https://discord.com/api/..." or "https://discordapp.com/api/webhooks/..."
diff --git a/components/Favorites/FavoriteItemCard.tsx b/components/Favorites/FavoriteItemCard.tsx
index fd0a38b04..393ae1f48 100644
--- a/components/Favorites/FavoriteItemCard.tsx
+++ b/components/Favorites/FavoriteItemCard.tsx
@@ -115,6 +115,7 @@ export default function FavoriteItemCard({ favorite, movement, isMovementLoading
Notify}
/>
@@ -135,6 +136,7 @@ export default function FavoriteItemCard({ favorite, movement, isMovementLoading
diff --git a/components/NotificationTargets/NotificationTargetForm.tsx b/components/NotificationTargets/NotificationTargetForm.tsx
index 16ff9b152..a2b5b29b4 100644
--- a/components/NotificationTargets/NotificationTargetForm.tsx
+++ b/components/NotificationTargets/NotificationTargetForm.tsx
@@ -5,6 +5,7 @@ import { Button, Form } from 'react-bootstrap'
import api from '../../api/ApiHelper'
import askForNotificationPermissons from '../../utils/NotificationPermisson'
import { getNotficationWhenEnumAsString, getNotificationTypeAsString } from '../../utils/NotificationUtils'
+import { isValidDiscordWebhookUrl } from '../../utils/NotificationChannelUtils'
const TYPE_OPTIONS: NotificationType[] = ['DiscordWebhook', 'FIREBASE', 'InGame']
const WHEN_OPITIONS: NotificationWhen[] = ['NEVER', 'AfterFail', 'ALWAYS']
@@ -22,6 +23,8 @@ function NotificationTargetForm(props: Props) {
let [when, setWhen] = useState(props.defaultNotificationTarget?.when || 'ALWAYS')
let [disabled, setDisabled] = useState(false)
+ let discordUrlInvalid = type === 'DiscordWebhook' && !!target && !isValidDiscordWebhookUrl(target)
+
async function addNotificationTarget() {
let targetToSend = target
@@ -81,9 +84,9 @@ function NotificationTargetForm(props: Props) {
type="text"
onChange={e => setTarget(e.target.value)}
placeholder={type === 'DiscordWebhook' ? 'Discord Webhook Url (https://discord.com/api/... or https://discordapp.com/api/...)' : 'Webhook Url'}
- isInvalid={(type === 'DiscordWebhook' && target && !target?.startsWith('https://discord.com/api/') && !target?.startsWith('https://discordapp.com/api/webhooks/')) === true}
+ isInvalid={discordUrlInvalid}
/>
- {type === 'DiscordWebhook' && target && !target?.startsWith('https://discord.com/api/') && !target?.startsWith('https://discordapp.com/api/webhooks/') ? (
+ {discordUrlInvalid ? (
The Discord Webhook URL has to start with "https://discord.com/api/..." or "https://discordapp.com/api/webhooks/..."
diff --git a/components/NotificationTargets/NotificationTargets.tsx b/components/NotificationTargets/NotificationTargets.tsx
index d51f422b1..b86562263 100644
--- a/components/NotificationTargets/NotificationTargets.tsx
+++ b/components/NotificationTargets/NotificationTargets.tsx
@@ -2,6 +2,7 @@
import { useEffect, useState } from 'react'
import { Button, Table } from 'react-bootstrap'
+import { toast } from 'react-toastify'
import api from '../../api/ApiHelper'
import NotificationTargetForm from './NotificationTargetForm'
import DeleteIcon from '@mui/icons-material/Delete'
@@ -29,9 +30,20 @@ function NotificationTargets() {
}
function deleteNotificationTarget(target: NotificationTarget) {
- api.deleteNotificationTarget(target).then(() => {
- setNotificationTargets(notificationTargets.filter(t => t.name !== target.name))
- })
+ api.deleteNotificationTarget(target)
+ .then(() => {
+ setNotificationTargets(notificationTargets.filter(t => t.name !== target.name))
+ })
+ .catch(error => {
+ // the http layer rejects with a plain string like `HTTP 500: {"slug":"subscription_depends",...}`,
+ // so apiErrorHandler (which only handles error.message) stays silent - surface it here instead.
+ let message = typeof error === 'string' ? error : error?.message
+ if (message && message.includes('subscription_depends')) {
+ toast.error(`"${target.name}" is still used by at least one notifier. Switch those notifiers to another channel first, then delete it.`)
+ } else {
+ toast.error(message || 'Could not delete this channel. Please try again.')
+ }
+ })
}
function sendTestNotification(target: NotificationTarget) {
@@ -42,7 +54,7 @@ function NotificationTargets() {
<>
{!showAddNotificationTarget ? (
-
+
) : (