Angular ile Pushizo Web SDK
@pushizo/web Angular'da bir servis içinde başlatılır (provideAppInitializer), abonelik durumu signal/BehaviorSubject ile bileşenlere akar. Bu rehber standalone bileşen mimarisini (Angular 17+) esas alır; NgModule…
@pushizo/web Angular'da bir servis içinde başlatılır (provideAppInitializer), abonelik durumu signal/BehaviorSubject ile bileşenlere akar. Bu rehber standalone bileşen mimarisini (Angular 17+) esas alır; NgModule tabanlı projelerde aynı servis APP_INITIALIZER ile kurulur.
1. Kurulum
npm install @pushizo/webService worker site kökünden sunulmalıdır. src/PushizoSDKWorker.js dosyasını oluşturup angular.json assets listesine kök çıktıyla ekleyin:
// src/PushizoSDKWorker.js
const apiUrl = new URL(self.location.href).searchParams.get('apiUrl') || 'https://api.example.com';
importScripts(`${apiUrl.replace(/\/$/, '')}/sdk-web/PushizoSDKWorker.js`);"assets": [
{ "glob": "**/*", "input": "public" },
{ "glob": "PushizoSDKWorker.js", "input": "src", "output": "/" }
]Angular PWA (@angular/pwa, ngsw-worker.js) kullanıyorsanız iki service worker aynı kapsamda (/) kayıt olamaz. İki seçenek:
- Pushizo worker'ını farklı kapsamda kaydedin:
serviceWorkerScope: '/pushizo/'(push aboneliği kapsamdan bağımsız çalışır; bildirim tıklaması yineurl'yi açar). Kapsam dosya konumundan dar olduğundan ek başlık gerekmez. - Ya da ngsw'yi kullanmayıp kendi worker'ınıza
importScriptssatırını ekleyin.
Ortam: src/environments/environment.ts içinde hsAppId, hsApiUrl.
2. Servis
// src/app/pushizo.service.ts
import { Injectable, PLATFORM_ID, inject, signal } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';
import Pushizo, { type PushizoSdk, type InitOptions, type PushSubscriptionState } from '@pushizo/web';
import { environment } from '../environments/environment';
@Injectable({ providedIn: 'root' })
export class PushizoService {
readonly sdk: PushizoSdk = Pushizo;
readonly ready = signal(false);
readonly error = signal<string | null>(null);
readonly subscription = signal<PushSubscriptionState | null>(null);
private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
/** `provideAppInitializer` içinden çağrılır; SSR'da (Angular Universal) hiçbir şey yapmaz. */
async init(options?: Partial<InitOptions>): Promise<void> {
if (!this.isBrowser) return;
this.sdk.User.pushSubscription.addObserver((state) => this.subscription.set(state));
try {
await this.sdk.init({
appId: environment.hsAppId,
apiUrl: environment.hsApiUrl,
serviceWorkerPath: '/PushizoSDKWorker.js',
allowLocalhostAsSecureOrigin: !environment.production,
bell: { enabled: true },
...options,
});
this.ready.set(true);
this.subscription.set(this.sdk.User.pushSubscription.state);
} catch (err) {
this.error.set(err instanceof Error ? err.message : String(err));
}
}
get permission() {
return this.ready() ? this.sdk.Notifications.permission : 'default';
}
requestPermission() {
return this.sdk.Notifications.requestPermission();
}
login(externalId: string, identityHash?: string) {
return this.sdk.login(externalId, identityHash);
}
logout() {
return this.sdk.User.externalId ? this.sdk.logout() : Promise.resolve();
}
}// src/app/app.config.ts
import { ApplicationConfig, provideAppInitializer, inject } from '@angular/core';
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
import { PushizoService } from './pushizo.service';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes),
// Uygulama açılışını bloklamamak için init beklenmez; bileşenler `ready()` sinyalini izler.
provideAppInitializer(() => {
void inject(PushizoService).init();
}),
],
};NgModule projelerinde: { provide: APP_INITIALIZER, useFactory: (hs: PushizoService) => () => { void hs.init(); }, deps: [PushizoService], multi: true }.
3. İzin ve abonelik bileşeni
// src/app/push-toggle.component.ts
import { Component, inject } from '@angular/core';
import { PushizoService } from './pushizo.service';
@Component({
selector: 'app-push-toggle',
standalone: true,
template: `
@if (!hs.ready()) {
<p>SDK yükleniyor…</p>
} @else if (hs.permission === 'unsupported') {
<p>Bu tarayıcı web push desteklemiyor (HTTPS gerekir).</p>
} @else if (hs.permission === 'denied') {
<p>Bildirimler tarayıcı ayarlarından engellenmiş.</p>
} @else if (hs.subscription()?.optedIn) {
<button type="button" (click)="hs.sdk.User.pushSubscription.optOut()">Bildirimleri kapat</button>
} @else {
<button type="button" (click)="hs.requestPermission()">Bildirimlere izin ver</button>
}
`,
})
export class PushToggleComponent {
readonly hs = inject(PushizoService);
}4. Login / logout
Kimlik doğrulama servisinizin kullanıcı akışına bağlayın:
// src/app/auth.service.ts (ilgili bölüm)
import { effect, inject } from '@angular/core';
import { PushizoService } from './pushizo.service';
export class AuthService {
private readonly hs = inject(PushizoService);
readonly user = signal<{ id: string; identityHash?: string } | null>(null);
constructor() {
effect(() => {
if (!this.hs.ready()) return;
const user = this.user();
if (user) void this.hs.login(user.id, user.identityHash); // identity_hash: HMAC-SHA256(sdk_secret, external_id), sunucuda üretilir
else void this.hs.logout();
});
}
}5. Tag, olay ve rota takibi
hs.sdk.User.addTags({ plan: 'pro' });
hs.sdk.User.removeTag('plan');
hs.sdk.Session.sendEvent('purchase', { sku: 'A1' }, 49.9);In-app screen tetiği için rota değişimlerini bildirin:
// src/app/app.component.ts
import { Component, inject } from '@angular/core';
import { Router, NavigationEnd } from '@angular/router';
import { filter } from 'rxjs';
import { PushizoService } from './pushizo.service';
export class AppComponent {
private readonly hs = inject(PushizoService);
constructor(router: Router) {
router.events.pipe(filter((e): e is NavigationEnd => e instanceof NavigationEnd)).subscribe((e) => {
if (this.hs.ready()) this.hs.sdk.InAppMessages.setScreen(e.urlAfterRedirects);
});
}
}6. Bildirim kutusu bileşeni
// src/app/inbox.component.ts
import { Component, DestroyRef, effect, inject, signal } from '@angular/core';
import type { InboxItem } from '@pushizo/web';
import { PushizoService } from './pushizo.service';
@Component({
selector: 'app-inbox',
standalone: true,
template: `
<section aria-label="Bildirim kutusu">
<h2>Bildirimler ({{ unread() }} okunmamış)</h2>
<button type="button" [disabled]="loading()" (click)="load()">{{ loading() ? 'Yükleniyor…' : 'Yenile' }}</button>
<button type="button" (click)="markAllRead()">Tümünü okundu işaretle</button>
@if (error()) {
<p role="alert">{{ error() }}</p>
} @else if (items().length === 0) {
<p>Bildirim yok.</p>
} @else {
<ul>
@for (item of items(); track item.id) {
<li [attr.data-read]="item.read">
<strong>{{ item.title ?? '(başlıksız)' }}</strong>
@if (item.body) { <p>{{ item.body }}</p> }
@if (item.url) { <a [href]="item.url">Aç</a> }
<button type="button" [disabled]="item.read" (click)="markRead(item.id)">Okundu</button>
<button type="button" (click)="archive(item.id)">Arşivle</button>
</li>
}
</ul>
}
</section>
`,
})
export class InboxComponent {
private readonly hs = inject(PushizoService);
readonly items = signal<InboxItem[]>([]);
readonly unread = signal(0);
readonly loading = signal(false);
readonly error = signal<string | null>(null);
constructor(destroyRef: DestroyRef) {
effect(() => {
if (!this.hs.ready()) return;
void this.load();
const stop = this.hs.sdk.Inbox.addObserver((e) => this.unread.set(e.unreadCount));
destroyRef.onDestroy(stop);
});
}
async load() {
this.loading.set(true);
try {
const page = await this.hs.sdk.Inbox.list({ limit: 20 });
this.items.set(page.items);
this.unread.set(page.unreadCount);
this.error.set(null);
} catch (e) {
this.error.set(e instanceof Error ? e.message : String(e)); // kullanıcı/abonelik yoksa açıklayıcı hata
} finally {
this.loading.set(false);
}
}
async markRead(id: string) {
const updated = await this.hs.sdk.Inbox.markRead(id);
this.items.update((list) => list.map((i) => (i.id === id ? updated : i)));
}
async markAllRead() {
await this.hs.sdk.Inbox.markAllRead();
await this.load();
}
async archive(id: string) {
await this.hs.sdk.Inbox.archive(id);
this.items.update((list) => list.filter((i) => i.id !== id));
}
}7. Bildirim tıklaması
// PushizoService.init sonrasında
const off = this.sdk.Notifications.addClickListener(({ notification, result }) => {
void this.router.navigateByUrl(result.url ?? notification.url ?? '/');
});
// uygulama ömrü boyunca tutulur; gerekirse off() ile kaldırılırAngular zone.js kullanıyorsa SDK geri çağrıları zone dışında gelebilir; signal tabanlı durum güncellemeleri zone'dan bağımsız çalışır, ChangeDetectorRef gerekmez. Zone tabanlı şablon bağlamalarında NgZone.run içinde güncelleyin.
8. Sorun giderme
ReferenceError: window is not defined(SSR):inityalnızcaisPlatformBrowserdoğruysa çağrılmalı.- Service worker kapsam çakışması (ngsw):
serviceWorkerScope: '/pushizo/'kullanın. PushizoSDKWorker.js404:angular.jsonassetskaydı kök çıktıya ("output": "/") yazmalı.- Diğer belirtiler: Web SDK sorun giderme; iOS Safari için iOS web push.
Vue ile Pushizo Web SDK
@pushizo/web Vue 3'te bir plugin ile başlatılır; bileşenler usePushizo() composable'ı üzerinden SDK'ya, hazır olma durumuna ve abonelik durumuna (reaktif) erişir. Nuxt 3 için yalnızca istemci tarafında çalışan bir…
Kurumsal SSO (SAML 2.0 / OpenID Connect)
Pushizo paneline girişi kimlik sağlayıcınıza (Okta, Microsoft Entra ID, Google Workspace, Keycloak, OneLogin…) bağlayın. SSO Enterprise planındadır. Kararlar: ADR-0023.