Пайплайн уведомлений
Это Vue-версия флагманского сценария Triggery. Сначала прочитай рецепт на React — файл триггера идентичен, и проза там разбирает сценарий. Ниже — код компонентов специфично под Vue.
Открыть в StackBlitz Открыть пример на GitHubФайловая раскладка
Заголовок раздела «Файловая раскладка»- README.md обзорное описание
- index.html точка входа Vite
Директорияsrc/
- App.vue продьюсеры + реакторы
- main.ts точка входа
Директорияtriggers/
- index.ts правило — события, условия, действия, обработчик
Триггер (идентичный)
Заголовок раздела «Триггер (идентичный)»src/triggers/message.trigger.ts
import { createTrigger } from '@triggery/core';
type Settings = { sound: boolean; notifications: boolean; dnd: boolean };
type Message = { id: string; author: string; authorId: string; text: string; channelId: string };
export const messageTrigger = createTrigger<{
events: { 'new-message': Message };
conditions: { settings: Settings; activeChannelId: string | null; currentUserId: string };
actions: {
showToast: { title: string; body: string };
playSound: 'beep' | 'mention';
incrementBadge: string;
};
}>({
id: 'message-received',
events: ['new-message'],
required: ['settings', 'currentUserId'],
handler({ event, conditions, actions, check }) {
const msg = event.payload;
if (msg.channelId === conditions.activeChannelId) return;
if (msg.authorId === conditions.currentUserId) return;
actions.incrementBadge?.(msg.channelId);
if (check.is('settings', s => s.notifications)) actions.showToast?.({ title: msg.author, body: msg.text });
if (check.is('settings', s => s.sound && !s.dnd)) actions.debounce(800).playSound?.('beep');
},
});Провайдеры (Vue refs)
Заголовок раздела «Провайдеры (Vue refs)»src/features/settings/SettingsPanel.vue
<script setup lang="ts">
import { useCondition } from '@triggery/vue';
import { ref } from 'vue';
import { messageTrigger } from '../../triggers/message.trigger';
const settings = ref({ sound: true, notifications: true, dnd: false });
useCondition(messageTrigger, 'settings', () => settings.value);
</script>
<template>
<fieldset>
<legend>Notifications</legend>
<label>
<input type="checkbox" v-model="settings.notifications" />
Show toasts
</label>
<label>
<input type="checkbox" v-model="settings.sound" />
Play sound
</label>
<label>
<input type="checkbox" v-model="settings.dnd" />
Do Not Disturb
</label>
</fieldset>
</template>src/features/session/SessionProvider.vue
<script setup lang="ts">
import { useCondition } from '@triggery/vue';
import { messageTrigger } from '../../triggers/message.trigger';
const props = defineProps<{ userId: string }>();
useCondition(messageTrigger, 'currentUserId', () => props.userId);
</script>
<template>
<slot />
</template>src/features/chat/Chat.vue
<script setup lang="ts">
import { useEvent, useCondition } from '@triggery/vue';
import { messageTrigger } from '../../triggers/message.trigger';
const props = defineProps<{ channelId: string | null }>();
useCondition(messageTrigger, 'activeChannelId', () => props.channelId);
const fireMessage = useEvent(messageTrigger, 'new-message');
function simulate() {
fireMessage({
id: crypto.randomUUID(),
author: 'Alice',
authorId: 'u-alice',
text: 'hi',
channelId: 'c-lunch',
});
}
</script>
<template>
<button @click="simulate()">simulate inbound</button>
</template>Реактор
Заголовок раздела «Реактор»src/features/notifications/NotificationLayer.vue
<script setup lang="ts">
import { useAction } from '@triggery/vue';
import { useBadgeStore } from '../../stores/badge';
import { messageTrigger } from '../../triggers/message.trigger';
const badge = useBadgeStore();
let audio: HTMLAudioElement | null = null;
useAction(messageTrigger, 'showToast', ({ title, body }) => {
console.log('toast', title, body);
});
useAction(messageTrigger, 'playSound', () => {
audio ??= new Audio('/beep.mp3');
audio.play().catch(() => {});
});
useAction(messageTrigger, 'incrementBadge', channelId => {
badge.increment(channelId);
});
</script>
<template>
<span hidden />
</template>Подключаем приложение
Заголовок раздела «Подключаем приложение»src/main.ts
import { createRuntime } from '@triggery/core';
import { TriggerRuntimePlugin } from '@triggery/vue';
import { createApp } from 'vue';
import App from './App.vue';
const runtime = createRuntime();
createApp(App).use(TriggerRuntimePlugin, { runtime }).mount('#root');См. также
Заголовок раздела «См. также» Версия для React (каноническая) Полный нарратив + сравнение с useEffect.
Версия для Solid C createSignal.
Vue-счётчик Уменьшенный hello-world рецепт.
Пакет @triggery/vue Справочник по хукам.