Реалізація системних сповіщень у десктоп-застосунку

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Реалізація системних сповіщень у десктоп-застосунку
Проста
від 1 робочого дня до 3 робочих днів
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Реалізація системних сповіщень у десктоп-приложенні

Системні сповіщення — один з небагатьох каналів, через який десктоп-приложение спілкується з користувачем навіть коли вікно згорнуто. Реалізація залежить від стека: Electron (Node.js Notification), Tauri (tauri-plugin-notification), або браузерний Notification API для PWA.

Electron: нативні сповіщення

// main/notifications.ts
import { Notification, nativeImage, app } from 'electron'
import path from 'path'

export interface NotificationOptions {
  title: string
  body: string
  icon?: string
  urgency?: 'normal' | 'critical' | 'low'
  actions?: Array<{ type: 'button'; text: string }>
  timeoutType?: 'default' | 'never'
}

export function sendNotification(opts: NotificationOptions): Notification {
  const iconPath = opts.icon
    ? nativeImage.createFromPath(path.resolve(opts.icon))
    : nativeImage.createFromPath(
        path.join(app.getAppPath(), 'resources', 'icon.png')
      )

  const n = new Notification({
    title: opts.title,
    body: opts.body,
    icon: iconPath,
    urgency: opts.urgency ?? 'normal',
    timeoutType: opts.timeoutType ?? 'default',
    actions: opts.actions,
  })

  n.on('click', () => {
    const { BrowserWindow } = require('electron')
    const win = BrowserWindow.getAllWindows()[0]
    if (win) {
      if (win.isMinimized()) win.restore()
      win.focus()
    }
  })

  n.show()
  return n
}

На Windows 10+ встановіть App User Model ID:

// main/index.ts
import { app } from 'electron'

app.setAppUserModelId('com.yourcompany.yourapp')

IPC: відправка з Renderer

// preload/index.ts
import { contextBridge, ipcRenderer } from 'electron'

contextBridge.exposeInMainWorld('notifications', {
  send: (opts: NotificationOptions) =>
    ipcRenderer.invoke('notification:send', opts),
})
// renderer/hooks/useNotification.ts
export function useNotification() {
  const send = (opts: NotificationOptions) => {
    if (window.notifications) {
      window.notifications.send(opts)
    }
  }
  return { send }
}

Tauri: плагін сповіщень

# src-tauri/Cargo.toml
[dependencies]
tauri-plugin-notification = "2"
// src/lib/notifications.ts
import {
  isPermissionGranted,
  requestPermission,
  sendNotification,
} from '@tauri-apps/plugin-notification'

export async function notify(title: string, body: string) {
  let permissionGranted = await isPermissionGranted()

  if (!permissionGranted) {
    const permission = await requestPermission()
    permissionGranted = permission === 'granted'
  }

  if (permissionGranted) {
    sendNotification({ title, body })
  }
}

Браузерний Notification API (PWA)

// src/services/notification.service.ts
export class NotificationService {
  static async requestPermission(): Promise<boolean> {
    if (!('Notification' in window)) return false
    if (Notification.permission === 'granted') return true
    if (Notification.permission === 'denied') return false

    const result = await Notification.requestPermission()
    return result === 'granted'
  }

  static async send(title: string, options: NotificationOptions = {}): Promise<Notification | null> {
    const granted = await this.requestPermission()
    if (!granted) return null

    const notification = new Notification(title, options)
    if (options.onClick) {
      notification.onclick = () => {
        window.focus()
        options.onClick?.()
      }
    }
    return notification
  }
}

Для Service Worker (PWA на фоні):

// service-worker.js
self.addEventListener('push', (event) => {
  const data = event.data?.json() ?? {}
  event.waitUntil(
    self.registration.showNotification(data.title ?? 'Сповіщення', {
      body: data.body,
      icon: '/icons/icon-192x192.png',
      badge: '/icons/badge-72x72.png',
      data: { url: data.url },
      actions: data.actions ?? [],
    })
  )
})

self.addEventListener('notificationclick', (event) => {
  event.notification.close()
  const url = event.notification.data?.url ?? '/'
  event.waitUntil(clients.openWindow(url))
})

Черга сповіщень

// src/services/notification-queue.ts
export class NotificationQueue {
  private queue: QueuedNotification[] = []
  private shown = new Set<string>()
  private timer: ReturnType<typeof setTimeout> | null = null
  private readonly cooldownMs: number

  constructor(cooldownMs = 3000) {
    this.cooldownMs = cooldownMs
  }

  enqueue(id: string, title: string, body: string) {
    if (this.shown.has(id)) return // дедупликація
    this.queue.push({ id, title, body, timestamp: Date.now() })
    this.process()
  }

  private process() {
    if (this.timer) return
    const item = this.queue.shift()
    if (!item) return

    this.shown.add(item.id)
    new Notification(item.title, { body: item.body })

    setTimeout(() => this.shown.delete(item.id), 10_000)

    this.timer = setTimeout(() => {
      this.timer = null
      this.process()
    }, this.cooldownMs)
  }
}

Базова інтеграція: 4 години. Повна реалізація з чергою, дедупликацією, кастомними Toast XML, кнопками дій та тестами: 1–2 робочих дні.