kaneo (minimalist kanban) fork to experiment adding a tangled integration github.com/usekaneo/kaneo
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix: enable all configured notification channels

Tin 70afa9df 795d10e2

+117 -22
+62 -3
apps/api/src/notification-preferences/service.ts
··· 1 - import { and, eq, inArray } from "drizzle-orm"; 1 + import { and, eq, inArray, or } from "drizzle-orm"; 2 2 import { HTTPException } from "hono/http-exception"; 3 3 import db from "../database"; 4 4 import { ··· 72 72 webhookEnabled: boolean; 73 73 projectMode: NotificationPreferenceProjectMode; 74 74 selectedProjectIds?: string[]; 75 + }; 76 + 77 + type WorkspaceRuleChannelState = { 78 + emailEnabled: boolean; 79 + ntfyEnabled: boolean; 80 + gotifyEnabled: boolean; 81 + webhookEnabled: boolean; 75 82 }; 76 83 77 84 function normalizeOptionalString(value: string | null | undefined) { ··· 268 275 const webhookEnabled = 269 276 input.webhookEnabled ?? decryptedExisting?.webhookEnabled ?? false; 270 277 278 + const enabledRuleCascade: WorkspaceRuleChannelState = { 279 + emailEnabled: false, 280 + ntfyEnabled: false, 281 + gotifyEnabled: false, 282 + webhookEnabled: false, 283 + }; 284 + 271 285 const shouldValidateNtfy = 272 286 ntfyEnabled || 273 287 input.ntfyServerUrl !== undefined || ··· 386 400 webhookEnabled?: boolean; 387 401 } = {}; 388 402 403 + const hadEmailEnabled = decryptedExisting?.emailEnabled ?? false; 404 + const hadNtfyEnabled = decryptedExisting?.ntfyEnabled ?? false; 405 + const hadGotifyEnabled = decryptedExisting?.gotifyEnabled ?? false; 406 + const hadWebhookEnabled = decryptedExisting?.webhookEnabled ?? false; 407 + 389 408 if (!emailEnabled) { 390 409 ruleCascade.emailEnabled = false; 391 410 } ··· 402 421 ruleCascade.webhookEnabled = false; 403 422 } 404 423 405 - if (Object.keys(ruleCascade).length > 0) { 424 + if (emailEnabled && !hadEmailEnabled && emailAddress) { 425 + enabledRuleCascade.emailEnabled = true; 426 + } 427 + 428 + if (ntfyEnabled && !hadNtfyEnabled && ntfyServerUrl && ntfyTopic) { 429 + enabledRuleCascade.ntfyEnabled = true; 430 + } 431 + 432 + if ( 433 + gotifyEnabled && 434 + !hadGotifyEnabled && 435 + gotifyServerUrl && 436 + data.gotifyToken 437 + ) { 438 + enabledRuleCascade.gotifyEnabled = true; 439 + } 440 + 441 + if (webhookEnabled && !hadWebhookEnabled && webhookUrl) { 442 + enabledRuleCascade.webhookEnabled = true; 443 + } 444 + 445 + const ruleEnableCascade = Object.fromEntries( 446 + Object.entries(enabledRuleCascade).filter(([, value]) => value), 447 + ) as Partial<WorkspaceRuleChannelState>; 448 + 449 + if ( 450 + Object.keys(ruleCascade).length > 0 || 451 + Object.keys(ruleEnableCascade).length > 0 452 + ) { 406 453 await db 407 454 .update(userNotificationWorkspaceRuleTable) 408 455 .set({ 456 + ...ruleEnableCascade, 409 457 ...ruleCascade, 410 458 updatedAt: new Date(), 411 459 }) 412 - .where(eq(userNotificationWorkspaceRuleTable.userId, userId)); 460 + .where( 461 + and( 462 + eq(userNotificationWorkspaceRuleTable.userId, userId), 463 + eq(userNotificationWorkspaceRuleTable.isActive, true), 464 + or( 465 + eq(userNotificationWorkspaceRuleTable.emailEnabled, true), 466 + eq(userNotificationWorkspaceRuleTable.ntfyEnabled, true), 467 + eq(userNotificationWorkspaceRuleTable.gotifyEnabled, true), 468 + eq(userNotificationWorkspaceRuleTable.webhookEnabled, true), 469 + ), 470 + ), 471 + ); 413 472 } 414 473 415 474 return getNotificationPreferences(userId, emailAddress);
+55 -19
apps/web/src/components/account/notification-preferences-settings.tsx
··· 39 39 webhook: { enabled: boolean; url: string; secret: string }; 40 40 }; 41 41 42 + function createWorkspaceRuleState(input: { 43 + hasEmailChannel: boolean; 44 + hasGotifyChannel: boolean; 45 + hasNtfyChannel: boolean; 46 + hasWebhookChannel: boolean; 47 + rule?: WorkspaceRuleState; 48 + }): WorkspaceRuleState { 49 + if (input.rule) { 50 + return { 51 + isActive: input.rule.isActive, 52 + emailEnabled: input.rule.emailEnabled, 53 + ntfyEnabled: input.rule.ntfyEnabled, 54 + gotifyEnabled: input.rule.gotifyEnabled, 55 + webhookEnabled: input.rule.webhookEnabled, 56 + projectMode: input.rule.projectMode, 57 + selectedProjectIds: input.rule.selectedProjectIds, 58 + }; 59 + } 60 + 61 + return { 62 + isActive: false, 63 + emailEnabled: input.hasEmailChannel, 64 + ntfyEnabled: input.hasNtfyChannel, 65 + gotifyEnabled: input.hasGotifyChannel, 66 + webhookEnabled: input.hasWebhookChannel, 67 + projectMode: "all", 68 + selectedProjectIds: [], 69 + }; 70 + } 71 + 42 72 function createDefaultGlobalChannelPrefs(): GlobalChannelPrefsState { 43 73 return { 44 74 emailEnabled: false, ··· 137 167 workspace: WorkspaceSummary; 138 168 }) { 139 169 const { t } = useTranslation(); 140 - const [state, setState] = React.useState<WorkspaceRuleState>({ 141 - isActive: rule?.isActive ?? false, 142 - emailEnabled: rule?.emailEnabled ?? false, 143 - ntfyEnabled: rule?.ntfyEnabled ?? false, 144 - gotifyEnabled: rule?.gotifyEnabled ?? false, 145 - webhookEnabled: rule?.webhookEnabled ?? false, 146 - projectMode: rule?.projectMode ?? "all", 147 - selectedProjectIds: rule?.selectedProjectIds ?? [], 148 - }); 170 + const [state, setState] = React.useState<WorkspaceRuleState>(() => 171 + createWorkspaceRuleState({ 172 + hasEmailChannel, 173 + hasGotifyChannel, 174 + hasNtfyChannel, 175 + hasWebhookChannel, 176 + rule, 177 + }), 178 + ); 149 179 const [isSaving, setIsSaving] = React.useState(false); 150 180 const [isDeleting, setIsDeleting] = React.useState(false); 151 181 const { data: projects } = useGetProjects({ ··· 157 187 }); 158 188 159 189 React.useEffect(() => { 160 - setState({ 161 - isActive: rule?.isActive ?? false, 162 - emailEnabled: rule?.emailEnabled ?? false, 163 - ntfyEnabled: rule?.ntfyEnabled ?? false, 164 - gotifyEnabled: rule?.gotifyEnabled ?? false, 165 - webhookEnabled: rule?.webhookEnabled ?? false, 166 - projectMode: rule?.projectMode ?? "all", 167 - selectedProjectIds: rule?.selectedProjectIds ?? [], 168 - }); 169 - }, [rule]); 190 + setState( 191 + createWorkspaceRuleState({ 192 + hasEmailChannel, 193 + hasGotifyChannel, 194 + hasNtfyChannel, 195 + hasWebhookChannel, 196 + rule, 197 + }), 198 + ); 199 + }, [ 200 + hasEmailChannel, 201 + hasGotifyChannel, 202 + hasNtfyChannel, 203 + hasWebhookChannel, 204 + rule, 205 + ]); 170 206 171 207 const isConnected = Boolean(rule); 172 208 const isBusy = isSaving || isDeleting;