JavaScript köprü API'si
appmintNative (yapay zekâyla oluşturulan uygulamalar)
Yapay zekayla oluşturulan her projenin @/lib/appmintNative içinden içe aktardığı tür tanımlı sarmalayıcılar. Her biri uygulamanın içinde yerel yolu, tarayıcıda ise bir web yolunu ya da dürüstçe none seçer - tek çağrı her yerde çalışır.
appmintNative.actionSheet appmintNative#
actionSheet(title: string, options: { title: string; destructive?: boolean }[]): Promise<number | null>
Örnek
Bir "bunlardan birini seçin" alt sayfası gösterir - bir satırın eylem menüsü için yerel denetim.
Döndürür: Promise<number | null> - seçilen seçeneğin dizini, ya da kullanıcı sayfayı kapattığında (arka plana dokunma, Escape, sistem iptali) null. Kapatmak normaldir, hata değildir. Gerekenler: hiçbir şey.
import { actionSheet, haptic } from '@/lib/appmintNative';
export async function onNoteMenu(note: { id: string }, api: {
edit(id: string): void; share(id: string): void; remove(id: string): void;
}) {
const i = await actionSheet('Note', [
{ title: 'Edit' },
{ title: 'Share' },
{ title: 'Delete', destructive: true },
]);
if (i === null) return; // dismissed
if (i === 0) api.edit(note.id);
if (i === 1) api.share(note.id);
if (i === 2) { haptic('warning'); api.remove(note.id); }
}Görünür bir düğmeden ve satıra uzun basarak açın:
import { useEffect, useRef } from 'react';
import { actionSheet, longPress } from '@/lib/appmintNative';
export function Row({ label }: { label: string }) {
const ref = useRef<HTMLLIElement>(null);
const openMenu = () => actionSheet(label, [{ title: 'Rename' }, { title: 'Delete', destructive: true }]);
useEffect(() => (ref.current ? longPress(ref.current, () => void openMenu()) : undefined), []);
return <li ref={ref}>{label} <button aria-label="More actions" onClick={() => void openMenu()}>⋯</button></li>;
}Capacitor biçimli form ActionSheet.showActions({ title?, options }) aynı seçenekleri alır ve { index } ile çözülür; kapatma için -1 döner:
import { ActionSheet } from '@/lib/appmintNative';
const { index } = await ActionSheet.showActions({
title: 'Photo',
options: [{ title: 'Save' }, { title: 'Delete', destructive: true }],
});
if (index === 1) console.log('delete'); // -1 = dismissed; never test with `if (index)`Notlar:
- Android uygulamasında Capacitor ActionSheet eklentisi sistem sayfasını çizer;
destructiveonun yıkıcı stilini kullanır. - Başka yerde, uygulamanın paletinde (
--surface,--ink,--danger) gerçek bir paneldir; arka plan, Escape, panele taşınan odak ve bir seçim dokunsal geri bildirimiyle. - Boş bir
optionslistesi hemennullile çözülür. Eylem listesi dışındaki herhangi bir içerik için şunu kullanınsheet().
appmintNative.ActionSheet appmintNative#
import { ActionSheet } from '@/lib/appmintNative'
Örnek
appmintNative.actionSheet ile birlikte belgelenmiştir - oradaki örnek bunu da gösterir.
Bir "bunlardan birini seçin" alt sayfası gösterir - bir satırın eylem menüsü için yerel denetim.
Döndürür: Promise<number | null> - seçilen seçeneğin dizini, ya da kullanıcı sayfayı kapattığında (arka plana dokunma, Escape, sistem iptali) null. Kapatmak normaldir, hata değildir. Gerekenler: hiçbir şey.
import { actionSheet, haptic } from '@/lib/appmintNative';
export async function onNoteMenu(note: { id: string }, api: {
edit(id: string): void; share(id: string): void; remove(id: string): void;
}) {
const i = await actionSheet('Note', [
{ title: 'Edit' },
{ title: 'Share' },
{ title: 'Delete', destructive: true },
]);
if (i === null) return; // dismissed
if (i === 0) api.edit(note.id);
if (i === 1) api.share(note.id);
if (i === 2) { haptic('warning'); api.remove(note.id); }
}Görünür bir düğmeden ve satıra uzun basarak açın:
import { useEffect, useRef } from 'react';
import { actionSheet, longPress } from '@/lib/appmintNative';
export function Row({ label }: { label: string }) {
const ref = useRef<HTMLLIElement>(null);
const openMenu = () => actionSheet(label, [{ title: 'Rename' }, { title: 'Delete', destructive: true }]);
useEffect(() => (ref.current ? longPress(ref.current, () => void openMenu()) : undefined), []);
return <li ref={ref}>{label} <button aria-label="More actions" onClick={() => void openMenu()}>⋯</button></li>;
}Capacitor biçimli form ActionSheet.showActions({ title?, options }) aynı seçenekleri alır ve { index } ile çözülür; kapatma için -1 döner:
import { ActionSheet } from '@/lib/appmintNative';
const { index } = await ActionSheet.showActions({
title: 'Photo',
options: [{ title: 'Save' }, { title: 'Delete', destructive: true }],
});
if (index === 1) console.log('delete'); // -1 = dismissed; never test with `if (index)`Notlar:
- Android uygulamasında Capacitor ActionSheet eklentisi sistem sayfasını çizer;
destructiveonun yıkıcı stilini kullanır. - Başka yerde, uygulamanın paletinde (
--surface,--ink,--danger) gerçek bir paneldir; arka plan, Escape, panele taşınan odak ve bir seçim dokunsal geri bildirimiyle. - Boş bir
optionslistesi hemennullile çözülür. Eylem listesi dışındaki herhangi bir içerik için şunu kullanınsheet().
appmintNative.addContact appmintNative#
addContact(contact: Contact): Promise<boolean>
Örnek
Telefonun adres defterine yeni bir kişi kaydeder. Yalnızca Android uygulaması: kabuğun köprüsü onu doğrudan kaydeder (Kişiler ekranı açılmaz).
Dönüş değeri: Promise<boolean> - kaydedildiğinde true, kullanıcı kişi iznini reddettiğinde veya kayıt başarısız olduğunda false. Error ile reddedilir ve bunun code değeri 'not_enabled' (derleme kişi yazmayı açmadı) veya 'foreign_origin' olur; ayrıca yanıtsız 5 dakika beklendikten sonra da reddedilir. Gereksinim: Adım 4'teki (Erişim) Kişileri Düzenle anahtarı - kodunuz bunu çağırdığında derleme açar - ve ilk kullanımda kullanıcının izni.
Bir formdan müşteri kaydedin.
import { addContact, can } from '@/lib/appmintNative';
export async function saveCustomer(name: string, phone: string, email: string): Promise<string> {
if (can('contactAdd') === 'none') return 'Saving contacts works in the app only.';
try {
const ok = await addContact({ name, phones: [phone], emails: email ? [email] : [] });
return ok ? 'Saved to contacts' : 'Allow contacts access to save it';
} catch (e) {
return (e as { code?: string }).code === 'not_enabled'
? 'Saving contacts is not switched on in this app.'
: 'No answer from the phone. Try again.'; // 5-minute timeout
}
}Zaten kayıtlı bir kişiyi atlayın. searchContacts adres defterini okur; bu yüzden eklemeden önce kontrol edin:
import { addContact, searchContacts, can } from '@/lib/appmintNative';
export async function addIfNew(name: string, phone: string): Promise<'added' | 'exists' | 'refused' | 'app_only'> {
if (can('contactAdd') === 'none') return 'app_only';
const found = await searchContacts(name, 5);
if (found.some((c) => c.phones?.includes(phone))) return 'exists';
return (await addContact({ name, phones: [phone] })) ? 'added' : 'refused';
}Derleme hatasını reddedilmeden ayırt edin. code 'foreign_origin' veya 'not_enabled' ile gelen bir ret derleme sorunudur, false kullanıcının tercihidir:
import { useState } from 'react';
import { addContact } from '@/lib/appmintNative';
export function SaveContactButton({ name, phone }: { name: string; phone: string }) {
const [msg, setMsg] = useState('');
async function onTap() {
try {
setMsg((await addContact({ name, phones: [phone] })) ? 'Saved.' : 'Contacts access was declined.');
} catch (e) {
const code = (e as { code?: string }).code;
setMsg(code === 'foreign_origin' || code === 'not_enabled' ? 'This build cannot save contacts.' : 'Try again.');
}
}
return <button onClick={onTap}>{msg || 'Save to contacts'}</button>;
}Notlar: Hemen kaydeder - hiçbir kişi düzenleyicisi açılmaz - bu yüzden önce kendi ekranınızda kullanıcıyla onaylayın. Telefon numaralarını cep numarası olarak ve kişiyi cihaza kaydeder (hesap yok). Kişi yazmak hassastır: Play Veri güvenliği formunuzda ve gizlilik politikanızda bildirin.
appmintNative.alarmSupport appmintNative#
alarmSupport() — @/lib/appmintNative
Örnek
scheduleAlarm şu anda bir alarm kurabilir mi ve değilse neyin eksik olduğu.
Dönüş değeri: 'ready', 'not_enabled' (Alarm saati olmadan derlendi), 'needs_exact_alarm' (Android 12+: tam zamanlı alarmlar kapalı), 'needs_notification_permission' (Android 13+) veya 'unavailable' (web ve iOS - orada alarm denetimleri çizmeyin). Gereksinim: derleme sırasında Erişim adımında (Adım 4) 🔔 Alarm saati.
import { alarmSupport } from '@/lib/appmintNative';
export function AlarmSetupBanner() {
const s = alarmSupport();
if (s === 'ready' || s === 'unavailable') return null;
const text = {
not_enabled: 'This app was built without alarm clocks.',
needs_exact_alarm: 'Allow "Alarms & reminders" for this app in Settings.',
needs_notification_permission: 'Allow notifications so you can stop the alarm.',
}[s];
return <p role="alert">{text}</p>;
}Günlük bir alarmı yalnızca telefon hazırsa kurun ve değilse nedenini söyleyin:
import { alarmSupport, scheduleAlarm } from '@/lib/appmintNative';
export function setMorningAlarm(hour: number, minute: number): string {
const s = alarmSupport();
if (s !== 'ready') return `Alarm not set (${s}).`; // 'not_enabled', 'needs_exact_alarm', ...
const first = new Date();
first.setHours(hour, minute, 0, 0);
if (first.getTime() <= Date.now()) first.setDate(first.getDate() + 1);
const ok = scheduleAlarm('morning', { at: first.getTime(), title: 'Wake up', repeat: 'daily', snoozeMinutes: 5 });
return ok ? 'Alarm set.' : 'The phone did not accept the alarm.';
}Web'de alarm ekranını gizleyin ve bir alarm bittiğinde tepki verin; bunu şununla birleştirin: onAlarm:
import { useEffect, useState } from 'react';
import { alarmSupport, onAlarm, snoozeAlarm, stopAlarm } from '@/lib/appmintNative';
export function AlarmPanel() {
const [ringing, setRinging] = useState<string | null>(null);
useEffect(() => onAlarm((e) => {
if (e.action === 'open') setRinging(e.ringing ? e.id : null);
else setRinging(null); // dismiss, snooze, missed, error
}), []);
if (alarmSupport() === 'unavailable') return null; // web and iOS: draw nothing
if (!ringing) return null;
return (
<div role="alertdialog">
<button onClick={() => stopAlarm(ringing)}>Stop</button>
<button onClick={() => snoozeAlarm(ringing, 10)}>Snooze 10 min</button>
</div>
);
}appmintNative.App appmintNative#
App.getInfo(): Promise<{ name, id, build, version }> / App.exitApp(): void / App.addListener(event, fn)
Örnek
Uygulamanın adı, paket kimliği ve sürümü, uygulamayı kapatmanın bir yolu ve uygulamanın olayları - Capacitor'ın App eklentisi biçiminde.
Döndürür: getInfo() → Promise<{ name: string; id: string; build: string; version: string }>. exitApp() → void. addListener(event, fn) → Capacitor'ın biçimi (remove() da olan tutamacın bir Promise'i). Olaylar: appStateChange ({ isActive }), pause, resume (sayfanın arka plana gitmesi / arka plandan gelmesi), appUrlOpen ({ url }, bir derin bağlantı - onDeepLink ile aynı), backButton (geri basışı veya hareketi; işleyiciniz karar verir - uygulama ayrıca kapanmaz). Gerekenler: hiçbir şey.
import { App } from '@/lib/appmintNative';
const info = await App.getInfo();
aboutLine.textContent = `${info.name} ${info.version}${info.build ? ` (${info.build})` : ''}`;İleri gitmenin tek yolu olmayan bir "Çık" öğesinden uygulamayı kapatın:
import { App, Dialog } from '@/lib/appmintNative';
async function quit() {
const { value } = await Dialog.confirm({ title: 'Quit?', message: 'Unsaved changes will be lost.' });
if (value) App.exitApp();
}Uygulama sürümünü bir React ekranında gösterin.
import { useEffect, useState } from 'react';
import { App } from '@/lib/appmintNative';
export function VersionLabel() {
const [text, setText] = useState('');
useEffect(() => {
void App.getInfo().then((i) => setText(`${i.name} v${i.version}`));
}, []);
return <small>{text}</small>;
}Notlar: Uygulamada getInfo Capacitor App eklentisini okur; o yoksa kabuk köprüsünün adını ve sürümünü kullanır (id ve build o zaman '' olur), web'de ise boş sürümlü document.title. exitApp uygulamada kabuğun closeWindow işlevini kullanır ve web'de hiçbir şey yapmaz; bu yüzden Çık'ı asla tek ilerleme yolu yapmayın. Duraklat/devam et olayları ve derin bağlantılar için Capacitor.Plugins.App, launchUrl() ve onDeepLink().
appmintNative.appLauncher appmintNative#
appLauncher.canOpen(url): Promise<boolean> / appLauncher.open(url): Promise<boolean>
Örnek
Başka bir uygulamayı bağlantıyla açar - whatsapp://send?text=…, tel:, mailto:, geo: veya işletim sisteminin kendi uygulamasına verdiği bir https bağlantısı - ve önce bunun çalışıp çalışamayacağını sorar.
Dönüş değeri: canOpen() → Promise<boolean>. open() → Promise<boolean> (bağlantıyı hiçbir uygulama almadığında false). Gereksinim: hiçbir şey.
import { appLauncher } from '@/lib/appmintNative';
async function chat(phone: string, text: string) {
const url = `whatsapp://send?phone=${phone}&text=${encodeURIComponent(text)}`;
if (!(await appLauncher.canOpen(url))) {
showMessage('WhatsApp is not installed on this phone.');
return;
}
await appLauncher.open(url);
}Arayın veya e-posta gönderin (kontrol gerekmez; telefonda bunlar için her zaman bir uygulama vardır):
import { appLauncher } from '@/lib/appmintNative';
await appLauncher.open('tel:+15551234567');
await appLauncher.open('mailto:[email protected]?subject=Order%2042');geo: bağlantısıyla harita uygulamasını açın ve hiçbir şey almadıysa bunu söyleyin:
import { appLauncher } from '@/lib/appmintNative';
export async function showOnMap(lat: number, lng: number, label: string) {
const ok = await appLauncher.open(`geo:${lat},${lng}?q=${lat},${lng}(${encodeURIComponent(label)})`);
if (!ok) showMessage('No maps app could open this place.');
}Notlar: Uygulamada Capacitor AppLauncher eklentisini kullanır. Android 11+ üzerinde canOpen yalnızca uygulamanın bildirdiği şemaları görebilir (https, geo, google.navigation, tel, mailto, sms, whatsapp, intent, upi); diğerleri için uygulama yüklü olsa bile false yanıtı verir - bunun yerine open() çağırın ve sonucuna bakın. Web'de özel bir şema denetlenemez, bu yüzden canOpen yalnızca http(s) için true olur ve open bağlantıya gider.
appmintNative.appLock appmintNative#
appLock({ timeoutSec?, title?, onLocked? }) — from '@/lib/appmintNative'
Örnek
Kullanıcı bir süre arka planda kaldıktan sonra geri geldiğinde uygulamayı yeniden kilitler: parmak izi / yüz / PIN ister (unlock()) ve uygulama değiştirici içeriğinizi göstermesin diye gizlilik ekranını açar.
Döndürür: kilidi durduran bir işlev (oturum kapatırken veya bileşen kaldırılırken çağırın). onLocked(true) uygulama kilitlendiğinde, onLocked(false) başarılı bir kilit açmadan sonra tetiklenir. Gerekenler: unlock() ile aynı - Android: derlerken Parmak izi işaretli.
import { useEffect, useState } from 'react';
import { appLock } from '@/lib/appmintNative';
/** Call once in your root component; render a lock screen while it returns true. */
export function useAppLock(): boolean {
const [locked, setLocked] = useState(false);
useEffect(() => appLock({
timeoutSec: 60, // lock after 1 minute in the background (default 30)
title: 'Unlock MyNotes',
onLocked: setLocked, // true → cover the screen, false → show the app again
}), []); // appLock returns its own stop function = the cleanup
return locked;
}Kök bileşende: const locked = useAppLock(); if (locked) return <LockScreen />;.
Oturum kapatıldığında kilidi durdurun; böylece oturumu kapatan kullanıcıdan kilit açması istenmez:
import { appLock } from '@/lib/appmintNative';
let stopLock: (() => void) | null = null;
export function onSignedIn() {
stopLock = appLock({ timeoutSec: 120, title: 'Unlock your wallet' });
}
export function onSignedOut() {
stopLock?.(); // removes the listener and turns the privacy screen off
stopLock = null;
}Kilidi yalnızca cihazın açabildiği yerde sunun ve elle "Şimdi kilitle" düğmesi için unlock() ile birleştirin:
import { appLock, can, unlock } from '@/lib/appmintNative';
export function LockSettings({ onLockedChange }: { onLockedChange: (locked: boolean) => void }) {
if (can('unlock') === 'none') return <p>This device has no screen lock to use.</p>; // appLock would do nothing here
return (
<>
<button onClick={() => appLock({ onLocked: onLockedChange })}>Turn on app lock</button>
<button onClick={async () => { onLockedChange(true); if (await unlock('Unlock')) onLockedChange(false); }}>
Lock now
</button>
</>
);
}Notlar: Cihazda kilidi açacak bir şey yoksa (can('unlock') === 'none') hiçbir şey yapmaz ve boş bir işlev döndürür - hiç açılamayan bir uygulama kilidi kullanıcıyı hapseder. Kullanıcı iptal ederse beş kereye kadar yeniden sorar, sonra uygulama arka plana gidip geri gelene kadar kilitli kalır (kilit ekranınızı açık tutun). Bu, derleme sihirbazındaki ve uygulamanın açılışta yerel olarak gösterdiği PIN Kilidi seçeneğinden ayrıdır.
appmintNative.appUpdate appmintNative#
appUpdate.check(): Promise<AppUpdateInfo | null> / appUpdate.start(): Promise<boolean>
Örnek
Google Play'de daha yeni bir sürüm olup olmadığını kontrol eder ve Play'in uygulama içi güncellemesini başlatır (yerinde indirir ve kurar).
Dönüş değeri: check() → Promise<{ currentVersion, availableVersion?, currentVersionCode?, updateAvailable, inPlace } | null> (web'de, Play hizmetleri olmadan veya Play başarısız olduğunda null). updateAvailable mağazanın kendi yanıtıdır. start() → Promise<boolean>: true yalnızca Play'in güncellemesi tamamlandığında, kullanıcı iptal ettiğinde, başarısız olduğunda veya hiçbiri olmadığında false. Gereksinim: hiçbir şey; uygulama Google Play'den yüklenmiş olmalıdır.
import { appUpdate } from '@/lib/appmintNative';
async function offerUpdate() {
const info = await appUpdate.check();
if (!info?.inPlace) return; // Play can install an update now
showBanner('A new version is ready', async () => {
const done = await appUpdate.start(); // Play's full-screen flow / the App Store page
if (!done) showBanner('Update later from the store.', null);
});
}Play'in sürüm kodunu yüklü olanla karşılaştırın ve null değerini "burada kontrol edilemiyor" olarak ele alın:
import { appUpdate } from '@/lib/appmintNative';
export async function updateStatus(): Promise<string> {
const info = await appUpdate.check();
if (!info) return 'Update check is not available here.'; // web, no Play services, or Play failed
if (!info.updateAvailable) return `You have the latest version (${info.currentVersion}).`;
return `Update available: version code ${info.availableVersion} (you have ${info.currentVersionCode}).`;
}Ekran açıldığında bir kez kontrol eden bir React afişi:
import { useEffect, useState } from 'react';
import { appUpdate, type AppUpdateInfo } from '@/lib/appmintNative';
export function UpdateBanner() {
const [info, setInfo] = useState<AppUpdateInfo | null>(null);
useEffect(() => { void appUpdate.check().then(setInfo); }, []);
if (!info?.updateAvailable) return null;
return <button onClick={() => void appUpdate.start()}>Update now</button>;
}Notlar: updateAvailable Play'in kendi yanıtıdır; inPlace ayrıca Play'in uygulama içi güncellemesini şu anda çalıştırabileceğini ekler. availableVersion Play'in sürüm KODUDUR (Play ad vermez) - bunu sürüm adı olan currentVersion ile değil, currentVersionCode ile karşılaştırın.
appmintNative.askForReview appmintNative#
askForReview(options): Promise<ReviewOutcome>
Örnek
Google Play'den uygulama içinde kendi puanlama sayfasını göstermesini ister ve ne olduğunu bildirir.
Döndürür: Promise<ReviewOutcome> - { result, openedStore, message? }, result 'shown' | 'not_from_play' | 'quota' | 'error' | 'unavailable' ile (web'de 'unavailable'). { openStore: true }, sayfa gösterilmediğinde Play sayfasını açar. Gerekenler: hiçbir şey; sayfa yalnızca Google Play'den yüklenen uygulamada görünür.
import { askForReview } from '@/lib/appmintNative';
export async function onRateTap() {
const r = await askForReview({ openStore: true }); // listing if Play shows no sheet
if (r.result === 'shown' || r.openedStore) toast('Thanks for rating!');
}Kullanıcıya her sonuç için ne olduğunu söyleyin:
import { askForReview } from '@/lib/appmintNative';
export async function rateMessage(): Promise<string> {
const r = await askForReview();
switch (r.result) {
case 'shown': return 'Thanks for rating!';
case 'not_from_play': return 'Rating works for installs from Google Play.';
case 'quota': return 'Google limits how often the sheet shows. Try again later.';
case 'unavailable': return ''; // the web: draw no rating button at all
default: return `Could not open the rating sheet${r.message ? `: ${r.message}` : ''}.`;
}
}Kullanıcı üçüncü görevini bitirdikten sonra bir kez sorun ve sorduğunuzu hatırlayın:
import { askForReview } from '@/lib/appmintNative';
export async function maybeAskAfterTask(completed: number) {
if (completed !== 3 || localStorage.getItem('asked-review')) return;
localStorage.setItem('asked-review', '1');
await askForReview();
}Notlar: Bir başarı anından sonra ve seyrek isteyin. Yandan yüklenmiş bir test kurulumu her zaman yanıtlar not_from_play.
appmintNative.backgroundLocation appmintNative#
import { backgroundLocation } from '@/lib/appmintNative'
Örnek
Uygulama arka plandayken gelmeye devam eden konum: start, stop, onFix, buffered, clearBuffered, requestAlways, permissionState, available.
Dönüş değeri: start(options) → Promise<BgResult> ({ ok, background, backgroundPermission, precise } veya { ok: false, reason }). stop / clearBuffered → Promise<boolean>. buffered() → Promise<LocationFix[]>. requestAlways() → Promise<{ foreground, background, precise, error? }>. onFix(cb) → bir abonelikten çıkma; cb her appmint:location ayrıntısını alır. Gereksinim: derlemede Arka plan konumu; konum izni (önce navigator.geolocation ile isteyin).
import { backgroundLocation, type LocationFix } from '@/lib/appmintNative';
export async function startRun(onPoint: (p: LocationFix) => void) {
const r = await backgroundLocation.start({ intervalMs: 5000, minDistanceM: 5, accuracy: 'high',
uploadUrl: 'https://api.example.com/run-points' });
if (!r.ok) throw new Error(r.reason);
const off = backgroundLocation.onFix((d) => { if (d.type === 'fix') onPoint(d); });
// Points recorded while the screen was off:
(await backgroundLocation.buffered()).forEach(onPoint);
await backgroundLocation.clearBuffered();
return async () => { off(); await backgroundLocation.stop(); };
}Başlamadan önce "Her zaman izin ver" seçeneğini isteyin ve reddi açıklayın:
import { backgroundLocation } from '@/lib/appmintNative';
export async function beginTracking(): Promise<string> {
if (backgroundLocation.available() === 'none') return 'Background location works in the app only.';
const p = await backgroundLocation.requestAlways();
if (p.error) return `Could not ask for permission (${p.error}).`;
if (p.background !== 'granted') return 'Choose "Allow all the time" to keep tracking with the screen off.';
const r = await backgroundLocation.start({ intervalMs: 10000, accuracy: 'balanced' });
return r.ok ? 'Tracking.' : `Could not start (${r.reason}).`;
}Canlı puanları gösteren ve ekran kapandığında dinlemeyi bırakan bir React kancası:
import { useEffect, useState } from 'react';
import { backgroundLocation, type LocationFix } from '@/lib/appmintNative';
export function useLastFix(): LocationFix | null {
const [fix, setFix] = useState<LocationFix | null>(null);
useEffect(() => backgroundLocation.onFix((d) => {
if (d.type === 'fix') setFix(d); // { type: 'error', reason } events are skipped here
}), []); // onFix returns its unsubscribe
return fix;
}appmintNative.backgroundSync appmintNative#
import { backgroundSync } from '@/lib/appmintNative'
Örnek
Uygulama kapalıyken çalışan yerel HTTP istekleri: enqueue (tek seferlik, çevrim içi olunca gönderilen kalıcı otomatik kayıt), schedule (tekrarlayan istek, en az her 15 dakikada), cancel ve list.
Döndürür: enqueue / schedule → Promise<boolean>; cancel → Promise<void>; list → Promise<BackgroundJob[]> ({ id, url, periodic, createdAt?, pendingDelivery? } - pendingDelivery Android'de: henüz teslim edilmemiş tek seferlik bir iş). Gerekenler: Android uygulamasında hiçbir şey (WorkManager kullanır). Web'de can('backgroundSync') değeri 'none' olur ve enqueue / schedule çözülür false.
Kalıcı otomatik kayıt: aynı kimlik, henüz gönderilmemiş kaydın yerini alır.
import { backgroundSync, can } from '@/lib/appmintNative';
export async function saveDraft(draft: object, token: string) {
if (can('backgroundSync') === 'none') return false; // web: save normally instead
return backgroundSync.enqueue({
id: 'draft',
url: 'https://api.example.com/drafts',
method: 'PUT',
headers: { Authorization: `Bearer ${token}` },
body: JSON.stringify(draft),
});
}Tekrarlayan eşitleme, listeleme ve iptal:
await backgroundSync.schedule({
id: 'sync', url: 'https://api.example.com/sync', body: '{}', intervalMinutes: 60,
});
const jobs = await backgroundSync.list();
console.log(jobs.map((j) => `${j.id} ${j.periodic ? 'repeating' : 'one-off'}`));
await backgroundSync.cancel('sync');Ham olay (sarmalayıcının Android'de beklediği):
window.addEventListener('appmint:work', function (e) {
// { id, status: 'enqueued' | 'scheduled' | 'cancelled' | 'failed', reason?, intervalMinutes? }
// or { requestId, jobs: [...] } for a list
console.log('work', e.detail);
});Notlar: true işin sıraya alındığı, teslim edilmediği anlamına gelir. false sıraya alınmadığı anlamına gelir: kabuk status: 'failed' yanıtı verdi (http ile başlamayan bir URL, boş bir kimlik veya sistemin reddetmesi) veya 10 saniye içinde yanıt vermedi. Sayfanın JavaScript'i arka planda asla çalışmaz; yalnızca bu istek çalışır. 4xx yanıtı yeniden denenmez; ağ hataları ve 5xx 8 kereye kadar yeniden denenir.
appmintNative.backgroundTasks appmintNative#
import { backgroundTasks } from '@/lib/appmintNative'
Örnek
Sayfanın kendi kodu, uygulama kapalıyken düzenli aralıklarla çalıştırılır (şunun üzerinde türlendirilmiş sarmalayıcı AppMint.background): register, unregister, list, runNow, done, isBackgroundRun, currentRun, onRun, available.
Dönüş değeri: register / runNow → Promise<BgResult>; unregister / done → Promise<boolean>; list() → lastRunAt / lastResult ile kayıtlı görevler; isBackgroundRun() / currentRun() eşzamanlıdır. Gereksinim: derlemede Kapalıyken sayfa kodunu çalıştır.
import { backgroundTasks, notify } from '@/lib/appmintNative';
// In the app's entry file, before rendering:
if (backgroundTasks.isBackgroundRun()) {
backgroundTasks.onRun(async (run) => {
try {
const unread = await fetch('/api/unread').then((r) => r.json());
if (unread.count > 0) await notify({ title: 'New messages', body: `${unread.count} unread` });
await backgroundTasks.done(run.runId, { ok: true });
} catch {
await backgroundTasks.done(run.runId, { ok: false, retry: true });
}
});
} else {
void backgroundTasks.register('unread', { intervalMinutes: 30, requiresNetwork: true });
// renderApp();
}Oturum açılınca kaydedin, çıkışta kaldırın ve kayıtlı olanları listeleyin:
import { backgroundTasks } from '@/lib/appmintNative';
export async function onSignedIn() {
const r = await backgroundTasks.register('sync', { intervalMinutes: 60, requiresNetwork: true });
if (!r.ok) console.warn('Background sync not set up:', r.reason); // 'not_enabled' or 'unsupported'
}
export async function onSignedOut() {
await backgroundTasks.unregister('sync');
}
export async function taskCountText(): Promise<string> {
const tasks = await backgroundTasks.list();
return tasks.length ? `Registered tasks: ${tasks.length}` : 'No background tasks.';
}Bir "Şimdi eşitle" düğmesinden bir görevi hemen çalıştırın ve yanıtı kontrol edin:
import { backgroundTasks } from '@/lib/appmintNative';
export function SyncNowButton() {
return (
<button
disabled={backgroundTasks.available() === 'none'}
onClick={async () => {
const r = await backgroundTasks.runNow('sync');
if (!r.ok) alert(`Could not run it (${r.reason}).`);
}}
>
Sync now
</button>
);
}appmintNative.badge appmintNative#
import { badge } from '@/lib/appmintNative'
Örnek
Uygulamanın başlatıcı simgesindeki okunmamış sayısını ayarlar veya temizler.
Döndürür: badge.set(count) → Promise<boolean> (Badge eklentisi yoksa, örn. web'de false); badge.clear() → Promise<void>. Gerekenler: Android'de hiçbir şey (yerleşik Badge eklentisini kullanır).
import { badge, pushInbox } from '@/lib/appmintNative';
export async function syncBadge() {
const unread = pushInbox().filter((m) => !m.read).length;
await badge.set(unread); // 0 or less clears it
}
export async function onInboxOpened() {
await badge.clear();
}Uygulama açıkken bir push mesajı geldiğinde okunmamış sayısını yansıtın:
import { badge, pushInbox } from '@/lib/appmintNative';
export function watchInbox(): () => void {
const refresh = () => void badge.set(pushInbox().filter((m) => !m.read).length);
window.addEventListener('appmint:push', refresh);
refresh();
return () => window.removeEventListener('appmint:push', refresh);
}Her şeyi okundu işaretleyin ve sayacı birlikte temizleyin; rozet olmayan yerde rozeti atlayın:
import { badge, can, markPushRead } from '@/lib/appmintNative';
export async function markAllRead(): Promise<boolean> {
markPushRead(); // no id = every message
if (can('badge') === 'none') return false; // the web: nothing to clear
await badge.clear();
return true;
}Notlar: Android'de görünüm başlatıcıya bağlıdır: çoğu bir sayı gösterir, bazıları yalnızca nokta, bazıları hiçbir şey göstermez - bilgiyi yalnızca rozete koymayın. Webde can('badge') 'none'.
appmintNative.barcodeFormats appmintNative#
barcodeFormats(): Promise<string[]>
Örnek
Bu cihazın BarcodeDetector aracının okuyabildiği barkod ve QR biçimlerini listeler.
Döndürür: Promise<string[]> - 'qr_code', 'ean_13', 'code_128' gibi biçim adları; burada barkod okuma yoksa boş bir dizi. Asla reddetmez. Gerekenler: hiçbir şey.
"Ürün barkodunu tara" seçeneğini yalnızca cihaz o biçimi okuyorsa gösterin:
import { useEffect, useState } from 'react';
import { barcodeFormats } from '@/lib/appmintNative';
export function useCanReadEan(): boolean {
const [ok, setOk] = useState(false);
useEffect(() => {
void barcodeFormats().then((f) => setOk(f.includes('ean_13')));
}, []);
return ok;
}Bir taramayı cihazın desteklediği biçimlerle sınırlayın:
import { barcodeFormats, readBarcodeFrom, type BarcodeFormat } from '@/lib/appmintNative';
declare const photo: File;
const wanted: BarcodeFormat[] = ['qr_code', 'ean_13'];
const have = await barcodeFormats();
const codes = await readBarcodeFrom(photo, wanted.filter((f) => have.includes(f)));Yalnızca bu cihazın okuyabildiklerini listeleyen bir biçim seçici oluşturun:
import { useEffect, useState } from 'react';
import { barcodeFormats } from '@/lib/appmintNative';
export function FormatPicker({ onPick }: { onPick: (format: string) => void }) {
const [formats, setFormats] = useState<string[]>([]);
useEffect(() => { void barcodeFormats().then(setFormats); }, []);
if (formats.length === 0) return <p>Barcode reading is not available on this device.</p>; // [] = none
return (
<select onChange={(e) => onPick(e.target.value)}>
{formats.map((f) => <option key={f} value={f}>{f}</option>)}
</select>
);
}Notlar:
- Tarayıcı motorunun
BarcodeDetector.getSupportedFormats()işlevine sorar. Android uygulamasının WebView'ında vardır (ölçüldü: aztec, code_128, code_39, code_93, codabar, data_matrix, ean_13, ean_8, itf, pdf417, qr_code, upc_a, upc_e); birçok masaüstü tarayıcıda ve Safari'de yoktur ve şunu yanıtlar:[]. - Bu liste
readBarcodeFrom()/scanBarcode()ile ilgilidir. UygulamadakiscanCode()bunun yerine Google'ın tarayıcısını kullanır ve buna bağlı değildir.
appmintNative.batteryOptimization appmintNative#
import { batteryOptimization } from '@/lib/appmintNative'
Örnek
Pil optimizasyonu muafiyeti: isIgnoring() ve request().
Dönüş değeri: isIgnoring() → boolean. request() → Promise<string>: 'already_ignoring', 'dialog_opened' (derleme seçeneği "Pil optimizasyonunu doğrudan atlamayı sor"), 'settings_opened', 'unavailable' veya Android uygulaması dışında 'unsupported'. Gereksinim: ayarlar listesi için hiçbir şey.
import { batteryOptimization } from '@/lib/appmintNative';
export async function makeReliable() {
if (batteryOptimization.isIgnoring()) return true;
const how = await batteryOptimization.request();
if (how === 'settings_opened') alert('Choose this app in the list and set it to "Not optimised".');
return false;
}Mevcut durumu bir ayarlar satırında gösterin:
import { useState } from 'react';
import { batteryOptimization } from '@/lib/appmintNative';
export function BatteryRow() {
const [ignoring, setIgnoring] = useState(batteryOptimization.isIgnoring());
return (
<button
disabled={ignoring}
onClick={async () => {
const how = await batteryOptimization.request();
if (how === 'unsupported' || how === 'unavailable') alert('This phone has no setting for it.');
setIgnoring(batteryOptimization.isIgnoring()); // reads the current answer after the dialog or settings screen
}}
>
{ignoring ? 'Battery limits are off' : 'Allow background work'}
</button>
);
}Uzun bir arka plan işini başlatmadan önce muafiyeti isteyin, şununla birlikte keepAlive:
import { batteryOptimization, keepAlive } from '@/lib/appmintNative';
export async function startUpload() {
if (!batteryOptimization.isIgnoring()) await batteryOptimization.request();
const r = await keepAlive.start({ title: 'Uploading photos', type: 'dataSync' });
if (!r.ok) throw new Error(r.reason);
}appmintNative.bleSensors appmintNative#
import { bleSensors } from '@/lib/appmintNative'
Örnek
Standart Bluetooth fitness sensörleri - kalp atış hızı, bisiklet hızı/pedal devri, güç, koşu hızı/adım sayısı ve sensör pili - sayılar zaten ayrıştırılmış olarak, Android uygulamasında.
Döndürür: her yöntem Promise<void> döndürür (setWheelCircumference void döndürür). Diğer her şey detail = {kind:'device', address, name, sensors, rssi} | {kind:'state', state, address?, services?} | {kind:'data', type, data} | {kind:'error', error} ile appmint:ble pencere olayları olarak gelir. Gerekenler: Bluetooth; kod bleSensors kullandığında derleme bunu kendisi açar. Tarayıcıda (web önizlemesi) sensör köprüsü yoktur: startScan() {kind:'error', error:'not_enabled'} ile yanıtlar; bu hatayı ele alın. Önceden bilmek için can('bluetooth') === 'native' değerini test edin - !== 'none' değil: Chrome'da can('bluetooth') değeri 'web' olur (Web Bluetooth); bleSensors bunu kullanmaz.
import { useEffect, useState } from 'react';
import { bleSensors } from '@/lib/appmintNative';
type BleDetail = {
kind: 'device' | 'state' | 'data' | 'error';
address?: string; name?: string | null; sensors?: string[]; rssi?: number;
state?: 'scanning' | 'scan_stopped' | 'connecting' | 'connected' | 'ready' | 'disconnected';
type?: 'heart_rate' | 'speed_cadence' | 'power' | 'running' | 'battery';
data?: { bpm?: number; speedKmh?: number; cadenceRpm?: number; watts?: number; cadenceSpm?: number; percent?: number };
error?: string;
};
export function SensorPanel() {
const [devices, setDevices] = useState<BleDetail[]>([]);
const [bpm, setBpm] = useState<number | null>(null);
const [status, setStatus] = useState('');
useEffect(() => {
const onBle = (e: Event) => {
const d = (e as CustomEvent<BleDetail>).detail;
if (d.kind === 'device') setDevices((list) => [...list, d]);
else if (d.kind === 'state') setStatus(d.state ?? '');
else if (d.kind === 'data' && d.type === 'heart_rate') setBpm(d.data?.bpm ?? null);
else if (d.kind === 'error') {
setStatus(d.error === 'not_enabled' ? 'Sensors work in the installed app.' : `Bluetooth: ${d.error}`);
}
};
window.addEventListener('appmint:ble', onBle);
return () => { window.removeEventListener('appmint:ble', onBle); void bleSensors.disconnect(); };
}, []);
return (
<div>
<button onClick={() => { setDevices([]); bleSensors.setWheelCircumference(2105); void bleSensors.startScan(); }}>
Find sensors
</button>
{devices.map((d) => (
<button key={d.address} onClick={() => void bleSensors.connect(d.address!)}>
{d.name ?? 'Sensor'} ({d.sensors?.join(', ')})
</button>
))}
<p>{status}</p>
{bpm !== null && <p>{bpm} bpm</p>}
</div>
);
}Ekranı yüklü uygulamaya göre kapıyla koruyun.
import { bleSensors, can } from '@/lib/appmintNative';
export function SensorsEntry() {
// 'native' only: in Chrome can('bluetooth') is 'web', which bleSensors does not use.
if (can('bluetooth') !== 'native') return <p>Heart-rate sensors work in the installed app.</p>;
return <button onClick={() => void bleSensors.startScan()}>Find sensors</button>;
}İlk sensörde taramayı durdurun ve bisiklet hızını izleyin.
import { useEffect, useState } from 'react';
import { bleSensors } from '@/lib/appmintNative';
type BikeEvent = { kind: string; address?: string; type?: string; data?: { speedKmh?: number } };
export function BikeSpeed() {
const [kmh, setKmh] = useState<number | null>(null);
useEffect(() => {
bleSensors.setWheelCircumference(2096); // 700x23c
const onBle = (e: Event) => {
const d = (e as CustomEvent<BikeEvent>).detail;
if (d.kind === 'device' && d.address) {
void bleSensors.stopScan().then(() => bleSensors.connect(d.address!));
} else if (d.kind === 'data' && d.type === 'speed_cadence') {
setKmh(d.data?.speedKmh ?? null);
}
};
window.addEventListener('appmint:ble', onBle);
void bleSensors.startScan();
return () => { window.removeEventListener('appmint:ble', onBle); void bleSensors.disconnect(); };
}, []);
return <p>{kmh === null ? 'Looking for a sensor...' : `${kmh.toFixed(1)} km/h`}</p>;
}Notlar: Taramalar 15 saniye sürer (stopScan() birini erken bitirir); aynı anda tek bir sensör bağlıdır. Hata kodları: not_enabled, permission_denied, bluetooth_unavailable, bluetooth_off, scan_failed, invalid_address. Hız için iki paket ve teker boyutu gerekir (varsayılan 2096 mm). Aynı mesajlar window.onAppMintBle öğesine de gider; olayı dinlemek daha basittir. Diğer tüm Bluetooth cihazları için şunu kullanın: navigator.bluetooth.
appmintNative.brightness appmintNative#
brightness.get() / brightness.set(value) / brightness.restore()
Örnek
Biniş kartı veya QR kodu için ekranı parlatır, sonra denetimi sisteme geri verir. Yalnızca yerel - bir web sayfası arka ışığı yönetemez.
Döndürür: get() → Promise<number | null> (uygulama seviyeyi geçersiz kılarken 0-1; uygulama sistem seviyesini izlerken -1; web'de veya okunamadığında null). set(value) → Promise<boolean> (değer 0-1 aralığına sıkıştırılır; web'de false). restore() → Promise<void>. Gerekenler: hiçbir şey.
import { brightness, can } from '@/lib/appmintNative';
import { useEffect } from 'react';
function TicketScreen() {
useEffect(() => {
if (can('brightness') === 'none') return;
void brightness.set(1);
return () => { void brightness.restore(); };
}, []);
return <QrCode />;
}Seviyeyi hatırlayın, parlatın, sonra geri koyun.
import { brightness } from '@/lib/appmintNative';
export async function showBoardingPass(render: () => Promise<void>) {
const before = await brightness.get(); // -1 = following the system, null = not readable here
const ok = await brightness.set(1);
try {
await render();
} finally {
if (ok) await brightness.restore(); // returns to the system level
}
return before;
}Bir kod gösterilirken ekranı parlak ve uyanık tutun.
import { brightness, keepAwake, can } from '@/lib/appmintNative';
import { useEffect } from 'react';
export function useQrPresenting(active: boolean) {
useEffect(() => {
if (!active) return;
if (can('brightness') !== 'none') void brightness.set(0.95);
void keepAwake(true);
return () => { void brightness.restore(); void keepAwake(false); };
}, [active]);
}Notlar: Yalnızca bu uygulamanın penceresini değiştirir, telefonun parlaklık ayarını değil; geçersiz kılma uygulama kapanınca biter. restore(), -1 değerini ayarlar (sistemi izle). get() pencerenin geçersiz kılmasını okur, bu yüzden set() çağırana kadar -1 olur - sistem seviyesi değildir.
appmintNative.browser appmintNative#
browser.open(url, { toolbarColor? }) / browser.close() / browser.onClosed(cb)
Örnek
Bir sayfayı uygulama içi tarayıcı sekmesinde (Chrome Custom Tabs) açar - şartlar, yardım sayfaları veya bir giriş sayfası için - ve web'de yeni bir sekmede.
Döndürür: open() → Promise<boolean> (sekme açılamadığında false). close() → Promise<void>. onClosed(cb) → bir abonelikten çıkma işlevi; cb, kullanıcı sekmeyi kapattığında çalışır (yalnızca uygulamada). Gerekenler: hiçbir şey.
import { browser } from '@/lib/appmintNative';
async function openHelp() {
const off = browser.onClosed(() => {
off();
void refreshAccount(); // the user may have changed something on the site
});
const ok = await browser.open('https://example.com/help', { toolbarColor: '#0B0F1A' });
if (!ok) { off(); showMessage('Could not open the help page.'); }
}Bir React bileşeninde şartlar bağlantısı.
import { browser, toast } from '@/lib/appmintNative';
export function TermsLink() {
return (
<button
onClick={async () => {
if (!(await browser.open('https://example.com/terms'))) toast('Could not open the terms page');
}}
>
Terms of use
</button>
);
}Sekmeyi koddan kapatın ve kullanıcı geri geldiğinde yenileyin.
import { useEffect } from 'react';
import { browser } from '@/lib/appmintNative';
export function usePaymentTab(onBack: () => void) {
useEffect(() => browser.onClosed(onBack), [onBack]); // the returned function unsubscribes
return {
start: (url: string) => browser.open(url),
cancel: () => browser.close(), // closes the tab in the app; does nothing on the web
};
}Notlar: toolbarColor varsayılan olarak uygulamanın --bg belirtecini kullanır. Sekme ayrı bir tarayıcıdır - uygulamanın çerezlerini veya localStorage verisini paylaşmaz. close(), uygulamada sekmeyi koddan kapatır; web'de hiçbir şey yapmaz. onClosed web'de asla tetiklenmez.
appmintNative.calendar appmintNative#
calendar.requestPermission / createEvent / listEvents / deleteEvent / openCalendar
Örnek
Kullanıcının KENDİ telefon takviminde (Android'in takvim sağlayıcısı) etkinlik ekler, listeler ve siler ve takvim uygulamasını bir tarihte açar.
Döndürür: requestPermission() → Promise<'granted' | 'denied'>. createEvent(input) → Promise<string | null> (olay kimliği; kullanıcı takvim erişimini reddettiğinde null - olayın kendisi oluşturulamadığında, örn. geçersiz tarihler, reddedilir). listEvents({ from, to }) → Promise<{ id, title, start, end, location?, notes?, allDay? }[]> (zamanlar epoch ms). deleteEvent(id) / openCalendar(date?) → Promise<boolean>. Yalnızca createEvent reddeder, o da yalnızca oluşturulamayan bir olay için. Gerekenler: derlerken Adım 4'te (Erişim) Takvim'i açın - kodda calendar.createEvent(, calendar.listEvents( veya calendar.requestPermission( görürse derleme bunu sizin için işaretler.
import { calendar } from '@/lib/appmintNative';
async function addBooking(b: { name: string; at: Date; minutes: number; place: string }) {
if ((await calendar.requestPermission()) !== 'granted') {
showMessage('Allow calendar access to add the booking.');
return;
}
const id = await calendar.createEvent({
title: `Booking: ${b.name}`,
start: b.at,
end: new Date(b.at.getTime() + b.minutes * 60_000),
location: b.place,
notes: 'Added by the app',
});
if (id) saveEventId(b, id);
}Bu haftayı listeleyin, silin, açın:
import { calendar, can } from '@/lib/appmintNative';
if (can('calendarRead') !== 'none') {
const week = await calendar.listEvents({ from: Date.now(), to: Date.now() + 7 * 86_400_000 });
if (week[0]) await calendar.deleteEvent(week[0].id);
}
await calendar.openCalendar(new Date());Takvime yalnızca yazmak mümkünse ekleyin.
import { calendar, can, toast } from '@/lib/appmintNative';
export async function addReminder(title: string, when: Date) {
if (can('calendarWrite') === 'none') { toast('Calendar is not available here'); return null; }
const id = await calendar.createEvent({ title, start: when, end: new Date(when.getTime() + 30 * 60_000) });
toast(id ? 'Added to your calendar' : 'Calendar access was refused');
return id;
}Notlar: Webde createEvent, kullanıcının takviminde açtığı bir .ics dosyası indirir (UID'sini döndürür), listEvents [] döndürür (can('calendarRead') 'none' olur), deleteEvent / openCalendar ise false döndürür. Hatırlatıcılar ve tekrarlayan olaylar bu sarmalayıcı üzerinden değil Capacitor.Plugins.Calendar üzerinden kullanılabilir.
appmintNative.camera appmintNative#
camera.take(opts?) / camera.pick(opts?)
Örnek
Kamerayla fotoğraf çeker veya galeriden fotoğraf seçer: uygulamada Capacitor Camera eklentisi, web'de capture olan bir dosya girişi.
Döndürür: take() → Promise<Photo | null>, { dataUrl, mime } ile; iptalde veya reddedilen izinde null. pick() → Promise<Photo[]>, iptalde boş. Gerekenler: 4. Adım'da (Erişim) Kamera - kodunuz bunu çağırdığında derleme onu açar camera.take.
Ön kamerayla profil fotoğrafı çekin.
import { camera } from '@/lib/appmintNative';
export async function takeSelfie(img: HTMLImageElement): Promise<boolean> {
const photo = await camera.take({ source: 'camera', direction: 'front', quality: 70 });
if (!photo) return false; // cancelled or permission refused
img.src = photo.dataUrl; // "data:image/jpeg;base64,..."
return true;
}Galeriden take ile bir fotoğraf veya pick ile birkaç fotoğraf seçin.
import { camera } from '@/lib/appmintNative';
const one = await camera.take({ source: 'photos' }); // Photo | null
const many = await camera.pick({ limit: 5, quality: 80 }); // Photo[] (limit 0 = no limit)
console.log(one?.mime, many.length);Veri URL'sini Blob'a çevirerek fotoğraf yükleyin.
import { camera } from '@/lib/appmintNative';
const p = await camera.take();
if (p) {
const blob = await (await fetch(p.dataUrl)).blob();
const form = new FormData();
form.append('file', blob, 'photo.jpg');
await fetch('https://api.example.com/upload', { method: 'POST', body: form });
}Notlar: Seçenekler: source (varsayılan 'camera' veya 'photos'), quality 0-100 (varsayılan 80), direction (varsayılan 'rear' veya 'front'), allowEditing. Fotoğraflar galeriye kaydedilmez. Reddedilen izin null / [] ile sonuçlanır, asla hata fırlatmaz - kullanıcı neden bir şey olmadığını anlasın diye kısa bir ipucu gösterin. Kamera hassastır: Play Veri güvenliği formunuzda belirtin.
appmintNative.can appmintNative#
can(cap: Capability): 'native' | 'web' | 'none'
Örnek
Bir özelliğin mevcut yüzeyde gerçekten nerede çalışacağını söyler, böylece uygulama yalnızca çalışan denetimleri çizer.
Döndürür: eşzamanlı olarak bir dize: 'native' (uygulamanın kendi uygulaması - kabuk köprüsü veya uygulamaya gömülü bir Capacitor eklentisi), 'web' (bu tarayıcıda gerçek bir web standardı) veya 'none' (burada kullanılamaz - denetimi çizmeyin). Gerekenler: hiçbir şey.
Bu yüzeyde çalışamayan bir denetimi gizleyin:
import { can, share } from '@/lib/appmintNative';
export function ShareButton({ url }: { url: string }) {
if (can('share') === 'none') return null; // never ship a dead button
return <button onClick={() => void share({ title: 'Have a look', url })}>Share</button>;
}İfadeyi yüzeye göre değiştirin:
import { can } from '@/lib/appmintNative';
const contacts = can('contactList') !== 'none'; // 'none' on the web
const lockLabel = can('unlock') === 'native' ? 'Unlock with fingerprint' : 'Unlock with a passkey';Yetenek adları: haptic share clipboardWrite clipboardRead speak fullscreen notify deviceInfo · filePick fileSave unlock · contactPick contactList contactAdd · smsCompose smsSend smsList callLog · toast dialog actionSheet keyboardInsets orientationLock network statusBar · swipe backGesture · folderAccess secureKeys mediaControls cast bluetooth · shortcuts · appLaunch browser camera photos location screenReader textZoom privacyScreen fileViewer fileTransfer calendarRead calendarWrite backgroundSync motion · keepAwake brightness review appUpdate badge speech hapticPatterns docScanner systemPalette liveProgress widget nfc pip shareTarget notificationActions · barcodeScan codeScanner passkeys.
Ölü bir denetim yerine bir ipucu gösterin ve bir özelliği düz bir evet/hayıra çevirin, şununla has():
import { can, has, keepAwake } from '@/lib/appmintNative';
export function KeepScreenOn({ on }: { on: boolean }) {
if (!has('keepAwake')) return <p>Keep-awake is not available on this device.</p>;
return <label><input type="checkbox" checked={on} onChange={(e) => void keepAwake(e.target.checked)} /> Keep screen on</label>;
}
export const scansInApp = can('codeScanner') === 'native';Notlar:
- Uygulamaya gömülü bir Capacitor eklentisi her zaman
'native'yanıtlar; aksi halde önce kabuk köprüsü, sonra web standardı kontrol edilir. toast,actionSheet,dialog,statusBarveswipeiçin gerçek bir web sürümü vardır; bu yüzden tarayıcıda'none'değildir.contactList,contactAdd,smsSend,smsList,callLog,shortcuts,shareTargetvewidgetweb'de'none'değerindedir.- Bazı özellikler yalnızca derleme onları açtığında vardır (klasör erişimi, güvenli anahtarlar, medya denetimleri, SMS, kişiler).
can()sorun, şunu değilisNativeApp(). - Bilinmeyen bir ad
'none'döndürür. Düz bir evet/hayır için şunu kullanınhas(cap).
appmintNative.clearPushInbox appmintNative#
import { clearPushInbox } from '@/lib/appmintNative'
Örnek
Bu telefondaki AppMint Push gelen kutusunu boşaltır. Kalıcıdır - geri alma yoktur.
Döndürür: boolean eşzamanlı olarak - temizlendiğinde true; web'de her zaman false. Gerekenler: Android: derleme yaparken Entegrasyon adımında (3. Adım) AppMint Push açık.
import { clearPushInbox, pushInbox } from '@/lib/appmintNative';
export async function onClearAll(confirmDelete: () => Promise<boolean>) {
if (!(await confirmDelete())) return pushInbox();
clearPushInbox();
return pushInbox(); // []
}Temizle'yi yalnızca temizlenecek bir şey olduğunda gösterin ve sonucu bildirin:
import { clearPushInbox, pushInbox, toast } from '@/lib/appmintNative';
export function InboxToolbar({ onChange }: { onChange: () => void }) {
const count = pushInbox().length;
if (count === 0) return null;
return (
<button
onClick={() => {
// false on the web, or in an app built without AppMint Push
if (clearPushInbox()) { toast('Inbox cleared'); onChange(); }
else toast('Could not clear the inbox here');
}}
>
Clear all ({count})
</button>
);
}Her şeyi silmek yerine okundu işaretleyin; hiçbir şey kaybolmaz:
import { markPushRead, pushInbox } from '@/lib/appmintNative';
export function markAllRead(): number {
const unread = pushInbox().filter((m) => !m.read).length;
markPushRead(); // no argument = every message
return unread;
}Notlar: tepsideki bildirimlere ve AppMint'teki gönderim geçmişinize dokunulmaz; yeni mesajlar yine gelir.
appmintNative.click appmintNative#
click() — @/lib/appmintNative
Örnek
Uygulamanın dokunma sesini kendi düğmeleriniz için bir kez çalar.
Dönüş değeri: void. Gereksinim: derleme sırasında Adım 3'te (Entegrasyon) açık Dokunma Sesi. Web'de ve Dokunma Sesi olmadan derlenen uygulamalarda hiçbir şey yapmaz.
import { click, haptic } from '@/lib/appmintNative';
export function KeypadKey({ label, onPress }: { label: string; onPress: () => void }) {
return (
<button
onClick={() => {
click(); // sound
haptic(); // feel
onPress();
}}
>
{label}
</button>
);
}Kullanıcının sesi kendi ayarlarınızdan kapatmasına izin verin:
import { click } from '@/lib/appmintNative';
export function useTapSound(enabled: boolean) {
return () => { if (enabled) click(); }; // a no-op on the web and in apps built without Tap Sound
}Bir liste satırından bir toast ile birlikte oynatın:
import { click, toast } from '@/lib/appmintNative';
export function Row({ name, onPick }: { name: string; onPick: () => void }) {
return <li onClick={() => { click(); onPick(); toast(`${name} selected`); }}>{name}</li>;
}Notlar: Telefon ayarını izle (önerilir) ile, kullanıcı telefonda "Dokunma sesleri"ni kapattıysa tık sesi çıkmaz. Her dokunuşta her zaman çal ile her dokunuş zaten tıklar, bu yüzden ek click() çağrılarına gerek yoktur. Yalnızca Android (kabuğun WebToApk.playClick).
appmintNative.Clipboard appmintNative#
Clipboard.write({ string?, url? }) / Clipboard.read(): Promise<{ value, type }>
Örnek
Capacitor'ın Clipboard eklentisi biçiminde pano - şununla aynı kod: copy() / paste().
Döndürür: write() → Promise<void>. read() → Promise<{ value: string; type: 'text/plain' }> (pano boşsa value değeri '' olur). Gerekenler: hiçbir şey.
import { Clipboard, can } from '@/lib/appmintNative';
async function copyInvite(code: string) {
await Clipboard.write({ string: code });
}
async function pasteInvite(): Promise<string> {
if (can('clipboardRead') === 'none') return ''; // hide the Paste button instead
try {
const { value } = await Clipboard.read();
return value;
} catch {
return ''; // the browser denied clipboard access
}
}Bir kodu kopyalayın ve bir toast ile onaylayın:
import { Clipboard, toast } from '@/lib/appmintNative';
export async function copyCode(code: string) {
await Clipboard.write({ string: code });
toast('Code copied');
}Metin yoksa bir bağlantı yazın ve boş panoyu yapıştırılacak bir şey olmaması olarak değerlendirin:
import { Clipboard } from '@/lib/appmintNative';
export async function copyUrl(url: string) {
await Clipboard.write({ url }); // used only because string is empty
}
export async function readCode(): Promise<string | null> {
try {
const { value } = await Clipboard.read();
return value.trim() || null; // '' = the clipboard is empty
} catch {
return null; // the browser denied access
}
}Notlar: Uygulamada Capacitor Clipboard eklentisini kullanır; tarayıcıda navigator.clipboard. write yalnızca string gönderir (string boşsa url) - görsel yok. Tarayıcıda kullanıcı erişimi reddederse read() hata fırlatabilir; uygulamada boş pano hata değil, '' olur. Yapıştır denetimini göstermeden önce can('clipboardRead') sorgulayın.
appmintNative.collapsingHeader appmintNative#
collapsingHeader(header: HTMLElement, opts?: { scroller?: HTMLElement | Window; range?: number }): () => void
Örnek
Büyük başlıklı bir üst bilginin, CSS'inizin kullanabileceği 0 → 1 değeri yayınlayarak, yerel olanlar gibi kaydırmaya tepki vermesini sağlar.
Döndürür: değişkeni ve özniteliği de kaldıran bir abonelik iptali işlevi. Gerekenler: hiçbir şey.
Üst bilgide --collapse değerini (en üstte 0 değerinden range px kaydırma sonrası 1 değerine kadar) ayarlar ve tamamen daraldığında data-collapsed ekler:
import { useEffect, useRef } from 'react';
import { collapsingHeader } from '@/lib/appmintNative';
export function NotesScreen() {
const headerRef = useRef<HTMLElement>(null);
const listRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!headerRef.current || !listRef.current) return;
return collapsingHeader(headerRef.current, { scroller: listRef.current, range: 80 });
}, []);
return (
<div className="flex h-full flex-col">
<header ref={headerRef} className="app-header">Notes</header>
<div ref={listRef} className="flex-1 overflow-y-auto">{/* rows */}</div>
</div>
);
}.app-header { font-size: calc(28px - 10px * var(--collapse, 0)); padding-top: var(--safe-top); }
.app-header[data-collapsed] { box-shadow: 0 1px 0 var(--border); }Düz betikten dinlemeyi durdurun; bu aynı zamanda --collapse değerini de kaldırır ve data-collapsed:
import { collapsingHeader } from '@/lib/appmintNative';
const header = document.querySelector<HTMLElement>('.app-header')!;
const stop = collapsingHeader(header); // whole-page scroll, default 64 px range
addEventListener('pagehide', stop);Notlar:
- Varsayılan
scrollerpenceredir (bütün olarak kayan bir sayfa); varsayılanrange64 px'tir. - Kaydırma dinleyicisi pasiftir ve animasyon karesi başına bir kez güncellenir; bu yüzden kaydırmayı yavaşlatmaz.
appmintNative.composeSms appmintNative#
composeSms(phoneNumber: string, message: string): boolean
Örnek
Mesajlaşma uygulamasını numara ve metin doldurulmuş olarak açar; kullanıcı gönder'e basar. Android uygulamasında (kabuk köprüsü) ve web'de (sms: bağlantısı) çalışır.
Döndürür: boolean hemen - mesajlaşma uygulamasının açılması istendiyse true. Gerekenler: izin yok.
Bir "Dükkâna yaz" düğmesi.
import { composeSms, can } from '@/lib/appmintNative';
export function setUpTextButton(button: HTMLButtonElement) {
if (can('smsCompose') === 'none') { button.hidden = true; return; }
button.addEventListener('click', () => {
const opened = composeSms('+911234567890', 'Hi, is my order ready?');
if (!opened) alert('Could not open the messaging app.');
});
}Sayı olmadığında düğmeyi gizleyen, yeniden kullanılabilir bir hook.
import { composeSms, can } from '@/lib/appmintNative';
export function useTextShop(phone: string) {
const available = can('smsCompose') !== 'none' && phone.trim() !== '';
return {
available,
send: (orderId: string) => composeSms(phone, `Hi, about order ${orderId}.`),
};
}Bir arkadaşı davet edin; gönder düğmesine kullanıcı basar.
import { composeSms, toast } from '@/lib/appmintNative';
export function inviteFriend(number: string, link: string) {
if (!composeSms(number, `Join me: ${link}`)) toast('Could not open the messaging app');
}Notlar: Kullanıcının yazdığı veya onayladığı her şey için bunu sendSms() yerine tercih edin - kullanıcı gönder'e basmadan hiçbir şey telefondan çıkmaz. Web'de sms:…?body=… adresine gider. composeSms( çağrısı derleme sırasında SMS (oku ve gönder) seçeneğini açmaz, bu yüzden Play tarafından kısıtlanan bir izin eklemez.
appmintNative.copy appmintNative#
copy(text: string): Promise<boolean>
Örnek
Metni sistem panosuna koyar.
Dönüş değeri: Promise<boolean> - metin kopyalandığında true, pano kullanılamadığında veya tarayıcı reddettiğinde false. Gereksinim: hiçbir şey.
import { can, copy, haptic, toast } from '@/lib/appmintNative';
export function CopyLink({ link }: { link: string }) {
if (can('clipboardWrite') === 'none') return null;
return (
<button
onClick={async () => {
if (await copy(link)) { haptic('success'); toast('Link copied'); }
else toast('Could not copy the link');
}}
>
Copy link
</button>
);
}Capacitor biçimli form aynı şeyi yapar:
import { Clipboard } from '@/lib/appmintNative';
await Clipboard.write({ string: 'ABC-123' });Her iki sonucu da bildirin, dokunsal geri bildirim yalnızca başarıda:
import { copy, haptic, toast } from '@/lib/appmintNative';
export async function copyCoupon(code: string) {
const ok = await copy(code);
if (ok) haptic('success');
toast(ok ? `Copied ${code}` : 'Copy failed, press and hold to copy');
return ok;
}Notlar: Uygulamada Capacitor Clipboard eklentisi metni yazar (eklenti hatası false döndürmek yerine promise'i reddeder). Tarayıcıda navigator.clipboard.writeText kullanılır; bu, https sayfası ve genellikle kullanıcı dokunuşu gerektirir.
appmintNative.copyImage appmintNative#
import { copyImage } from '@/lib/appmintNative'
Örnek
Bir görseli panoya kopyalar; böylece sohbet ve e-posta uygulamalarına yapıştırılabilir: Android uygulamasında uygulamanın panosu, web'de Async Clipboard API.
Döndürür: Promise<boolean> - kopyalandığında true. Gerekenler: hiçbir şey; denetimi şuna göre koşullandırın: can('clipboardImage').
import { can, copyImage, toast } from '@/lib/appmintNative';
export async function copyQr(canvas: HTMLCanvasElement) {
if (can('clipboardImage') === 'none') return;
const png = await new Promise<Blob | null>((r) => canvas.toBlob(r, 'image/png'));
if (png && (await copyImage(png))) toast('QR code copied');
else toast('Could not copy the image');
}Kameradan bir fotoğrafı kopyalayın, sonra bir toast ile onaylayın.
import { camera, copyImage, toast } from '@/lib/appmintNative';
export async function copySnapshot() {
const photo = await camera.take({ source: 'camera' });
if (!photo) return; // the user cancelled
const blob = await (await fetch(photo.dataUrl)).blob();
toast((await copyImage(blob)) ? 'Photo copied' : 'Could not copy the photo');
}Düğmeyi yalnızca panonun görsel kabul ettiği yerde gösterin ve bir URL'den görsel kopyalayın.
import { useEffect, useState } from 'react';
import { can, copyImage, toast } from '@/lib/appmintNative';
export function CopyLogoButton({ src }: { src: string }) {
const [ok, setOk] = useState(false);
useEffect(() => { setOk(can('clipboardImage') !== 'none'); }, []);
if (!ok) return null;
return (
<button onClick={async () => {
const blob = await (await fetch(src)).blob();
toast((await copyImage(blob)) ? 'Logo copied' : 'Copy failed');
}}>
Copy logo
</button>
);
}Notlar: PNG her yerde en güvenli türdür; Android uygulaması JPEG ve WebP'yi de kabul eder.
appmintNative.Device appmintNative#
Device.getInfo() / Device.getId() / Device.getLanguageCode()
Örnek
@capacitor/device biçimi: telefon modeli, işletim sistemi sürümü, sabit bir kimlik ve dil. Uygulamada Capacitor Device eklentisini, web'de tarayıcının cevabını kullanır.
Döndürür: getInfo() → Promise<DeviceInfo & { operatingSystem, isVirtual }> (Capacitor ile ayrıca manufacturer, webViewVersion, …); getId() → Promise<{ identifier }>; getLanguageCode() → Promise<{ value }> (iki harf, örn. "en"). Gerekenler: hiçbir şey.
Hakkında ekranında modeli ve işletim sistemi sürümünü gösterin.
import { Device } from '@/lib/appmintNative';
export async function aboutText(): Promise<string> {
const info = await Device.getInfo();
if (info.platform === 'web') return `Browser (${info.language})`;
return `${info.model ?? 'Unknown model'} · ${info.operatingSystem} ${info.osVersion ?? ''}` +
(info.isVirtual ? ' · emulator' : '');
}Sabit bir kimlik ve dil kodu.
import { Device } from '@/lib/appmintNative';
const { identifier } = await Device.getId(); // Capacitor ID in the app, installId() on the web
const { value: lang } = await Device.getLanguageCode();
await fetch('https://api.example.com/hello', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ device: identifier, lang }),
});Ayarlar altbilgisinde uygulama sürümünü ve cihaz modelini gösterin. Eşitleme raporu uygulama sürümünü verir; eklenti işletim sistemini ekler.
import { deviceInfo, Device } from '@/lib/appmintNative';
export async function footerText(): Promise<string> {
const base = deviceInfo(); // sync: platform, language, model, appVersion
const rich = await Device.getInfo(); // async: adds operatingSystem, isVirtual
const app = base.appVersion ? `v${base.appVersion}` : 'web';
return `${app} · ${rich.model ?? 'browser'} · ${rich.operatingSystem}`;
}Notlar: Uygulamada eşitleme deviceInfo() ayrıca model ve osVersion alanlarını da doldurur; Device.getInfo() operatingSystem, isVirtual ve eklentinin ek alanlarını ekler. Web'de operatingSystem 'unknown' olur ve model boştur. Web'de getId(), installId() içinden gelen localStorage kimliğidir ve depolama engellendiğinde hata fırlatır.
appmintNative.deviceInfo appmintNative#
deviceInfo(): DeviceInfo
Örnek
Uygulamanın nerede çalıştığını (platform) ve dili eşzamanlı olarak söyler, ayrıca Android uygulamasının içinde Android kabuğunun tam cihaz raporunu verir.
Dönüş değeri: DeviceInfo hemen: her zaman { platform, language } - platform uygulamada 'android', tarayıcıda 'web' olur. Android uygulamasında ayrıca model, manufacturer, osVersion (örn. "14") ve appVersion (örn. "1.4.0") alanlarını doldurur ve WebToApk.getDeviceInfo() içindeki her anahtarı taşır: android, hardware, screen, app, runtime, installId. Web'de osVersion ekler (kullanıcı aracısı). Gereksinim: hiçbir şey.
Oluşturma sırasında platforma göre dallanın.
import { deviceInfo } from '@/lib/appmintNative';
const info = deviceInfo();
const isAndroidApp = info.platform === 'android';
const lang = info.language; // e.g. "en-IN"Android ayrıntılarını okuyun. Yaygın alanlar en üsttedir; kabuğun raporunun geri kalanı iç içe gruplarında kalır.
import { deviceInfo } from '@/lib/appmintNative';
const info: any = deviceInfo();
if (info.platform === 'android') {
console.log(`${info.manufacturer} ${info.model}`); // "OPPO CPH2269"
console.log(`Android ${info.osVersion}, app ${info.appVersion}`);
console.log(`Battery ${info.runtime?.batteryLevel}%`);
}Platforma göre doğru denetimi seçin.
import { deviceInfo } from '@/lib/appmintNative';
export function ShareHint() {
const { platform, language } = deviceInfo();
return platform === 'android'
? <p>Tap Share to send this from your phone.</p>
: <p>Copy the link to share it ({language}).</p>;
}Notlar: model ve appVersion yalnızca uygulamada doldurulur. Webde osVersion bir işletim sistemi sürümü değil, tarayıcının user-agent dizesidir. Kabuğun yanıtı geçerli JSON değilse hata fırlatır (gerçek bir kusur, beklenmez).
appmintNative.Dialog appmintNative#
Dialog.alert / Dialog.confirm / Dialog.prompt
Örnek
Gerçek sistem diyalogları: bir mesaj, bir evet/hayır sorusu, tek satırlık bir metin sorusu. Uygulama Capacitor Dialog eklentisini kullanır; web tarayıcının kendi alert / confirm / prompt.
Döndürür: alert({ title?, message }) → Promise<void>. confirm({ title?, message, okButtonTitle?, cancelButtonTitle? }) → Promise<{ value: boolean }>. prompt({ title?, message, inputPlaceholder?, inputText? }) → Promise<{ value: string; cancelled: boolean }>. confirm ve prompt asla reddetmez (kapatmak da bir cevaptır); alert yalnızca gösterilemediğinde (mesaj yok) reddeder. Gerekenler: hiçbir şey.
import { Dialog } from '@/lib/appmintNative';
async function deleteList(id: string) {
const { value } = await Dialog.confirm({
title: 'Delete list?',
message: 'All items in it will be removed.',
okButtonTitle: 'Delete',
cancelButtonTitle: 'Keep',
});
if (value) await removeList(id);
}Uyarı ve soru kutusu:
import { Dialog } from '@/lib/appmintNative';
await Dialog.alert({ title: 'Offline', message: 'Your changes will sync when you are back online.' });
const r = await Dialog.prompt({ title: 'Rename', message: 'New name', inputText: current, inputPlaceholder: 'List name' });
if (!r.cancelled && r.value.trim()) rename(r.value.trim());Çıkış yapmadan önce onay alın. İletişim kutusunu kapatmak "hayır" sayılır.
import { Dialog, toast } from '@/lib/appmintNative';
export async function signOut(doSignOut: () => Promise<void>) {
const { value } = await Dialog.confirm({
title: 'Sign out?',
message: 'You will need to sign in again.',
okButtonTitle: 'Sign out',
cancelButtonTitle: 'Stay',
});
if (!value) return;
await doSignOut();
toast('Signed out');
}Notlar: Bir onayı kapatmak { value: false } verir; bir istemi kapatmak { value: '', cancelled: true } verir. İletişim kutuları ekranı engeller - bunları gerçek kararlar için kullanın, "Kaydedildi" gibi mesajlar için toast() kullanın. prompt özel düğme başlıklarını eklentiye iletmez.
appmintNative.dismissPushMessage appmintNative#
import { dismissPushMessage } from '@/lib/appmintNative'
Örnek
Bir mesajı AppMint Push gelen kutusundan gizler (kaydırıp kaldırma). Geri almak için: restorePushMessage(id).
Dönüş değeri: boolean, eşzamanlı olarak - gizlendiyse true. Web'de her zaman false. Gereksinim: Android: derleme sırasında Entegrasyon adımında (Adım 3) açık AppMint Push.
import { dismissPushMessage, restorePushMessage, pushInbox } from '@/lib/appmintNative';
export function swipeAway(id: string, setItems: (v: ReturnType<typeof pushInbox>) => void,
showUndo: (onUndo: () => void) => void) {
if (!dismissPushMessage(id)) return;
setItems(pushInbox());
showUndo(() => { // e.g. a 5-second "Undo" snackbar
restorePushMessage(id);
setItems(pushInbox());
});
}Gelen kutusunu bir kaydırma işleyicisi ve geri alma durumuyla gösterin; böylece kaydırma asla kesin olmaz.
import { useState } from 'react';
import { pushInbox, dismissPushMessage, restorePushMessage } from '@/lib/appmintNative';
export function Inbox() {
const [items, setItems] = useState(pushInbox());
const [undoId, setUndoId] = useState<string | null>(null);
const swipe = (id: string) => {
if (!dismissPushMessage(id)) return; // false on the web or when AppMint Push is off
setItems(pushInbox());
setUndoId(id);
};
const undo = () => {
if (undoId && restorePushMessage(undoId)) setItems(pushInbox());
setUndoId(null);
};
return (
<ul>
{items.map((m) => <li key={m.id} onClick={() => swipe(m.id)}>{m.title}</li>)}
{undoId && <button onClick={undo}>Undo</button>}
</ul>
);
}Okunmuş tüm mesajları bir kerede gizleyin ve kaç tanesinin gizlendiğini bildirin.
import { pushInbox, dismissPushMessage, toast } from '@/lib/appmintNative';
export function tidyReadMessages(): number {
const hidden = pushInbox().filter((m) => m.read && dismissPushMessage(m.id)).length;
toast(hidden ? `${hidden} message(s) hidden` : 'Nothing to hide');
return hidden;
}Notlar: yalnızca bu telefonun gelen kutusu değişir; bildirim çubuğundaki bildirim ve gönderim geçmişiniz kalır.
appmintNative.docScanner appmintNative#
docScanner.scan({ pageLimit?, pdf?, quality? }): Promise<{ pages, pdfPath? } | null> / docScanner.isSupported(): Promise<boolean>
Örnek
Sistemin belge tarayıcısını açar - kenar algılama, düzeltme, birkaç sayfa - Android uygulamasında Google'ın ML Kit tarayıcısı. Sayfalar JPEG veri URL'leri olarak döner.
Döndürür: Promise<{ pages: string[]; pdfPath?: string } | null> - kullanıcı iptal ettiğinde, tarayıcı başlatılamadığında veya webde null. isSupported() → Promise<boolean>: bu telefonun tarayıp tarayamayacağı (Google Play hizmetleri); webde false. Gerekenler: açılacak bir şey yok (Android'de tarayıcı Google Play hizmetlerinde çalışır ve kamera izni gerektirmez).
import { docScanner, can } from '@/lib/appmintNative';
export function ScanButton({ onPages }: { onPages: (p: string[]) => void }) {
if (can('docScanner') === 'none') return null; // no web scanner: hide the button
return (
<button onClick={async () => {
const res = await docScanner.scan({ pageLimit: 3, quality: 0.8 });
if (res) onPages(res.pages); // each a data:image/jpeg;base64,… URL
}}>
Scan document
</button>
);
}Önce telefona sorun, çünkü eski bir cihazda tarayıcı bulunmayabilir; sonra sayfaları saklayın.
import { useEffect, useState } from 'react';
import { docScanner, can } from '@/lib/appmintNative';
export function ScanPanel() {
const [ready, setReady] = useState(false);
const [pages, setPages] = useState<string[]>([]);
useEffect(() => {
if (can('docScanner') === 'none') return; // web: no scanner at all
docScanner.isSupported().then(setReady); // false without Google Play services
}, []);
if (!ready) return <p>Scanning is not available on this phone.</p>;
return (
<>
<button onClick={async () => {
const res = await docScanner.scan({ pageLimit: 5 });
if (!res) return; // cancelled or could not start
setPages((p) => [...p, ...res.pages]);
}}>Scan pages</button>
{pages.map((src, i) => <img key={i} src={src} alt={`Page ${i + 1}`} width={120} />)}
</>
);
}Tarayın, sonra dosya aktarma yardımcısıyla ilk sayfayı yükleyin.
import { docScanner, fileTransfer } from '@/lib/appmintNative';
export async function scanAndUpload(): Promise<boolean> {
const res = await docScanner.scan({ pageLimit: 1, quality: 0.7 });
if (!res || res.pages.length === 0) return false;
const blob = await (await fetch(res.pages[0])).blob();
const file = new File([blob], 'page-1.jpg', { type: 'image/jpeg' });
const r = await fileTransfer.upload('https://example.com/api/scans', file, { fieldName: 'scan' });
return r.responseCode >= 200 && r.responseCode < 300;
}Notlar: pageLimit 0 (varsayılan) = sınır yok; quality 0.1-1 (varsayılan 0.85). pdf: true ile pdfPath, sayfanın yükleyebileceği bir URL değil, uygulamanın önbelleğindeki bir dosya yoludur. Veri URL'leri büyüktür - çoğunu localStorage içinde tutmak yerine yeniden boyutlandırın veya yükleyin. Düz bir fotoğraf için şunu kullanın: camera.take().
appmintNative.enableNotifications appmintNative#
enableNotifications(): Promise<EnableNotificationsResult>
Örnek
Bildirimleri reddeden veya atlayan bir kullanıcıyı, dokunduğu bir düğmeden geri getirir: izin diyaloğu hâlâ gösterilebiliyorsa o, değilse (Android uygulaması) bu uygulamanın bildirim ayarları.
Döndürür: Promise<{ state, changed, openedSettings }> - state 'granted' | 'prompt' | 'blocked' | 'unavailable' değerindedir, changed çağrıdan öncekinden farklıysa true, openedSettings ayarlar ekranı kullanıldıysa true. Asla reddetmez. Web'de Notification.requestPermission() ile sorar. Gerekenler: derlemede bildirimler (uygulama bildirim gönderdiği veya aldığı sürece açıktır).
import { enableNotifications, getNotificationState } from '@/lib/appmintNative';
export async function TurnOnNotificationsBanner(el: HTMLElement) {
const state = await getNotificationState();
el.hidden = !(state === 'prompt' || state === 'blocked');
el.querySelector('button')!.addEventListener('click', async () => {
const r = await enableNotifications();
el.hidden = r.state === 'granted';
});
}Notlar: başlığı yalnızca 'prompt' ve 'blocked' için gösterin; 'granted' ve 'unavailable' için gizleyin. Bir dokunuştan sorun, asla açılışta değil.
appmintNative.fileTransfer appmintNative#
fileTransfer.download / fileTransfer.upload from '@/lib/appmintNative'
Örnek
Bir dosyayı ilerleme geri çağırmasıyla indirir veya yükler. Uygulamada aktarım yerel olarak çalışır (Capacitor FileTransfer); webde aynı geri çağırmayla fetch / XMLHttpRequest kullanır.
Dönüş değeri: download(url, fileName, opts?) → Promise<{ path?: string; blob?: Blob }> - uygulamada path (uygulamanın önbelleğindeki bir dosya), web'de blob (dosya orada ayrıca tarayıcı üzerinden de kaydedilir). upload(url, file, opts?) → Promise<{ responseCode: number; responseBody: string }>. upload, sunucunun verdiği HTTP durumu ne olursa olsun çözülür (responseCode değerini kontrol edin), uygulamada da web'de de aynı şekilde; aktarımın kendisi başarısız olduğunda (bağlantı yok, bozuk dosya) ikisi de reddedilir. Gereksinim: açılacak bir şey yok.
İlerleme çubuğuyla indirin:
import { fileTransfer, fileViewer } from '@/lib/appmintNative';
async function getReport(bar: HTMLProgressElement) {
try {
const r = await fileTransfer.download('https://example.com/report.pdf', 'report.pdf', {
headers: { Authorization: `Bearer ${token}` },
onProgress: (p) => { bar.value = p.fraction; }, // {bytes, total, fraction 0..1}
});
if (r.path) await fileViewer.open(r.path);
} catch (e) {
alert(`Download failed: ${String(e)}`);
}
}Bir File yükleyin, şuradan <input type="file">:
import { fileTransfer } from '@/lib/appmintNative';
async function sendPhoto(file: File) {
const r = await fileTransfer.upload('https://example.com/api/photos', file, {
fieldName: 'photo', // form field name, default 'file'
headers: { Authorization: `Bearer ${token}` },
onProgress: (p) => console.log(Math.round(p.fraction * 100) + '%'),
});
if (r.responseCode >= 200 && r.responseCode < 300) console.log('uploaded', r.responseBody);
else alert(`Upload failed (${r.responseCode})`);
}İlerleme durumuyla indirin, sonra dosyayı açın, tek bir kancada.
import { useState } from 'react';
import { fileTransfer, fileViewer } from '@/lib/appmintNative';
export function useOpenDocument() {
const [percent, setPercent] = useState(0);
const [error, setError] = useState<string | null>(null);
const run = async (url: string, name: string) => {
setError(null);
try {
const r = await fileTransfer.download(url, name, { onProgress: (p) => setPercent(Math.round(p.fraction * 100)) });
if (r.path) await fileViewer.open(r.path); // in the app; on the web the browser saved it
} catch (e) {
setError(String(e));
}
};
return { percent, error, run };
}Notlar: sunucu uzunluk göndermediği sürece fraction 0'dır. Uygulamada indirme, uygulamanın ÖNBELLEK klasörüne düşer (kullanıcının İndirilenler klasörüne değil); kullanıcıya bir kopya vermek için saveFile kullanın. Yüklemede uygulama önce dosyayı önbelleğine kopyalar, bu yüzden çok büyük dosyalar o kadar boş alan gerektirir. method (varsayılan 'POST') ve fileName isteğe bağlı yükleme seçenekleridir.
appmintNative.fileViewer appmintNative#
fileViewer.open(target) from '@/lib/appmintNative'
Örnek
Bir belgeyi (PDF, görsel, ofis dosyası) telefonun görüntüleyici uygulamasında açar. Web'de adresi yeni sekmede açar.
Dönüş değeri: Promise<boolean> - görüntüleyici açıldığında true, başarısız olduğunda (o tür için uygulama yok, hatalı yol) veya hedef bu yüzeyde açılamadığında false. Gereksinim: açılacak bir şey yok; uygulamada gelen Capacitor FileViewer eklentisini kullanır.
Web'den bir belge açın:
import { fileViewer } from '@/lib/appmintNative';
async function openManual() {
const ok = await fileViewer.open('https://example.com/manual.pdf');
if (!ok) alert('No app on this phone can open this file.');
}Önce fileTransfer.download ile indirdiğiniz bir dosyayı açın (uygulamada yerel bir yol):
import { fileTransfer, fileViewer } from '@/lib/appmintNative';
const r = await fileTransfer.download('https://example.com/invoice.pdf', 'invoice.pdf');
if (r.path) await fileViewer.open(r.path); // in the app
else if (r.blob) console.log('saved by the browser'); // on the webBir düğmeden bağlantı açın ve hiçbir görüntüleyici açamadığında kullanıcıya söyleyin.
import { fileViewer, toast } from '@/lib/appmintNative';
export function ManualButton({ url }: { url: string }) {
return (
<button onClick={async () => {
if (!(await fileViewer.open(url))) toast('No app can open this file');
}}>
View manual
</button>
);
}Notlar: Bir http(s) hedefi openDocumentFromUrl kullanır; diğer her şey yerel yol sayılır (file:// kaldırılır). Webde yalnızca http(s):, blob: ve data: hedefleri açılır; yerel bir yol şunu yanıtlar: false.
appmintNative.folder appmintNative#
import { folder } from '@/lib/appmintNative'
Örnek
Kullanıcının bir kez izin verdiği ve uygulamanın açılışlar boyunca sakladığı, içinde gerçek dosya işlemleri bulunan bütün bir klasör (uygulamada: kabuğun klasör köprüsü).
Döndürür: her üye eşzamansızdır - connected() → boolean, label() → string, request() → boolean (iptalde false), disconnect() → void, list(path) → [{ name, uri, isDirectory, length, lastModified }], readText(path) → string | null (null = böyle bir dosya yok), writeText(path, text) / mkdir(path) / rename(path, newName) / remove(path, recursive) → boolean, stat(path) → { name, size, mime, lastModified, uri, isDirectory, canWrite } | null. Gerekenler: yapay zekayla oluşturulan bir uygulama, kod folder kullandığında Yerel Klasör Erişimi (SAF) anahtarını kendisi açar; elle yapılan derlemede 4. Adım'da (Erişim) işaretleyin.
Bağlanın, klasörü gösterin ve bağlantıyı kesin:
import { can, folder } from '@/lib/appmintNative';
export async function setupFolderPanel(el: { name: HTMLElement; connect: HTMLButtonElement; disconnect: HTMLButtonElement }) {
if (can('folderAccess') === 'none') { el.connect.hidden = true; return; } // web build, or not shipped
const refresh = async () => {
const on = await folder.connected();
el.name.textContent = on ? `Saving to: ${await folder.label()}` : 'No folder chosen';
el.connect.hidden = on;
el.disconnect.hidden = !on;
};
el.connect.onclick = async () => { await folder.request(); await refresh(); }; // from a tap only
el.disconnect.onclick = async () => { await folder.disconnect(); await refresh(); };
await refresh();
}Okuyun, yazın, listeleyin, yeniden adlandırın ve silin - her yol izin verilen klasöre görelidir ('' onun köküdür):
import { folder } from '@/lib/appmintNative';
await folder.mkdir('notes');
const saved = await folder.writeText('notes/today.md', '# Today\n'); // parents created
const text = await folder.readText('notes/today.md'); // null if missing
const entries = await folder.list('notes');
entries.forEach((e) => console.log(e.isDirectory ? 'dir ' : 'file', e.name, e.length));
const info = await folder.stat('notes/today.md');
if (info) console.log(info.size, 'bytes, writable:', info.canWrite);
await folder.rename('notes/today.md', 'journal.md'); // a new NAME, not a path
await folder.remove('notes', true); // recursive for a non-empty folderBir notu izin verilen klasöre yedekleyin ve hiçbir klasör bağlı değilse bunu açıkça söyleyin.
import { can, folder, toast } from '@/lib/appmintNative';
export async function backupNote(name: string, text: string) {
if (can('folderAccess') === 'none') return;
if (!(await folder.connected()) && !(await folder.request())) {
toast('Choose a folder to save backups');
return;
}
const ok = await folder.writeText(`backups/${name}.md`, text);
toast(ok ? 'Backed up' : 'Could not write to the folder');
}Notlar: can('folderAccess') web'de 'none' olur - telefon tarayıcısında kalıcı klasör karşılığı yoktur, bu yüzden özelliği orada göstermeyin (tek dosyalar için pickFile() / saveFile() kullanın). request() çağrısını yalnızca kullanıcı dokunuşundan yapın. .. ve mutlak yollar reddedilir. İzin verilen klasörün kendisi asla kaldırılamaz. stat() dosyalar ve alt klasörler için çalışır (bir klasörde isDirectory: true ve size: 0 bulunur); stat('') - kök - null olur, bunun için label() kullanın. request() iptalden sonra tekrar çağrılabilir.
appmintNative.fullscreen appmintNative#
fullscreen.enter() / fullscreen.exit() / fullscreen.isOn()
Örnek
Bir video, oyun veya okuma görünümü için sistem çubuklarını gizler (uygulamada) veya tarayıcı tam ekranına geçer (web'de).
Dönüş değeri: enter() ve exit(), Promise<void> döndürür ve asla reddedilmez; isOn() eşzamanlı olarak boolean döndürür. Gereksinim: hiçbir şey.
import { useState } from 'react';
import { can, fullscreen } from '@/lib/appmintNative';
export function FullscreenToggle() {
const [on, setOn] = useState(fullscreen.isOn());
if (can('fullscreen') === 'none') return null;
return (
<button
onClick={async () => {
if (fullscreen.isOn()) await fullscreen.exit(); else await fullscreen.enter();
setOn(fullscreen.isOn());
}}
>
{on ? 'Exit full screen' : 'Full screen'}
</button>
);
}Görünüm kapandığında tam ekrandan çıkın:
import { useEffect } from 'react';
import { fullscreen } from '@/lib/appmintNative';
export function useFullscreenWhileOpen() {
useEffect(() => {
void fullscreen.enter();
return () => { void fullscreen.exit(); };
}, []);
}Okuma görünümü için sistem çubuklarını gizleyin ve durumu bir düğmeden takip edin.
import { useEffect, useState } from 'react';
import { fullscreen, can } from '@/lib/appmintNative';
export function useReaderMode() {
const [on, setOn] = useState(false);
useEffect(() => () => { void fullscreen.exit(); }, []); // always leave on unmount
const toggle = async () => {
if (can('fullscreen') === 'none') return;
if (fullscreen.isOn()) await fullscreen.exit(); else await fullscreen.enter();
setOn(fullscreen.isOn()); // a refused web request leaves it off
};
return { on, toggle };
}Notlar:
- Uygulamada bu, kabuğun kendi tam ekranıdır (durum ve gezinme çubukları gizlenir); sayfa için her zaman kullanılabilir.
- Web'de Fullscreen API yalnızca kullanıcı dokunuşundan çalışır. Reddedilen istek sessizce yok sayılır; bu yüzden varsaymak yerine sonrasında
isOn()değerini okuyun.
appmintNative.geofences appmintNative#
import { geofences } from '@/lib/appmintNative'
Örnek
Uygulama kapalıyken tetiklenen ve yeniden başlatmalardan sonra da süren coğrafi sınırlar: add, remove, list, events, clearEvents, onEvent, available.
Döndürür: add(spec) → Play hizmetleri kabul ettiğinde veya reddettiğinde çözülen Promise<BgResult> ({ id, ok, reason? } - background_permission_required, location_off, too_many_geofences, …). remove / clearEvents → Promise<boolean>. list() → Promise<GeofenceSpec[]>. events() → Promise<GeofenceEvent[]> (uygulama kapalıyken olanlar dahil son 100). onEvent(cb) → bir abonelikten çıkma. Gerekenler: derlemede Coğrafi çitler ve "Her zaman izin ver" (backgroundLocation.requestAlways()).
import { geofences, backgroundLocation } from '@/lib/appmintNative';
export async function remindAtStore(lat: number, lng: number) {
let r = await geofences.add({ id: 'store', lat, lng, radiusM: 200, events: ['enter'],
notify: { title: 'You are near the store', body: 'Your list is ready' } });
if (!r.ok && r.reason === 'background_permission_required') {
const p = await backgroundLocation.requestAlways();
if (p.background === 'granted') r = await geofences.add({ id: 'store', lat, lng, radiusM: 200, events: ['enter'] });
}
return r.ok;
}
geofences.onEvent((e) => console.log(e.id, e.event, new Date(e.time)));Etkin coğrafi sınırları ve uygulama kapalıyken kaydedilen geçişleri listeleyin.
import { geofences } from '@/lib/appmintNative';
export async function geofenceReport(): Promise<string[]> {
if (geofences.available() === 'none') return []; // web build: no geofences
const active = await geofences.list();
const events = await geofences.events(); // oldest first, last 100
return [
`${active.length} active zone(s)`,
...events.map((e) => `${e.id} ${e.event} at ${new Date(e.time).toLocaleTimeString()}`),
];
}Giriş ve çıkış için bir çalışma bölgesi ekleyin, reddedilmeyi nedenine göre yanıtlayın ve daha sonra kaldırın.
import { geofences } from '@/lib/appmintNative';
export async function watchOffice(lat: number, lng: number): Promise<string> {
const r = await geofences.add({ id: 'office', lat, lng, radiusM: 150, events: ['enter', 'exit'] });
if (r.ok) return 'Office zone is on';
if (r.reason === 'location_off') return 'Turn location on to use this';
if (r.reason === 'too_many_geofences') return 'Remove a zone first';
return `Could not add the zone (${r.reason ?? 'unknown'})`;
}
export async function stopWatchingOffice() {
await geofences.remove('office');
await geofences.clearEvents();
}appmintNative.Geolocation appmintNative#
Geolocation.getCurrentPosition / watchPosition / clearWatch / checkPermissions / requestPermissions
Örnek
@capacitor/geolocation biçimi: uygulamada Capacitor Geolocation eklentisi, web'de standart navigator.geolocation. Hepsi W3C konum nesnesini döndürür.
Dönüş değeri: getCurrentPosition(opts) → Promise<GeolocationPosition> (reddedilme, zaman aşımı veya konum yokluğunda reddeder); watchPosition(opts, cb) → Promise<string> (izleme kimliği); clearWatch({ id }) → Promise<void>; checkPermissions() / requestPermissions() → Promise<{ location, coarseLocation }> (her biri 'granted' | 'denied' | 'prompt'; Android 12+ üzerinde "Yaklaşık" seçen kullanıcı yalnızca coarseLocation verir). Gereksinim: Adım 4'te (Erişim) GPS / Konum - kodunuz getCurrentPosition veya çağırdığında derleme ikisini de açar watchPosition.
Neredeyim, bir kez.
import { Geolocation } from '@/lib/appmintNative';
export async function currentSpot(): Promise<string> {
try {
const perm = await Geolocation.checkPermissions();
if (perm.location !== 'granted') {
const asked = await Geolocation.requestPermissions();
if (asked.location !== 'granted') return 'Location is off for this app.';
}
const pos = await Geolocation.getCurrentPosition({ enableHighAccuracy: true, timeout: 15000 });
return `${pos.coords.latitude.toFixed(5)}, ${pos.coords.longitude.toFixed(5)}`;
} catch {
return 'Could not get your location. Is location turned on?';
}
}Bir koşuyu izleyin, sonra durdurun. Geri çağırma null alır ve bir konum alınamazsa hata verir.
import { Geolocation } from '@/lib/appmintNative';
let watchId: string | null = null;
export async function startRun(onPoint: (lat: number, lng: number) => void) {
watchId = await Geolocation.watchPosition({ enableHighAccuracy: true }, (pos, err) => {
if (!pos) { console.warn('location error', err); return; }
onPoint(pos.coords.latitude, pos.coords.longitude);
});
}
export async function stopRun() {
if (watchId) { await Geolocation.clearWatch({ id: watchId }); watchId = null; }
}Tek seferlik bir konum alın. Kullanıcı reddederse null döndürür.
import { Geolocation } from '@/lib/appmintNative';
export async function nearestCoords(): Promise<{ lat: number; lng: number } | null> {
try {
const pos = await Geolocation.getCurrentPosition({ timeout: 10000 });
return { lat: pos.coords.latitude, lng: pos.coords.longitude };
} catch {
return null; // refused, timed out or location is off
}
}Notlar: Düz navigator.geolocation uygulamada da çalışır; Capacitor biçimini istediğinizde bunu kullanın. Android 12+ üzerinde kullanıcı yalnızca yaklaşık konuma izin verebilir - yine de bir konum alırsınız, yalnızca daha az doğru olur. Web'de requestPermissions() tarayıcı isteminin çıkması için gerçek bir konum isteği yapar. Konum kişisel veridir: Play Veri güvenliği formunuzda bildirin.
appmintNative.getNotificationState appmintNative#
getNotificationState(): Promise<NotificationState>
Örnek
Bildirimlerin açık olup olmadığı ve değilse uygulamanın hâlâ sorup soramayacağı ya da yalnızca Ayarlar'ın düzeltebileceği.
Döndürür: Promise<'granted' | 'prompt' | 'blocked' | 'unavailable'>. Webde Notification.permission aynı şekilde eşlenir (default → 'prompt', denied → 'blocked'); hiç bildirim olmayan yerde 'unavailable'. Gerekenler: hiçbir şey.
import { getNotificationState } from '@/lib/appmintNative';
export async function shouldShowNotifBanner(): Promise<boolean> {
const state = await getNotificationState();
return state === 'prompt' || state === 'blocked';
}Notlar: uygulama ön plana döndüğünde (visibilitychange) yeniden kontrol edin - kullanıcı Ayarlar'da değiştirmiş olabilir.
appmintNative.getPushTags appmintNative#
import { getPushTags } from '@/lib/appmintNative'
Örnek
Bu yüklemenin AppMint Push etiketlerini bir nesne olarak okur.
Döndürür: Record<string, string> eşzamanlı olarak - örn. { plan: 'pro', cart: 'open' }; hiç yoksa ve webde {}. Gerekenler: Android: derleme sırasında Entegre et adımında (Adım 3) AppMint Push açık olmalı.
import { getPushTags, setPushTag } from '@/lib/appmintNative';
// Every setPushTag makes the install check in, so skip the calls that change nothing.
export function ensurePushTag(key: string, value: string) {
if ((getPushTags()[key] ?? '') !== value) setPushTag(key, value);
}
ensurePushTag('plan', 'pro');Bir ekran birden fazla etiketi değiştirdiğinde, setPushTags ile birkaç etiketi tek değişiklikte ayarlayın ve etiketini kaldırmak için bir değeri boşaltın:
import { setPushTags } from '@/lib/appmintNative';
// Checkout finished: the cart tag goes away and the plan tag is set, all saved or none.
export function onCheckoutDone(plan: string) {
const saved = setPushTags({ cart: '', plan });
if (!saved) console.warn('push tags not saved (web, AppMint Push off, or a refused key or value)');
}Yalnızca bu kurulumda bir etiket varsa ipucu göstermek için etiketleri geri okuyun:
import { getPushTags } from '@/lib/appmintNative';
export function OffersHint() {
const tags = getPushTags(); // {} on the web
if (Object.keys(tags).length === 0) return null;
return <p>Offers for the {tags.plan ?? 'standard'} plan are on.</p>;
}Notlar: bu, telefonun elinde tuttuğu şeydir; gönderim merkezi bunu yüklemenin sonraki kontrolünden sonra görür.
appmintNative.getPushUser appmintNative#
import { getPushUser } from '@/lib/appmintNative'
Örnek
Bu kurulumun AppMint Push'a en son verdiği kullanıcı kimliğini okur, şununla: setPushUser.
Dönüş değeri: string, eşzamanlı olarak - kaydedilen kimlik veya oturum kapalıyken, hiç ayarlanmadığında ya da web'de ''. Gereksinim: Android: derleme sırasında Entegrasyon adımında (Adım 3) açık AppMint Push.
import { getPushUser, setPushUser } from '@/lib/appmintNative';
// On app start: the session expired while the app was closed, so stop personal messages here.
export function syncPushUser(currentUserId: string | null) {
const want = currentUserId ?? '';
if (getPushUser() !== want) setPushUser(want);
}Oturum açtıktan sonra hesap kimliğiyle, oturumu kapatırken '' ile setPushUser çağırın; kimlik reddedilirse veya AppMint Push kapalıysa false döndürür:
import { setPushUser } from '@/lib/appmintNative';
export function onSignedIn(userId: string) {
if (!setPushUser(userId)) console.warn('push user id not saved'); // ids only: no email or phone number
}
export function onSignedOut() {
setPushUser('');
}Kaydedilen kimliği bir React kancasında, o kullanıcıya ait bir etiketle birlikte okuyun:
import { useEffect, useState } from 'react';
import { getPushUser, setPushTag } from '@/lib/appmintNative';
export function usePushUser(plan: string) {
const [id] = useState(getPushUser);
useEffect(() => {
if (id) setPushTag('plan', plan); // only tag installs that belong to a signed-in user
}, [id, plan]);
return id;
}Notlar: bu, telefonun tuttuğu değerdir; gönderim merkezi değişikliği, yüklemenin bir sonraki kontrolünden sonra görür, çevrimiçiyse birkaç saniye sonra.
appmintNative.getVolume appmintNative#
getVolume(stream) — @/lib/appmintNative
Örnek
Bir akış için telefonun kendi ses düzeyini okur: 'media', 'alarm', 'ring', 'notification' veya 'call'.
Dönüş değeri: { stream, current, max, percent, muted? } (telefonun kendi adımlarında current/max, percent 0-100) veya web'de null. Gereksinim: hiçbir şey.
import { getVolume } from '@/lib/appmintNative';
export function AlarmVolumeHint() {
const v = getVolume('alarm');
if (!v || v.percent > 0) return null;
return <p role="alert">Your alarm volume is off, so the alarm will not be heard.</p>;
}getVolume web'de null yanıtı verir, bu yüzden düzeyi yalnızca bir nesne olduğunda çizin:
import { getVolume } from '@/lib/appmintNative';
export function MediaLevel() {
const v = getVolume('media');
if (!v) return null; // web: no phone volume to show
return <progress max={100} value={v.percent} aria-label="Media volume" />;
}Donanım tuşlarını onVolumeChange ile izleyin ve sessiz bir düzeyi şununla yükseltin setVolume:
import { useEffect, useState } from 'react';
import { getVolume, onVolumeChange, setVolume, requestDndAccess } from '@/lib/appmintNative';
export function useMediaPercent(): number | null {
const [pct, setPct] = useState(() => getVolume('media')?.percent ?? null);
useEffect(() => onVolumeChange((l) => { if (l.stream === 'media') setPct(l.percent); }), []);
return pct;
}
export function raiseIfQuiet() {
const v = getVolume('media');
if (!v || v.percent >= 40) return;
const r = setVolume('media', 60, true);
if (!r.ok && r.error === 'dnd_access_required') requestDndAccess(); // the user must allow Do Not Disturb access
}appmintNative.haptic appmintNative#
haptic(kind?) — @/lib/appmintNative
Örnek
Dokunuşlar ve sonuçlar için kısa, yerel hissi veren bir titreşim. Her anlamlı dokunuşta çağırın.
Dönüş değeri: void (ateşle ve unut). kind: 'light' (varsayılan), 'medium', 'heavy', 'selection', 'success', 'warning', 'error'. Gereksinim: Adım 4'te (Erişim) Titreşim; kod şunu çağırdığında yapay zeka derlemeleri bunu sizin için açar haptic.
import { haptic } from '@/lib/appmintNative';
export function SaveButton({ onSave }: { onSave: () => Promise<boolean> }) {
return (
<button
onClick={async () => {
haptic(); // light tap on press
const ok = await onSave();
haptic(ok ? 'success' : 'error'); // result feedback
}}
>
Save
</button>
);
}Sekmeler, anahtarlar ve seçiciler için 'selection' kullanın; silme onayı için şunu kullanın 'heavy':
import { haptic } from '@/lib/appmintNative';
function onTabChange(tab: string) { haptic('selection'); setTab(tab); }
function onConfirmDelete() { haptic('heavy'); deleteItem(); }Açarken hafif, kapatırken daha sert titreşen bir beğeni düğmesi:
import { haptic } from '@/lib/appmintNative';
export function LikeButton({ liked, onToggle }: { liked: boolean; onToggle: () => void }) {
return (
<button aria-pressed={liked} onClick={() => { haptic(liked ? 'light' : 'medium'); onToggle(); }}>
{liked ? 'Liked' : 'Like'}
</button>
);
}Notlar: Uygulamada Capacitor Haptics eklentisini (impact, notification, selectionChanged) kullanır. Web'de tarayıcıda varsa navigator.vibrate kullanır, başka yerde bir şey yapmaz; asla hata fırlatmaz. Daha uzun ritimler için şunu kullanın: hapticPlay.
appmintNative.hapticPlay appmintNative#
hapticPlay(pattern) — @/lib/appmintNative
Örnek
Özel bir an için (bir zafer, bir seri, bir kalp atışı) zengin bir titreşim deseni çalar. Normal dokunuşlar için haptic() kullanın.
Dönüş değeri: Promise<boolean>: desen çalındığında true, çalınamadığında false. Gereksinim: Adım 4'te (Erişim) Titreşim.
Adlandırılmış desenler: 'success-ramp', 'heartbeat', 'tick-tick-thud', 'drum-roll', 'nudge'.
import { hapticPlay } from '@/lib/appmintNative';
async function onLevelComplete() {
const felt = await hapticPlay('success-ramp');
if (!felt) console.log('No vibration on this device');
showConfetti();
}Kendi deseniniz: bir { at, duration?, intensity?, sharpness? } listesi (ms, ms, 0-1, 0-1):
import { hapticPlay } from '@/lib/appmintNative';
await hapticPlay([
{ at: 0, intensity: 0.4 }, // soft tap
{ at: 150, intensity: 0.4 }, // soft tap
{ at: 320, duration: 120, intensity: 1 }, // strong buzz
]);haptic() ile birleştirin: her cevap için normal bir dokunma geri bildirimi, uzun desen yalnızca özel an için:
import { haptic, hapticPlay } from '@/lib/appmintNative';
export async function onAnswer(correct: boolean, streak: number) {
haptic(correct ? 'success' : 'error'); // every answer
if (correct && streak > 0 && streak % 5 === 0) {
await hapticPlay('drum-roll'); // every fifth in a row
}
}Notlar: Uygulama AppwrightHaptics Capacitor eklentisini kullanır (telefon destekliyorsa vuruş başına şiddet). Web yalnızca zamanlamayı alır. sharpness kabul edilir ama Android'de etkisi yoktur. haptic.play(pattern) aynı işlevdir.
appmintNative.Haptics appmintNative#
Haptics — @/lib/appmintNative
Örnek
Capacitor tarzı Haptics nesnesi: @capacitor/haptics ile aynı adlar ve seçenekler, şununla aynı kodda çalışır: haptic().
Döndürür: her yöntem void döndürür (Promise değil; üzerinde await zararsızdır). Gerekenler: Adım 4'te (Erişim) Titreşim; kod şunu kullandığında yapay zekâ derlemeleri bunu sizin için açar Haptics.
import { Haptics } from '@/lib/appmintNative';
Haptics.impact({ style: 'LIGHT' }); // 'LIGHT' | 'MEDIUM' | 'HEAVY' (default 'LIGHT')
Haptics.notification({ type: 'SUCCESS' }); // 'SUCCESS' | 'WARNING' | 'ERROR' (default 'SUCCESS')
Haptics.vibrate({ duration: 300 }); // ms, default 300Değer değiştikçe tıklayan bir seçici:
import { Haptics } from '@/lib/appmintNative';
export function Stepper({ value, onChange }: { value: number; onChange: (v: number) => void }) {
const step = (d: number) => { Haptics.selectionChanged(); onChange(value + d); };
return (
<div onPointerDown={() => Haptics.selectionStart()} onPointerUp={() => Haptics.selectionEnd()}>
<button aria-label="Less" onClick={() => step(-1)}>-</button>
<span>{value}</span>
<button aria-label="More" onClick={() => step(1)}>+</button>
</div>
);
}Bir bildirim titreşimiyle onaylayan ve bir alan yanlış olduğunda kısa bir titreşim ekleyen bir form:
import { Haptics } from '@/lib/appmintNative';
export function onSubmit(valid: boolean) {
Haptics.notification({ type: valid ? 'SUCCESS' : 'ERROR' });
if (!valid) Haptics.vibrate({ duration: 200 });
}Notlar: selectionStart ve selectionChanged ikisi de bir seçim tıklaması verir; selectionEnd hiçbir şey yapmaz. Uygulamada çalışır (Capacitor Haptics eklentisi); web'de varsa navigator.vibrate kullanır, yoksa hiçbir şey yapmaz.
appmintNative.has appmintNative#
has(cap: Capability): boolean
Örnek
can() işlevinin evet/hayır biçimi: bu yüzeyde özellik gerçekten bir şey yapacaksa true.
Döndürür: boolean, eşzamanlı olarak - tam olarak can(cap) !== 'none'. Gerekenler: hiçbir şey.
import { has, copy, toast } from '@/lib/appmintNative';
export function CopyCode({ code }: { code: string }) {
if (!has('clipboardWrite')) return <code>{code}</code>; // no button where it cannot copy
return (
<button onClick={async () => { if (await copy(code)) toast('Copied'); }}>
Copy {code}
</button>
);
}Bir özelliğin tamamını kapıyla koruyun:
import { has, scanCode } from '@/lib/appmintNative';
export async function onScanTap(): Promise<string | null> {
if (!has('codeScanner')) return null; // better: do not render the Scan button at all
const hit = await scanCode({ formats: ['qr_code'] });
return hit ? hit.value : null;
}Etiketi nerede çalıştığına göre seçin: has() denetimin çizilip çizilmeyeceğine karar verir, can() ise hangi türde olduğunu söyler:
import { can, has } from '@/lib/appmintNative';
export function shareLabel(): string | null {
if (!has('share')) return null; // nothing to share with: draw no button
return can('share') === 'native' ? 'Share…' : 'Share link';
}Notlar: can() ile aynı yetenek adlarını alır (tam liste için o girişe bakın). Nerede çalıştığını bilmeniz gerektiğinde can() kullanın ('native' veya 'web'), örneğin bir etiketi değiştirmek için.
appmintNative.hideKeyboard appmintNative#
hideKeyboard(): void
Örnek
Ekrandaki klavyeyi kapatır - örneğin bir form gönderildiğinde veya alanın üzerinde bir panel açıldığında.
Döndürür: hiçbir şey. Gerekenler: hiçbir şey.
import { hideKeyboard, toast } from '@/lib/appmintNative';
export function SearchForm({ onSearch }: { onSearch: (q: string) => void }) {
return (
<form
onSubmit={(e) => {
e.preventDefault();
const q = new FormData(e.currentTarget).get('q') as string;
hideKeyboard(); // show the results, not the keyboard
onSearch(q);
if (!q) toast('Type something to search');
}}
>
<input name="q" type="search" enterKeyHint="search" aria-label="Search" />
</form>
);
}Bir sayfa alanın üzerinde açıldığında klavyeyi kapatın; böylece sayfa onun arkasında kalmaz:
import { useState } from 'react';
import { hideKeyboard } from '@/lib/appmintNative';
export function useSheet() {
const [open, setOpen] = useState(false);
return {
open,
show: () => { hideKeyboard(); setOpen(true); },
hide: () => setOpen(false),
};
}Kullanıcı metin alanı olmayan herhangi bir yere dokunduğunda kapatın:
import type { ReactNode } from 'react';
import { hideKeyboard } from '@/lib/appmintNative';
export function Screen({ children }: { children: ReactNode }) {
return (
<div onPointerDown={(e) => {
if (!(e.target as HTMLElement).closest('input, textarea')) hideKeyboard();
}}>
{children}
</div>
);
}Notlar: Uygulamada Capacitor Keyboard eklentisinden klavyeyi gizlemesini ister. Web'de odaklı alanı bulanıklaştırır; tarayıcıda klavyeyi kapatan budur. Hiç klavye açık değilken çağırmak güvenlidir.
appmintNative.inAppCampaigns appmintNative#
import { inAppCampaigns } from '@/lib/appmintNative'
Örnek
Bu yüklemenin şu an elinde tuttuğu uygulama içi mesajlar, {id, name}[] olarak: oluşturucunun, bu kullanıcının hedef kitlesinde olduğu kampanyaları.
Döndürür: { id: string; name: string }[] eşzamanlı olarak; web'de ve AppMint Push olmadan []. Gerekenler: Android: derleme yaparken Entegrasyon adımında (3. Adım) AppMint Push açık.
import { inAppCampaigns } from '@/lib/appmintNative';
console.log(inAppCampaigns().map((c) => c.name));Bir yönetici ekranında hata ayıklama listesi:
import { inAppCampaigns } from '@/lib/appmintNative';
export function InAppDebug() {
const list = inAppCampaigns();
return <ul>{list.length ? list.map((c) => <li key={c.id}>{c.name}</li>) : <li>None for this install</li>}</ul>;
}Oluşturucu zaten o adla bir mesaj çalıştırıyorsa kendi ipucunuzu atlayın:
import { inAppCampaigns } from '@/lib/appmintNative';
export const hasCampaign = (name: string) => inAppCampaigns().some((c) => c.name === name);appmintNative.installId appmintNative#
installId(): string
Örnek
Bu kurulum için sabit rastgele bir kimlik verir: Android uygulamasında kabuğun kendi kimliği, webde localStorage içinde saklanan rastgele bir kimlik.
Döndürür: string hemen. Gerekenler: hiçbir şey.
Ücretsiz denemeyi cihaza bağlayın.
import { installId } from '@/lib/appmintNative';
export async function claimTrial(): Promise<boolean> {
let id: string;
try {
id = installId();
} catch {
return false; // web with storage blocked (private mode): no stable id exists
}
const r = await fetch('https://api.example.com/trial', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ device: id }),
});
return r.ok;
}Bu yüklemeyi kendi analitiğinizde bir kez sayın.
import { installId } from '@/lib/appmintNative';
export function reportFirstOpen() {
let id: string;
try { id = installId(); } catch { return; } // web with storage blocked: no stable id
if (localStorage.getItem('first-open-sent')) return;
navigator.sendBeacon('https://api.example.com/first-open', JSON.stringify({ install: id }));
localStorage.setItem('first-open-sent', '1');
}Daha sonra arayabileceğiniz kısa bir destek kodu gösterin ve bir kopyala düğmesi ekleyin.
import { installId, copy, toast } from '@/lib/appmintNative';
export function SupportCode() {
let code = '';
try { code = installId().slice(0, 8); } catch { /* storage blocked: show nothing */ }
if (!code) return null;
return <button onClick={async () => { if (await copy(code)) toast('Copied'); }}>Support code {code}</button>;
}Notlar: Uygulama güncellemelerinde aynı kalır; kaldırıldığında veya "verileri temizle" yapıldığında değişir. Bir donanım kimliği değildir. Web'de localStorage engellendiğinde bilerek hata fırlatır - paylaşılan boş bir kimlik kullanıcıları karıştırırdı.
appmintNative.isAlarmRinging appmintNative#
isAlarmRinging() — @/lib/appmintNative
Örnek
Uygulamanın alarm saatlerinden birinin şu anda çalıp çalmadığı.
Dönüş değeri: boolean, eşzamanlı olarak; web'de her zaman false. Gereksinim: derleme sırasında Erişim adımında (Adım 4) 🔔 Alarm saati.
import { useEffect, useState } from 'react';
import { isAlarmRinging, onAlarm } from '@/lib/appmintNative';
export function useRinging(): boolean {
const [ringing, setRinging] = useState(isAlarmRinging);
useEffect(() => onAlarm(() => setRinging(isAlarmRinging())), []);
return ringing;
}Bir alarm çalarken Durdur ve Ertele düğmelerini gösterin:
import { isAlarmRinging, stopAlarm, snoozeAlarm } from '@/lib/appmintNative';
export function RingingBar() {
if (!isAlarmRinging()) return null; // always false on the web
return (
<div role="alert">
<button onClick={() => stopAlarm()}>Stop</button>
<button onClick={() => snoozeAlarm('', 5)}>Snooze 5 min</button>
</div>
);
}Önce desteği kontrol edin; böylece sayfa alarm denetimlerini yalnızca alarmın çalabileceği yerde çizer:
import { alarmSupport, isAlarmRinging } from '@/lib/appmintNative';
export function alarmState(): 'hidden' | 'ringing' | 'idle' {
if (alarmSupport() === 'unavailable') return 'hidden'; // web or iOS: no alarm controls
return isAlarmRinging() ? 'ringing' : 'idle';
}appmintNative.isNativeApp appmintNative#
isNativeApp(): boolean
Örnek
Kodun yüklü Android uygulamasının içinde çalışıp çalışmadığını söyler (window.WebToApk kabuk köprüsü mevcuttur).
Döndürür: boolean hemen. Tarayıcıda false. Gerekenler: hiçbir şey.
Bir "Android uygulaması" rozeti ve tarayıcıda bir indirme bağlantısı gösterin.
import { isNativeApp } from '@/lib/appmintNative';
export function appBadgeText(): string {
if (isNativeApp()) return 'Android app';
return 'Get the app on Google Play';
}Analitik olaylarınızı geldikleri yerle etiketleyin.
import { isNativeApp, platform } from '@/lib/appmintNative';
export function analyticsSource(): string {
return isNativeApp() ? `app-${platform()}` : 'browser'; // 'app-android' or 'browser'
}Uygulamanın içinde "Uygulamayı edinin" başlığını gizleyin.
import { isNativeApp } from '@/lib/appmintNative';
export function InstallBanner() {
if (isNativeApp()) return null; // already installed: no banner
return <a href="https://play.google.com/store/apps/details?id=com.example.app">Get the app</a>;
}Notlar: platform() aynı yanıtı bir dize olarak verir (uygulamada 'android', tarayıcıda 'web'). Bir özelliğin gösterilip gösterilemeyeceğine karar vermek için bunun yerine can('<capability>') kullanın: hangi platformda hangi özelliğin olduğunu bilir.
appmintNative.keepAlive appmintNative#
import { keepAlive } from '@/lib/appmintNative'
Örnek
Uygulamayı bir ön plan hizmeti bildirimiyle arka planda çalışır tutar (start, update, stop, isRunning, onState, available).
Dönüş değeri: start / update → Promise<BgResult> ({ ok, reason?, type?, notificationShown? }; tarayıcıda veya iOS'ta reason: 'unsupported', derleme Arka planda çalışmaya devam et seçeneğini işaretlemediyse 'not_enabled'). stop → Promise<boolean>. isRunning() → boolean. onState(cb) → bir abonelikten çıkma fonksiyonu; cb appmint:background-service ayrıntısını alır. Gereksinim: derlemede Arka planda çalışmaya devam et.
import { keepAlive } from '@/lib/appmintNative';
export async function startRecording() {
if (keepAlive.available() === 'none') return;
const r = await keepAlive.start({ title: 'Recording', text: 'Tap to return', stopLabel: 'Stop' });
if (!r.ok) console.warn('keep running refused:', r.reason);
}
const off = keepAlive.onState((d) => {
if (d.state === 'stopped') console.log('service stopped:', d.reason);
});
// later: await keepAlive.update({ title: 'Recording', text: '12:30' }); await keepAlive.stop(); off();Hizmeti durdurun ve start işleminin neden reddedildiğini okuyun:
import { keepAlive } from '@/lib/appmintNative';
export async function toggleTracking(on: boolean) {
if (!on) { await keepAlive.stop(); return; }
const r = await keepAlive.start({ title: 'Tracking', text: 'Running', type: 'dataSync' });
if (!r.ok) {
// 'unsupported' = browser or iOS; 'not_enabled' = the build did not tick Keep running in background
alert(r.reason === 'not_enabled' ? 'Turn on Keep running in background and rebuild.' : 'Not available here.');
}
}Bir React düğmesini hizmetle eşit tutun ve ilerlemeyi bildirimde gösterin:
import { useEffect, useState } from 'react';
import { keepAlive } from '@/lib/appmintNative';
export function useKeepAlive() {
const [running, setRunning] = useState(keepAlive.isRunning);
useEffect(() => keepAlive.onState((d) => setRunning(d.state === 'running')), []);
return running;
}
export function reportProgress(done: number, total: number) {
void keepAlive.update({ title: 'Uploading', text: `${done} of ${total}`, progress: { value: done, max: total } });
}appmintNative.keepAwake appmintNative#
keepAwake(on: boolean): Promise<boolean>
Örnek
Bir tarif, bir rota, bir QR kodu veya bir zamanlayıcı gösterilirken ekranı açık tutar ve tekrar uyumasına izin verir.
Döndürür: Promise<boolean> - istek etkili olduysa true, hiçbir şey ekranı açık tutamadıysa (veya tarayıcı reddettiyse) false. Gerekenler: hiçbir şey.
import { keepAwake } from '@/lib/appmintNative';
import { useEffect } from 'react';
function CookingMode() {
useEffect(() => {
void keepAwake(true);
return () => { void keepAwake(false); }; // always release on unmount
}, []);
return <Steps />;
}Ekranı yalnızca bir zamanlayıcı çalışırken açık tutun ve yapılamadığında kullanıcıya söyleyin.
import { keepAwake, toast } from '@/lib/appmintNative';
import { useEffect } from 'react';
export function useAwakeWhile(running: boolean) {
useEffect(() => {
if (!running) return;
let held = false;
void keepAwake(true).then((ok) => {
held = ok;
if (!ok) toast('Your screen may turn off while the timer runs');
});
return () => { if (held) void keepAwake(false); };
}, [running]);
}Önce özelliği kontrol edin ve sekme öne geldiğinde kilidi yeniden alın (sayfa gizlendiğinde web kilidi bırakır).
import { keepAwake, can } from '@/lib/appmintNative';
export function holdScreenOn(): () => void {
if (can('keepAwake') === 'none') return () => {};
void keepAwake(true);
const again = () => { if (document.visibilityState === 'visible') void keepAwake(true); };
document.addEventListener('visibilitychange', again);
return () => {
document.removeEventListener('visibilitychange', again);
void keepAwake(false);
};
}Notlar: Uygulamada KeepAwake eklentisini (pencerenin ekranı açık tut bayrağı) kullanır; webde Screen Wake Lock API'sini kullanır, tarayıcılar sekme gizlendiğinde bunu kendiliğinden bırakır - ekranın açık kalması gerekiyorsa sayfa görünür olduğunda keepAwake(true) yeniden çağırın. Yalnızca uygulama öndeyken çalışır.
appmintNative.Keyboard appmintNative#
Keyboard.hide() / Keyboard.addListener(event, handler): { remove() }
Örnek
Capacitor'ın Keyboard eklentisi biçiminde yazılım klavyesi: kapatın ve kapladığı alan değiştiğinde haber alın.
Dönüş değeri: hide() → Promise<void>. addListener(event, handler) → Capacitor'ın biçimi: kendisi de remove() içeren tutamacın bir Promise'i, böylece await, .then(h => h.remove()) ve .remove() hepsi çalışır. event şunlardan biridir: 'keyboardWillShow', 'keyboardDidShow', 'keyboardWillHide' veya 'keyboardDidHide'; işleyici { keyboardHeight } alır. Gereksinim: hiçbir şey.
Bir form gönderildiğinde veya alanın üzerinde bir panel açıldığında klavyeyi kapatın:
import { Keyboard } from '@/lib/appmintNative';
async function onSubmit(e: React.FormEvent) {
e.preventDefault();
await Keyboard.hide();
await save();
}Dinleyin (bileşen kaldırıldığında tutamacı kaldırın):
import { Keyboard } from '@/lib/appmintNative';
import { useEffect } from 'react';
useEffect(() => {
const h = Keyboard.addListener('keyboardDidShow', ({ keyboardHeight }) => {
composer.style.paddingBottom = `${keyboardHeight}px`;
});
const g = Keyboard.addListener('keyboardDidHide', () => { composer.style.paddingBottom = '0px'; });
return () => { h.remove(); g.remove(); };
}, []);İkisini birleştirin: klavye açıldığında odaklanan alanı görünüme kaydırın, sonra Enter'a basınca klavyeyi kapatın:
import { Keyboard } from '@/lib/appmintNative';
import { useEffect } from 'react';
export function useKeyboardScroll() {
useEffect(() => {
const h = Keyboard.addListener('keyboardDidShow', () => {
document.activeElement?.scrollIntoView({ block: 'center', behavior: 'smooth' });
});
const onKey = (e: KeyboardEvent) => { if (e.key === 'Enter') void Keyboard.hide(); };
window.addEventListener('keydown', onKey);
return () => { h.remove(); window.removeEventListener('keydown', onKey); };
}, []);
}Notlar: keyboardHeight, ham yükseklik değil, sayfanın hâlâ temizlemesi gereken klavye kısmıdır: Android uygulamasında kabuk sayfayı klavye için yeniden boyutlandırır; bu yüzden klavye açıkken orada 0 olur (gösterme olayı yine de tetiklenir ve sayı değiştiğinde yeniden tetiklenir). Gizleme olayları yalnızca klavye gerçekten kapandığında tetiklenir; bu yüzden "klavye açık mı" durumunu izleyebilirler. "Will" ve "did" olayları birlikte tetiklenir. Yalnızca dolgu için onKeyboard((px) => …) daha basittir.
appmintNative.launchUrl appmintNative#
launchUrl(): Promise<string>
Örnek
Uygulamayı başlatan veya devam ettiren bağlantıyı verir - derin bağlantı, simge kısayolu, widget dokunuşu, bildirim - böylece uygulama doğru ekranı açabilir.
Dönüş değeri: Promise<string> - tam URL veya uygulama normal açıldığında ''. Web'de her zaman '', çünkü adres çubuğu zaten yoldur. Gereksinim: kısayollar ve widget dokunuşları için hiçbir şey; dış bağlantılar için derleme sırasında Derin Bağlantı seçeneği gerekir.
Uygulama bağlandığında bir kez buna göre yönlendirin:
import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { launchUrl } from '@/lib/appmintNative';
export function useLaunchRoute() {
const navigate = useNavigate();
useEffect(() => {
void launchUrl().then((u) => {
if (!u) return;
const { pathname, search } = new URL(u);
navigate(pathname + search, { replace: true });
});
}, [navigate]);
}Uygulama zaten açıkken gelen bağlantılar için onDeepLink() ile birlikte kullanın:
import { onDeepLink } from '@/lib/appmintNative';
const off = onDeepLink((url) => console.log('opened with', url));Boş dizeyi "normal açıldı" olarak ele alın ve başlangıç ekranını yalnızca bağlantı varsa ondan seçin. appmint-shortcut://<package>/orders gibi bir kısayolun yolu içinde rota bulunur.
import { launchUrl, onDeepLink } from '@/lib/appmintNative';
export async function startRoute(): Promise<string> {
const u = await launchUrl();
if (!u) return '/'; // normal start, or the web
const { pathname, search } = new URL(u);
return pathname + search;
}
// While the app stays open, later links arrive here.
export function watchLinks(go: (route: string) => void): () => void {
return onDeepLink((u) => { const x = new URL(u); go(x.pathname + x.search); });
}Notlar:
- Değer TUTULUR, tüketilmez: geç yüklenen bir bileşen de onu görür. Daha yeni bir bağlantı onun yerini alır.
- Kabuktan (
WebToApk.getLaunchUrl()) okunur; yapay zekayla oluşturulan bir uygulamada bunu asla doğrudan çağırmayın. Kısayollar ve widget dokunuşları şu şekilde gelir:appmint-shortcut://<package><route>. new URL(u)ile ayrıştırın ve yoluna gidin; URL'nin kendisini yüklemeyin.
appmintNative.listCallLog appmintNative#
listCallLog(limit = 100, offset = 0, sinceMillis = 0): Promise<CallLogEntry[]>
Örnek
Arama geçmişini en yeniden eskiye okur. Yalnızca Android uygulaması.
Döndürür: Promise<CallLogEntry[]> - her biri { number, name, type, date, duration }; type incoming, outgoing, missed, voicemail, rejected, blocked veya other değerindedir, date epoch milisaniyesidir, duration saniyedir. Arama geçmişi kapalıysa veya izin reddedildiyse boş. Gerekenler: 4. Adım'daki (Erişim) Arama geçmişi (okuma) anahtarı - kodunuz bunu çağırdığında derleme onu açar - ve ilk kullanımda kullanıcının izni.
Son 7 günün cevapsız aramaları.
import { listCallLog, can } from '@/lib/appmintNative';
export async function missedThisWeek() {
if (can('callLog') === 'none') return null; // web: not possible
const since = Date.now() - 7 * 24 * 60 * 60 * 1000;
try {
const calls = await listCallLog(500, 0, since); // a number here; the wrapper converts it
return calls.filter((c) => c.type === 'missed');
} catch {
return []; // 2-minute timeout
}
}Kişi başına toplam konuşma süresi, giden ve gelen aramalardan, en uzunu önce.
import { listCallLog, can } from '@/lib/appmintNative';
export async function talkTimeByPerson() {
if (can('callLog') === 'none') return null;
const calls = await listCallLog(1000, 0).catch(() => []);
const totals = new Map<string, number>();
for (const c of calls) {
if (c.type !== 'incoming' && c.type !== 'outgoing') continue;
const who = c.name || c.number || 'Unknown';
totals.set(who, (totals.get(who) ?? 0) + (c.duration ?? 0)); // seconds
}
return [...totals].sort((a, b) => b[1] - a[1]);
}Boş listeyi açıklayan bir ekran, çünkü "arama yok" ile "izin reddedildi" aynı görünür.
import { listCallLog, can } from '@/lib/appmintNative';
import { useEffect, useState } from 'react';
export function useCalls() {
const [calls, setCalls] = useState<Awaited<ReturnType<typeof listCallLog>> | null>(null);
const supported = can('callLog') !== 'none';
useEffect(() => {
if (supported) void listCallLog(50).then(setCalls).catch(() => setCalls([]));
}, [supported]);
const hint = !supported ? 'Open the app on your phone to see calls'
: calls && calls.length === 0 ? 'No calls found. Allow call log access in Settings if you expected some.' : '';
return { calls: calls ?? [], hint };
}Notlar: limit uygulama tarafından 2000 ile sınırlanır. Boş bir liste "arama yok" ile "izin reddedildi" arasını ayıramaz. can('callLog') webde 'none' olur. Arama kaydı, Google Play'in kısıtlı bir iznidir: Play Console'da onaylı bir İzin Beyanı gerekir, yoksa uygulama reddedilir.
appmintNative.listContacts appmintNative#
listContacts(limit = 200, offset = 0): Promise<Contact[]>
Örnek
Rehberi sayfa sayfa okur. Yalnızca Android uygulamasında, kabuğun köprüsü üzerinden çalışır.
Döndürür: Promise<Contact[]>. Kişi yoksa, erişim reddedilmişse ya da Kişiler derleme sırasında etkinleştirilmemişse boş bir dizi. Gerekenler: Adım 4'teki (Erişim) Kişiler anahtarı - kodunuz bunu çağırdığında derleme onu açar - ve ilk kullanımda kullanıcının izni.
Bir kişi listesi ekranı. Yalnızca çalışabileceği yerde gösterin. Her kişi şöyledir: { id?, name?, phones?: string[], emails?: string[], photoUri? }.
import { listContacts, can, type Contact } from '@/lib/appmintNative';
type Row = { id: string; name: string; phone: string };
function toRow(c: Contact): Row {
return { id: c.id ?? '', name: c.name ?? '', phone: c.phones?.[0] ?? '' };
}
export async function loadPage(page: number): Promise<Row[] | null> {
if (can('contactList') === 'none') return null; // web: show "open the app" instead
try {
const contacts = await listContacts(100, page * 100);
return contacts.map(toRow);
} catch {
return []; // 2-minute timeout
}
}Rehberi sayfa sayfa tek bir listeye okuyarak tüm rehberde arayın.
import { listContacts, can, type Contact } from '@/lib/appmintNative';
export async function findContacts(query: string): Promise<Contact[] | null> {
if (can('contactList') === 'none') return null;
const all: Contact[] = [];
for (let offset = 0; ; offset += 200) {
const page = await listContacts(200, offset).catch(() => []);
all.push(...page);
if (page.length < 200) break;
}
const q = query.trim().toLowerCase();
return all.filter((c) => (c.name ?? '').toLowerCase().includes(q));
}Bir kişinin ilk numarasına mesaj atmak için şununla eşleştirin: sendSms.
import { sendSms, type Contact } from '@/lib/appmintNative';
export async function invite(c: Contact): Promise<boolean> {
const phone = c.phones?.[0];
if (!phone) return false;
return sendSms(phone, `Hi ${c.name ?? ''}, join me on the app!`);
}Notlar: Boş bir sonuç "kişi yok" ile "izin reddedildi" arasını ayıramaz - liste boşken kısa bir ipucu gösterin ("Ayarlar'da kişilere izin verin"). Kişiler isme göre A'dan Z'ye sıralı gelir; limit 2000 ile sınırlanır. Tek bir kişi için izin gerektirmeyen pickContact() kullanın. Kişiler kişisel veridir: Play Veri güvenliği formunuzda ve gizlilik politikanızda belirtin.
appmintNative.listSms appmintNative#
listSms(box: 'inbox' | 'sent' | 'draft' = 'inbox', limit = 100, offset = 0): Promise<SmsMessage[]>
Örnek
SMS mesajlarını gelen kutusundan, gönderilenlerden veya taslaklardan, en yeniden eskiye okur. Yalnızca Android uygulaması.
Döndürür: Promise<SmsMessage[]> - her biri { id, address, body, date, read, box }. SMS kapalıysa, izin reddedilmişse veya kutu boşsa boştur. Gerekenler: Adım 4'teki (Erişim) SMS (okuma ve gönderme) anahtarı - kodunuz bunu çağırdığında derleme onu açar - ve ilk kullanımda kullanıcının izni.
Gelen kutusundaki son 20 mesajı gösterin.
import { listSms, can, type SmsMessage } from '@/lib/appmintNative';
export async function latestInbox(): Promise<SmsMessage[] | null> {
if (can('smsList') === 'none') return null; // web: hide the screen
try {
const msgs = await listSms('inbox', 20, 0);
return msgs.map((m) => ({ ...m, body: (m.body ?? '').slice(0, 120) }));
} catch {
return []; // 2-minute timeout
}
}Tek bir göndericiden okunmamış mesajlar, en yenisi önce.
import { listSms, can } from '@/lib/appmintNative';
export async function unreadFrom(address: string) {
if (can('smsList') === 'none') return null;
const msgs = await listSms('inbox', 200, 0).catch(() => []);
return msgs.filter((m) => m.address === address && !m.read);
}Gönderilenleri ve taslakları tek seferde okuyun ve birlikte tarihe göre sıralayın.
import { listSms, type SmsMessage } from '@/lib/appmintNative';
export async function outgoing(): Promise<SmsMessage[]> {
const [sent, drafts] = await Promise.all([
listSms('sent', 50, 0).catch(() => []),
listSms('draft', 50, 0).catch(() => []),
]);
return [...sent, ...drafts].sort((a, b) => (b.date ?? 0) - (a.date ?? 0));
}Notlar: Boş bir liste "mesaj yok" ile "izin reddedildi" arasında ayrım yapamaz. limit uygulama tarafından 500 ile sınırlanır. can('smsList') web'de 'none' değerindedir. Gelen yeni mesajlar bu sarmalayıcının parçası değildir; el yazısı sayfalar bunları appmint:sms-received olayından alır (ham köprü için listSms bölümüne bakın). SMS, Google Play'in kısıtlı bir iznidir: Play Console'da onaylı bir İzin Beyanı gerekir; bu genellikle yalnızca varsayılan SMS uygulamalarına verilir.
appmintNative.liveProgress appmintNative#
import { liveProgress } from '@/lib/appmintNative'
Örnek
Android bildirim gölgesinde yerinde güncellediğiniz, yapışkan ve sessiz bir durum kartı - bir teslimat, bir yükleme, bir zamanlayıcı, bir antrenman. start, update, end.
Dönüş değeri: start / update → Promise<boolean> (kullanılamadığında, bildirimlere izin verilmediğinde veya kullanıcı "Canlı durum" kategorisini kapattığında false); end → Promise<void>. Gereksinim: Android uygulaması ve Android 13+ üzerinde bildirim izni. Web'de can('liveProgress') 'none' olur - orada aynı durumu uygulamanızın içinde gösterin.
import { liveProgress, can } from '@/lib/appmintNative';
export async function trackUpload(files: File[]) {
const card = can('liveProgress') !== 'none';
if (card) await liveProgress.start({ id: 'upload', title: 'Uploading photos', text: 'Starting…', indeterminate: true });
for (let i = 0; i < files.length; i++) {
await uploadOne(files[i]);
if (card) await liveProgress.update({
id: 'upload', title: 'Uploading photos',
text: `${i + 1} of ${files.length}`,
progress: (i + 1) / files.length, // 0–1
route: '/uploads', // opens this screen when tapped
});
}
if (card) await liveProgress.end('upload'); // removes the card
}Çalışan bir zamanlayıcı: startedAt o andan itibaren ileri sayan bir saat gösterir.
await liveProgress.start({ id: 'run', title: 'Morning run', text: '5 km goal', startedAt: Date.now() });
// ...later
await liveProgress.end('run');Bir reddi ele alın. start false yanıtı verir; liveProgress.lastError nedenini söyler.
import { liveProgress, toast } from '@/lib/appmintNative';
export async function startDelivery() {
const ok = await liveProgress.start({ id: 'order', title: 'Order on its way', text: 'Packing', progress: 0.1, route: '/orders' });
if (!ok) {
if (liveProgress.lastError === 'permission') toast('Allow notifications to follow your order here');
else if (liveProgress.lastError === 'channel_disabled') toast('Live status is turned off in system settings');
}
return ok;
}Notlar: aynı id bir kartı günceller; farklı kimlikler ayrı kartlar oluşturur (varsayılan kimlik 'live'). Kullanıcı onu kaydırıp kapatamaz; bu yüzden her zaman end çağırın. Android 13+'da önce bildirim izni isteyin (Notification.requestPermission()): bu olmadan start false yanıtlar ve hiçbir şey gösterilmez.
appmintNative.LocalNotifications appmintNative#
import { LocalNotifications } from '@/lib/appmintNative'
Örnek
Capacitor eklentisine göre yazılmış kod için @capacitor/local-notifications biçimi (LocalNotifications.schedule({ notifications })). schedule olmayan bir bildirim hemen gösterilir (notify() ile); schedule: { at } olan ise o saatte bir hatırlatıcıdır ve yeniden başlatmalarda korunur.
Döndürür: Promise<{ notifications: { id }[] }>. Kurulamayan bir hatırlatıcı (bir tarayıcı, Bildirimler olmayan bir derleme, reddedilen izin) nedeniyle reddeder - bunun yerine erken gösterilmez. Tekrarlayan hatırlatıcılar: scheduleAlarm veya hatırlatıcı API'si. Gerekenler: Android: derleme yaparken Erişim adımında (4. Adım) Bildirimler açık.
import { LocalNotifications } from '@/lib/appmintNative';
export async function announceSync(count: number) {
await LocalNotifications.schedule({
notifications: [
{ title: 'Sync complete', body: `${count} items updated`, tag: 'sync' },
],
});
}Tek çağrıda birden fazla bildirim; her biri aynı tag.
import { LocalNotifications } from '@/lib/appmintNative';
export async function reportBatch(done: number, failed: number) {
await LocalNotifications.schedule({
notifications: [
{ title: 'Upload finished', body: `${done} files sent`, tag: 'upload-ok' },
...(failed ? [{ title: 'Some files failed', body: `${failed} need a retry`, tag: 'upload-failed' }] : []),
],
});
}Gösterilip gösterilmediğini bilmeniz gerekiyorsa bunun yerine notify() kullanın ve cevaba tepki verin.
import { notify, toast } from '@/lib/appmintNative';
export async function remind(text: string) {
const shown = await notify({ title: 'Reminder', body: text, tag: 'reminder' });
if (!shown) toast(text); // notifications are off: tell them in the app
}Notlar: yalnızca anlık - Capacitor alanları id, schedule, at, actionTypeId ve extra desteklenmez; her öğe { title, body?, icon?, tag? } biçimindedir. Daha sonraki bir zamanda bildirim için el yazısı sayfa WebToApk.scheduleNotificationEx kullanır (Android). Düğmeler için şunu kullanın: notifyWithActions.
appmintNative.lockOrientation appmintNative#
lockOrientation(to: 'portrait' | 'landscape' | 'any'): Promise<boolean>
Örnek
Ekranı yalnızca o yönde anlamlı olan bir görünüm için - bir oyun tahtası, geniş bir grafik, bir imza alanı - tek yönde sabitler. 'any' kilidi serbest bırakır.
Döndürür: Promise<boolean> - kilit (veya serbest bırakma) uygulandıysa true, platform reddettiyse false. Asla reddetmez. Gerekenler: hiçbir şey.
Görünüm açıkken kilitleyin, ayrılırken serbest bırakın:
import { useEffect } from 'react';
import { can, lockOrientation } from '@/lib/appmintNative';
export function ChartScreen() {
useEffect(() => {
if (can('orientationLock') === 'none') return;
void lockOrientation('landscape');
return () => { void lockOrientation('any'); }; // always give the rotation back
}, []);
return <div className="h-full w-full">{/* the chart */}</div>;
}Görünüm cevaba bağlı olduğunda cevabı kontrol edin:
import { lockOrientation, toast } from '@/lib/appmintNative';
const ok = await lockOrientation('portrait');
if (!ok) toast('Turn your phone upright for the best view');Ekran gidince kilidi serbest bırakın, birkaç ekranın paylaştığı bir kanca olarak.
import { useEffect } from 'react';
import { lockOrientation, type OrientationLock } from '@/lib/appmintNative';
export function useOrientation(mode: OrientationLock) {
useEffect(() => {
void lockOrientation(mode);
return () => { void lockOrientation('any'); };
}, [mode]);
}
// useOrientation('landscape') inside the game screenNotlar:
- Uygulamada Capacitor ScreenOrientation eklentisi kilidi uygular.
- Web'de
screen.orientation.lock()kullanır. Çoğu tarayıcı bunu tam ekran dışında reddeder ve masaüstü tarayıcılar her zaman reddeder; bu yüzden oradafalsebekleyin. - Kullanıcıya kilitli bir görünümden çıkış yolu her zaman verin.
appmintNative.longPress appmintNative#
longPress(el: HTMLElement, handler: (e: PointerEvent) => void, opts?: { ms?: number; haptics?: boolean }): () => void
Örnek
Kullanıcı bir öğenin üzerinde parmağını tuttuğunda sizi çağırır - bir satırın bağlam menüsü, tepkiler veya kart eylemleri için.
Dönüş değeri: bir abonelikten çıkma fonksiyonu - bileşen kaldırılırken çağırın. Gereksinim: hiçbir şey.
Seçenekler: ms - basılı tutma süresi, varsayılan 450; haptics - tetiklendiğinde orta şiddette dokunsal geri bildirim, varsayılan true.
Uzun basışta bir eylem sayfası açın; aynı menü için görünür bir düğmeyle:
import { useEffect, useRef } from 'react';
import { actionSheet, longPress } from '@/lib/appmintNative';
export function PhotoTile({ src, onDelete }: { src: string; onDelete(): void }) {
const ref = useRef<HTMLDivElement>(null);
const openMenu = async () => {
const i = await actionSheet('Photo', [{ title: 'Delete', destructive: true }]);
if (i === 0) onDelete();
};
useEffect(() => {
if (!ref.current) return;
return longPress(ref.current, () => void openMenu());
}, []);
return (
<div ref={ref} className="relative">
<img src={src} alt="" />
<button aria-label="Photo actions" className="absolute right-1 top-1" onClick={() => void openMenu()}>⋯</button>
</div>
);
}Dokunsal geri bildirimsiz, daha yavaş bir basılı tutma:
import { longPress } from '@/lib/appmintNative';
declare const card: HTMLElement;
const off = longPress(card, (e) => console.log('held at', e.clientX, e.clientY), { ms: 700, haptics: false });Özel bir onay için haptic ile eşleştirin ve her liste satırı için temizleyin.
import { useEffect, useRef } from 'react';
import { haptic, longPress } from '@/lib/appmintNative';
export function SelectableRow({ id, onSelect, children }: { id: string; onSelect(id: string): void; children: React.ReactNode }) {
const ref = useRef<HTMLLIElement>(null);
useEffect(() => {
if (!ref.current) return;
return longPress(ref.current, () => { haptic('heavy'); onSelect(id); }, { haptics: false });
}, [id, onSelect]);
return <li ref={ref}>{children}</li>;
}Notlar:
- Her basılı tutuşta bir kez tetiklenir ve ardından gelecek tıklamayı yutar; böylece uzun basış aynı zamanda "dokunuş" olmaz.
- 8 pikselden fazla hareket ederse iptal olur, bu yüzden kaydırma onu asla tetiklemez. Ayrıca o öğedeki tarayıcının kendi uzun basma menüsünü engeller.
- Uzun basma görünmezdir. Aynı eylem için her zaman görünür bir düğme bulundurun.
appmintNative.markPushRead appmintNative#
import { markPushRead } from '@/lib/appmintNative'
Örnek
Bir AppMint Push gelen kutusu mesajını veya argümansız çağrılırsa tüm mesajları okundu olarak işaretler.
Döndürür: boolean eşzamanlı olarak - bir şey değiştiyse true. Webde her zaman false. Gerekenler: Android: derleme sırasında Entegre et adımında (Adım 3) AppMint Push açık olmalı.
import { pushInbox, markPushRead } from '@/lib/appmintNative';
export function openMessage(id: string) {
markPushRead(id);
return pushInbox().find((m) => m.id === id);
}
export function markAllRead() {
markPushRead(); // no id = all messages
return pushInbox();
}Bir mesajı işaretledikten sonra okunmamışları sayın; kendi başlığınızdaki bir rozet için.
import { pushInbox, markPushRead } from '@/lib/appmintNative';
export function openAndCount(id: string) {
const changed = markPushRead(id); // false on the web or when nothing changed
const unread = pushInbox().filter((m) => !m.read).length;
return { changed, unread };
}Kullanıcı gelen kutusu ekranını açtığında her şeyi okundu işaretleyin ve yeni mesaj geldiğinde yenileyin.
import { useEffect, useState } from 'react';
import { pushInbox, markPushRead } from '@/lib/appmintNative';
export function useInbox() {
const [items, setItems] = useState(() => pushInbox());
useEffect(() => {
markPushRead();
const refresh = () => setItems(pushInbox());
window.addEventListener('appmint:push', refresh);
return () => window.removeEventListener('appmint:push', refresh);
}, []);
return items;
}Notlar: okundu durumu yalnızca bu telefonda tutulur; tepsideki bildirim kaldırılmaz.
appmintNative.mediaSession appmintNative#
mediaSession — @/lib/appmintNative
Örnek
Ses veya video oynatan bir uygulama için kilit ekranı ve bildirim oynatma denetimleri: oynatılanı yayınlayın, Oynat / Duraklat / Sonraki / Önceki / Durdur düğmelerini alın ve bir videoyu televizyona aktarın.
Döndürür: setMetadata, setPlaybackState, clear → void. onAction(handler) → bir abonelikten çıkma işlevi. cast(url) → Promise<boolean> (hiçbir şey sunulamadığında false). Gerekenler: derlerken Adım 4'te (Erişim) Medya Bildirimi; kod şunu kullandığında yapay zekâ derlemeleri bunu sizin için açar mediaSession.
import { useEffect, useRef } from 'react';
import { mediaSession } from '@/lib/appmintNative';
type Track = { url: string; title: string; artist: string; cover: string };
export function Player({ track, onNext, onPrev }: { track: Track; onNext: () => void; onPrev: () => void }) {
const audio = useRef<HTMLAudioElement>(null);
const unsubscribe = useRef<(() => void) | null>(null);
const publish = () => {
const a = audio.current;
if (!a) return;
mediaSession.setPlaybackState({
playing: !a.paused,
positionMs: a.currentTime * 1000,
durationMs: (a.duration || 0) * 1000,
});
};
const onPlay = () => {
// Register the buttons when playback starts, not on mount (see Notes).
if (!unsubscribe.current) {
unsubscribe.current = mediaSession.onAction((action) => {
const a = audio.current;
if (!a) return;
if (action === 'play') void a.play();
if (action === 'pause') a.pause();
if (action === 'next') onNext();
if (action === 'prev') onPrev();
if (action === 'stop') { a.pause(); mediaSession.clear(); }
});
}
mediaSession.setMetadata({ title: track.title, artist: track.artist, artworkUrl: track.cover });
publish();
};
useEffect(() => () => { unsubscribe.current?.(); mediaSession.clear(); }, []);
return <audio ref={audio} src={track.url} controls onPlay={onPlay} onPause={publish} onSeeked={publish} />;
}Geçerli videoyu bir TV'ye gönderin:
import { can, mediaSession } from '@/lib/appmintNative';
export function CastButton({ url }: { url: string }) {
if (can('cast') === 'none') return null;
return <button onClick={async () => { if (!(await mediaSession.cast(url))) alert('No cast target found'); }}>Cast</button>;
}Oynatma bittiğinde kilit ekranı kartını temizleyin ve platformda medya denetimleri yoksa ona dokunmayın.
import { can, mediaSession } from '@/lib/appmintNative';
export function publishEnded(audio: HTMLAudioElement): () => void {
if (can('mediaControls') === 'none') return () => {};
const ended = () => {
mediaSession.setPlaybackState({ playing: false, positionMs: 0, durationMs: audio.duration * 1000 || 0 });
mediaSession.clear();
};
audio.addEventListener('ended', ended);
return () => audio.removeEventListener('ended', ended);
}Notlar:
- Android'de düğmeler, sayfa yüklemeyi bitirdiğinde kurulan uygulamanın
navigator.mediaSessionöğesi üzerinden gelir. Daha önce (örneğin bir mount efektinde) kaydedilen bironActionkaybolur, bu yüzden ilk oynatmada kaydedin. seekeylemi (positionMsile) yalnızca web'de iletilir; Android bildirimi aramaya sahip değildir.artworkUrlinternette tam birhttps://adresi olmalıdır. Android'decast(url)"Şuraya yayınla..." seçicisini açar (bir devretme; seçici istendiğindetrueile çözülür). Web'de Remote Playback API'sini kullanır.
appmintNative.Motion appmintNative#
Motion.addListener('accel' | 'orientation', cb): Promise<{ remove() }>
Örnek
Standart devicemotion ve deviceorientation olaylarının üzerindeki @capacitor/motion biçimi: telefonun hareketi ve eğimi. Aynı kod uygulamada ve tarayıcıda çalışır.
Döndürür: Promise<{ remove(): Promise<void> }> - durdurmak için remove() çağırın. 'accel' { acceleration: {x,y,z}, accelerationIncludingGravity: {x,y,z}, rotationRate: {alpha,beta,gamma}, interval } ile geri çağırır; 'orientation' { alpha, beta, gamma } ile (derece). Gerekenler: hiçbir şey.
Yenilemek için sallayın.
import { Motion, type AccelEvent } from '@/lib/appmintNative';
let handle: { remove(): Promise<void> } | null = null;
export async function startShakeDetect(onShake: () => void) {
let last = 0;
handle = await Motion.addListener('accel', (e) => {
const g = (e as AccelEvent).accelerationIncludingGravity;
const force = Math.sqrt(g.x * g.x + g.y * g.y + g.z * g.z);
if (force > 25 && Date.now() - last > 1000) { last = Date.now(); onShake(); }
});
}
export async function stopShakeDetect() {
await handle?.remove();
handle = null;
}Yönelim olayıyla bir su terazisi kabarcığını eğin.
import { Motion, type OrientationEvent } from '@/lib/appmintNative';
const tilt = await Motion.addListener('orientation', (e) => {
const { beta, gamma } = e as OrientationEvent; // front-back, left-right
bubble.style.transform = `translate(${gamma * 2}px, ${beta * 2}px)`;
});
// later: await tilt.remove();Telefonun hareketini okuyan bir React bileşeni için otomatik temizlemeli kanca biçimi.
import { useEffect, useState } from 'react';
import { Motion, can, type AccelEvent } from '@/lib/appmintNative';
export function useAcceleration() {
const [g, setG] = useState({ x: 0, y: 0, z: 0 });
const [supported] = useState(() => can('motion') !== 'none');
useEffect(() => {
if (!supported) return;
let handle: { remove(): Promise<void> } | undefined;
let cancelled = false;
void Motion.addListener('accel', (e) => setG((e as AccelEvent).accelerationIncludingGravity)).then((h) => {
if (cancelled) void h.remove(); else handle = h;
});
return () => { cancelled = true; void handle?.remove(); };
}, [supported]);
return { g, supported };
}Notlar: Bazı tarayıcılar önce hareket erişimi ister; bu yüzden orada bir dokunuştan başlatın; kullanıcı reddederse tutamaç döndürülür ama hiçbir olay gelmez. can('motion'), tarayıcıda DeviceMotionEvent yoksa 'none' olur. Eksik sensör değerleri 0 olarak gelir. Ekran kapandığında her zaman remove() çağırın - hareket olayları saniyede birçok kez tetiklenir.
appmintNative.musicVolume appmintNative#
musicVolume() — @/lib/appmintNative
Örnek
Uygulamanın döngüde çalan arka plan müziğinin seviyesini 0 ile 100 arasında okur.
Dönüş değeri: number, eşzamanlı olarak: 0-100 veya müzik yoksa -1 (web'de ya da müzik dosyası olmadan derlenmiş bir uygulamada). Gereksinim: derleme sırasında Adım 3'te (Entegrasyon) Arka Plan Müziği altında seçilmiş bir dosya.
Bir müzik kaydırıcısını yalnızca müzik varsa çizin:
import { useState } from 'react';
import { musicVolume, setMusicVolume } from '@/lib/appmintNative';
export function MusicSetting() {
const [level, setLevel] = useState(() => musicVolume());
if (level < 0) return null; // no music in this app: no control
return (
<label>
Music
<input
type="range" min={0} max={100} value={level}
onChange={(e) => {
const v = Number(e.target.value);
setLevel(v);
setMusicVolume(v);
}}
/>
</label>
);
}Seviyeyi bir "Müziği sessize al" anahtarıyla eşit tutun ve geri dönülecek ses düzeyini hatırlayın.
import { useRef, useState } from 'react';
import { musicVolume, setMusicVolume } from '@/lib/appmintNative';
export function MuteMusicSwitch() {
const before = useRef(Math.max(musicVolume(), 50));
const [muted, setMuted] = useState(() => musicVolume() === 0);
if (musicVolume() < 0) return null; // no music in this build
const toggle = () => {
if (muted) setMusicVolume(before.current);
else { before.current = musicVolume(); setMusicVolume(0); }
setMuted(!muted);
};
return <button onClick={toggle}>{muted ? 'Unmute music' : 'Mute music'}</button>;
}Bir video oynarken müziği kısın, sonra eski haline getirin.
import { musicVolume, setMusicVolume } from '@/lib/appmintNative';
export function duckMusicDuring(video: HTMLVideoElement) {
const level = musicVolume();
if (level < 0) return; // nothing to duck
video.addEventListener('play', () => setMusicVolume(0));
video.addEventListener('pause', () => setMusicVolume(level));
video.addEventListener('ended', () => setMusicVolume(level));
}Notlar: Yalnızca Android uygulaması; web -1 yanıtı verir. setMusicVolume ile ayarlanan düzey bir sonraki açılış için saklanır; sayfa bir düzey ayarlayana kadar derleme düzeyidir.
appmintNative.Network appmintNative#
Network.getStatus(): Promise<{ connected, connectionType }> / Network.addListener(event, handler): { remove() }
Örnek
Capacitor'ın Network eklentisi biçiminde çevrim içi/çevrim dışı durumu - şununla aynı kod: networkStatus() / onNetworkChange().
Dönüş değeri: getStatus() → Promise<{ connected: boolean; connectionType: string }>. addListener(event, handler) → Capacitor'ın biçimi: kendisi de remove() içeren tutamacın bir Promise'i; işleyici her değişiklikte aynı biçimi alır. Gereksinim: hiçbir şey.
import { Network } from '@/lib/appmintNative';
import { useEffect, useState } from 'react';
function useOnline() {
const [online, setOnline] = useState(true);
useEffect(() => {
void Network.getStatus().then((s) => setOnline(s.connected));
const h = Network.addListener('networkStatusChange', (s) => setOnline(s.connected));
return () => h.remove();
}, []);
return online;
}Geçerli türü bir kez okuyun ve bir durum çipinde gösterin.
import { Network } from '@/lib/appmintNative';
import { useEffect, useState } from 'react';
export function ConnectionChip() {
const [label, setLabel] = useState('Checking...');
useEffect(() => {
void Network.getStatus().then((s) =>
setLabel(s.connected ? (s.connectionType === 'cellular' ? 'Mobile data' : 'Wi-Fi') : 'Offline'));
}, []);
return <span className="chip">{label}</span>;
}Mobil veride büyük bir indirmeyi geciktirin ve çevrimdışı durumu da yanıtlayın.
import { Network, toast } from '@/lib/appmintNative';
export async function startBigDownload(download: () => Promise<void>) {
const s = await Network.getStatus();
if (s.connectionType === 'none' || !s.connected) { toast('You are offline.'); return; }
if (s.connectionType === 'cellular' && !window.confirm('Download over mobile data?')) return;
await download();
}Notlar: Olay adı yok sayılır - her dinleyici bağlantı değişikliklerini alır. connectionType 'wifi', 'cellular', 'none' veya 'unknown' olur: uygulamada getStatus() ve dinleyici Network eklentisini kullanır, web'de navigator.onLine / navigator.connection.type. connected telefonun bir ağı olduğu anlamına gelir, sunucunuzun yanıt verdiği anlamına gelmez.
appmintNative.networkStatus appmintNative#
networkStatus(): { connected: boolean; type: string }
Örnek
Cihazın şu anda çevrim içi olup olmadığını söyler.
Döndürür: { connected, type }, eşzamanlı olarak. type şunlardan biridir: 'wifi', 'cellular', 'none' (çevrim dışı) veya 'unknown'. Gerekenler: hiçbir şey.
Oluşturma sırasında çağırmak için yeterince ucuz:
import { networkStatus } from '@/lib/appmintNative';
export function SyncButton({ onSync }: { onSync: () => void }) {
const { connected } = networkStatus();
return (
<button disabled={!connected} onClick={onSync}>
{connected ? 'Sync now' : 'Offline — will sync later'}
</button>
);
}Başlangıç değerini okuyun, sonra değişiklikleri takip edin:
import { useEffect, useState } from 'react';
import { networkStatus, onNetworkChange } from '@/lib/appmintNative';
export function useOnline(): boolean {
const [online, setOnline] = useState(() => networkStatus().connected);
useEffect(() => onNetworkChange((s) => setOnline(s.connected)), []);
return online;
}Mobil veride daha hafif bir görsel boyutu seçin; 'none' (çevrim dışı) durumunu da yanıtlayın.
import { networkStatus } from '@/lib/appmintNative';
export function imageUrl(id: string): string {
const { connected, type } = networkStatus();
if (!connected) return '/img/placeholder.svg'; // offline: use the packaged picture
return `/api/images/${id}?size=${type === 'cellular' ? 'small' : 'large'}`;
}Notlar:
- Uygulamada bu, Network eklentisinin yanıtıdır (arka planda güncel tutulur, bu yüzden ilk çağrı yine WebView'in kendi değerini okuyabilir). Web'de
navigator.onLineartı tarayıcının bildirdiği yerdenavigator.connection.type, aksi halde'unknown'olur -'4g'gibi bir hız sınıfı asla tür olarak bildirilmez. - "Bağlı", cihazın bir ağı olduğu anlamına gelir, sunucunuzun yanıt verdiği anlamına gelmez. Yine de başarısız istekleri ele alın.
appmintNative.nfc appmintNative#
import { nfc } from '@/lib/appmintNative'
Örnek
Web NFC üzerinde tek çağrılık yardımcılar (NDEFReader): desteği kontrol edin, etiketleri okuyun, metin etiketi yazın. Android uygulaması kendi NDEFReader'ını kullanır; web'de Chrome'un kendi Web NFC'sidir (Android'de Chrome).
Dönüş değeri: isSupported(): Promise<boolean>; scan(onTag): Promise<() => Promise<void>> (durdurma fonksiyonu) ve onTag etiket başına Array<{recordType, text, mediaType?}> alır - Web NFC'nin tür adları ('text', 'url', 'mime', …) ve her platformda çözülmüş metin; write(text): Promise<boolean> (etiket yazıldığında true, herhangi bir hatada false). Gereksinim: Android'de derleme sırasında Adım 4'te (Erişim) Web NFC Desteği'ni açın. scan(), NDEFReader yoksa hata fırlatır - önce can('nfc') kontrol edin.
import { useEffect, useRef, useState } from 'react';
import { can, nfc } from '@/lib/appmintNative';
export function TagReader() {
const [last, setLast] = useState('');
const stopRef = useRef<null | (() => Promise<void>)>(null);
useEffect(() => () => { void stopRef.current?.(); }, []);
// Check at the tap: on Android the app installs NDEFReader while the page loads.
const start = async () => {
if (can('nfc') === 'none' || !(await nfc.isSupported())) { setLast('NFC is not available here.'); return; }
try {
stopRef.current = await nfc.scan((records) => {
setLast(records.filter((r) => r.recordType === 'text' || r.recordType === 'url').map((r) => r.text).join(' | '));
});
setLast('Hold a tag to the phone.');
} catch (e) {
setLast(`NFC error: ${(e as Error).message}`);
}
};
const writeTag = async () => {
if (can('nfc') === 'none') { setLast('NFC is not available here.'); return; }
setLast('Tap a tag to write it...');
setLast((await nfc.write('Hello from my app')) ? 'Tag written.' : 'Write failed or cancelled.');
};
return (
<div>
<button onClick={start}>Read tags</button>
<button onClick={writeTag}>Write tag</button>
<p>{last}</p>
</div>
);
}Bir etikete bağlantı yazın ve başarısız olduğunda nedenini kullanıcıya söyleyin.
import { can, nfc, toast } from '@/lib/appmintNative';
export async function writeShareTag(url: string) {
if (can('nfc') === 'none') { toast('This device cannot write NFC tags.'); return; }
toast('Hold a blank tag to the back of the phone...');
const ok = await nfc.write(url);
toast(ok ? 'Tag written.' : 'Could not write the tag. Try again.');
}Bir etiketi okuyun, sonra taramayı durdurun.
import { nfc } from '@/lib/appmintNative';
export function readOnce(): Promise<string> {
return new Promise<string>((resolve, reject) => {
let stop: (() => Promise<void>) | undefined;
let done = false;
nfc.scan((records) => {
if (done) return;
done = true;
void stop?.();
resolve(records.map((r) => r.text).join(' '));
}).then((s) => { stop = s; if (done) void s(); }, reject);
});
}Notlar: Android'de isSupported(), derlemede NFC olduğunda true olur - NFC'nin açık olup olmadığını söylemez (window.AndroidNFC.startScan() söyler). Durdurma işlevinden sonra onTag bir daha çağrılmaz. Android'de uygulama önde olduğu sürece etiketleri kendisi yakalamaya devam eder (bir dokunuş başka bir uygulamayı açmasın diye), yalnızca size iletmeyi bırakır. Bir metin kaydının text değerinde dil öneki yoktur; bir URL kaydının text değeri tam URL'dir. write(), bir etiket okutulana kadar bekler. URL veya mime kayıtları yazmak ve bir etiketi kilitlemek için doğrudan NDEFReader kullanın.
appmintNative.notify appmintNative#
import { notify } from '@/lib/appmintNative'
Örnek
Yapay zekayla oluşturulan bir uygulamadan, Android uygulamasında ve web'de hemen bir bildirim gösterir.
Döndürür: Promise<boolean> - gönderildiğinde true; kullanıcı bildirim iznini reddettiğinde, uygulama için bildirimler kapalıysa veya burada kullanılamıyorsa false. İzni ilk seferde kendisi ister (Android 13+ ve web). Gerekenler: Android: derleme yaparken Erişim adımında (4. Adım) Bildirimler açık.
import { notify, can } from '@/lib/appmintNative';
export async function onOrderReady(orderId: string) {
if (can('notify') === 'none') return; // hide the feature instead
const ok = await notify({
title: 'Order ready',
body: `Order #${orderId} is ready to collect`,
icon: 'https://example.com/img/order.png', // shown beside the text
tag: `order-${orderId}`, // same tag replaces the old one
});
if (!ok) setBanner('Turn on notifications to get order alerts.');
}Uygun bir anda sorun: ilk notify() izin iletişim kutusunu gösterir. Herhangi bir şey gönderilmeden önce kendi "Uyarıları aç" düğmenizden sormak için, uygulamanın da desteklediği standart API'yi kullanın:
async function enableAlerts() {
if (typeof Notification === 'undefined') return false;
const perm = Notification.permission === 'granted' ? 'granted' : await Notification.requestPermission();
return perm === 'granted';
}Kullanıcı bildirime dokunduğunda doğru ekranı açın, notify ile şunu birleştirerek onNotificationClick.
import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { notify, onNotificationClick } from '@/lib/appmintNative';
export async function announceMessage(id: string, from: string) {
await notify({ title: `New message from ${from}`, tag: `msg-${id}`, data: { messageId: id } });
}
export function NotificationRouter() {
const navigate = useNavigate();
useEffect(() => onNotificationClick((e) => {
const { messageId } = (e.data ?? {}) as { messageId?: string };
if (messageId) navigate(`/messages/${messageId}`);
}), [navigate]);
return null;
}Notlar: Android, kabuğun bildirim köprüsünü kullanır (icon büyük simge olur; bir https:// veya data: görseli kullanın); web, Notification API'sini kullanır. Kullanıcı kalıcı olarak reddettiğinde notify() tekrar sormadan false ile çözülür - onları telefonun ayarlarına yönlendirin. Düğmeler için notifyWithActions kullanın; yapışkan bir ilerleme kartı için şunu kullanın liveProgress.
appmintNative.notifyWithActions appmintNative#
import { notifyWithActions } from '@/lib/appmintNative'
Örnek
En fazla 3 düğmeli ("Tamamlandı işaretle", "Ertele") bir bildirim gösterir. Dokunuş şu yoldan geri gelir: onNotificationAction.
Döndürür: Promise<boolean> - gönderildiğinde true, bildirim izni reddedildiğinde false (ilk seferde notify() gibi izin ister). Gerekenler: Android: derleme yaparken Erişim adımında (4. Adım) Bildirimler açık ve Android 13+'da kullanıcının izni. Web'de can('notificationActions') şudur: 'none'.
import { notifyWithActions, onNotificationAction, can } from '@/lib/appmintNative';
export async function remindTask(taskId: string, title: string) {
if (can('notificationActions') === 'none') return false; // web: hide the buttons
return notifyWithActions({
title,
body: 'Due now',
tag: `task-${taskId}`, // comes back as e.tag
actions: [
{ id: 'done', title: 'Mark done' },
{ id: 'snooze', title: 'Snooze' },
],
});
}
// Mount once (e.g. in App.tsx):
export const stopListening = onNotificationAction((e) => {
// e = { actionId: 'snooze', tag: 'task-42', text: '' }
const taskId = e.tag.replace('task-', '');
if (e.actionId === 'done') completeTask(taskId);
if (e.actionId === 'snooze') snoozeTask(taskId);
});Bir zamanlayıcı bitti: bir düğme beş dakika ekler, diğeri durdurur.
import { notifyWithActions, onNotificationAction, can } from '@/lib/appmintNative';
export async function timerDone() {
if (can('notificationActions') === 'none') return false;
return notifyWithActions({
title: 'Timer finished',
tag: 'timer',
actions: [{ id: 'more', title: '+5 min' }, { id: 'stop', title: 'Stop' }],
});
}
export const stopTimerButtons = onNotificationAction((e) => {
if (e.tag !== 'timer') return; // ignore other notifications' buttons
if (e.actionId === 'more') startTimer(5);
});Reddedilen bir izni çağrıdan sonra ele alın ve bir bileşende temizleyin.
import { useEffect, useState } from 'react';
import { notifyWithActions, onNotificationAction } from '@/lib/appmintNative';
export function ApprovalRequest({ requestId }: { requestId: string }) {
const [note, setNote] = useState('');
useEffect(() => onNotificationAction((e) => {
if (e.tag === `req-${requestId}`) setNote(e.actionId === 'approve' ? 'Approved' : 'Rejected');
}), [requestId]);
const ask = async () => {
const posted = await notifyWithActions({
title: 'Approve request?', tag: `req-${requestId}`,
actions: [{ id: 'approve', title: 'Approve' }, { id: 'reject', title: 'Reject' }],
});
if (!posted) setNote('Notifications are off, so no buttons were shown.');
};
return <button onClick={ask}>{note || 'Ask me'}</button>;
}Notlar: Her düğme title değerini gösterir; id değeri e.actionId olarak geri gelir. input seçeneği (satır içi yanıt kutusu) bu uygulamada desteklenmez - yok sayılır, düğmede yanıt kutusu olmaz ve e.text her zaman '' olur. Bir dokunuş sayfaya yalnızca uygulama çalışırken ulaşır. Web'de sarmalayıcı düğmesiz düz bir bildirim gönderirdi; bu yüzden örnek önce can() değerini kontrol eder.
appmintNative.offlineBanner appmintNative#
offlineBanner(opts?: { message?: string; onlineMessage?: string }): () => void
Örnek
Bağlantı koptuğunda durum çubuğunun altına kayan ve geri geldiğinde kısaca "Tekrar çevrimiçi" diyen hazır bir şerit ekler.
Döndürür: başlığı kaldıran ve dinlemeyi bırakan bir abonelik iptali işlevi. Gerekenler: hiçbir şey.
Uygulama başlangıcında bir kez bağlayın:
import { useEffect } from 'react';
import { offlineBanner } from '@/lib/appmintNative';
export function App() {
useEffect(() => offlineBanner(), []); // cleanup removes it
return <main>{/* … */}</main>;
}Kendi sözcükleriniz (örneğin uygulamanın dilinde):
import { offlineBanner } from '@/lib/appmintNative';
const off = offlineBanner({ message: 'Sin conexión', onlineMessage: 'Conectado de nuevo' });Afişi yalnızca uygulamanın oturum açılmış alanında bağlayın ve çıkış yapılınca kaldırın.
import { useEffect } from 'react';
import { offlineBanner } from '@/lib/appmintNative';
export function useOfflineBanner(signedIn: boolean) {
useEffect(() => {
if (!signedIn) return;
return offlineBanner({ message: 'Offline: changes will sync later' });
}, [signedIn]);
}Notlar:
- Varsayılanlar: "Çevrim dışısınız" ve "Yeniden çevrim içi". Çevrim içi mesajı yaklaşık 1,8 saniye sonra gizlenir; çevrim dışı mesaj bağlantı dönene kadar kalır.
- Bileşen yüklendiğinde cihaz zaten çevrimdışıysa, başlık hemen gösterilir.
onNetworkChange()üzerine kurulu, bu yüzden her yüzeyde gerçektir. Renkler uygulamanın belirteçlerinden gelir (çevrim dışı için--ink/--bg, çevrim içi için--accent/--accent-ink). Ekran okuyucular içinrole="status"vardır.- Bir kez bağlayın. İki çağrı iki afiş çizer.
appmintNative.onAlarm appmintNative#
onAlarm(cb) — @/lib/appmintNative
Örnek
Alarm sonuçlarını dinler: bir alarmın açtığı uygulama (action: 'open', ringing ile) ve bir çalmanın nasıl bittiği - dismiss, snooze, missed (sonuna kadar çaldı veya başka bir alarmla değiştirildi) ya da error. Uygulama kapalıyken olan sonuçlar sayfa bir sonraki yüklendiğinde iletilir.
Döndürür: dinlemeyi durduran bir işlev. Geri çağırma { id, action, ringing?, source?, snoozeMinutes?, snoozedUntil?, error?, soundError? } alır. Gerekenler: derlerken Erişim adımında (Adım 4) 🔔 Çalar saat.
import { useEffect, useState } from 'react';
import { onAlarm } from '@/lib/appmintNative';
export function AlarmHost() {
const [ringingId, setRingingId] = useState<string | null>(null);
useEffect(() => onAlarm((e) => {
if (e.action === 'open' && e.ringing) setRingingId(e.id);
if (e.action === 'dismiss' || e.action === 'snooze' || e.action === 'missed') setRingingId(null);
}), []);
return ringingId ? <RingScreen id={ringingId} onClose={() => setRingingId(null)} /> : null;
}Önce alarmSupport() yanıtını alarak bir alarm kurun.
import { alarmSupport, scheduleAlarm } from '@/lib/appmintNative';
export function setWakeUp(at: Date): string {
const support = alarmSupport();
if (support === 'unavailable') return 'Alarms are only available in the Android app.';
if (support !== 'ready') return `Alarm needs attention first (${support}).`;
const ok = scheduleAlarm('wake-up', { at: at.getTime(), title: 'Wake up', repeat: 'weekdays', snoozeMinutes: 10 });
return ok ? 'Alarm set.' : 'Could not set the alarm.';
}Çalan alarmı kendi ekranınızdan durdurun veya erteleyin ve sonuca tepki verin.
import { useEffect, useState } from 'react';
import { onAlarm, stopAlarm, snoozeAlarm, isAlarmRinging } from '@/lib/appmintNative';
export function RingControls() {
const [ringing, setRinging] = useState(() => isAlarmRinging());
const [last, setLast] = useState('');
useEffect(() => onAlarm((e) => {
if (e.action === 'open') setRinging(!!e.ringing);
else { setRinging(false); setLast(e.action === 'error' ? `Alarm error: ${e.error}` : `Alarm ${e.action}`); }
}), []);
if (!ringing) return <p>{last}</p>;
return (
<div>
<button onClick={() => stopAlarm()}>Stop</button>
<button onClick={() => snoozeAlarm('', 5)}>Snooze 5 min</button>
</div>
);
}Notlar: uygulamayı açan sıradan hatırlatıcılar da buraya action: 'open' ile gelir ve ringing: false.
appmintNative.onBackGesture appmintNative#
onBackGesture(handler: () => boolean): () => void
Örnek
Açık bir sayfanın, modalın, ışık kutusunun veya sihirbaz adımının GERİ hareketine önce yanıt vermesini sağlar - uygulamada Android kenar kaydırması / geri düğmesi, web'de tarayıcının geri düğmesi.
Döndürür: aboneliği iptal eden bir işlev - görünüm kapandığında veya kaldırıldığında çağırın. İşleyiciniz basışı karşılamak için (bir şeyi kapattınız) true, geçmesine izin vermek için false döndürür. Gerekenler: hiçbir şey.
Yalnızca panel açıkken kaydedin:
import { useEffect, useState } from 'react';
import { onBackGesture } from '@/lib/appmintNative';
export function FilterPanel() {
const [open, setOpen] = useState(false);
useEffect(() => {
if (!open) return;
return onBackGesture(() => { setOpen(false); return true; }); // back closes THIS, not the app
}, [open]);
return (
<>
<button onClick={() => setOpen(true)}>Filters</button>
{open && <div role="dialog" aria-label="Filters">{/* … */}</div>}
</>
);
}Bir sihirbazda geri gidin ve son adımın geri düğmesinin normal şekilde çıkmasına izin verin:
import { useEffect } from 'react';
import { onBackGesture } from '@/lib/appmintNative';
export function useWizardBack(step: number, setStep: (n: number) => void) {
useEffect(() => onBackGesture(() => {
if (step === 0) return false; // not ours: normal back navigation happens
setStep(step - 1);
return true;
}), [step, setStep]);
}Bir ışık kutusunu geri hareketiyle kapatın, kaydı görselin açık olmasına bağlı tutarak.
import { useEffect } from 'react';
import { onBackGesture } from '@/lib/appmintNative';
export function Lightbox({ src, onClose }: { src: string | null; onClose: () => void }) {
useEffect(() => {
if (!src) return;
return onBackGesture(() => { onClose(); return true; });
}, [src, onClose]);
return src ? <img src={src} alt="" onClick={onClose} /> : null;
}Notlar:
- İşleyiciler bir yığın oluşturur: en son kaydedilen (üstteki sayfa) ilk sorulur. Hata fırlatan bir işleyici hiçbir şeyi tüketmez.
- Android'de kabuğun geri işleyicisinden geçer. Bu olmadan, bir sayfa açıkken geri kaydırmak tüm uygulamayı kapatır.
- Web'de herhangi bir işleyici kayıtlıyken bir geçmiş girdisi ekler ve son işleyici aboneliğini bıraktığında kaldırır.
- Yine de görünür bir geri / kapat denetimi çizin: birçok kullanıcı geri hareketini hiç kullanmaz.
- React Router normal sayfa geri gitmeyi zaten yönetir; bunu yalnızca yönlendiricinin bilmediği durumlar için kullanın. Aboneliği her zaman iptal edin, yoksa kapatılmış bir panel geri basışlarını yemeye devam eder.
appmintNative.onDeepLink appmintNative#
onDeepLink(cb: (url: string) => void): () => void
Örnek
Bir bağlantı uygulama zaten çalışırken onu açtığında sizi çağırır - bir derin bağlantı, bir simge kısayolu, bir widget dokunuşu.
Döndürür: bir abonelik iptali işlevi - bileşen kaldırıldığında çağırın. Gerekenler: kısayollar ve widget dokunuşları için açılacak bir şey yok; dış bağlantılar için derleme yaparken Derin Bağlantı seçeneği gerekir.
import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { launchUrl, onDeepLink } from '@/lib/appmintNative';
export function DeepLinkRouter() {
const navigate = useNavigate();
useEffect(() => {
const go = (u: string) => { const { pathname, search } = new URL(u); navigate(pathname + search); };
void launchUrl().then((u) => { if (u) go(u); }); // the link that started the app
return onDeepLink(go); // links while it runs; returns the unsubscribe
}, [navigate]);
return null;
}El yazısı bir sayfa (React'siz) aynı kabuk olayını doğrudan dinleyebilir:
window.addEventListener('appmint:deep-link', function (e) {
var url = e.detail && e.detail.url;
if (url) console.log('deep link', url);
});Bir widget veya kısayola dokunulduğunda belirli bir ürünü açın, bize ait olmayan bağlantıları yok sayın.
import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { onDeepLink } from '@/lib/appmintNative';
export function ProductLinks() {
const navigate = useNavigate();
useEffect(() => onDeepLink((url) => {
const m = new URL(url).pathname.match(/^\/product\/(\w+)$/);
if (m) navigate(`/products/${m[1]}`);
}), [navigate]);
return null;
}Notlar:
- Android: kabuğun
appmint:deep-linkolayını (detail.url) dinler; bir sayfa tanımladığında kabuk ayrıcawindow.onAppMintDeepLink(detail)çağırır. Bağlantı geldikten sonralaunchUrl()onu da döndürür. - Web: asla tetiklenmez (geri çağırma çalışmaz) ve abonelikten çıkma hiçbir şey yapmaz.
appmintNative.onInAppMessage appmintNative#
import { onInAppMessage } from '@/lib/appmintNative'
Örnek
Bu yüklemede gösterilen, tıklanan, kapatılan veya yanıtlanan her uygulama içi mesaj (oluşturucunun kampanyaları) için sizi çağırır. Abonelikten çıkma fonksiyonunu döndürür.
Dönüş değeri: () => void. Geri çağırma {type: 'shown' | 'clicked' | 'dismissed' | 'answered', id, name, template?, presented?, message?, button?, link?, question?, value?} alır. Web'de asla tetiklenmez. Gereksinim: Android: derleme sırasında Entegrasyon adımında (Adım 3) açık AppMint Push.
import { useEffect } from 'react';
import { onInAppMessage } from '@/lib/appmintNative';
export function useInAppAnalytics(log: (event: string, name: string) => void) {
useEffect(() => onInAppMessage((e) => log('inapp_' + e.type, e.name)), [log]);
}Mesajları kendiniz çizin (setInAppPresentation('none') ile presented değeri false olur ve message tasarımı taşır):
import { useEffect, useState } from 'react';
import { onInAppMessage, setInAppPresentation, type InAppMessageEvent } from '@/lib/appmintNative';
export function useOwnInAppDialog() {
const [msg, setMsg] = useState<InAppMessageEvent['message'] | null>(null);
useEffect(() => {
setInAppPresentation('none');
const off = onInAppMessage((e) => { if (e.type === 'shown' && !e.presented && e.message) setMsg(e.message); });
return () => { off(); setInAppPresentation('auto'); };
}, []);
return { msg, close: () => setMsg(null) };
}Kullanıcının uygulamanın kendi çiziminde dokunduğu bir düğmeye tepki verin:
import { onInAppMessage } from '@/lib/appmintNative';
onInAppMessage((e) => {
if (e.type === 'clicked' && e.link === 'appmint://notifications/enable') refreshNotificationBadge();
});Notlar: button, mesajın kendisine dokunulduğunda -1, aksi halde düğme dizinidir.
Yanıtlar (type: 'answered'). Yıldız satırı olan (Yıldızlı anket, Bizi değerlendirin) veya 0-10 puan çipleri olan (NPS puanı) bir mesaj, yanıtı kapandığında bir kez bildirir: question 'stars' (değer 1-5) veya 'nps' (değer 0-10) olur ve template geldiği galeri şablonunu adlandırır. Gönderim merkezi ortalamayı veya NPS puanını gösterir; kodunuz takip edebilir:
import { onInAppMessage } from '@/lib/appmintNative';
onInAppMessage((e) => {
if (e.type === 'answered' && e.question === 'nps' && (e.value ?? 10) <= 6) openFeedbackSheet();
});Yıldız puanlarının kendi kaydınızı tutun:
import { onInAppMessage } from '@/lib/appmintNative';
onInAppMessage((e) => {
if (e.type !== 'answered' || e.question !== 'stars') return;
void fetch('/api/ratings', { method: 'POST', body: JSON.stringify({ stars: e.value, campaign: e.name }) });
});Yüksek puan verenlere bir React bileşeninde teşekkür edin:
import { useEffect, useState } from 'react';
import { onInAppMessage } from '@/lib/appmintNative';
export function useHappyUser() {
const [happy, setHappy] = useState(false);
useEffect(() => onInAppMessage((e) => {
if (e.type === 'answered' && ((e.question === 'stars' && (e.value ?? 0) >= 4) || (e.question === 'nps' && (e.value ?? 0) >= 9))) setHappy(true);
}), []);
return happy;
}appmintNative.onKeyboard appmintNative#
onKeyboard(cb: (insetPx: number) => void): () => void
Örnek
Ekran klavyesinin sayfanın kaç pikselini kapladığını, bu her değiştiğinde söyler; böylece alt çubuk onun üzerinde durabilir.
Döndürür: bir abonelikten çıkma işlevi - bileşen kaldırılırken çağırın. Geri çağırma CSS pikseli cinsinden bir sayı alır: ihtiyaç duyduğunuz ek alt dolgu, klavye kapalıyken 0. Gerekenler: hiçbir şey.
import { useEffect, useState } from 'react';
import { onKeyboard } from '@/lib/appmintNative';
export function Composer() {
const [kb, setKb] = useState(0);
useEffect(() => onKeyboard(setKb), []); // the returned function unsubscribes
return (
<form
className="fixed inset-x-0 bottom-0 flex gap-2 p-3"
style={{ paddingBottom: `calc(var(--safe-bottom) + 12px + ${kb}px)` }}
>
<input className="flex-1" enterKeyHint="send" aria-label="Message" />
<button type="submit">Send</button>
</form>
);
}Klavye açılırken bir sohbet listesini en yeni mesaja kaydırılmış tutun:
import { useEffect, useRef } from 'react';
import { onKeyboard } from '@/lib/appmintNative';
export function MessageList({ children }: { children: React.ReactNode }) {
const endRef = useRef<HTMLDivElement>(null);
useEffect(() => onKeyboard((inset) => {
if (inset > 0) endRef.current?.scrollIntoView({ block: 'end' }); // keyboard just opened
}), []);
return <div className="overflow-y-auto">{children}<div ref={endRef} /></div>;
}Klavye açıkken kaybolan bir güvenli alan boşluğu olan, yeniden kullanılabilir bir hook:
import { useEffect, useState } from 'react';
import { onKeyboard } from '@/lib/appmintNative';
export function useKeyboardInset(): number {
const [inset, setInset] = useState(0); // 0 until the keyboard first opens
useEffect(() => onKeyboard(setInset), []);
return inset;
}
// In a component: the home-indicator gap is only needed when the keyboard is closed.
// const kb = useKeyboardInset();
// <footer style={{ paddingBottom: kb > 0 ? kb : 'var(--safe-bottom)' }}>...</footer>Notlar:
- Uygulamada Capacitor Keyboard eklentisini dinler ve yalnızca düzenin zaten emmediği klavye kısmını bildirir. Kabuk genellikle sayfayı klavye için yeniden boyutlandırır; bu yüzden değer orada çoğu zaman
0olur - bu doğrudur; ham klavye yüksekliğini kendiniz eklemeyin. - Tam ekran veya kiosk uygulamasında (sayfa yeniden boyutlanmaz) klavyenin tüm yüksekliğini bildirir.
- Web'de
visualViewportkullanır, geri çağırmayı hemen bir kez çağırır ve 80 px altını tarayıcı arayüzü sayar (şu şekilde bildirilir:0). - Uygulamada ilk çağrı klavye ilk açıldığında gelir; bu yüzden durumunuzu şu değerden başlatın
0.
appmintNative.onNetworkChange appmintNative#
onNetworkChange(cb: (status: { connected: boolean; type: string }) => void): () => void
Örnek
Bağlantı koptuğunda veya geri geldiğinde sizi çağırır - çevrimdışı başlığı için veya sıraya alınmış yazmaları yeniden denemek için.
Dönüş değeri: bir abonelikten çıkma fonksiyonu - bileşen kaldırılırken çağırın. Gereksinim: hiçbir şey.
Cihaz tekrar çevrimiçi olduğunda sıradaki işi yeniden deneyin:
import { useEffect } from 'react';
import { onNetworkChange, toast } from '@/lib/appmintNative';
export function useRetryWhenOnline(flushQueue: () => Promise<number>) {
useEffect(() => {
const off = onNetworkChange(async (s) => {
if (!s.connected) return;
const sent = await flushQueue();
if (sent) toast(`Synced ${sent} change(s)`);
});
return off; // unsubscribe on unmount
}, [flushQueue]);
}Mevcut durumu hemen gösterin, sonra değişiklikleri takip edin (geri çağırma abone olurken çağrılmaz):
import { useEffect, useState } from 'react';
import { networkStatus, onNetworkChange } from '@/lib/appmintNative';
export function useOnline(): boolean {
const [online, setOnline] = useState(() => networkStatus().connected);
useEffect(() => onNetworkChange((s) => setOnline(s.connected)), []);
return online;
}Mobil veride büyük indirmeleri bekletin ve 'none' değerini çevrim dışı sayın:
import { useEffect } from 'react';
import { onNetworkChange, toast } from '@/lib/appmintNative';
export function useDataSaver(setHeavyAllowed: (ok: boolean) => void) {
useEffect(() => onNetworkChange((s) => {
if (s.type === 'none' || !s.connected) { toast('You are offline'); setHeavyAllowed(false); return; }
setHeavyAllowed(s.type !== 'cellular'); // 'wifi' and 'unknown' may download freely
}), [setHeavyAllowed]);
}Notlar:
- Abone olduğunuzda size geçerli durumla seslenmez. Başlangıç değeri için
networkStatus()okuyun. - Uygulamada Capacitor Network eklentisini (
networkStatusChange) dinler;typeşunlardan biridir:'wifi','cellular','none'veya'unknown'. - Webde
online/offlineolaylarını dinler ve bildirirnetworkStatus(). - Hazır bir afiş için bunun üzerine kurulu olan
offlineBanner()kullanın.
appmintNative.onNotificationAction appmintNative#
import { onNotificationAction } from '@/lib/appmintNative'
Örnek
Bildirim düğmelerine dokunuşları dinler ( notifyWithActions).
Döndürür: dinlemeyi durduran bir işlev. Geri çağırma { actionId, tag } alır - düğmenin kimliği ve bildirimin etiketi. Android uygulaması yanıt text göndermez (satır içi yanıt desteklenmez), bu yüzden e.text değerine güvenmeyin. Gerekenler: şununla aynı notifyWithActions.
import { useEffect } from 'react';
import { onNotificationAction } from '@/lib/appmintNative';
export function useNotificationActions(openChat: (chatId: string) => void) {
useEffect(() => {
const stop = onNotificationAction(({ actionId, tag }) => {
if (actionId === 'Open') openChat(tag);
if (actionId === 'Mute') muteChat(tag);
});
return stop; // remove on unmount
}, [openChat]);
}Elle yazılmış bir sayfada ham olay (Android uygulaması):
window.addEventListener('appmint:notification-action', function (e) {
// e.detail = { actionId: 'Mute', tag: 'chat-7' }
console.log(e.detail.actionId, e.detail.tag);
});Düğmeli bir bildirimi yalnızca derlemenin desteklediği yerde gönderin ve dokunuşu bildirimin etiketine göre işleyin:
import { useEffect } from 'react';
import { can, notifyWithActions, onNotificationAction, toast } from '@/lib/appmintNative';
export async function remindWithButtons(taskId: string, title: string) {
if (can('notificationActions') === 'none') { toast(title); return; } // no buttons here
await notifyWithActions({
title, body: 'Tap a button', tag: `task-${taskId}`,
actions: [{ id: 'done', title: 'Done' }, { id: 'later', title: 'Later' }],
});
}
export function useTaskActions(complete: (id: string) => void, snooze: (id: string) => void) {
useEffect(() => onNotificationAction(({ actionId, tag }) => {
const id = tag.replace(/^task-/, '');
if (actionId === 'done') complete(id);
else if (actionId === 'later') snooze(id);
}), [complete, snooze]);
}Notlar: Android uygulamasında bu appmint:notification-action olayıdır (ayrıca window.onAppMintNotificationAction(detail)); web'de hiçbir şey yapmaz. Dokunuş yalnızca uygulama çalışırken iletilir - bir düğmeye dokunmak uygulamayı açmaz.
appmintNative.onNotificationClick appmintNative#
import { onNotificationClick } from '@/lib/appmintNative'
Örnek
Kullanıcı uygulamanın gönderdiği bir bildirime dokunduğunda - uygulamayı soğuk başlatan dokunuş dahil - bir geri çağırma çalıştırır; böylece uygulama doğru ekranı açabilir.
Döndürür: abonelik iptali işlevi. Geri çağırma { tag, title, body, data } alır - data, notify({ …, data }) işlevine ilettiğiniz şeydir. Gerekenler: notify() gibi bildirimler. Arkasındaki olay: appmint:notification-click.
import { useEffect } from 'react';
import { notify, onNotificationClick } from '@/lib/appmintNative';
export function useOrderAlerts(navigate: (path: string) => void) {
useEffect(() => onNotificationClick((e) => {
const d = e.data as { orderId?: number } | null;
if (d?.orderId) navigate(`/orders/${d.orderId}`);
}), [navigate]);
}
async function orderShipped(orderId: number) {
await notify({ title: 'Order shipped', body: `#${orderId} is on its way`, tag: `order-${orderId}`, data: { orderId } });
}Dokunuştan sonra, uygulamayı sıfırdan başlatsa bile doğru ekranı açın ve veri taşımayan bildirimleri yok sayın:
import { useEffect } from 'react';
import { onNotificationClick } from '@/lib/appmintNative';
export function useNotificationRouting(navigate: (path: string) => void) {
useEffect(() => onNotificationClick(({ tag, data }) => {
const d = data as { screen?: string; id?: string } | null;
if (!d?.screen) { console.log('plain notification tapped:', tag); return; }
navigate(d.id ? `/${d.screen}/${d.id}` : `/${d.screen}`);
}), [navigate]);
}Bildirimi yalnızca bildirimlerin var olduğu yerde, dokunuş için verilerle gönderin (data gerektirir tag):
import { can, notify, toast } from '@/lib/appmintNative';
export async function announceMessage(chatId: string, from: string, text: string) {
if (can('notify') === 'none') return; // nothing to tap on this surface
const shown = await notify({ title: from, body: text, tag: `chat-${chatId}`, data: { screen: 'chat', id: chatId } });
if (!shown) toast('Notifications are turned off');
}Notlar: Dokunuş, açılış ekranı ve PIN ekranı üzerinden sayfaya ulaşır. data JSON olmalıdır (16 KB altında) ve gerektirir tag.
appmintNative.onPullToRefresh appmintNative#
onPullToRefresh(el: HTMLElement, onRefresh: () => void | Promise<void>, opts?: { threshold?: number }): () => void
Örnek
Kaydırılan bir listeye aşağı çekerek yenileme hareketini ekler: en üstte aşağı çekin, bırakın ve verileriniz yeniden yüklenir.
Dönüş değeri: bir abonelikten çıkma fonksiyonu - bileşen kaldırılırken çağırın. Gereksinim: hiçbir şey.
el kaydırılan öğedir (veya tüm sayfa kayıyorsa document.documentElement). Döndürücü, onRefresh işlevinden gelen promise tamamlanana kadar kalır:
import { useEffect, useRef } from 'react';
import { onPullToRefresh, toast } from '@/lib/appmintNative';
export function Inbox({ reload }: { reload: () => Promise<void> }) {
const listRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!listRef.current) return;
return onPullToRefresh(listRef.current, async () => {
try { await reload(); }
catch { toast('Could not refresh — check your connection'); }
});
}, [reload]);
return <div ref={listRef} className="h-full overflow-y-auto">{/* rows */}</div>;
}Bütün olarak kayan bir sayfa, daha uzun bir çekmeyle:
import { onPullToRefresh } from '@/lib/appmintNative';
declare function reloadFeed(): Promise<void>;
const off = onPullToRefresh(document.documentElement, reloadFeed, { threshold: 96 });Bir listeyi veri hook'u ile yenileyin; veri geldiğinde dokunsal bir tık verin:
import { useEffect, useRef } from 'react';
import { onPullToRefresh, haptic } from '@/lib/appmintNative';
export function useListRefresh(refetch: () => Promise<unknown>) {
const ref = useRef<HTMLUListElement>(null);
useEffect(() => {
const el = ref.current;
if (!el) return;
return onPullToRefresh(el, async () => {
await refetch();
haptic('success'); // the spinner stays until this promise settles
}, { threshold: 80 });
}, [refetch]);
return ref; // <ul ref={ref} className="h-full overflow-y-auto">
}Notlar:
- Yalnızca liste en üste kaydırıldığında başlar. Varsayılan
threshold72 px çekmedir. - Uygulamanın renklerinde (
--surface,--accent) yuvarlak bir gösterge çizer, onay noktasında orta şiddette bir dokunsal geri bildirim verir ve bir yenileme sürerken yeni çekişleri yok sayar. - Sayfayı değil, VERİLERİNİZİ yeniler. Kabuğun kendi sayfa yenileme çekişi (Adım 3'te (Entegrasyon) Çekerek Yenilemeyi Etkinleştir) varsayılan olarak kapalıdır - bunu kullanırken kapalı tutun, böylece bir çekiş sayfayı da yenilemez.
- Pointer Events üzerine kurulduğundan her yüzeyde aynıdır.
elüzerindeoverscroll-behavior-y: containayarlar ve abonelik iptalinde geri yükler.
appmintNative.onVolumeChange appmintNative#
onVolumeChange(cb) — @/lib/appmintNative
Örnek
Telefonun ses seviyelerinden biri değiştiğinde cb işlevini çağırır - donanım tuşları, sistem kaydırıcısı, başka bir uygulama.
Döndürür: dinlemeyi durduran bir işlev (durduran son kişi yerel izlemeyi kapatır). Geri çağırma, değişen her akış için { stream, current, max, percent, muted? } alır. Web'de hiçbir şey yapmaz. Gerekenler: hiçbir şey.
import { useEffect, useState } from 'react';
import { getVolume, onVolumeChange } from '@/lib/appmintNative';
export function MediaVolumeBadge() {
const [pct, setPct] = useState(() => getVolume('media')?.percent ?? null);
useEffect(() => onVolumeChange((v) => { if (v.stream === 'media') setPct(v.percent); }), []);
return pct === null ? null : <span>🔊 {pct}%</span>;
}Bir video oynarken medya ses seviyesi tamamen kısıldığında uyarın ve bileşen kaldırıldığında dinlemeyi bırakın:
import { useEffect } from 'react';
import { onVolumeChange, toast } from '@/lib/appmintNative';
export function useQuietWarning(playing: boolean) {
useEffect(() => {
if (!playing) return;
return onVolumeChange((v) => {
if (v.stream === 'media' && (v.percent === 0 || v.muted)) toast('Volume is off');
});
}, [playing]);
}Telefonun düzeyini bir kaydırıcıda yansıtın ve kullanıcının değişikliğini geri yazın (web'de ses düzeyi yoktur, bu yüzden getVolume orada null olur):
import { useEffect, useState } from 'react';
import { getVolume, setVolume, onVolumeChange } from '@/lib/appmintNative';
export function MediaVolumeSlider() {
const [pct, setPct] = useState<number | null>(() => getVolume('media')?.percent ?? null);
useEffect(() => onVolumeChange((v) => { if (v.stream === 'media') setPct(v.percent); }), []);
if (pct === null) return null; // not the Android app
return (
<input type="range" min={0} max={100} value={pct}
onChange={(e) => { const r = setVolume('media', Number(e.target.value)); if (r.ok) setPct(Number(e.target.value)); }} />
);
}appmintNative.openNotificationSettings appmintNative#
openNotificationSettings(): Promise<boolean>
Örnek
Bu uygulamanın kendi bildirim ayarları ekranını açar (Android uygulaması).
Döndürür: Promise<boolean> - ekran açıldıysa true; web'de ve böyle bir ekranın olmadığı her yerde false. Denetimi çizmeden önce can('notificationSettings') değerini kontrol edin. Gerekenler: hiçbir şey.
import { can, openNotificationSettings } from '@/lib/appmintNative';
export function NotificationSettingsRow(row: HTMLElement) {
row.hidden = can('notificationSettings') === 'none';
row.addEventListener('click', () => { void openNotificationSettings(); });
}Notlar: tek dokunuşluk "Bildirimleri aç" için enableNotifications() kullanın; bu, izin penceresi artık gösterilemediğinde yalnızca bu ekranı açar.
appmintNative.passkeys appmintNative#
passkeys — available / create / get / origin / assetLinks
Örnek
Passkey sunucunuzun ürettiği JSON seçenekleriyle passkey ile giriş ve kayıt. Android uygulamasında Android'in passkey yöneticisini kullanır; web'de şunu kullanır: navigator.credentials.
Döndürür: available() → 'native' | 'web' | 'none'; create(optionsJSON) → RegistrationResponseJSON Promise'i; get(optionsJSON) → AuthenticationResponseJSON Promise'i (ikisini de sunucunuza POST edin). Bir DOMException ile reddedilirler (NotAllowedError, InvalidStateError, SecurityError, NotSupportedError). origin() → metin, assetLinks() → dizi (yalnızca uygulamada; webde '' / []). Gerekenler: bir passkey sunucusu; uygulamada ayrıca Google Play hizmetleri olan Android 9+ ve alan adınızın assetlinks.json.
import { passkeys } from '@/lib/appmintNative';
export async function signInWithPasskey(): Promise<boolean> {
if (passkeys.available() === 'none') return false; // hide the passkey button
const options = await (await fetch('https://your-domain.com/passkey/login-options')).json();
try {
const answer = await passkeys.get(options);
const res = await fetch('https://your-domain.com/passkey/login-verify', {
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(answer),
});
return res.ok;
} catch (e) {
if ((e as DOMException).name === 'NotAllowedError') return false; // cancelled / none here
throw e;
}
}
export async function addPasskey() {
const options = await (await fetch('https://your-domain.com/passkey/register-options')).json();
const answer = await passkeys.create(options); // InvalidStateError = already registered
await fetch('https://your-domain.com/passkey/register-verify', {
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(answer),
});
}Google Play üzerinden kurulan uygulamadan tek seferlik kurulum değerleri:
import { passkeys } from '@/lib/appmintNative';
console.log(JSON.stringify(passkeys.assetLinks(), null, 2)); // → /.well-known/assetlinks.json
console.log(passkeys.origin()); // → add to expected originsPasskey düğmesini yalnızca çalışabileceği yerde sunun ve çalışamadığında bunu söyleyin:
import { passkeys } from '@/lib/appmintNative';
export function PasskeyButton({ onClick }: { onClick: () => void }) {
const kind = passkeys.available(); // 'native' | 'web' | 'none'
if (kind === 'none') return <p>Passkeys are not available on this device.</p>;
return <button onClick={onClick}>{kind === 'native' ? 'Use your phone passkey' : 'Use a passkey'}</button>;
}Notlar: Webde güvenli (https://) bir sayfa gerekir. Uygulamaya gömülü bir sayfanın alan adı yoktur: sunucunuzun seçenekleri rp.id / rpId değerini alan adınıza ayarlamalıdır. Passkey otomatik doldurma (mediation: 'conditional') uygulamada yoktur - bir düğmeden başlatın.
appmintNative.paste appmintNative#
paste(): Promise<string>
Örnek
Sistem panosundaki metni okur.
Dönüş değeri: Promise<string> - pano metni veya pano boşsa ''. Bu tarayıcı panoyu hiç okuyamıyorsa veya kullanıcı izni reddederse REDDEDİLİR. Gereksinim: hiçbir şey.
import { can, paste, toast } from '@/lib/appmintNative';
export function PasteCode({ onCode }: { onCode: (c: string) => void }) {
if (can('clipboardRead') === 'none') return null; // e.g. Firefox and Safari
return (
<button
onClick={async () => {
try {
const text = (await paste()).trim();
if (!text) { toast('The clipboard is empty'); return; }
onCode(text);
} catch {
toast('Clipboard access was blocked'); // denied permission, not "empty"
}
}}
>
Paste code
</button>
);
}Capacitor biçimli şekil:
import { Clipboard } from '@/lib/appmintNative';
const { value } = await Clipboard.read();Her sonuç için bir mesajla bir metin kutusuna yapıştırın ve şununla eşleştirin copy:
import { copy, paste, toast } from '@/lib/appmintNative';
export async function pasteInto(setValue: (s: string) => void) {
try {
const text = await paste();
if (text) setValue(text);
else toast('Nothing to paste');
} catch {
toast('Allow clipboard access to paste');
}
}
export async function shoutClipboard() {
const text = await paste().catch(() => '');
if (text && await copy(text.toUpperCase())) toast('Converted and copied');
}Notlar: Uygulamada Clipboard eklentisi metni okur; boş pano hata değil, '' verir. Tarayıcıda navigator.clipboard.readText() (yalnızca Chromium) kullanılır ve kullanıcıdan izin ister - bir dokunuştan çağırın.
appmintNative.pickContact appmintNative#
pickContact(): Promise<Contact | null>
Örnek
Kullanıcının bir kişiyi seçmesi için sistem kişi seçicisini açar: uygulamada Android'in seçicisi, Android'de Chrome'da Contact Picker API.
Döndürür: Promise<Contact | null> - kullanıcı iptal ettiğinde veya erişim reddedildiğinde null. Gerekenler: hiçbir şey (sistem seçicisi izin gerektirmez).
Bir kişi seçin ve bir formu doldurun. Kişi, uygulamada ve webde aynı biçimdedir: { id?, name?, phones?: string[], emails?: string[], photoUri? }.
import { pickContact, can } from '@/lib/appmintNative';
export async function onPickClick(setName: (s: string) => void, setPhone: (s: string) => void) {
if (can('contactPick') === 'none') return; // hide the button instead
try {
const c = await pickContact();
if (!c) return; // cancelled
setName(c.name ?? '');
setPhone(c.phones?.[0] ?? '');
} catch {
// only a 5-minute timeout rejects
}
}Seçimi gösteren bir "Kişi seç" düğmesi ve seçicinin olmadığı yerde düz bir numara kutusu:
import { useState } from 'react';
import { can, pickContact, type Contact } from '@/lib/appmintNative';
export function InvitePicker() {
const [who, setWho] = useState<Contact | null>(null);
if (can('contactPick') === 'none') return <input placeholder="Phone number" />; // type it by hand
return (
<div>
<button onClick={async () => setWho((await pickContact()) ?? who)}>Choose a contact</button>
{who && <p>{who.name} {who.phones?.[0]}</p>}
</div>
);
}Seçilen numarayı mesaj uygulamasına verin, kişinin numarası yoksa bir mesajla:
import { pickContact, toast } from '@/lib/appmintNative';
export async function textAFriend(message: string) {
const c = await pickContact();
if (!c) return; // cancelled
const phone = c.phones?.[0];
if (!phone) { toast(`${c.name ?? 'That contact'} has no phone number`); return; }
window.location.href = `sms:${encodeURIComponent(phone)}?body=${encodeURIComponent(message)}`;
}Notlar: Uygulamada yalnızca dokunulan numara geri gelir, asla e-postalar gelmez ve photoUri kişi fotoğrafının bir content:// adresidir. can('contactPick'), Contact Picker API'si olmayan tarayıcılarda (masaüstü, Safari) 'none' olur - orada düğmeyi çizmeyin.
appmintNative.pickFile appmintNative#
pickFile(accept?) from '@/lib/appmintNative'
Örnek
Kullanıcının bir dosya seçip metin olarak okumasını sağlar. Uygulamada Android'in belge seçicisini açar; web'de normal bir dosya girişi açar.
Döndürür: Promise<PickedFile | null> - { name, mime, size, text(): Promise<string> }, ya da yalnızca kullanıcı iptal ettiğinde null. Şu durumlarda Error ile reddedilir; bu hatanın code değeri 'no-picker' (telefonda belge seçici uygulaması yok), 'disabled' veya hiçbir seçici açılamadığında 'failed' olur. text() tüm dosyayı okur ve okuyamadığında köprünün koduyla (örneğin 'not-found') reddeder. Gerekenler: hiçbir şey. Adım 4'te (Erişim) Yerel Klasör Erişimi (SAF) işaretliyse uygulama Android'in belge seçicisini kullanır; değilse sayfanın kendi dosya seçicisini kullanır.
import { pickFile } from '@/lib/appmintNative';
async function importCsv() {
const f = await pickFile('text/csv'); // default '*/*' = any file
if (!f) return; // cancelled (or picker not enabled in the build)
if (f.size > 2_000_000) { alert('Please choose a file under 2 MB.'); return; }
try {
setRows(parseCsv(await f.text()));
} catch (e) {
alert(`Could not read ${f.name}: ${String(e)}`);
}
}Bir JSON yedeğini içe aktarın ve pickFile işleminin başarısız olabileceği her durumu ele alın:
import { pickFile, toast } from '@/lib/appmintNative';
export async function restoreBackup(apply: (data: unknown) => void) {
try {
const f = await pickFile('application/json');
if (!f) return; // cancelled
apply(JSON.parse(await f.text()));
toast(`Restored ${f.name}`);
} catch (e) {
const code = (e as { code?: string }).code;
if (code === 'no-picker') toast('This phone has no file picker app');
else if (e instanceof SyntaxError) toast('That file is not a valid backup');
else toast('Could not open the file');
}
}Bir metin dosyası seçin ve dosyanın ayrıntılarının yanında bir düzenleyiciye koyun:
import { useState } from 'react';
import { pickFile } from '@/lib/appmintNative';
export function OpenText() {
const [doc, setDoc] = useState<{ name: string; kb: number; text: string } | null>(null);
return (
<div>
<button onClick={async () => {
const f = await pickFile('text/*');
if (f) setDoc({ name: f.name, kb: Math.ceil(f.size / 1024), text: await f.text() });
}}>Open a text file</button>
{doc && <><h3>{doc.name} ({doc.kb} KB)</h3><textarea defaultValue={doc.text} /></>}
</div>
);
}Notlar: Uygulamada text() dosyayı sona kadar 4 MB'lık parçalar halinde okur ve UTF-8 olarak çözer, bu yüzden büyük dosyalar tam okunur - ekranın kaldırabileceği şey için kendi boyut sınırınızı koruyun. Açılamayan bir seçici reddeder, bu yüzden pickFile() çağrısını da try/catch içine alın. Web'de text() tarayıcının kendi File.text() işlevidir. İkili dosyalar veya geri yazma için elle yazılmış sayfalar şunu kullanır WebToApkFS.
appmintNative.pip appmintNative#
pip.enter(video?) / pip.exit(video?)
Örnek
Kullanıcı başka bir şey yaparken bir videoyu küçük bir kayan pencerede oynatmaya devam ettirir.
Döndürür: enter(), Promise<boolean> döndürür - kayan pencere açıldığında true. exit(), Promise<void> döndürür. Hiçbiri reddedilmez. Gerekenler: Android uygulamasında, Adım 3'te (Entegre et) derlemenin Resim içinde resim (PiP) seçeneği - kodunuz şunu çağırdığında otomatik olarak açılır: pip.enter(.
Bir oynatıcıda "küçült" düğmesi:
import { useRef } from 'react';
import { can, pip, toast } from '@/lib/appmintNative';
export function Player({ src }: { src: string }) {
const videoRef = useRef<HTMLVideoElement>(null);
return (
<div>
<video ref={videoRef} src={src} controls playsInline />
{can('pip') !== 'none' && (
<button
onClick={async () => {
const ok = await pip.enter(videoRef.current ?? undefined);
if (!ok) toast('Picture-in-picture is not available here');
}}
>
Minimise
</button>
)}
</div>
);
}Oynatıcı kapandığında bırakın (web):
import { pip } from '@/lib/appmintNative';
declare const video: HTMLVideoElement;
await pip.exit(video);Kullanıcı oynayan bir videodan ayrıldığında, yalnızca desteklenen yerlerde otomatik olarak pencere içinde pencereye (PiP) geçin:
import { useEffect, type RefObject } from 'react';
import { can, pip } from '@/lib/appmintNative';
export function useAutoPip(videoRef: RefObject<HTMLVideoElement>) {
useEffect(() => {
if (can('pip') === 'none') return;
const onHide = () => {
const v = videoRef.current;
if (document.hidden && v && !v.paused) void pip.enter(v);
};
document.addEventListener('visibilitychange', onHide);
return () => document.removeEventListener('visibilitychange', onHide);
}, [videoRef]);
}Notlar:
- Android (8+): TÜM uygulama kayan pencereye küçülür ve
videobağımsız değişkeni yok sayılır. Eski Android'de veya PiP seçeneği olmayan bir derlemedefalsedöndürür. Kullanıcı geri dönmek için pencereyi genişletir; oradaexit()hiçbir şey yapmaz. - Web:
<video>öğesinin kendi resim içinde resmi. Video yüklenmiş olmalıdır (genellikle oynuyor);videoolmadan yapılan bir çağrı şunu döndürürfalse. - Bir dokunuştan çağırın.
appmintNative.platform appmintNative#
platform(): 'android' | 'ios' | 'web'
Örnek
Kodun hangi yüzeyde çalıştığını söyler: yüklü Android uygulaması veya bir web tarayıcısı.
Dönüş değeri: hemen bir dize: uygulamanın içinde 'android', tarayıcıda 'web'. Önce Capacitor'a sorar (Capacitor.getPlatform()), sonra Android kabuk köprüsünü kontrol eder, yoksa 'web'. Tür ayrıca 'ios' de listeler, ancak bir AppMint uygulaması bunu asla almaz - AppMint yalnızca Android uygulamaları derler. Gereksinim: hiçbir şey.
Doğru bağlantıyı seçin ve kopyalayın.
import { platform } from '@/lib/appmintNative';
const inApp = platform() === 'android';
const rateLink = inApp
? 'https://play.google.com/store/apps/details?id=com.example.app'
: '/feedback';
const backHint = inApp ? 'Press Back to go back' : 'Use the browser\'s back button';Yalnızca uygulamaya özel denetimleri gösterin.
import { platform } from '@/lib/appmintNative';
export function InstallBanner() {
// In the installed app there is nothing to install.
if (platform() === 'android') return null;
return <a href="https://play.google.com/store/apps/details?id=com.example.app">Get the Android app</a>;
}Analitik olaylarını yüzeyle etiketleyin.
import { platform } from '@/lib/appmintNative';
export function track(event: string, data: Record<string, unknown> = {}) {
return fetch('/api/track', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ event, surface: platform(), ...data }),
});
}Notlar: platform() kurulu uygulamanın içinde asla 'web' yanıtını vermez. Bir özelliğin var olup olmadığına karar vermek için kullanmayın - birçok özellik derleme anahtarlarına da bağlıdır; bunu can('<capability>') yanıtlar.
appmintNative.privacyScreen appmintNative#
privacyScreen — enable() / disable() / isEnabled()
Örnek
Gizli bilgi içeren bir ekran açıkken ekran görüntülerini, ekran kaydını ve uygulama değiştirici önizlemesini engeller.
Dönüş değeri: enable() → Promise<boolean> (engelleyebilecek hiçbir şey olmadığında false); disable() → Promise<void>; isEnabled() → Promise<boolean>. Gereksinim: hiçbir şey. Android uygulamasında kabuğun ekran görüntüsü engelini kullanır (ekran görüntüleri, ekran kaydı, yayınlama ve son uygulamalar küçük resmi).
import { useEffect } from 'react';
import { privacyScreen } from '@/lib/appmintNative';
/** Use in the component that shows the secret (a card number, a statement). */
export function usePrivacyScreen() {
useEffect(() => {
void privacyScreen.enable();
return () => { void privacyScreen.disable(); }; // e.g. a modal closed without a route change
}, []);
}Kullanıcıya neyin korunduğunu söyleyin - yalnızca gerçekten korunduğunda:
import { privacyScreen, can } from '@/lib/appmintNative';
export async function protectionLabel(): Promise<string> {
if (can('privacyScreen') === 'none') return ''; // web: nothing can stop a screenshot
return (await privacyScreen.isEnabled()) ? 'Screenshots are blocked on this screen' : '';
}Hassas bir görünüm için korumayı açın ve uygulanamadıysa kullanıcıya söyleyin:
import { privacyScreen, toast } from '@/lib/appmintNative';
export async function openStatement(show: () => void) {
const protectedNow = await privacyScreen.enable(); // false where nothing can block capture
if (!protectedNow) toast('Screenshots cannot be blocked here');
show();
}
export async function closeStatement(hide: () => void) {
hide();
await privacyScreen.disable();
}Notlar: Uygulamada engel mevcut sayfaya aittir ve uygulama başka bir yola gittiğinde kaldırılır, bu yüzden o ekran tekrar göründüğünde yeniden etkinleştirin. Web'de can('privacyScreen') 'none' olur ve enable() false döndürür - orada koruma iddia etmeyin. Hiçbir şey ekrana tutulan bir kamerayı durdurmaz.
appmintNative.pushInbox appmintNative#
import { pushInbox } from '@/lib/appmintNative'
Örnek
Bu yüklemenin aldığı AppMint Push mesajlarını (en yeniden eskiye, en fazla 50), uygulama içi bir bildirimler ekranı için döndürür.
Döndürür: PushMessage[] eşzamanlı olarak: { id, title, body, imageUrl, link, receivedAt, read }. Web'de ve AppMint Push kapalıyken boş bir dizi. Gerekenler: Android: derleme yaparken Entegrasyon adımında (3. Adım) AppMint Push açık.
import { useEffect, useState } from 'react';
import { pushInbox, markPushRead, type PushMessage } from '@/lib/appmintNative';
export function useInbox() {
const [items, setItems] = useState<PushMessage[]>(() => pushInbox());
useEffect(() => {
const refresh = () => setItems(pushInbox());
// detail: { type: 'received', message } or { type: 'revoked', id }
window.addEventListener('appmint:push', refresh);
return () => window.removeEventListener('appmint:push', refresh);
}, []);
const unread = items.filter((m) => !m.read).length;
const open = (m: PushMessage) => {
markPushRead(m.id);
setItems(pushInbox());
};
return { items, unread, open };
}Yeni gelenleri izleyen bir okunmamış rozet sayısı gösterin:
import { useEffect, useState } from 'react';
import { pushInbox } from '@/lib/appmintNative';
export function UnreadBadge() {
const [n, setN] = useState(() => pushInbox().filter((m) => !m.read).length);
useEffect(() => {
const recount = () => setN(pushInbox().filter((m) => !m.read).length);
window.addEventListener('appmint:push', recount);
return () => window.removeEventListener('appmint:push', recount);
}, []);
return n > 0 ? <span aria-label={`${n} unread`}>{n}</span> : null;
}Listeyi, web için ve AppMint Push olmayan derlemeler için boş bir durumla ve "Tümünü okundu işaretle" düğmesiyle oluşturun:
import { useState } from 'react';
import { pushInbox, markPushRead } from '@/lib/appmintNative';
export function NotificationsScreen() {
const [items, setItems] = useState(() => pushInbox());
if (items.length === 0) return <p>No notifications yet.</p>;
return (
<section>
<button onClick={() => { markPushRead(); setItems(pushInbox()); }}>Mark all read</button>
<ul>
{items.map((m) => (
<li key={m.id} style={{ fontWeight: m.read ? 400 : 700 }}>
{m.title} <small>{new Date(m.receivedAt).toLocaleString()}</small>
<p>{m.body}</p>
</li>
))}
</ul>
</section>
);
}Notlar: gelen kutusu kayıtlı gerçektir - bağlanırken okuyun; appmint:push olayı yalnızca uygulama ekrandayken tetiklenir. Kapatılan mesajlar dışarıda bırakılır.
appmintNative.readBarcodeFrom appmintNative#
readBarcodeFrom(source: Blob | CanvasImageSource, formats?: BarcodeFormat[]): Promise<{ value, format }[]>
Örnek
Sayfanın zaten sahip olduğu bir resimden barkodları ve QR kodlarını okur - bir fotoğraf dosyası, bir Blob, bir <img>, bir <canvas> veya bir video karesi. Kamera açılmaz.
Döndürür: Promise<{ value: string; format: string }[]> - bulunan her kod, dedektörün sırasıyla; hiç yoksa, görsel okunamıyorsa veya barkod okuma kullanılamıyorsa boş bir dizi. Asla reddetmez. Gerekenler: hiçbir şey.
Kullanıcının seçtiği bir fotoğraftan kod okuyun:
import { can, readBarcodeFrom, toast } from '@/lib/appmintNative';
export function ReadFromPhoto({ onCode }: { onCode: (v: string) => void }) {
if (can('barcodeScan') === 'none') return null;
return (
<label>
Read a code from a photo
<input
type="file"
accept="image/*"
onChange={async (e) => {
const file = e.currentTarget.files?.[0];
if (!file) return;
const codes = await readBarcodeFrom(file, ['qr_code']);
if (codes.length) onCode(codes[0].value);
else toast('No QR code found in that photo');
}}
/>
</label>
);
}Sayfada zaten bulunan bir görselden:
import { readBarcodeFrom } from '@/lib/appmintNative';
const img = document.querySelector('img#ticket') as HTMLImageElement;
const [first] = await readBarcodeFrom(img);
if (first) console.log(first.format, first.value);Bir canvas karesinden kod okuyun, önce cihazın desteklediğini kontrol ederek:
import { barcodeFormats, readBarcodeFrom } from '@/lib/appmintNative';
export async function scanCanvas(canvas: HTMLCanvasElement): Promise<string | null> {
const supported = await barcodeFormats();
if (supported.length === 0) return null; // no reader on this device
const wanted = ['qr_code', 'ean_13'].filter((f) => supported.includes(f)) as ('qr_code' | 'ean_13')[];
const [hit] = await readBarcodeFrom(canvas, wanted);
return hit?.value ?? null;
}Notlar:
- Tarayıcı motorunun
BarcodeDetectorözelliğini kullanır (Android uygulamasının WebView'inde bulunur; Safari'de ve birçok masaüstü tarayıcıda yoktur - o zaman şunu döndürür[]). - Düz bir
<input type="file" accept="image/*">Android fotoğraf seçicisini açar ve depolama izni gerektirmez. - Biliyorsanız
formatsdeğerini daraltın: daha hızlıdır ve daha az yanlış okur.barcodeFormats(), cihazın desteklediklerini listeler.
appmintNative.readOtp appmintNative#
import { readOtp } from '@/lib/appmintNative'
Örnek
Bu uygulama için SMS tek kullanımlık kodunu bekler ve döndürür - Android uygulamasında Google'ın SMS onay paneli, web'de Chrome'da WebOTP.
Dönüş değeri: Promise<string | null> - kod veya kullanıcı reddettiğinde, beş dakika geçtiğinde ya da signal iptal ettiğinde null. can('otp') 'none' olduğunda hata fırlatır. Gereksinim: açılacak bir şey yok. SMS şununla bitmelidir: @<host> #<code> (Android uygulamasında ana makine appassets.androidplatform.net).
import { useEffect, useState } from 'react';
import { can, readOtp } from '@/lib/appmintNative';
export function CodeField({ onDone }: { onDone: (code: string) => void }) {
const [code, setCode] = useState('');
useEffect(() => {
if (can('otp') === 'none') return;
const ac = new AbortController();
readOtp({ signal: ac.signal }).then((c) => { if (c) { setCode(c); onDone(c); } });
return () => ac.abort(); // leaving the screen stops the wait
}, [onDone]);
return (
<input value={code} onChange={(e) => setCode(e.target.value)}
inputMode="numeric" autoComplete="one-time-code" aria-label="Verification code" />
);
}"Kod yok" cevabını ele alın ve bekleme kodsuz bittiğinde yeniden gönderme sunun:
import { useState } from 'react';
import { can, readOtp } from '@/lib/appmintNative';
export function VerifyStep({ resend, submit }: { resend: () => void; submit: (c: string) => void }) {
const [waited, setWaited] = useState(false);
async function start() {
resend();
if (can('otp') === 'none') return; // readOtp would throw here — the user types it
const code = await readOtp(); // null: declined, 5 minutes passed, or aborted
if (code) submit(code); else setWaited(true);
}
return (
<div>
<button onClick={start}>Send code</button>
{waited && <p>No code detected. <button onClick={start}>Send again</button></p>}
</div>
);
}can('otp') önce kontrol edilemiyorsa fırlatılan hatayı yakalayın:
import { readOtp } from '@/lib/appmintNative';
export async function tryAutoFill(): Promise<string | null> {
try {
return await readOtp({ signal: AbortSignal.timeout(60_000) });
} catch {
return null; // one-time codes unavailable: the user types it
}
}Notlar: bu bir hızlandırıcıdır - alanı her zaman yazılabilir tutun.
appmintNative.recognizeText appmintNative#
import { recognizeText } from '@/lib/appmintNative'
Örnek
Bir görseldeki metni cihazda okur (can('ocr')): Android uygulamasının ML Kit tanıyıcısı veya etkin olduğu yerde Chrome'un TextDetector'ı.
Döndürür: { text, blocks: [{ text, box, lines: [{ text, box }] }] } Promise'i, kutular görüntü pikselinde. can('ocr') 'none' olduğunda code 'unavailable' ile veya tanıma başarısız olduğunda bir DOMException ile reddedilir. Gerekenler: hiçbir şey, izin yok.
import { useState } from 'react';
import { can, recognizeText } from '@/lib/appmintNative';
export function ReceiptReader() {
const [total, setTotal] = useState<string | null>(null);
const [busy, setBusy] = useState(false);
if (can('ocr') === 'none') return null;
return (
<label>
Scan a receipt
<input type="file" accept="image/*" capture="environment" onChange={async (e) => {
const file = e.target.files?.[0];
if (!file) return;
setBusy(true);
try {
const { text } = await recognizeText(file);
setTotal(/total\s*[:$€£]?\s*([\d.,]+)/i.exec(text)?.[1] ?? 'not found');
} finally { setBusy(false); }
}} />
{busy ? <span role="status">Reading…</span> : total && <strong>Total: {total}</strong>}
</label>
);
}"Tanıyıcı yok" ve başarısızlık durumlarını açıkça ele alın:
import { can, recognizeText } from '@/lib/appmintNative';
export async function readLabel(file: Blob): Promise<string> {
if (can('ocr') === 'none') return '';
try {
const { text } = await recognizeText(file);
return text.trim();
} catch (e) {
if ((e as { code?: string }).code === 'unavailable') return '';
throw e; // a real recognition failure
}
}Latin dışındaki bir yazıyı okuyun ve her satırı konumuyla listeleyin:
import { recognizeText } from '@/lib/appmintNative';
export async function readHindiSign(img: HTMLImageElement) {
const { blocks } = await recognizeText(img, { script: 'devanagari' });
return blocks.flatMap((b) => b.lines).map((l) => ({ text: l.text, box: l.box }));
}Notlar: Yalnızca Latin yazıları. Yeni bir telefondaki ilk çağrı, Play hizmetleri modeli indirirken birkaç saniye sürebilir - ilerleme gösterin.
appmintNative.remoteConfig appmintNative#
remoteConfig.start() / fetch() / apply() / manifestUrl() / handledHere() / dismissed() / dismiss()
Örnek
Oluşturucunun Remote Update'ten yayınladığı duyuru şeridini ve bakım ekranını çizer - sayfa bir web tarayıcısında çalışırken sayfa için. Android uygulamasında kabuk bunu zaten kendisi yapar; bu yüzden orada bu çağrılar bir şey yapmaz.
Dönüş değeri: start(manifestUrl?) → Promise<void> (getirir + çizer; asla reddedilmez); fetch(url) → Promise<RemoteConfig | null> (asla reddedilmez; null = hiçbir şey uygulanmaz); apply(cfg) → bir temizleme fonksiyonu; manifestUrl() → string (<meta name="appmint-remote-manifest"> öğesinin content değeri, yoksa ''); handledHere() → boolean; dismissed(id) → boolean; dismiss(id) → void. Gereksinim: uygulamada hiçbir şey - Remote Update orada yerel olarak çalışır.
Açılışta çağırmak güvenlidir; uygulamada hemen döner:
import { useEffect } from 'react';
import { remoteConfig } from '@/lib/appmintNative';
// Your Remote Update manifest URL. AppMint does not write the meta tag into the page,
// so pass the URL when the same page also runs on the web.
const MANIFEST = 'https://example.com/remote-update/manifest.json';
export function App() {
useEffect(() => { void remoteConfig.start(MANIFEST); }, []); // does nothing in the Android app
return <main>{/* … */}</main>;
}Veya değerleri okuyun ve kendi arayüzünüzü çizin (yalnızca web):
import { remoteConfig, type RemoteConfig } from '@/lib/appmintNative';
export async function loadNotice(url: string): Promise<RemoteConfig | null> {
if (!remoteConfig.handledHere()) return null; // the Android app draws its own
const cfg = await remoteConfig.fetch(url);
if (!cfg || !cfg.announcementText) return cfg;
return remoteConfig.dismissed(cfg.announcementId) ? null : cfg;
}
// when the user closes your banner: remoteConfig.dismiss(cfg.announcementId)Bakım metnini webde kendiniz gösterin ve uygulamayı kendi ekranına bırakın:
import { useEffect, useState } from 'react';
import { remoteConfig } from '@/lib/appmintNative';
export function useMaintenance(url: string): string | null {
const [text, setText] = useState<string | null>(null);
useEffect(() => {
if (!remoteConfig.handledHere()) return; // the Android app shows its own screen
void remoteConfig.fetch(url).then((cfg) => {
setText(cfg?.maintenanceMode ? cfg.maintenanceText : null);
});
}, [url]);
return text;
}Notlar:
RemoteConfig={ version, announcementText, announcementId, announcementImageUrl, announcementBgColor, announcementTextColor, maintenanceMode, maintenanceText }.handledHere()Android uygulamasındafalseolur: kabuk manifesti yerel olarak okur ve duyuruyu kendisi çizer, böylece asla iki başlık görmezsiniz.start()argümansızmanifestUrl()kullanır. Bir AppMint derlemesi o meta etiketini eklemez, bu yüzden argümansız (veya kendi<meta name="appmint-remote-manifest" content="…">etiketiniz olmadan) hiçbir şey getirilmez.fetch()duraklatılmış veya geri alınmış bir yayın,platformslistesiwebiçermeyen bir manifest, daha yeni bir manifest biçimi ya da herhangi bir ağ hatası içinnulldöndürür.- Bakım modu kazanır:
apply()o zaman bütün sayfayı bakım metniyle kaplar. Kapatılan bir duyuru, yeni birannouncementIdyayınlanana kadar gizli kalır.
appmintNative.requestDndAccess appmintNative#
requestDndAccess() — @/lib/appmintNative
Örnek
Rahatsız Etmeyin erişimi ister; Android, Rahatsız Etmeyin açıkken uygulamanın zil veya bildirim ses düzeyini değiştirmesinden önce bunu gerektirir.
Dönüş değeri: 'granted', 'opened' (ayarlar ekranı gösterildi - kullanıcı döndüğünde tekrar kontrol edin), 'not_declared' veya web'de 'unavailable'. Gereksinim: işaretlenecek bir şey yok - kodunuzda çağırmak, derlemenin izni bildirmesini sağlar.
import { requestDndAccess, setVolume } from '@/lib/appmintNative';
export function silenceRinger(): string {
const r = setVolume('ring', 0);
if (r.ok) return 'done';
if (r.error === 'dnd_access_required') return requestDndAccess(); // 'opened' → ask the user to allow it
return r.error ?? 'failed';
}Bir kez sorun, sonra kullanıcı ayarlardan döndükten sonra ses değişikliğini yeniden deneyin:
import { useEffect, useRef } from 'react';
import { requestDndAccess, setVolume } from '@/lib/appmintNative';
export function useRingerMute() {
const pending = useRef(false);
useEffect(() => {
const onShow = () => {
if (pending.current && document.visibilityState === 'visible') {
pending.current = false;
setVolume('ring', 0); // allowed now, or the error comes back again
}
};
document.addEventListener('visibilitychange', onShow);
return () => document.removeEventListener('visibilitychange', onShow);
}, []);
return () => {
if (requestDndAccess() === 'opened') pending.current = true;
};
}Verebileceği her cevaba göre dallanın:
import { requestDndAccess, toast } from '@/lib/appmintNative';
export function explainDnd() {
switch (requestDndAccess()) {
case 'granted': toast('Do Not Disturb access is already on'); break;
case 'opened': toast('Allow this app, then come back'); break;
case 'not_declared': toast('This build was made without that permission'); break;
default: toast('Only available in the Android app'); // 'unavailable' on the web
}
}appmintNative.requestReview appmintNative#
requestReview(): Promise<boolean>
Örnek
Google Play'den, uygulamanın içinde kendi puanlama sayfasını göstermesini ister (Play In-App Review).
Dönüş değeri: Promise<boolean> - true yalnızca Play paneli gösterdiğinde; web'de, Google Play'den gelmeyen bir yüklemede, Play reddettiğinde (kotası) veya başarısız olduğunda false. Hangisi olduğunu öğrenmek için askForReview() kullanın. Gereksinim: hiçbir şey; panel yalnızca Google Play'den yüklenen bir uygulamada görünür.
import { requestReview } from '@/lib/appmintNative';
async function onOrderDelivered(count: number) {
// After a success moment, and rarely — not on launch, not after an error.
if (count === 3 && !localStorage.getItem('askedReview')) {
localStorage.setItem('askedReview', '1');
await requestReview();
}
}Kullanıcıyı başka yere göndermeden, sayfanın neden gösterilmediğini öğrenin:
import { askForReview } from '@/lib/appmintNative';
export async function maybeAsk() {
const { result } = await askForReview();
// 'shown' | 'not_from_play' | 'quota' | 'error' | 'unavailable'
if (result !== 'shown') console.info('review sheet not shown:', result);
}Yalnızca bir başarı anından sonra ve ayda en fazla bir kez sorun:
import { requestReview } from '@/lib/appmintNative';
const KEY = 'lastReviewAsk';
export async function afterSuccess() {
const last = Number(localStorage.getItem(KEY) ?? 0);
if (Date.now() - last < 30 * 24 * 3600 * 1000) return;
localStorage.setItem(KEY, String(Date.now()));
await requestReview();
}Notlar: Google Play paneli sınırlar ve çoğu zaman hiçbir şey göstermez - bir değerlendirmeyi asla ödüllendirmeyin ve panelin açılmasını bekleyen bir "Bizi değerlendirin" düğmesi yapmayın (düğme için askForReview({ openStore: true }) kullanın). Uygulamadan Play sayfasına asla çıkmaz.
appmintNative.restorePushMessage appmintNative#
import { restorePushMessage } from '@/lib/appmintNative'
Örnek
dismissPushMessage(id) işlemini geri alır - mesaj AppMint Push gelen kutusuna eski yerinde döner.
Döndürür: boolean eşzamanlı olarak - geri yüklendiyse true; kimlik bilinmiyorsa veya kapatılmamışsa false (web'de her zaman false). Gerekenler: Android: derleme yaparken Entegrasyon adımında (3. Adım) AppMint Push açık.
import { dismissPushMessage, restorePushMessage, pushInbox } from '@/lib/appmintNative';
let lastDismissed: string | null = null;
export function dismiss(id: string) {
if (dismissPushMessage(id)) lastDismissed = id;
return pushInbox();
}
export function undo() {
if (lastDismissed) restorePushMessage(lastDismissed);
lastDismissed = null;
return pushInbox();
}Küçük bir React satırında, kaydırıp kaldırdıktan hemen sonra Geri Al seçeneği sunun:
import { useState } from 'react';
import { dismissPushMessage, restorePushMessage } from '@/lib/appmintNative';
export function SwipeRow({ id, onChange }: { id: string; onChange: () => void }) {
const [undoable, setUndoable] = useState(false);
return undoable ? (
<button onClick={() => { if (restorePushMessage(id)) { setUndoable(false); onChange(); } }}>
Undo
</button>
) : (
<button onClick={() => { if (dismissPushMessage(id)) { setUndoable(true); onChange(); } }}>
Dismiss
</button>
);
}Yanıtı kontrol edin, çünkü bilinmeyen veya hiç kapatılmamış bir kimlik şunu verir false:
import { restorePushMessage, pushInbox } from '@/lib/appmintNative';
export function undoOrReport(id: string): string {
if (!restorePushMessage(id)) return 'Nothing to restore';
return `Back in your inbox (${pushInbox().length} messages)`;
}Notlar: clearPushInbox() ile kaldırılan veya sizin geri çektiğiniz mesajlar geri yüklenemez.
appmintNative.safeArea appmintNative#
safeArea(): { top: number; right: number; bottom: number; left: number }
Örnek
Durum çubuğunun, kamera çentiğinin ve gezinme çubuğunun kapladığı alanı CSS pikseli olarak verir; böylece sabit çubuklar bunların altında kalmaz.
Döndürür: { top, right, bottom, left } sayıları, eşzamanlı olarak. Gerekenler: hiçbir şey.
Çoğu zaman CSS yeterlidir - çalışma zamanı --safe-top, --safe-right, --safe-bottom, --safe-left değerlerini şuna koyar :root:
export function BottomNav() {
return (
<nav className="fixed inset-x-0 bottom-0" style={{ paddingBottom: 'var(--safe-bottom)' }}>
{/* tabs */}
</nav>
);
}Kodun hesap yapması gerektiğinde sayıları kullanın, örneğin kayan bir düğmeyi yerleştirmek için:
import { useEffect, useState } from 'react';
import { safeArea } from '@/lib/appmintNative';
export function useBottomInset(): number {
const [bottom, setBottom] = useState(0);
useEffect(() => {
const read = () => setBottom(safeArea().bottom);
read();
window.addEventListener('resize', read); // rotation changes the insets
return () => window.removeEventListener('resize', read);
}, []);
return bottom;
}Kaydırılan bir listeyi, dolgusunu sayılardan hesaplayarak çubuklardan uzak tutun:
import { useEffect, useState, type ReactNode } from 'react';
import { safeArea } from '@/lib/appmintNative';
export function PaddedList({ children }: { children: ReactNode }) {
const [inset, setInset] = useState({ top: 0, bottom: 0 });
useEffect(() => {
const read = () => { const s = safeArea(); setInset({ top: s.top, bottom: s.bottom }); };
read();
window.addEventListener('resize', read);
return () => window.removeEventListener('resize', read);
}, []);
return <div style={{ paddingTop: inset.top + 8, paddingBottom: inset.bottom + 8 }}>{children}</div>;
}Notlar:
- CSS
env(safe-area-inset-*)değerinden ölçülür. Değer önbelleğe alınır ve pencere yeniden boyutlandırıldıktan veya döndürüldükten sonra yeniden ölçülür. document.bodyvar olmadan önce (içe aktarma sırasında çok erken) hepsi sıfır döndürür - bir efektten çağırın.- Masaüstü tarayıcıda dördü de genellikle
0.
appmintNative.saveFile appmintNative#
saveFile(fileName, data, mime?) from '@/lib/appmintNative'
Örnek
Metni veya bir Blob'u, kullanıcının sakladığı bir dosya olarak, seçtiğiniz adla kaydeder. Uygulamada Android'in o adla "Farklı kaydet…" sayfasını açar; tarayıcıda normal bir indirmedir.
Dönüş değeri: (eşzamanlı) boolean - indirme başlatıldığında true, document yoksa veya oluşturması hata verdiyse false. Kullanıcının paneli kaydedip kaydetmediğini veya iptal edip etmediğini söylemez. Gereksinim: hiçbir şey.
import { saveFile } from '@/lib/appmintNative';
function exportScores(rows: { name: string; score: number }[]) {
const csv = ['name,score', ...rows.map((r) => `${r.name},${r.score}`)].join('\n');
if (!saveFile('Scores.csv', csv, 'text/csv')) alert('Could not start the download.');
}Bir Blob kendi türünü korur (üçüncü bağımsız değişken yok sayılır):
import { saveFile } from '@/lib/appmintNative';
const blob = new Blob([JSON.stringify(state, null, 2)], { type: 'application/json' });
saveFile('Backup.json', blob);Bir fetch sonucunu kaydedin ve başlatılamadığında bunu söyleyin:
import { saveFile, toast } from '@/lib/appmintNative';
export async function downloadInvoice(id: string) {
const res = await fetch(`/api/invoices/${id}.pdf`);
if (!res.ok) { toast('Could not get the invoice'); return; }
const pdf = await res.blob(); // the Blob keeps its own type
if (!saveFile(`Invoice-${id}.pdf`, pdf)) toast('Could not start the download');
}Notlar: Kaputun altında düz bir <a download> olup uygulama bunu "Farklı kaydet…" paneline çevirir (bkz. downloads). Her zaman uzantılı bir ad verin; olmadan dosya download olarak kaydedilir. Bir dize için varsayılan MIME türü text/plain.
appmintNative.saveToGallery appmintNative#
import { saveToGallery } from '@/lib/appmintNative'
Örnek
Bir fotoğrafı veya videoyu telefonun galerisine kaydeder - can('gallery') Android uygulamasında 'native', tarayıcıda 'web' olur (dosya orada indirilir).
Döndürür: kaydedilen öğenin URI'sinin Promise'i (webde ''). code 'permission-denied', 'download-failed', 'bad-source' veya 'failed' ile reddedilir. Gerekenler: hiçbir şey; Android 10+ üzerinde izin yok.
import { can, saveToGallery, toast } from '@/lib/appmintNative';
export function SaveButton({ canvas }: { canvas: HTMLCanvasElement }) {
if (can('gallery') === 'none') return null;
return (
<button onClick={() => canvas.toBlob(async (blob) => {
if (!blob) return;
try {
await saveToGallery(blob, { album: 'My Cards', fileName: 'card.png' });
toast('Saved to gallery');
} catch (e) {
toast('Could not save');
}
}, 'image/png')}>Save to gallery</button>
);
}Bir video klibi kaydedin ve reddedilebileceği hata kodlarını ele alın:
import { saveToGallery, toast } from '@/lib/appmintNative';
export async function saveClip(url: string) {
try {
await saveToGallery(url, { kind: 'video', album: 'My Clips', fileName: 'clip.mp4' });
toast('Saved to your Movies folder');
} catch (e) {
const code = (e as { code?: string }).code;
toast(code === 'download-failed' ? 'Check your connection and try again' : 'Could not save the clip');
}
}Bir veri URL'sinden görsel kaydedin ve yalnızca kaydedildiyse paylaşım paneline verin:
import { can, saveToGallery, share } from '@/lib/appmintNative';
export async function saveAndShare(dataUrl: string) {
if (can('gallery') !== 'none') await saveToGallery(dataUrl, { album: 'Receipts' });
await share({ title: 'My receipt', text: 'Here is my receipt' });
}Notlar: source bir data: URL'si veya http(s) URL'si de olabilir; video MIME türü olmayan bir video URL'si için { kind: 'video' } iletin.
appmintNative.scanBarcode appmintNative#
scanBarcode(video: HTMLVideoElement, opts?: { signal?, formats?, facingMode? }): Promise<{ value, format } | null>
Örnek
Kameranızı düzeninize YERLEŞTİRDİĞİNİZ bir <video> içinde açar ve gördüğü ilk barkod veya QR kodunla sonuçlanır.
Döndürür: Promise<{ value: string; format: string } | null> - ilk kod; iptal edildiğinde (signal iptal etti), kamera reddedildiğinde veya tarama kullanılamadığında null. Asla reddetmez ve kamerayı her zaman durdurur. Gerekenler: kamera erişimi - Android uygulamasında derleme yaparken 4. Adım'da (Erişim) Kamera seçeneğini açın.
Yalnızca vizörün kendi ekranınızın içinde durması gerektiğinde kullanın; aksi halde scanCode() daha basittir.
import { useEffect, useRef, useState } from 'react';
import { can, scanBarcode, haptic } from '@/lib/appmintNative';
export function InlineScanner({ onCode }: { onCode: (v: string) => void }) {
const videoRef = useRef<HTMLVideoElement>(null);
const [error, setError] = useState('');
useEffect(() => {
const video = videoRef.current;
if (!video || can('barcodeScan') === 'none') return;
const ctl = new AbortController();
void scanBarcode(video, { signal: ctl.signal, formats: ['qr_code'] }).then((hit) => {
if (hit) { haptic('success'); onCode(hit.value); }
else if (!ctl.signal.aborted) setError('Camera not available. Type the code instead.');
});
return () => ctl.abort(); // leaving the screen stops the camera
}, [onCode]);
return (
<div>
<video ref={videoRef} className="aspect-square w-full object-cover" muted playsInline />
{error && <p role="alert">{error}</p>}
</div>
);
}Ön kamerayı kullanın:
import { scanBarcode } from '@/lib/appmintNative';
declare const video: HTMLVideoElement;
const hit = await scanBarcode(video, { facingMode: 'user' });İlk eşleşmeden sonra tarayıcıyı kapatın ve kamera kullanılamıyorsa elle yazılan bir kod sunun:
import { useRef, useState } from 'react';
import { can, scanBarcode } from '@/lib/appmintNative';
export function TicketCheck({ onCode }: { onCode: (v: string) => void }) {
const videoRef = useRef<HTMLVideoElement>(null);
const ctl = useRef<AbortController | null>(null);
const [typed, setTyped] = useState(false);
async function start() {
if (!videoRef.current || can('barcodeScan') === 'none') { setTyped(true); return; }
ctl.current = new AbortController();
const hit = await scanBarcode(videoRef.current, { signal: ctl.current.signal, formats: ['ean_13', 'code_128'] });
if (hit) onCode(hit.value);
else if (!ctl.current.signal.aborted) setTyped(true); // null and not cancelled: no camera
}
return (
<div>
<video ref={videoRef} muted playsInline />
<button onClick={start}>Scan ticket</button>
<button onClick={() => ctl.current?.abort()}>Cancel</button>
{typed && <input placeholder="Type the ticket number" onBlur={(e) => onCode(e.target.value)} />}
</div>
);
}Notlar:
getUserMediaçağırır (böylece kullanıcı kamera izni istemini görür) ve her karedeBarcodeDetectorile çözümler. İkisinden biri eksiksecan('barcodeScan')'none'olur.- Derleme kodunuzu tarar: bir
scanBarcode(çağrısı Kamera'yı sizin için açar. Elle kapattıysanız kamera isteği başarısız olur ve şunu alırsınıznull. - Bir kullanıcı eyleminden başlatın (Tara düğmesi veya tarama ekranını açma), uygulama açılışında asla.
nulldeğerini elle giriş seçeneğiyle normal bir sonuç olarak ele alın.
appmintNative.scanCode appmintNative#
scanCode(opts?: { formats?: BarcodeFormat[]; manualInput?: boolean }): Promise<ScanCodeResult | null>
Örnek
Tam ekran bir tarayıcı açar ve ilk barkod veya QR koduyla çözülür - tek çağrı, çizecek bir şey yok.
Dönüş değeri: Promise<ScanCodeResult | null> - { value, format, type?, displayValue?, url?, wifi?, phone?, email?, sms?, geo?, contact? } veya kullanıcı tarayıcıyı kapattığında null. Diğer her şey Error ile reddedilir ve bunun code değeri 'unavailable' (burada tarayıcı yok - uygulamada Play hizmetleri eksik; web'de BarcodeDetector veya kamera yok), 'busy' (zaten bir tarama açık), 'camera-denied' (web: kamera izni reddedildi) veya 'failed' olur. Gereksinim: uygulamada hiçbir şey (Google'ın tarayıcısı kamera izni gerektirmez).
Bir Tara düğmesinden:
import { can, scanCode, haptic, toast } from '@/lib/appmintNative';
export function ScanButton({ onCode }: { onCode: (v: string) => void }) {
if (can('codeScanner') === 'none') return null; // nothing can scan here
return (
<button
onClick={async () => {
try {
const hit = await scanCode({ formats: ['qr_code'] });
if (!hit) return; // closed: a normal outcome
haptic('success');
onCode(hit.value);
} catch (e) {
const code = (e as { code?: string }).code;
toast(code === 'camera-denied' ? 'Allow the camera to scan.' : 'Scanning is not available right now.');
}
}}
>
Scan QR code
</button>
);
}Uygulamanın doldurduğu ayrıştırılmış parçaları kullanın (Wi-Fi, bağlantılar, kişiler …), Google'ın tarayıcısında elle yazılan bir yedekle:
import { scanCode } from '@/lib/appmintNative';
const hit = await scanCode({ manualInput: true });
if (hit?.wifi) console.log('Network', hit.wifi.ssid, hit.wifi.encryption);
else if (hit?.url) window.open(hit.url.url, '_blank');
else if (hit) console.log(hit.type ?? 'text', hit.value);Kendi alarm ekranınızdaki bir düğmeden erteleyin; alarmın ne zaman çaldığını bilmek için onAlarm kullanın.
import { useEffect, useState } from 'react';
import { onAlarm, snoozeAlarm, stopAlarm } from '@/lib/appmintNative';
export function RingingScreen() {
const [ringing, setRinging] = useState(false);
useEffect(() => onAlarm((e) => setRinging(e.action === 'open' && e.ringing === true)), []);
if (!ringing) return null;
return (
<div>
<button onClick={() => { if (snoozeAlarm('', 10)) setRinging(false); }}>Snooze 10 min</button>
<button onClick={() => { stopAlarm(); setRinging(false); }}>Stop</button>
</div>
);
}snoozeAlarm hiçbir şey çalmıyorsa false yanıtı verir, bu yüzden kullanıcıya ikinci bir çalma vaat etmeden önce kontrol edin.
import { snoozeAlarm } from '@/lib/appmintNative';
export function snoozeOrExplain(minutes = 0) {
return snoozeAlarm('', minutes)
? `Ringing again in ${minutes || 'a few'} minutes`
: 'No alarm is ringing right now.';
}Notlar:
- Uygulamada bu, Google'ın kod tarayıcısıdır (Play hizmetleri, kabuğun
AppMint.scanCodeüzerinden): otomatik yakınlaştırma, kamera istemi yok.typeşunlardan biridir:'url','wifi','text','phone','email','sms','geo','contact','calendar','isbn','product'veya'driver_license', ve eşleşen alan doldurulur.manualInputkullanıcının kodu yazmasına izin verir. - Web'de kendi tam ekran kamera katmanını çizer (kapat düğmesi, geri hareketi kapatır) ve
BarcodeDetectorile çözümler; orada yalnızcavalueveformatdoldurulur ve tarayıcı kamera izni ister. nullher zaman kullanıcının tarayıcıyı kapattığı anlamına gelir; diğer tüm sonuçlar bircodeile reddedilir, bu yüzden bir mesaj gösterincatch.- Sayfa yüklenirken değil, bir dokunuştan çağırın. Biliyorsanız
formatsdeğerlerini daraltın.
appmintNative.scheduleAlarm appmintNative#
scheduleAlarm(id, input) — @/lib/appmintNative
Örnek
Yapay zekayla oluşturulan bir uygulamadan gerçek bir çalar saat kurar: telefonun alarm ses düzeyinde çalar (sessiz moddan ve kullanıcı alarmlara izin verirse Rahatsız Etmeyin'den geçerek), Kapat / Ertele veya ringSeconds olana kadar çalmaya devam eder ve uygulamayı kilit ekranının üzerinde gösterir.
Döndürür: boolean, eşzamanlı olarak - ayarlandıysa true; web'de, iOS'ta veya alarmSupport() değeri 'ready' değilse false. Aynı kimlik eski bir alarmın yerini alır. Gerekenler: derleme yaparken Erişim adımında (4. Adım) 🔔 Alarm saati.
import { alarmSupport, scheduleAlarm } from '@/lib/appmintNative';
export function setWeekdayAlarm(hour: number, minute: number): string {
const support = alarmSupport();
if (support !== 'ready') return support; // show the matching fix-it step
const first = new Date();
first.setHours(hour, minute, 0, 0);
if (first.getTime() <= Date.now()) first.setDate(first.getDate() + 1);
const ok = scheduleAlarm('wake', {
at: first.getTime(),
title: 'Wake up',
repeat: 'weekdays',
days: [1, 2, 3, 4, 5], // ISO: Monday = 1 … Sunday = 7
snoozeMinutes: 9,
ringSeconds: 300,
});
return ok ? 'set' : 'failed';
}Alarmı yalnızca uygulama çalabiliyorsa sunun ve aksi halde neyin eksik olduğunu açıklayın:
import { alarmSupport, scheduleAlarm } from '@/lib/appmintNative';
const HELP: Record<string, string> = {
not_enabled: 'Alarm clock is switched off in this build.',
needs_exact_alarm: 'Allow "Alarms & reminders" for this app in Settings.',
needs_notification_permission: 'Allow notifications for this app in Settings.',
unavailable: 'Alarms work only in the Android app.',
};
export function setTimer(minutes: number): string {
const s = alarmSupport();
if (s !== 'ready') return HELP[s] ?? 'Alarm not available';
const ok = scheduleAlarm('timer', {
at: Date.now() + minutes * 60_000,
title: 'Timer done',
snoozeMinutes: 0, // hides the Snooze button
ringSeconds: 60,
});
return ok ? `Timer set for ${minutes} min` : 'Could not set the timer';
}Çalan bir alarmın nasıl bittiğine tepki verin ve kendi ekranınızdan durdurun:
import { useEffect, useState } from 'react';
import { onAlarm, stopAlarm, snoozeAlarm, isAlarmRinging } from '@/lib/appmintNative';
export function useAlarmScreen() {
const [ringing, setRinging] = useState(isAlarmRinging());
useEffect(() => onAlarm((e) => {
if (e.action === 'open') setRinging(!!e.ringing);
else if (e.action === 'dismiss' || e.action === 'snooze' || e.action === 'missed') setRinging(false);
else if (e.action === 'error') console.warn('Alarm error', e.error);
}), []);
return {
ringing,
stop: () => stopAlarm(), // '' = whichever alarm is ringing
snooze: () => snoozeAlarm('', 5), // ring again in 5 minutes
};
}Notlar: Yalnızca Android uygulaması. repeat günlük / haftalık / hafta içi / aylık / yıllık, yaz saati değişikliklerinde aynı saat dilimini korur. Köprüde cancelNotification(id) ile iptal edin; sonuçlar şu yoldan gelir onAlarm.
appmintNative.ScreenOrientation appmintNative#
ScreenOrientation.lock({ orientation }) / ScreenOrientation.unlock()
Örnek
Ekranı bir görünüm için dikey veya yatay yöne sabitler; Capacitor'ın ScreenOrientation eklentisi biçiminde - şununla aynı kod: lockOrientation().
Döndürür: ikisi için de Promise<void>. lock, Capacitor'ınki gibi, orientation bir kilit türü değilse veya platform kilidi reddederse reddeder. Gerekenler: hiçbir şey.
import { ScreenOrientation } from '@/lib/appmintNative';
import { useEffect } from 'react';
function GameBoard() {
useEffect(() => {
void ScreenOrientation.lock({ orientation: 'landscape' });
return () => { void ScreenOrientation.unlock(); }; // release when leaving
}, []);
return <canvas id="board" />;
}Yalnızca bir ekran için, kilide izin verildiğinde kilitleyin ve kullanıcının geri döndürmesine izin verin:
import { useEffect, useState } from 'react';
import { ScreenOrientation } from '@/lib/appmintNative';
export function ChartView() {
const [locked, setLocked] = useState(true);
useEffect(() => {
if (locked) void ScreenOrientation.lock({ orientation: 'landscape-primary' }); // any 'landscape*' value
else void ScreenOrientation.unlock();
return () => { void ScreenOrientation.unlock(); };
}, [locked]);
return <button onClick={() => setLocked((v) => !v)}>{locked ? 'Free rotation' : 'Lock landscape'}</button>;
}Kilidin tutup tutmadığını bilmeniz gerekiyorsa lockOrientation() işlevini doğrudan kullanın (kilitlemenin reddedildiği yerlerde, örneğin masaüstü tarayıcılarda false yanıtlar):
import { lockOrientation, toast } from '@/lib/appmintNative';
export async function openSignaturePad() {
const ok = await lockOrientation('landscape');
if (!ok) toast('Turn your phone sideways for more room');
}Notlar: orientation, Capacitor'ın kilit türlerinden biridir - 'any', 'natural', 'portrait', 'landscape', 'portrait-primary', 'portrait-secondary', 'landscape-primary', 'landscape-secondary' - ve verildiği gibi uygulanır. Masaüstü tarayıcılar kilitleri reddeder; telefon tarayıcıları genellikle yalnızca tam ekranda izin verir. lockOrientation(to) true/false döndürür. Kullanıcıya kilitli görünümden çıkış yolu verin.
appmintNative.screenReader appmintNative#
screenReader.isEnabled() / screenReader.speak(text, lang?) / screenReader.onChange(cb)
Örnek
Uygulamaya TalkBack'in açık olup olmadığını söyler ve kısa mesajları duyurur.
Dönüş değeri: isEnabled() → Promise<boolean> (web'de false - web API'si yoktur). speak() → Promise<void>. onChange(cb) → bir abonelikten çıkma fonksiyonu; cb(enabled) ekran okuyucu açıldığında veya kapatıldığında çalışır (yalnızca uygulamada). Gereksinim: hiçbir şey.
import { screenReader } from '@/lib/appmintNative';
import { useEffect, useState } from 'react';
function useScreenReader() {
const [on, setOn] = useState(false);
useEffect(() => {
void screenReader.isEnabled().then(setOn);
return screenReader.onChange(setOn);
}, []);
return on; // e.g. skip auto-advancing carousels when true
}
async function onPaid() {
await screenReader.speak('Payment complete', 'en');
}Bir sonucu yalnızca ekran okuyucu kullananlara duyurun, görsel bildirimleri diğer herkese bırakın:
import { screenReader, toast } from '@/lib/appmintNative';
export async function notifySaved() {
if (await screenReader.isEnabled()) await screenReader.speak('Note saved', 'en');
else toast('Note saved');
}TalkBack açıkken bir animasyonu kaldırın ve bileşen gidince dinlemeyi bırakın:
import { useEffect, useState } from 'react';
import { screenReader } from '@/lib/appmintNative';
export function Banner({ text }: { text: string }) {
const [reduce, setReduce] = useState(false);
useEffect(() => {
let alive = true;
void screenReader.isEnabled().then((v) => { if (alive) setReduce(v); });
const off = screenReader.onChange(setReduce); // returns an unsubscribe function
return () => { alive = false; off(); };
}, []);
return <div className={reduce ? '' : 'animate-pulse'} role="status" aria-live="polite">{text}</div>;
}Notlar: Uygulamada speak ScreenReader eklentisinden geçer; web'de (veya eklenti başarısız olursa) metin okumayla seslendirilir. Çoğu duyuru için iyi işaretleme (aria-label, aria-live bölgeleri) tercih edin; kısa, önemli değişiklikler için speak kullanın.
appmintNative.searchContacts appmintNative#
searchContacts(query: string, limit = 50): Promise<Contact[]>
Örnek
Bir arama sözcüğüyle eşleşen kişileri bulur. Yalnızca uygulama.
Döndürür: Promise<Contact[]> - hiçbir şey eşleşmezse, erişim reddedilirse veya Kişiler kapalıysa boş. Gerekenler: listContacts ile aynı: Adım 4'te (Erişim) Kişiler (kodunuz bunu çağırdığında derleme açar) ve ilk kullanımda kullanıcının izni.
Yazarken küçük bir gecikmeli arama kutusu.
import { searchContacts, can } from '@/lib/appmintNative';
let timer: ReturnType<typeof setTimeout> | undefined;
let latest = 0;
export function onSearchInput(text: string, show: (names: string[]) => void) {
if (can('contactList') === 'none') return;
clearTimeout(timer);
timer = setTimeout(async () => {
const mine = ++latest;
const found = await searchContacts(text, 20).catch(() => []);
if (mine !== latest) return; // a newer search already started
show(found.map((c) => c.name ?? ''));
}, 250);
}Kişiler kullanılamadığında ve hiçbir şey eşleşmediğinde net bir durum gösterin:
import { useState } from 'react';
import { can, searchContacts, type Contact } from '@/lib/appmintNative';
export function ContactPicker({ onPick }: { onPick: (c: Contact) => void }) {
const [rows, setRows] = useState<Contact[] | null>(null);
if (can('contactList') === 'none') return <p>Choosing a contact works only in the Android app.</p>;
return (
<div>
<input placeholder="Search contacts" onChange={async (e) => setRows(await searchContacts(e.target.value, 10))} />
{rows && rows.length === 0 && <p>No contacts match, or access was not allowed.</p>}
{rows?.map((c) => <button key={c.id ?? c.name} onClick={() => onPick(c)}>{c.name} {c.phones?.[0]}</button>)}
</div>
);
}Bir kişiyi ada göre bulun, sonra ona önceden doldurulmuş bir mesaj yazın:
import { searchContacts, composeSms } from '@/lib/appmintNative';
export async function textPerson(name: string, message: string): Promise<boolean> {
const [first] = await searchContacts(name, 1);
const number = first?.phones?.[0];
return number ? composeSms(number, message) : false; // the user presses send
}Notlar: Eşleşme, telefonun kişi aramasıyla adlar üzerinde yapılır. Her kişi { id?, name?, phones?: string[], emails?: string[], photoUri? } biçimindedir. Web'de can('contactList') değeri 'none' olur - adres defterini okuyacak bir web API'si yoktur. Kişiler kişisel veridir: Play Veri güvenliği formunuzda ve gizlilik politikanızda belirtin.
appmintNative.SecureKeyError appmintNative#
class SecureKeyError extends Error { code } — from '@/lib/appmintNative'
Örnek
secureKeys işleminin fırlattığı hata. code değeri, üzerinde dallanabileceğiniz kısa bir sözcüktür; uygulamada ve web'de aynıdır.
Döndürür: name === 'SecureKeyError' ve code ile bir Error: 'no-such-key', 'auth-required' (kilidi açın ve yeniden deneyin), 'key-invalidated' (biyometri veya ekran kilidi değişti - veri gitti), 'strongbox-unavailable', 'disabled' (Güvenli Anahtarlar bu derlemede yok / Web Crypto yok), 'bad-alias', 'bad-payload' (geçerli base64 değil veya çok kısa), 'too-large' (Android'de tek çağrıda 4 MB üzeri), 'foreign-origin' (başka bir siteden bir sayfa istedi), 'failed' (diğer her şey). Gerekenler: hiçbir şey.
import { secureKeys, SecureKeyError, unlock } from '@/lib/appmintNative';
export async function openSecret(sealed: string): Promise<string | null> {
try {
return await secureKeys.decrypt('vault', sealed);
} catch (e) {
if (!(e instanceof SecureKeyError)) throw e;
switch (e.code) {
case 'auth-required':
if (await unlock('Open vault')) return secureKeys.decrypt('vault', sealed);
return null;
case 'no-such-key':
case 'key-invalidated':
alert('This secret can no longer be opened.');
return null;
case 'disabled':
alert('Secure storage is not available in this app.');
return null;
default:
alert('Could not open it: ' + e.code);
return null;
}
}
}Kodu kullanıcı için tek yerde kısa bir mesaja çevirin:
import { SecureKeyError } from '@/lib/appmintNative';
export function secureKeyMessage(e: unknown): string {
if (!(e instanceof SecureKeyError)) return 'Something went wrong';
switch (e.code) {
case 'auth-required': return 'Unlock your phone to continue';
case 'key-invalidated': return 'Your screen lock changed, so this data was erased';
case 'too-large': return 'That is too big to protect in one piece';
case 'foreign-origin': return 'This page is not allowed to use secure storage';
default: return 'Secure storage failed (' + e.code + ')';
}
}Kurcalanmış veya yanlış bir yükü normal bir sonuç olarak değerlendirip temizleyin, ancak eksik bir anahtarı gösterdiğiniz bir hata olarak tutun:
import { secureKeys, SecureKeyError } from '@/lib/appmintNative';
export async function readNote(sealed: string): Promise<string | null> {
try {
return await secureKeys.decrypt('notes', sealed);
} catch (e) {
if (e instanceof SecureKeyError && (e.code === 'bad-payload' || e.code === 'failed')) {
localStorage.removeItem('note'); // the stored text cannot be read any more
return null;
}
throw e;
}
}Notlar: e.message, code veya "code: detail" olur; mesaja göre değil e.code değerine göre dallanın. Platformların kendi hata sözcükleri bu kodlara çevrilir ve özgün metin mesajda saklanır. Değişmiş veya yabancı bir yük webde 'bad-payload', uygulamada 'failed' olur; uygulama bunu diğer şifre hatalarından ayırt edemez.
appmintNative.secureKeys appmintNative#
secureKeys — generate / encrypt / decrypt / remove / has / list / isHardwareBacked
Örnek
Cihazın tuttuğu ve uygulamanın asla dışa aktaramayacağı şifreleme anahtarları: uygulamada Android Keystore, web'de dışa aktarılamaz bir Web Crypto anahtarı. Anahtarı kaldırmak tek gerçek "güvenli silme"dir.
Döndürür: hepsi Promise. generate(alias, {requireAuth?, authValiditySeconds?}) → {hardwareBacked}; encrypt(alias, text) → base64 dizesi; decrypt(alias, base64) → metin; remove(alias) / has(alias) / isHardwareBacked(alias) → boolean; list() → string[]. generate, encrypt ve decrypt, code ile SecureKeyError fırlatır. Gerekenler: derleme yaparken 4. Adım'da (Erişim) Güvenli Anahtarlar (şifreleme).
import { secureKeys, SecureKeyError, can } from '@/lib/appmintNative';
export async function saveDiary(text: string) {
if (can('secureKeys') === 'none') throw new Error('Secure storage is not available here');
if (!(await secureKeys.has('diary'))) {
const { hardwareBacked } = await secureKeys.generate('diary', { requireAuth: true, authValiditySeconds: 60 });
console.log(hardwareBacked ? 'Key in secure hardware' : 'Key in software');
}
const sealed = await secureKeys.encrypt('diary', text); // strings in, base64 out
localStorage.setItem('diary', sealed);
}
export async function readDiary(): Promise<string | null> {
const sealed = localStorage.getItem('diary');
if (!sealed) return null;
try {
return await secureKeys.decrypt('diary', sealed);
} catch (e) {
if (e instanceof SecureKeyError && e.code === 'key-invalidated') return null; // data is gone
throw e;
}
}Gerçekten silin - bundan sonra kimse mühürlü veriyi okuyamaz:
import { secureKeys } from '@/lib/appmintNative';
export async function wipeDiary() {
await secureKeys.remove('diary');
localStorage.removeItem('diary');
}Uygulamanın tuttuğu anahtarları listeleyin ve her birinin donanımla korunup korunmadığını gösterin (web her zaman şunu yanıtlar false):
import { secureKeys, can } from '@/lib/appmintNative';
export async function describeKeys(): Promise<string[]> {
if (can('secureKeys') === 'none') return [];
const aliases = await secureKeys.list();
return Promise.all(aliases.map(async (alias) =>
`${alias}: ${(await secureKeys.isHardwareBacked(alias)) ? 'secure hardware' : 'software'}`));
}Notlar: can('secureKeys'), uygulamada 'native', tarayıcıda 'web' olur; orada isHardwareBacked() her zaman false döndürür - orada asla "donanım" demeyin. Bir requireAuth anahtarı: uygulamada encrypt/decrypt, kilit açma süresi geçtikten sonra hiçbir şey göstermeden auth-required hatası fırlatır - unlock() çağırıp yeniden deneyin; webde çalışma zamanı unlock() öğesini kendisi gösterir. Bir seferde tek kaydı şifreleyin (uygulama bir çağrıyı 4 MB ile sınırlar). Takma adlar: harfler, rakamlar, _ - ., en çok 64.
appmintNative.sendSms appmintNative#
sendSms(phoneNumber: string, message: string): Promise<boolean>
Örnek
Onay ekranı olmadan doğrudan SMS gönderir. Yalnızca Android uygulaması.
Döndürür: Promise<boolean> - telefon mesajı gönderildi olarak bildirdiğinde true; başarısız olduğunda, SMS etkin olmadığında veya kullanıcı izni reddettiğinde false. Gerekenler: 4. Adım'daki (Erişim) SMS (okuma ve gönderme) anahtarı - kodunuz bunu çağırdığında derleme onu açar - ve ilk kullanımda kullanıcının izni.
Bir uyarı gönderin ve kullanıcıya ne olduğunu söyleyin.
import { sendSms, can } from '@/lib/appmintNative';
export async function sendAlert(to: string, text: string): Promise<string> {
if (can('smsSend') === 'none') return 'Sending SMS works only in the Android app.';
try {
const ok = await sendSms(to, text);
return ok ? 'Alert sent' : 'The SMS could not be sent';
} catch {
return 'No answer from the phone after 3 minutes';
}
}Önce onay isteyin, çünkü mesaj iptal edecek bir ekran olmadan gider:
import { sendSms, composeSms, can, toast } from '@/lib/appmintNative';
export async function sendCheckIn(to: string) {
if (can('smsSend') === 'none') { composeSms(to, 'I am safe'); return; } // the user presses send
if (!window.confirm('Send "I am safe" to ' + to + '? This may cost money.')) return;
toast((await sendSms(to, 'I am safe')) ? 'Sent' : 'Could not send. Check the SMS permission.');
}Aynı uyarıyı birkaç kişiye tek tek gönderin ve gidenleri sayın:
import { sendSms } from '@/lib/appmintNative';
export async function alertAll(numbers: string[], text: string) {
let sent = 0;
for (const n of numbers) {
if (await sendSms(n, text)) sent += 1; // waits for the phone's answer for each one
}
return { sent, failed: numbers.length - sent };
}Notlar: Sarmalayıcı İLK yanıtta çözülür; bu sent (mesaj telefondan çıktı) olur, delivered değil. Teslim raporları için appmint:sms olayıyla ham WebToApk.sendSms kullanın. can('smsSend') web'de 'none' değerindedir (hiçbir tarayıcı SMS gönderemez). Her SMS kullanıcıya para kazandırmayabilir, maliyet getirebilir. SMS, Google Play'in kısıtlı bir iznidir: Play Console'da onaylı bir İzin Beyanı gerekir; bu yüzden kullanıcı gönder'e basabiliyorsa composeSms() tercih edin.
appmintNative.setInAppPresentation appmintNative#
import { setInAppPresentation } from '@/lib/appmintNative'
Örnek
Uygulama içi bir mesajı kimin çizeceğini seçer (oluşturucunun kampanyaları): uygulama ('auto', varsayılan: oluşturucunun afiş, kart, panel veya tam ekran tasarımı) veya kendi arayüzünüz ('none', şu yoluyla onInAppMessage).
Dönüş değeri: boolean, eşzamanlı olarak: kaydedildiğinde true (telefonda kalır). Web'de ve AppMint Push olmadan her zaman false. Gereksinim: Android: derleme sırasında Entegrasyon adımında (Adım 3) açık AppMint Push.
import { setInAppPresentation } from '@/lib/appmintNative';
// The app draws its own messages from now on.
setInAppPresentation('none');Bir ayarlar anahtarı:
import { setInAppPresentation } from '@/lib/appmintNative';
export function onNativePopupsChange(on: boolean) {
if (!setInAppPresentation(on ? 'auto' : 'none')) console.info('In-app messages are not available here');
}Bir ekran açıkken devralın, sonra geri verin:
import { useEffect } from 'react';
import { setInAppPresentation } from '@/lib/appmintNative';
export function useQuietScreen() {
useEffect(() => {
setInAppPresentation('none');
return () => { setInAppPresentation('auto'); };
}, []);
}Notlar: kodunuza verilen bir mesaj gösterilmiş sayılır. 'none' ayarı, ekranda şu an açık olan bir mesajı da kapatır.
appmintNative.setMusicVolume appmintNative#
setMusicVolume(percent) — @/lib/appmintNative
Örnek
Uygulamanın döngüdeki arka plan müziğinin düzeyini 0 ile 100 arasında ayarlar (sizin için yuvarlanır ve sınırlanır).
Dönüş değeri: void. Gereksinim: derleme sırasında Adım 3'te (Entegrasyon) Arka Plan Müziği altında seçilmiş bir dosya. musicVolume() şu olduğunda hiçbir şey yapmaz -1.
Uygulama düzeyi bir sonraki açılış için saklar, bu yüzden tek ihtiyacınız bir sessize alma anahtarıdır:
import { musicVolume, setMusicVolume } from '@/lib/appmintNative';
export const hasMusic = () => musicVolume() >= 0;
export const toggleMute = () => setMusicVolume(musicVolume() > 0 ? 0 : 40); // remembered by the appYalnızca uygulama müzikle birlikte geldiğinde görünen bir ses kaydırıcısı:
import { useState } from 'react';
import { musicVolume, setMusicVolume } from '@/lib/appmintNative';
export function MusicSlider() {
const [level, setLevel] = useState(musicVolume());
if (level < 0) return null; // no music in this app: draw no control
return (
<input type="range" min={0} max={100} value={level}
onChange={(e) => { const v = Number(e.target.value); setLevel(v); setMusicVolume(v); }} />
);
}Bir video oynarken müziği kısın ve sonra geri koyun:
import { musicVolume, setMusicVolume } from '@/lib/appmintNative';
let before = -1;
export function duckMusic() { before = musicVolume(); if (before > 0) setMusicVolume(10); }
export function restoreMusic() { if (before >= 0) setMusicVolume(before); before = -1; }Notlar: 0 müziği sessiz yapar ama çalmaya devam eder. Seviye telefona kaydedilir (kullanıcı uygulamanın verilerini temizleyene kadar). Yalnızca Android uygulaması; web'de hiçbir şey olmaz.
appmintNative.setPushPresentation appmintNative#
import { setPushPresentation } from '@/lib/appmintNative'
Örnek
Uygulama açıkken uygulama içi stille (banner, açılır pencere, alt sayfa veya tam ekran) gönderdiğiniz bir AppMint Push mesajını kimin çizeceğini seçer: uygulama kabuğu ('auto', varsayılan) veya kendi kodunuz ('none').
Dönüş değeri: boolean, eşzamanlı olarak - mod kaydedildiğinde true. Web'de her zaman false. Gereksinim: Android: derleme sırasında Entegrasyon adımında (Adım 3) açık AppMint Push.
import { useEffect } from 'react';
import { setPushPresentation, pushInbox } from '@/lib/appmintNative';
// Option A — keep the shell's banner, and never draw a second one on top of it.
export function usePushBanner(show: (title: string) => void) {
useEffect(() => {
const onPush = (e: Event) => {
const d = (e as CustomEvent).detail;
// { type: 'received', message, presented } — presented = the shell already showed it
if (d.type === 'received' && !d.presented) show(d.message.title);
};
window.addEventListener('appmint:push', onPush);
return () => window.removeEventListener('appmint:push', onPush);
}, [show]);
}
// Option B — the app draws every message itself.
export function takeOverPushBanners() {
setPushPresentation('none'); // saved on the phone; 'auto' gives it back to the shell
return pushInbox();
}Cevabı kontrol edin; çünkü web'de veya AppMint Push kapalıyken false olur:
import { setPushPresentation } from '@/lib/appmintNative';
export function useOwnBanners(on: boolean): boolean {
const saved = setPushPresentation(on ? 'none' : 'auto');
if (!saved) console.info('Push banners are not controllable here');
return saved;
}Her mesaj için kendi afişinizi çizin ve gelen kutusunu uyumlu tutun:
import { useEffect, useState } from 'react';
import { setPushPresentation, markPushRead, pushInbox, type PushMessage } from '@/lib/appmintNative';
export function useCustomBanner() {
const [msg, setMsg] = useState<PushMessage | null>(null);
useEffect(() => {
setPushPresentation('none'); // the shell draws nothing; the page does
const onPush = (e: Event) => {
const d = (e as CustomEvent).detail;
if (d.type === 'received') setMsg(d.message);
if (d.type === 'revoked') setMsg((m) => (m?.id === d.id ? null : m));
};
window.addEventListener('appmint:push', onPush);
return () => { window.removeEventListener('appmint:push', onPush); setPushPresentation('auto'); };
}, []);
return { msg, unread: pushInbox().filter((m) => !m.read).length, dismiss: () => { if (msg) markPushRead(msg.id); setMsg(null); } };
}Notlar: gelen kutusu ve appmint:push olayı her iki kipte de aynı çalışır. 'none' ayarı, kabuğun şu an gösterdiği bir mesajı da kapatır.
appmintNative.setPushTag appmintNative#
import { setPushTag } from '@/lib/appmintNative'
Örnek
Bu kurulumda tek bir AppMint Push etiketi ayarlar. Gönderim merkezi bir etikete sahip herkese gönderebilir ve bir etiket otomasyonu, etiket ayarlandıktan belirli bir süre sonra mesaj gönderebilir. '' etiketi kaldırır.
Dönüş değeri: boolean, eşzamanlı olarak - kaydedildiğinde true; anahtar 1-32 harf, rakam veya _ değilse, değer 128 karakteri aşıyorsa ya da yükleme 50 etiketi geçecekse false. Web'de her zaman false. Gereksinim: Android: derleme sırasında Entegrasyon adımında (Adım 3) açık AppMint Push; etiket hedefleme ve otomasyonlar 30 Eylül 2026'dan itibaren derlenen uygulamalara ulaşır.
import { useEffect } from 'react';
import { setPushTag } from '@/lib/appmintNative';
// Abandoned cart: a tag automation on "cart" (say 60 minutes) reminds only phones whose cart is
// still open when the time is up. Setting 'open' again does not restart the clock.
export function useCartPushTag(itemCount: number) {
const hasItems = itemCount > 0;
useEffect(() => {
setPushTag('cart', hasItems ? 'open' : '');
}, [hasItems]);
}
export function onCheckoutComplete() {
setPushTag('cart', '');
}Sonucu kontrol edin: false etiketin reddedildiğini (geçersiz anahtar, 128 karakteri aşan değer, 50'den fazla etiket) ya da bunun web olduğunu gösterir; bu yüzden durumun kaydını kendiniz tutun.
import { setPushTag } from '@/lib/appmintNative';
export function markNewsletterChoice(subscribed: boolean) {
const saved = setPushTag('newsletter', subscribed ? 'yes' : '');
if (!saved) console.warn('Push tag not saved (web, Push off, or invalid key/value)');
return saved;
}Bir telefonu bir kilometre taşına ulaştığında etiketleyin; böylece gönderim merkezi o seviyedeki herkesi hedefleyebilir.
import { useEffect } from 'react';
import { setPushTag } from '@/lib/appmintNative';
export function useLevelTag(level: number) {
useEffect(() => {
if (level >= 10) setPushTag('veteran', 'yes');
setPushTag('level', String(level));
}, [level]);
}Notlar: etiketler yüklemeye aittir, oturum açmış kullanıcıya değil. Bir etikete asla özel veri koymayın.
appmintNative.setPushTags appmintNative#
import { setPushTags } from '@/lib/appmintNative'
Örnek
Birkaç AppMint Push etiketini tek değişiklikte ayarlar; '' değeri o etiketi kaldırır, adını vermediğiniz etiketler korunur.
Döndürür: boolean eşzamanlı olarak - kaydedildiğinde true; herhangi bir anahtar veya değer setPushTag kurallarını bozduğunda false (o zaman hiçbir şey kaydedilmez). Webde her zaman false. Gerekenler: Android: derleme sırasında Entegre et adımında (Adım 3) AppMint Push açık olmalı; etiket hedefleme ve otomasyonlar 30 Eyl 2026'dan itibaren derlenen uygulamalara ulaşır.
import { setPushTags, getPushTags, setPushUser } from '@/lib/appmintNative';
export function tagProfile(profile: { plan: 'free' | 'pro'; level: number; team?: string }) {
return setPushTags({
plan: profile.plan,
level: String(profile.level), // values are text
team: profile.team ?? '', // '' removes the tag
});
}
// Sign-out: clear every tag and the user id.
export function signOutOfPush() {
const cleared = Object.fromEntries(Object.keys(getPushTags()).map((k) => [k, '']));
setPushTags(cleared);
setPushUser('');
}Kullanıcı ayarlarını kaydettiğinde bir telefonu seçtiği dil ve konularla etiketleyin ve her etiketin kabul edildiğini kontrol edin.
import { setPushTags } from '@/lib/appmintNative';
export function savePreferences(prefs: { lang: string; topics: string[] }) {
const ok = setPushTags({
lang: prefs.lang,
topics: prefs.topics.join(',').slice(0, 128), // a value is at most 128 characters
onboarded: 'yes',
});
if (!ok) console.warn('Push tags not saved: nothing was changed');
return ok;
}Etiketleri bir React efektinde oturum açmış hesapla uyumlu tutun, artık geçerli olmayanları kaldırın.
import { useEffect } from 'react';
import { setPushTags, setPushUser } from '@/lib/appmintNative';
export function usePushProfile(user: { id: string; plan: 'free' | 'pro'; trialEnds?: string } | null) {
useEffect(() => {
if (!user) return;
setPushUser(user.id);
setPushTags({ plan: user.plan, trial: user.plan === 'pro' ? '' : (user.trialEnds ?? '') });
}, [user?.id, user?.plan, user?.trialEnds]);
}Notlar: bir çağrının hepsi kaydedilir ya da hiçbiri kaydedilmez.
appmintNative.setPushUser appmintNative#
import { setPushUser } from '@/lib/appmintNative'
Örnek
AppMint Push'a, bu yüklemede kullanıcılarınızdan hangisinin oturum açtığını bildirir; böylece gönderim merkezi o tek kullanıcıya mesaj gönderebilir. Oturum açtıktan sonra kendi sabit hesap kimliğinizi, oturumu kapatırken '' değerini iletin.
Dönüş değeri: boolean, eşzamanlı olarak - kimlik kaydedildiğinde true; harf, rakam ve _ @ . : + - dışında bir şey içeren kimlik için (128'e kadar) false. Web'de her zaman false. Gereksinim: Android: derleme sırasında Entegrasyon adımında (Adım 3) açık AppMint Push; bir kullanıcıya göndermek 30 Eylül 2026'dan itibaren derlenen uygulamalara ulaşır.
import { useEffect } from 'react';
import { setPushUser, getPushUser } from '@/lib/appmintNative';
import { supabase } from '@/lib/supabase';
// Keeps AppMint Push in step with the signed-in account: the auth user id, never the email.
export function usePushUser() {
useEffect(() => {
const { data } = supabase.auth.onAuthStateChange((_event, session) => {
const id = session?.user.id ?? '';
if (getPushUser() !== id) setPushUser(id);
});
return () => data.subscription.unsubscribe();
}, []);
}Reddedilen bir kimliği işleyin: setPushUser, bir e-posta adresi veya izin verilen kümenin dışında karakterler içeren her kimlik için false yanıtını verir, bu yüzden bunun yerine sabit bir kimlik gönderin.
import { setPushUser } from '@/lib/appmintNative';
export function linkPushToAccount(account: { id: string; email: string }) {
const ok = setPushUser(account.id); // the id, never the email
if (!ok) console.warn('Push user id not saved (web, Push off, or invalid characters)');
return ok;
}Kullanıcı oturumu kapattığında kimliği, düz bir tıklama işleyicisinde temizleyin.
import { setPushUser, getPushUser } from '@/lib/appmintNative';
export function SignOutButton({ signOut }: { signOut: () => Promise<void> }) {
return (
<button
onClick={async () => {
await signOut();
if (getPushUser()) setPushUser(''); // this install stops receiving "send to this user" messages
}}
>
Sign out
</button>
);
}Tek bir oturum açma adımı kullanıcıyı ve planını birlikte tanımlasın diye bir etiketle birleştirin.
import { setPushUser, setPushTag } from '@/lib/appmintNative';
export function onSignedIn(userId: string, plan: 'free' | 'pro') {
setPushUser(userId);
setPushTag('plan', plan);
}Notlar: kimlik kuruluma aittir, bu yüzden oturum kapatma onu temizlemelidir. Etiketleri temizlemez - kullanıcıya özel olanlar için '' değerleriyle setPushTags çağırın.
appmintNative.setStatusBar appmintNative#
setStatusBar(backgroundColor: string | null, style?: 'light' | 'dark' | null): void
Örnek
Sistem durum çubuğunu ekrana uyacak şekilde boyar ve onun için açık veya koyu simgeler seçer.
Döndürür: hiçbir şey. Gerekenler: hiçbir şey.
style SİMGELERİN rengidir: 'light' = koyu çubuk için açık renkli simgeler (varsayılan), 'dark' = açık çubuk için koyu simgeler. Açılışta uygulamanın --bg değeriyle bir kez, farklı arka planlı her ekranda yeniden çağırın:
import { useEffect } from 'react';
import { setStatusBar } from '@/lib/appmintNative';
export function App() {
useEffect(() => { setStatusBar('#0B0F1A', 'light'); }, []); // dark app, light icons
return <main>{/* … */}</main>;
}
export function PhotoViewer() {
useEffect(() => {
setStatusBar('#000000', 'light');
return () => setStatusBar('#0B0F1A', 'light'); // restore on leave
}, []);
return null;
}Açık renkli bir ekran:
import { setStatusBar } from '@/lib/appmintNative';
setStatusBar('#FFFFFF', 'dark');Capacitor biçimli form (stil ARKA PLANDAN adlandırılır: 'DARK' = açık simgeler):
import { StatusBar } from '@/lib/appmintNative';
await StatusBar.setBackgroundColor({ color: '#0B0F1A' });
await StatusBar.setStyle({ style: 'DARK' });Notlar:
- Android uygulamasında kabuk çubuğu kendisi boyar, bu Android 15 uçtan uca düzende de çalışır; ayrıştırılamayan bir renk yok sayılır -
#RRGGBBverin. Yalnızca stil içeren bir çağrı (backgroundColor = null) bu sayfanın en son ayarladığı rengi korur ve simgeleri çevirir; sayfa henüz renk ayarlamadıysa simgeleri StatusBar eklentisiyle çevirir ve çubuk derleme sırasında seçilen rengi korur. - Webde
<meta name="theme-color">ayarlar; simge rengini tarayıcı kendisi seçer.
appmintNative.setVolume appmintNative#
setVolume(stream, percent, showUi?) — @/lib/appmintNative
Örnek
Telefonun kendi ses düzeyini tek bir akış için 0-100 arasında ayarlar. showUi, telefonun ses kaydırıcısını hareket ederken gösterir.
Döndürür: { ok, stream?, current?, max?, percent?, error? } - hata unknown_stream, bad_percent, fixed_volume, dnd_access_required (Rahatsız Etmeyin zil / bildirimi engeller - requestDndAccess() çağırın), muted (Rahatsız Etmeyin "Tam sessizlik" akışı susturur, bu yüzden seviye değişmedi), not_applied, foreign_origin, failed veya web'de unavailable olur. Gerekenler: medya, alarm ve arama için hiçbir şey.
import { useState } from 'react';
import { getVolume, setVolume, requestDndAccess } from '@/lib/appmintNative';
export function RingVolume() {
const [level, setLevel] = useState(() => getVolume('ring')?.percent ?? null);
if (level === null) return null; // web: no phone volume to set
return (
<input
type="range" min={0} max={100} value={level}
onChange={(e) => {
const r = setVolume('ring', Number(e.target.value));
if (r.ok) setLevel(r.percent ?? level);
else if (r.error === 'dnd_access_required') requestDndAccess();
}}
/>
);
}Medya ses düzeyini donanım tuşlarını izleyen bir kaydırıcı olarak gösterin, şunu kullanarak onVolumeChange.
import { useEffect, useState } from 'react';
import { getVolume, setVolume, onVolumeChange } from '@/lib/appmintNative';
export function MediaVolume() {
const [pct, setPct] = useState<number | null>(() => getVolume('media')?.percent ?? null);
useEffect(() => onVolumeChange((l) => { if (l.stream === 'media') setPct(l.percent); }), []);
if (pct === null) return null; // web: no phone volume
return <input type="range" min={0} max={100} value={pct} onChange={(e) => setVolume('media', Number(e.target.value))} />;
}Bir alarm ekranı çalmadan önce alarm akışının sesini yükseltin ve neden değişmediğini bildirin.
import { setVolume } from '@/lib/appmintNative';
export function louderAlarm() {
const r = setVolume('alarm', 90, true); // true shows the phone's volume slider
if (!r.ok) {
switch (r.error) {
case 'fixed_volume': return 'This phone has a fixed volume.';
case 'unavailable': return 'Volume control needs the Android app.';
default: return `Could not change the volume (${r.error}).`;
}
}
return `Alarm volume is now ${r.percent}%`;
}appmintNative.sheet appmintNative#
sheet(opts: { title?, content, dismissible? }): { close(), element, closed }
Örnek
Gerçek panel davranışıyla bir modal alt panel açar - bir filtre paneli, bir yazma alanı veya bir ayrıntı önizlemesi için.
Döndürür: bir tutamaç: close() kapatır, element gövde öğesidir (doldurun veya güncelleyin), closed sayfa nasıl kapanırsa kapansın kapandığında çözülen bir Promise<void> nesnesidir. Gerekenler: hiçbir şey.
Seçenekler: title (isteğe bağlı başlık), content - bir öğe, düz metin (HTML değil) veya gövdeyi dolduran bir (host) => { … } işlevi; dismissible (varsayılan true: arka plana dokunmak, aşağı sürüklemek, Escape ve geri hareketi onu kapatır).
Düz DOM içeriği:
import { sheet } from '@/lib/appmintNative';
const h = sheet({
title: 'About this list',
content: (host) => {
const p = document.createElement('p');
p.textContent = 'Items sync to all your devices.';
host.appendChild(p);
},
});
await h.closed;React içeriği, bir portal ile panele işlenir:
import { useState } from 'react';
import { createPortal } from 'react-dom';
import { sheet } from '@/lib/appmintNative';
export function FiltersButton() {
const [host, setHost] = useState<HTMLElement | null>(null);
const [close, setClose] = useState<() => void>(() => () => {});
const open = () => {
const h = sheet({ title: 'Filters', content: () => {} });
setHost(h.element);
setClose(() => h.close);
void h.closed.then(() => setHost(null)); // unmount the portal after any close
};
return (
<>
<button onClick={open}>Filters</button>
{host && createPortal(
<form onSubmit={(e) => { e.preventDefault(); close(); }}>
<label><input type="checkbox" name="open" /> Open now</label>
<button type="submit">Apply</button>
</form>,
host,
)}
</>
);
}Kullanıcı seçene kadar açık kalan bir sayfa; dismissible: false ve closed promise'i kullanılır.
import { sheet } from '@/lib/appmintNative';
export async function confirmDelete(name: string): Promise<boolean> {
let answer = false;
const h = sheet({
title: 'Delete this item?',
dismissible: false, // only the buttons below close it
content: (host) => {
const p = document.createElement('p');
p.textContent = `"${name}" will be removed.`;
const yes = document.createElement('button');
yes.textContent = 'Delete';
yes.onclick = () => { answer = true; h.close(); };
const no = document.createElement('button');
no.textContent = 'Keep it';
no.onclick = () => h.close();
host.append(p, yes, no);
},
});
await h.closed; // resolves however the sheet closed
return answer;
}Notlar:
- Uygulamanın token'larıyla (
--surface,--ink,--muted,--border), güvenli alanın üzerinde, açılışta hafif bir dokunsal geri bildirimle çizilir. - Aşağı sürüklemek, 120 px'den fazla sürüklendiğinde veya hızla çekildiğinde kapatır; üstte değilse önce gövde kayar.
dismissible: falseile onu yalnızca sizin kendiclose()çağrınız kapatır - kullanıcıya bir düğme verin.- Düz bir eylem listesi için şunu kullanın
actionSheet().
appmintNative.shortcuts appmintNative#
shortcuts.set(items: { id?, title, route }[]) / shortcuts.clear()
Örnek
Uygulama simgesinin uzun basma menüsüne kısayollar ekler (Android dinamik kısayolları). Dokunuş, uygulamayı o yolda açar.
Döndürür: set(), Promise<boolean> döndürür - kısayollar sisteme verildiğinde true, hiç olmayan yerde (web) false. clear(), Promise<void> döndürür. Gerekenler: hiçbir şey.
Bunları başlangıçta bir kez ayarlayın ve açılış bağlantısına göre yönlendirin:
import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { can, shortcuts, launchUrl } from '@/lib/appmintNative';
export function useAppShortcuts() {
const navigate = useNavigate();
useEffect(() => {
if (can('shortcuts') !== 'none') {
void shortcuts.set([
{ id: 'new', title: 'New note', route: '/new' },
{ id: 'search', title: 'Search', route: '/search' },
]);
}
void launchUrl().then((u) => { if (u) navigate(new URL(u).pathname); });
}, [navigate]);
}Bunları kaldırın (örneğin çıkış yaptıktan sonra):
import { shortcuts } from '@/lib/appmintNative';
await shortcuts.clear();Uygulama zaten çalışırken bir kısayola dokunulduğunda eşleşen ekranı açın, şununla onDeepLink.
import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { onDeepLink } from '@/lib/appmintNative';
export function useShortcutRouting() {
const navigate = useNavigate();
useEffect(() => onDeepLink((url) => navigate(new URL(url).pathname)), [navigate]);
}Kısayolları yalnızca oturum açmış bir kullanıcı için gösterin ve set() işlevinin şunu yanıtladığı web'i ele alın false.
import { useEffect } from 'react';
import { shortcuts } from '@/lib/appmintNative';
export function useAccountShortcuts(signedIn: boolean) {
useEffect(() => {
if (!signedIn) { void shortcuts.clear(); return; }
void shortcuts.set([
{ id: 'inbox', title: 'Inbox', route: '/inbox' },
{ id: 'compose', title: 'Write a message', route: 'compose' }, // gets a leading "/"
]).then((handed) => { if (!handed) console.info('No launcher shortcuts on this platform'); });
}, [signedIn]);
}Notlar:
- En fazla 4 öğe; fazlası atılır. Başında
/olmayan birrouteöğesine eklenir.idisteğe bağlıdır. - Android 7.1+; eski Android'de çağrı bir şey yapmaz. Başlıklar 25 karakterde (kısa etiket) ve 50 karakterde (uzun etiket) kesilir.
- Bir dokunuş uygulamayı
appmint-shortcut://<package><route>başlatma bağlantısıyla açar; yaninew URL(u).pathnamesizin rotanızdır. BağlandığındalaunchUrl()ile, uygulama çalışırkenonDeepLink()ile okuyun. can('shortcuts')webde'none'olur: tarayıcının uygulama simgesi yoktur.
appmintNative.snoozeAlarm appmintNative#
snoozeAlarm(id?, minutes?) — @/lib/appmintNative
Örnek
Çalan alarmı durdurur ve minutes sonra yeniden çaldırır (0 veya boş = alarmın kendi snoozeMinutes değeri, varsayılan 10). Tekrarlayan bir alarmın yarınki çalışı kaymaz.
Dönüş değeri: boolean - çalıyorken ve ertelendiğinde true. Gereksinim: derleme sırasında Erişim adımında (Adım 4) 🔔 Alarm saati.
import { isAlarmRinging, snoozeAlarm } from '@/lib/appmintNative';
// A shake-to-snooze handler:
window.addEventListener('devicemotion', (e) => {
const a = e.accelerationIncludingGravity;
if (a && Math.abs(a.x ?? 0) > 25 && isAlarmRinging()) snoozeAlarm('', 5);
});Kendi alarm ekranınızdaki bir düğmeden erteleyin; alarmın ne zaman çaldığını bilmek için onAlarm kullanın.
import { useEffect, useState } from 'react';
import { onAlarm, snoozeAlarm, stopAlarm } from '@/lib/appmintNative';
export function RingingScreen() {
const [ringing, setRinging] = useState(false);
useEffect(() => onAlarm((e) => setRinging(e.action === 'open' && e.ringing === true)), []);
if (!ringing) return null;
return (
<div>
<button onClick={() => { if (snoozeAlarm('', 10)) setRinging(false); }}>Snooze 10 min</button>
<button onClick={() => { stopAlarm(); setRinging(false); }}>Stop</button>
</div>
);
}snoozeAlarm hiçbir şey çalmıyorsa false yanıtı verir, bu yüzden kullanıcıya ikinci bir çalma vaat etmeden önce kontrol edin.
import { snoozeAlarm } from '@/lib/appmintNative';
export function snoozeOrExplain(minutes = 0) {
return snoozeAlarm('', minutes)
? `Ringing again in ${minutes || 'a few'} minutes`
: 'No alarm is ringing right now.';
}appmintNative.speak appmintNative#
speak(text, opts?) — @/lib/appmintNative
Örnek
Metni telefonun metinden konuşmaya motoruyla sesli okur.
Döndürür: void - konuşmaya başlar (veya mevcut konuşmadan sonra sıraya girer) ve "bitti" sinyali vermez. opts: { lang?: string; rate?: number; pitch?: number } (varsayılanlar: motor dili, 1, 1). Gerekenler: hiçbir şey.
import { speak, stopSpeaking } from '@/lib/appmintNative';
export function ReadAloud({ text }: { text: string }) {
return (
<div>
<button onClick={() => speak(text, { lang: 'en-IN', rate: 0.9 })}>Read aloud</button>
<button onClick={() => stopSpeaking()}>Stop</button>
</div>
);
}Farklı diller ve hızlar:
import { speak } from '@/lib/appmintNative';
speak('வணக்கம்', { lang: 'ta-IN' }); // Tamil
speak('Slow and clear', { rate: 0.7 }); // rate 0.1..4, 1 = normal
speak('High voice', { pitch: 1.5 }); // pitch 0.1..4, 1 = normalBir eylemden sonra kısa bir onayı sesli söyleyin ve kullanıcıya metinle de bildirin.
import { speak, toast } from '@/lib/appmintNative';
export function announceSaved(title: string) {
toast('Saved');
speak(`${title} saved`, { rate: 1.1 });
}Bir listeyi öğe öğe okuyun; speak() sıraya alır, bu yüzden bir döngüde çağırın ve ekran kapanınca her şeyi stopSpeaking() ile durdurun.
import { useEffect } from 'react';
import { speak, stopSpeaking } from '@/lib/appmintNative';
export function ReadHeadlines({ items }: { items: string[] }) {
useEffect(() => () => stopSpeaking(), []); // leaving the screen stops the voice
return <button onClick={() => items.forEach((t) => speak(t))}>Read all</button>;
}Notlar: Android uygulamasında: uygulamanın yerel motoru. Web'de: speechSynthesis. Birkaç speak() çağrısı birbiri ardına çalınır. Konuşmanın ne zaman bittiğini bilmeniz gerekiyorsa (sözcükleri vurgulamak veya devam etmek için) bir konuşma parçasının onend olayıyla window.speechSynthesis işlevini doğrudan kullanın; bu Android uygulamasında da çalışır.
appmintNative.speech appmintNative#
speech — @/lib/appmintNative
Örnek
Dikte: kullanıcının konuşmasını metne çevirir. Android uygulamasında Android'in konuşma tanıyıcısı, Chrome'da Web Speech API.
Döndürür: speech.isAvailable() → Promise<boolean>. speech.start(onResult, opts?) → Promise<boolean>: dinleme başladığında true, mikrofon izni reddedildiğinde veya dikte kullanılamadığında false. onResult(text, isFinal) metinle çağrılır. speech.stop() → Promise<void>. opts: { lang?: string; partial?: boolean; prompt?: string }. Gerekenler: 4. Adım'da (Erişim) Mikrofon; kod konuşma tanıma kullandığında yapay zekayla oluşturulan uygulamalarda bu sizin için açılır.
import { useEffect, useState } from 'react';
import { speech } from '@/lib/appmintNative';
export function VoiceNote() {
const [ok, setOk] = useState(false);
const [text, setText] = useState('');
const [msg, setMsg] = useState('');
useEffect(() => { void speech.isAvailable().then(setOk); }, []);
if (!ok) return null; // no dictation here: hide the mic button
const listen = async () => {
setMsg('Listening...');
const started = await speech.start(
(t, isFinal) => { setText(t); if (isFinal) setMsg(''); },
{ lang: 'en-US' }, // partial results by default
);
if (!started) setMsg('Microphone permission is needed for voice input.');
};
return (
<div>
<button aria-label="Speak" onClick={listen}>🎤</button>
<button onClick={() => { void speech.stop(); setMsg(''); }}>Stop</button>
<p>{text}</p>
<p role="status">{msg}</p>
</div>
);
}Bir metin alanına dikte edin ve mikrofon reddedildiğinde bunu söyleyin, çünkü start() şu yanıtı verir false.
import { useState } from 'react';
import { speech } from '@/lib/appmintNative';
export function DictateField() {
const [value, setValue] = useState('');
const [error, setError] = useState('');
const dictate = async () => {
setError('');
const started = await speech.start((text, isFinal) => { if (isFinal) setValue((v) => (v ? `${v} ${text}` : text)); }, { partial: false });
if (!started) setError('Voice input is not available. Check the microphone permission.');
};
return (
<div>
<textarea value={value} onChange={(e) => setValue(e.target.value)} />
<button onClick={dictate}>Dictate</button>
{error && <p role="alert">{error}</p>}
</div>
);
}Ekran kapandığında dinlemeyi bırakın ve sonucu şununla geri okuyun speak.
import { useEffect, useState } from 'react';
import { speech, speak } from '@/lib/appmintNative';
export function VoiceCommand() {
const [heard, setHeard] = useState('');
useEffect(() => () => { void speech.stop(); }, []);
return (
<button
onClick={() => void speech.start((t, isFinal) => {
setHeard(t);
if (isFinal) speak(`You said ${t}`);
}, { lang: 'en-US' })}
>
{heard || 'Say something'}
</button>
);
}Notlar:
partialaçıkken (varsayılan)onResult(text, false)kelimeler geldikçe gelir, sonra nihai metinle bir kezonResult(text, true).partial: falseile yalnızca kullanıcı konuşmayı bıraktığında nihai olanı alırsınız.- İzin istemi ilk
start(). - Kullanıcı hiçbir şey söylemezse
onResultçağrılmaz; dinleme sadece biter. Arayüzünüzde bir Durdur düğmesi veya zaman aşımı bulundurun. - Dinleme bir duraklamadan sonra kendiliğinden durur.
langvarsayılan olarak telefonun dilidir.
appmintNative.splash appmintNative#
splash.hold() / splash.ready()
Örnek
"Açılış ekranı, sonra bir yükleme göstergesi" göstermek yerine, ilk gerçek ekran hazır olana kadar açılış ekranını tutar.
Döndürür: hiçbir şey (ikisi de eşzamanlı). Gerekenler: hiçbir şey.
Varsayılan olarak açılış ekranı, uygulamanın ilk çiziminde kendiliğinden gizlenir - hiçbir şeye ihtiyacınız yok. İlk ekran veri gerektirdiğinde, açılış ekranını modülün en üstünde tutun ve o ekran açıldığında serbest bırakın:
// main.tsx
import { splash } from '@/lib/appmintNative';
splash.hold(); // at import time, before the first render// Home.tsx
import { useEffect, useState } from 'react';
import { splash } from '@/lib/appmintNative';
export function Home({ load }: { load: () => Promise<string[]> }) {
const [items, setItems] = useState<string[] | null>(null);
useEffect(() => {
load()
.then(setItems)
.catch(() => setItems([]))
.finally(() => splash.ready()); // ALWAYS release, also when loading failed
}, [load]);
if (!items) return null;
return <ul>{items.map((t) => <li key={t}>{t}</li>)}</ul>;
}Oturum açmış bir oturum geri yüklenirken açılış ekranını tutun ve bir finally içinde serbest bırakın, böylece bir hata onu ekranda tutamaz.
// main.tsx
import { splash } from '@/lib/appmintNative';
import { supabase } from '@/lib/supabase';
splash.hold();
supabase.auth.getSession().finally(() => splash.ready());İlk ekranın verileri ekrana geldikten sonra bir kancadan bir kez serbest bırakın.
import { useEffect } from 'react';
import { splash } from '@/lib/appmintNative';
export function useReleaseSplash(loaded: boolean) {
useEffect(() => {
if (loaded) splash.ready(); // calling it again later does nothing harmful
}, [loaded]);
}Notlar:
- Android uygulamasında
hold()kabuğa açılış ekranını tutmasını söyler;ready()onu gizler (oluşturucunun en kısa açılış ekranı süresi yine geçerlidir). - Webde ikisi de hiçbir şey yapmaz - açılış ekranı yoktur.
- Kabuk bekletmeyi yaklaşık 6 saniyeyle sınırlar; bu yüzden hiç
ready()çağırmayan bir sayfa yine de açılır.
appmintNative.StatusBar appmintNative#
StatusBar.setBackgroundColor({ color }) / StatusBar.setStyle({ style })
Örnek
Capacitor'ın StatusBar eklentisi biçiminde durum çubuğu, şunun üzerine kurulu: setStatusBar().
Döndürür: her ikisi için Promise<void>. Gerekenler: hiçbir şey.
import { StatusBar } from '@/lib/appmintNative';
// Colour only — the icon style is left alone (the shell picks readable icons on Android).
await StatusBar.setBackgroundColor({ color: '#0B0F1A' });Renk ve simgeler birlikte - ikisini tek çağrıda ayarlayan setStatusBar() tercih edin:
import { setStatusBar } from '@/lib/appmintNative';
setStatusBar('#0B0F1A', 'light'); // dark bar, light icons
setStatusBar('#FFFFFF', 'dark'); // light bar, dark iconsCapacitor biçimli çağrıları birlikte kullanarak çubuğu uygulamanın kendi açık ve koyu temasıyla değiştirin.
import { useEffect } from 'react';
import { StatusBar } from '@/lib/appmintNative';
export function useStatusBarForTheme(dark: boolean) {
useEffect(() => {
void StatusBar.setBackgroundColor({ color: dark ? '#0B0F1A' : '#FFFFFF' });
void StatusBar.setStyle({ style: dark ? 'DARK' : 'LIGHT' }); // 'DARK' = dark bar, light icons
}, [dark]);
}Notlar: style, Capacitor'ı izler: 'DARK' koyu bir arka plan (açık simgeler) anlamına gelir, başka her şey koyu simgeler demektir. Android uygulamasında çubuğu kabuk köprüsü boyar (eklentinin renk çağrısı Android 15+ üzerinde hiçbir şey yapmaz) ve setStyle() simgeleri çubuğun mevcut rengine göre çevirir. Web'de renk şu olur <meta name="theme-color">.
appmintNative.stopAlarm appmintNative#
stopAlarm(id?) — @/lib/appmintNative
Örnek
Çalan alarmı, Kapat düğmesi gibi durdurur. Tekrarlayan bir alarm bir sonraki zamanı için kurulu kalır.
Dönüş değeri: boolean - çalıyorken ve şimdi durdurulduğunda true. id atlanırsa = hangisi çalıyorsa. Gereksinim: derleme sırasında Erişim adımında (Adım 4) 🔔 Alarm saati.
import { stopAlarm, snoozeAlarm } from '@/lib/appmintNative';
export function RingScreen({ id, onClose }: { id: string; onClose: () => void }) {
return (
<div className="ring">
<button onClick={() => { snoozeAlarm(id, 10); onClose(); }}>Snooze 10 min</button>
<button onClick={() => { stopAlarm(id); onClose(); }}>Stop</button>
</div>
);
}Alarmı, alarm çaldığında uygulamanın açtığı ekrandan durdurun ve bitişinin her yoluna tepki verin:
import { useEffect, useState } from 'react';
import { onAlarm, isAlarmRinging, stopAlarm } from '@/lib/appmintNative';
export function useRingingAlarm() {
const [ringingId, setRingingId] = useState<string | null>(null);
useEffect(() => onAlarm((e) => {
if (e.action === 'open' && e.ringing) setRingingId(e.id);
else setRingingId(null); // dismiss, snooze, missed or error: it is no longer ringing
}), []);
return {
ringingId,
stop: () => { if (isAlarmRinging()) stopAlarm(ringingId ?? ''); },
};
}Yanıtı kontrol edin: false, hiçbir şeyin çalmadığı (veya burada web'de olduğunuz) anlamına gelir; bu yüzden durduğunu iddia etmeyin:
import { stopAlarm, alarmSupport } from '@/lib/appmintNative';
export function stopWithFeedback(id: string, say: (m: string) => void) {
if (alarmSupport() === 'unavailable') return say('Alarms only work in the Android app');
say(stopAlarm(id) ? 'Alarm stopped' : 'That alarm is not ringing');
}appmintNative.stopSpeaking appmintNative#
stopSpeaking() — @/lib/appmintNative
Örnek
Metin okumayı hemen durdurur ve konuşulmayı bekleyen her şeyi bırakır.
Döndürür: void. Gerekenler: hiçbir şey.
import { useEffect } from 'react';
import { speak, stopSpeaking } from '@/lib/appmintNative';
export function StoryPage({ story }: { story: string }) {
// Stop reading when the user leaves the page.
useEffect(() => () => stopSpeaking(), []);
return (
<article>
<p>{story}</p>
<button onClick={() => { stopSpeaking(); speak(story); }}>Read from the start</button>
<button onClick={stopSpeaking}>Stop</button>
</article>
);
}Uzun bir metni kullanıcının seçtiği dil ve hızla sesli okuyun ve tek bir düğmeyle durdurun:
import { useState } from 'react';
import { speak, stopSpeaking } from '@/lib/appmintNative';
export function ReadAloud({ text, lang }: { text: string; lang: string }) {
const [rate, setRate] = useState(1);
return (
<div>
<input type="range" min={0.5} max={2} step={0.1} value={rate} onChange={(e) => setRate(Number(e.target.value))} />
<button onClick={() => { stopSpeaking(); speak(text, { lang, rate }); }}>Read aloud</button>
<button onClick={() => stopSpeaking()}>Stop</button>
</div>
);
}Kullanıcı yanıt yazmaya başladığında sesi kesin:
import { stopSpeaking } from '@/lib/appmintNative';
export function ReplyBox({ onSend }: { onSend: (text: string) => void }) {
return <textarea onFocus={() => stopSpeaking()} onBlur={(e) => onSend(e.currentTarget.value)} />;
}Notlar: Hiçbir şey konuşmuyorken çağırmak güvenlidir. Android uygulamasında: yerel motorun durdurması. Web'de: speechSynthesis.cancel().
appmintNative.swipeable appmintNative#
swipeable(el: HTMLElement, opts: SwipeOptions): () => void
Örnek
Bir öğeye kaydırma hareketleri ekler - kaydırarak silinen satırlar, sayfalama, bir kartı kapatma.
Dönüş değeri: öğenin özgün touch-action değerini de geri yükleyen bir abonelikten çıkma fonksiyonu. Gereksinim: hiçbir şey.
Seçenekler: onLeft, onRight, onUp, onDown (en az biri), threshold (px cinsinden hareket, varsayılan 56), edgeGuard (yatay kaydırmaların yok sayıldığı sol/sağ ekran kenarından px cinsinden mesafe, varsayılan 24), haptics (kaydırmada seçim dokunsal geri bildirimi, varsayılan true).
Silmek için sola kaydırın; aynı eylem için görünür bir düğme de olsun:
import { useEffect, useRef } from 'react';
import { swipeable, toast } from '@/lib/appmintNative';
export function MessageRow({ id, text, onDelete, onUndo }: {
id: string; text: string; onDelete(id: string): void; onUndo(id: string): void;
}) {
const ref = useRef<HTMLLIElement>(null);
useEffect(() => {
if (!ref.current) return;
return swipeable(ref.current, {
onLeft: () => { onDelete(id); toast('Deleted'); },
});
}, [id, onDelete]);
return (
<li ref={ref} className="flex items-center justify-between p-4">
<span>{text}</span>
<button onClick={() => onDelete(id)}>Delete</button>
{/* swipe is never the only way to an action */}
</li>
);
}Sekmeler arasında sola / sağa sayfalayın:
import { swipeable } from '@/lib/appmintNative';
declare const panel: HTMLElement;
declare let tab: number;
const off = swipeable(panel, { onLeft: () => { tab += 1; }, onRight: () => { tab -= 1; }, threshold: 72 });
// later: off();Bir kartı yukarı kaydırarak kapatın, işleyici bileşen kaldırılırken silinerek:
import { useEffect, useRef } from 'react';
import { swipeable } from '@/lib/appmintNative';
export function Banner({ onClose, children }: { onClose: () => void; children: React.ReactNode }) {
const ref = useRef<HTMLDivElement>(null);
useEffect(() => (ref.current ? swipeable(ref.current, { onUp: onClose, threshold: 40 }) : undefined), [onClose]);
return <div ref={ref} className="banner">{children}</div>;
}Notlar:
- Pointer Events üzerine kurulduğundan aynı kod dokunmatik, kalem ve fare sürüklemesiyle çalışır.
touch-actiondeğerini sizin için ayarlar: yatay işleyiciler içinpan-y(dikey kaydırma yine çalışır), dikey olanlar içinpan-x, ikisi içinnone.- Bir kaydırma açıkça tek yönlü olmalıdır (bir eksende 1,5 kat daha fazla); çapraz kaydırmalar tetiklenmez. İkinci bir parmak hareketi iptal eder.
- Kenar koruması vardır, çünkü Android'in sistem geri hareketi ekran kenarlarına sahiptir.
appmintNative.systemPalette appmintNative#
systemPalette(): Promise<SystemPalette>
Örnek
Telefonun Material You renklerini okur (Android 12+ üzerinde duvar kağıdından üretilir), böylece uygulamanın vurgu rengi sistem uygulamaları gibi kullanıcının duvar kağıdını izleyebilir.
Döndürür: Promise<{ available: boolean; accent1?, accent2?, accent3?, neutral1?, neutral2? }> - her renk örneği bir tonu ('0', '10', '50', '100' … '900', '1000') onaltılık bir renge eşler. Android 12'nin altında ve webde { available: false }. Gerekenler: hiçbir şey.
import { systemPalette } from '@/lib/appmintNative';
export async function applyWallpaperAccent() {
const p = await systemPalette();
if (!p.available || !p.accent1) return; // keep the app's own palette
const root = document.documentElement.style;
root.setProperty('--accent', p.accent1['600']);
root.setProperty('--accent-soft', p.accent1['100']);
}Duvar kağıdı renklerini yalnızca telefon sunduğunda kullanın ve uygulama ekrana döndüğünde yeniden okuyun:
import { useEffect, useState } from 'react';
import { systemPalette } from '@/lib/appmintNative';
export function useWallpaperAccent(enabled: boolean) {
const [accent, setAccent] = useState<string | null>(null);
useEffect(() => {
if (!enabled) { setAccent(null); return; }
const read = () => systemPalette().then((p) => setAccent(p.available ? p.accent1?.['600'] ?? null : null));
void read();
document.addEventListener('visibilitychange', read);
return () => document.removeEventListener('visibilitychange', read);
}, [enabled]);
return accent; // null = no wallpaper palette on this device (Android 11 and older, or the web)
}"Duvar kağıdıma uy" anahtarını yalnızca bir palet varsa gösterin, "burada yok" yanıtı olarak { available: false } kullanarak:
import { useEffect, useState } from 'react';
import { can, systemPalette } from '@/lib/appmintNative';
export function WallpaperToggle({ value, onChange }: { value: boolean; onChange: (v: boolean) => void }) {
const [ok, setOk] = useState(false);
useEffect(() => { if (can('systemPalette') !== 'none') systemPalette().then((p) => setOk(p.available)); }, []);
if (!ok) return null;
return <label><input type="checkbox" checked={value} onChange={(e) => onChange(e.target.checked)} /> Match my wallpaper</label>;
}Notlar: Metin veya düğmeler için bir duvar kağıdı tonu kullanmadan önce kontrastı kontrol edin - --accent arka planınızda yine de 3:1'e ulaşmalıdır. Bunu zorunlu bir tema olarak değil, bir seçenek ("Duvar kağıdıma uysun") olarak sunun. Uygulama devam ettiğinde yeniden çağırın; kullanıcı duvar kağıdını değiştirmiş olabilir.
appmintNative.TextToSpeech appmintNative#
TextToSpeech — @/lib/appmintNative
Örnek
Capacitor tarzı TextToSpeech nesnesi (speak / stop), speak() ile aynı kod üzerinde çalışır ve stopSpeaking().
Döndürür: speak({ text, lang?, rate?, pitch? }) → metin konuşulduğunda (veya stop() onu kestiğinde) çözülen, konuşulamadığında reddedilen Promise<void>. stop() → Promise<void>. Gerekenler: hiçbir şey.
import { TextToSpeech } from '@/lib/appmintNative';
async function announce(orderNo: number) {
await TextToSpeech.speak({ text: `Order ${orderNo} is ready`, lang: 'en-US', rate: 1, pitch: 1 });
// Here the text is queued; it may still be speaking.
}
async function silence() {
await TextToSpeech.stop();
}Bir satır listesini birbiri ardına sesli okuyun; her speak sıraya alınınca çözülür, böylece motor onları sırayla çalar:
import { TextToSpeech } from '@/lib/appmintNative';
export async function readSteps(steps: string[]) {
await TextToSpeech.stop(); // drop anything still waiting from before
for (const [i, s] of steps.entries()) {
await TextToSpeech.speak({ text: `Step ${i + 1}. ${s}`, lang: 'en-US' });
}
}Ekrandan ayrılınca konuşmayı durduran bir sesli okuma düğmesi:
import { useEffect } from 'react';
import { TextToSpeech } from '@/lib/appmintNative';
export function ArticleReader({ text }: { text: string }) {
useEffect(() => () => { void TextToSpeech.stop(); }, []);
return <button onClick={() => void TextToSpeech.speak({ text, rate: 0.9 })}>Listen</button>;
}Notlar: await TextToSpeech.speak(…) bitiş sinyalidir: konuşmadan sonraki adım doğrudan onu izleyebilir. Uygulamada: kabuğun yerel Android metin okuma motoru; web'de: speechSynthesis.
appmintNative.textZoom appmintNative#
textZoom.get() / textZoom.getPreferred() / textZoom.set(value)
Örnek
Uygulama içi metin boyutu denetimi. Uygulamada sayfanın metin yakınlaştırmasını değiştirir (1 = %100); webde kök yazı tipi boyutunu değiştirir, bu da rem tabanlı tüm düzenleri ölçekler.
Döndürür: get() → Promise<number>. getPreferred() → Promise<number> - kullanıcının telefon ayarlarında seçtiği boyut (web'de 1). set(value) → Promise<void>; değer 0.5-3 aralığına sıkıştırılır ve sayı olmayan bir değer için reddeder. Gerekenler: hiçbir şey.
import { textZoom } from '@/lib/appmintNative';
async function bigger() { await textZoom.set((await textZoom.get()) + 0.1); }
async function smaller() { await textZoom.set((await textZoom.get()) - 0.1); }
async function reset() { await textZoom.set(await textZoom.getPreferred()); }Açılışlar boyunca seçimi hatırlayın:
import { textZoom } from '@/lib/appmintNative';
const saved = Number(localStorage.getItem('textZoom'));
if (saved) await textZoom.set(saved);
export async function setSize(v: number) {
await textZoom.set(v);
localStorage.setItem('textZoom', String(v));
}Geçerli değeri okuyan, izin verilen aralıkta tutan ve yüzde olarak gösteren bir metin boyutu ayarlayıcı:
import { useEffect, useState } from 'react';
import { textZoom } from '@/lib/appmintNative';
export function TextSizeStepper() {
const [z, setZ] = useState(1);
useEffect(() => { textZoom.get().then(setZ); }, []);
const change = async (d: number) => {
const next = Math.min(3, Math.max(0.5, Math.round((z + d) * 10) / 10));
await textZoom.set(next);
setZ(next);
};
return (
<div>
<button onClick={() => change(-0.1)}>A-</button>
<span>{Math.round(z * 100)}%</span>
<button onClick={() => change(0.1)}>A+</button>
</div>
);
}Notlar: Uygulama zaten telefonun yazı tipi boyutu ayarını izler; bu ek, uygulama içi bir denetimdir. Düzeni büyük boyutlarda test edin - metin denetimlerindeki sabit yükseklikler metni keser.
appmintNative.toast appmintNative#
toast(message: string, opts?: { duration?: 'short' | 'long' }): void
Örnek
Yanıt gerektirmeyen kısa bir mesaj gösterir - "Kopyalandı", "Kaydedildi", "Listeye eklendi".
Döndürür: hiçbir şey (ateşle ve unut). Gerekenler: hiçbir şey.
import { toast, haptic } from '@/lib/appmintNative';
export function SaveButton({ onSave }: { onSave: () => Promise<void> }) {
return (
<button
onClick={async () => {
await onSave();
haptic('success');
toast('Saved'); // 'short' ≈ 2 s
}}
>
Save
</button>
);
}Daha uzun bir mesaj yaklaşık 3,5 saniye kalır:
import { toast } from '@/lib/appmintNative';
toast('Your export will be ready in a minute', { duration: 'long' });Capacitor biçimli form Toast.show({ text, duration? }) aynısını yapar ve çözülür Promise<void>:
import { Toast } from '@/lib/appmintNative';
await Toast.show({ text: 'Added to list' });Kullanıcının MUTLAKA görmesi gereken bir mesaj için bunun yerine bir iletişim kutusu kullanın:
import { Dialog } from '@/lib/appmintNative';
await Dialog.alert({ title: 'Payment failed', message: 'Your card was declined.' });Notlar:
- Android uygulamasında Capacitor Toast eklentisi bunu ekranın altında gösterir.
- Başka her yerde, uygulamanın kendi renklerinde (
--bgüzerinde--ink), güvenli alan boşluğunun üstünde, ekran okuyucuların duyurması içinrole="status"ile sayfanın üzerine çizilen küçük bir snackbar'dır. - Aynı anda yalnızca biri gösterilir; yeni bir bildirim eskisinin yerini alır.
- Toast'un gözden kaçması tasarım gereğidir. Kullanıcının işlem yapması gereken bir hata için asla kullanmayın.
appmintNative.Toast appmintNative#
import { Toast } from '@/lib/appmintNative'
Örnek
appmintNative.toast ile birlikte belgelenmiştir - oradaki örnek bunu da gösterir.
Yanıt gerektirmeyen kısa bir mesaj gösterir - "Kopyalandı", "Kaydedildi", "Listeye eklendi".
Döndürür: hiçbir şey (ateşle ve unut). Gerekenler: hiçbir şey.
import { toast, haptic } from '@/lib/appmintNative';
export function SaveButton({ onSave }: { onSave: () => Promise<void> }) {
return (
<button
onClick={async () => {
await onSave();
haptic('success');
toast('Saved'); // 'short' ≈ 2 s
}}
>
Save
</button>
);
}Daha uzun bir mesaj yaklaşık 3,5 saniye kalır:
import { toast } from '@/lib/appmintNative';
toast('Your export will be ready in a minute', { duration: 'long' });Capacitor biçimli form Toast.show({ text, duration? }) aynısını yapar ve çözülür Promise<void>:
import { Toast } from '@/lib/appmintNative';
await Toast.show({ text: 'Added to list' });Kullanıcının MUTLAKA görmesi gereken bir mesaj için bunun yerine bir iletişim kutusu kullanın:
import { Dialog } from '@/lib/appmintNative';
await Dialog.alert({ title: 'Payment failed', message: 'Your card was declined.' });Notlar:
- Android uygulamasında Capacitor Toast eklentisi bunu ekranın altında gösterir.
- Başka her yerde, uygulamanın kendi renklerinde (
--bgüzerinde--ink), güvenli alan boşluğunun üstünde, ekran okuyucuların duyurması içinrole="status"ile sayfanın üzerine çizilen küçük bir snackbar'dır. - Aynı anda yalnızca biri gösterilir; yeni bir bildirim eskisinin yerini alır.
- Toast'un gözden kaçması tasarım gereğidir. Kullanıcının işlem yapması gereken bir hata için asla kullanmayın.
appmintNative.torch appmintNative#
import { torch } from '@/lib/appmintNative'
Örnek
Telefonun el feneri: torch.on(), off(), toggle(), isOn(), isAvailable(). can('torch') web'de 'none' değerindedir.
Döndürür: on()/off() → Promise (code 'unavailable' veya 'CAMERA_IN_USE' ile reddeder); toggle() → yeni durum; isOn() / isAvailable() → boolean. Gerekenler: hiçbir şey, izin yok.
import { useEffect, useState } from 'react';
import { can, torch } from '@/lib/appmintNative';
export function LightButton() {
const [on, setOn] = useState(false);
const [ok, setOk] = useState(false);
useEffect(() => { if (can('torch') !== 'none') torch.isAvailable().then(setOk); }, []);
if (!ok) return null;
return (
<button aria-pressed={on} aria-label="Flashlight" onClick={async () => setOn(await torch.toggle())}>
{on ? 'Light off' : 'Light on'}
</button>
);
}Kullanıcı ekrandan ayrıldığında ışığı kapatın ve açılamadığında telefonun bildirdiği hatayı gösterin:
import { useEffect, useState } from 'react';
import { torch } from '@/lib/appmintNative';
export function LightSwitch() {
const [on, setOn] = useState(false);
const [err, setErr] = useState('');
useEffect(() => () => { void torch.off().catch(() => {}); }, []);
const flip = async () => {
try { setOn(await torch.toggle()); setErr(''); }
catch (e) {
const code = (e as { code?: string }).code;
setErr(code === 'CAMERA_IN_USE' ? 'Close the camera first' : 'No flashlight on this device');
}
};
return <div><button onClick={flip}>{on ? 'Off' : 'On'}</button>{err && <p role="alert">{err}</p>}</div>;
}Işığı bir sinyal olarak yakıp söndürün, sonra kapalı bırakın:
import { can, torch } from '@/lib/appmintNative';
export async function blink(times = 3) {
if (can('torch') === 'none') return;
for (let i = 0; i < times; i++) {
await torch.on();
await new Promise((r) => setTimeout(r, 200));
await torch.off();
await new Promise((r) => setTimeout(r, 200));
}
}Notlar: uygulama kapandığında kapatılır. Uygulamanın kendi kamera akışı açıkken, bunun yerine parçanın torch kısıtıyla yakın.
appmintNative.trackEvent appmintNative#
import { trackEvent } from '@/lib/appmintNative'
Örnek
Uygulamanızdaki bir anı bildirir; böylece oluşturucunun "Bir olay olduğunda" tetikleyicili (aynı adlı) uygulama içi mesajları gösterilir. Uygulama, oluşturucunun kampanyalarını AppMint Push gönderim merkezinden indirir ve kendisi gösterir.
Döndürür: boolean eşzamanlı olarak: geçersiz ad (harfler, rakamlar ve _ . : -, en çok 64), web ve AppMint Push olmadan derlenmiş uygulamalar için false. Gerekenler: Android: derleme sırasında Entegre et adımında (Adım 3) AppMint Push açık olmalı.
import { trackEvent } from '@/lib/appmintNative';
export async function completeCheckout(cart: { total: number }) {
await saveOrder(cart);
trackEvent('checkout_done', { total: cart.total }); // props are optional and not stored
}URL'nin göstermediği bir görünüm değişikliğini bildirin, böylece "Bir sayfada" mesajları bunu görür:
import { trackEvent } from '@/lib/appmintNative';
export function selectTab(tab: 'home' | 'cart' | 'profile') {
setActiveTab(tab);
trackEvent('appmint:route', { path: '/' + tab });
}Bir ekranın ilk kez açıldığını izleyen bir hook:
import { useEffect } from 'react';
import { can, trackEvent } from '@/lib/appmintNative';
export function useTrackOnce(name: string) {
useEffect(() => {
if (can('inAppMessages') === 'none') return;
const key = 'tracked:' + name;
if (localStorage.getItem(key)) return;
if (trackEvent(name)) localStorage.setItem(key, '1');
}, [name]);
}Notlar: URL değişiklikleri (sayfa yüklemeleri, pushState, #hash yolları) zaten görülür; yalnızca adresin değiştirmediği şeyleri bildirin.
appmintNative.transition appmintNative#
transition(update: () => void | Promise<void>, opts?: { type?: 'push' | 'pop' | 'fade' }): Promise<void>
Örnek
Bir ekran değişikliğini yerel hissi veren bir animasyonla çalıştırır: push yeni ekranı sağdan kaydırır, pop geri kaydırır, fade çapraz geçiş yapar.
Döndürür: değişiklikten ve animasyonundan sonra tamamlanan Promise<void>. update sizin durum değişikliğinizdir (bir yönlendirici navigate veya bir setState) ve her zaman uygulanır. Gerekenler: hiçbir şey.
import { useNavigate } from 'react-router-dom';
import { transition, haptic } from '@/lib/appmintNative';
export function NoteRow({ id, title }: { id: string; title: string }) {
const navigate = useNavigate();
return (
<button
onClick={() => { haptic('light'); void transition(() => navigate(`/note/${id}`)); }} // push
>
{title}
</button>
);
}
export function BackButton() {
const navigate = useNavigate();
return <button onClick={() => void transition(() => navigate(-1), { type: 'pop' })}>Back</button>;
}İki sekme arasında geçiş yapın:
import { transition } from '@/lib/appmintNative';
declare function setTab(t: 'list' | 'map'): void;
await transition(() => setTab('map'), { type: 'fade' });Sekme değişimi için, her sekmede tek bir işleyiciyle solma efekti kullanın:
import { transition } from '@/lib/appmintNative';
export function Tabs({ tab, setTab }: { tab: 'home' | 'stats'; setTab: (t: 'home' | 'stats') => void }) {
return (
<nav>
<button onClick={() => void transition(() => setTab('home'), { type: 'fade' })} aria-current={tab === 'home'}>Home</button>
<button onClick={() => void transition(() => setTab('stats'), { type: 'fade' })} aria-current={tab === 'stats'}>Stats</button>
</nav>
);
}Notlar:
- WebView destekliyorsa View Transitions API'yi kullanır. Aksi halde güncellemeyi uygular ve
#rootüzerinde (ya da şunun ilk çocuğunda) kısa bir giriş animasyonu oynatır<body>). - Kullanıcı sistemden azaltılmış hareket istediyse, yalnızca güncellemeyi uygular - animasyon yoktur.
- Animasyonlar yaklaşık 240 ms sürer.
appmintNative.unlock appmintNative#
unlock(title?, subtitle?) — from '@/lib/appmintNative'
Örnek
Telefonun sahibinden orada olduğunu kanıtlamasını ister - parmak izi, yüz veya cihaz PIN'i/parolası - ve bunu yapıp yapmadığını size bildirir.
Döndürür: Promise<boolean> - kilit açıldığında true; iptal edildiğinde, başarısız olduğunda, kilitlendiğinde veya bu cihazda hiçbir şey kontrol edemediğinde false. Asla hata fırlatmaz. Gerekenler: uygulamada, derleme yaparken 4. Adım'da (Erişim) Parmak izi. Web'de: tarayıcının platform doğrulayıcısı (WebAuthn).
import { unlock, can } from '@/lib/appmintNative';
export async function openPrivateNotes(show: () => void) {
if (can('unlock') === 'none') {
alert('This device has no fingerprint, face or PIN check.');
return;
}
const ok = await unlock('Unlock notes', 'Use your fingerprint or PIN');
if (ok) show();
}Kilidi açacak bir şey olmayan yerde düğmeyi gizleyin:
import { can } from '@/lib/appmintNative';
const showLockButton = can('unlock') !== 'none'; // 'native' | 'web' | 'none'Bir silme işlemini taze bir kilit açmayla koruyun, sonra bir dokunsal geri bildirim ve bir toast ile onaylayın:
import { unlock, haptic, toast } from '@/lib/appmintNative';
export async function deleteAccountData(wipe: () => Promise<void>) {
const ok = await unlock('Confirm delete', 'This removes your saved data');
if (!ok) { haptic('warning'); toast('Not unlocked, nothing was deleted'); return; }
await wipe();
haptic('success');
toast('Data deleted');
}Notlar: Uygulamada cihaz PIN'i, deseni veya parolası ikinci giriş yolu olarak kabul edilir. Webde ilk çağrı tarayıcının platform kimlik doğrulayıcısını kaydeder ve kilit açılmış sayılır. Bu, cihaz sahibinin orada olduğunu kanıtlar; bir oturum açma DEĞİLDİR - bir sunucuda duran verinin tek koruması olarak asla kullanmayın. Android 11 ve üstünde ekran kilidi olan her telefonda istem açılır - yalnızca PIN'li telefonda da açılır ve PIN ile kilit açar; Android 10 ve altında önce bir parmak izi kaydedilmiş olmalıdır, aksi halde istem göstermeden false yanıtını verir.
appmintNative.updates appmintNative#
updates.supported() / download() / stage() / confirm() / revertIfUnconfirmed() / current()
Örnek
Web paketi güncellemeleri için sayfa tarafı bir güncelleyici. Sayfa kodunun buna ihtiyacı yoktur: bir AppMint uygulamasında Remote Update yerel olarak Android uygulaması tarafından yapılır ve updates.supported() her yerde false döndürür.
Döndürür: supported() → boolean (şu an her zaman false); download(url, sha256, version, binaryVersion) → Promise<boolean> (desteklenmezken false); stage(dir, version, binaryVersion) → Promise<boolean>; confirm() → void; revertIfUnconfirmed(binaryVersion) → Promise<boolean>; current() → { path, version, binaryVersion } | null. Gerekenler: uygulamanız için hiçbir şey - güncellemeler AppMint'te Remote Update'ten yayınlanır.
Uygulama kodunda tek güvenli kullanım kontrol etmek ve kapalıysa hiçbir şey yapmamaktır:
import { updates } from '@/lib/appmintNative';
if (updates.supported()) {
// Not reached today: the Android app's own updater owns the whole update lane.
const staged = await updates.download(
'https://example.com/bundle-1.2.0.zip',
'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', // sha256, required
'1.2.0',
'1',
);
console.log('update staged:', staged);
}Hangi paketin çalıştığını gösterin (ayarlarda bir "Hakkında" satırı):
import { updates } from '@/lib/appmintNative';
const active = updates.current(); // null = no page-staged bundle (always, today)
const label = active ? `Content ${active.version}` : 'Built-in content';Çalışan içerik sürümünü bir ayarlar satırında gösterin; sayfada hazırlanmış paket yoksa yerleşik etiketi kullanın:
import { useMemo } from 'react';
import { updates } from '@/lib/appmintNative';
export function AboutRow() {
const label = useMemo(() => {
const c = updates.current();
return c ? `Content ${c.version} (app ${c.binaryVersion})` : 'Built-in content';
}, []);
return <p>{label}</p>;
}Notlar:
- Android uygulamasında kabuğun Remote Update yöneticisi güncellemeleri indirir, doğrular ve sunar; yüklenemeyen bir paketi geri alır. Sayfayı kabuğun kendisi sunar, bu yüzden sayfa kodundan hazırlanan bir paket asla gösterilmez - bu nedenle
supported()false. - Web'de güncellenecek bir şey yoktur; sayfa sunucunun gönderdiği şeydir.
- Güncellemeler yalnızca HTML, CSS ve JavaScript'i değiştirebilir; yeni bir yerel özellik için yine de yeni bir uygulama derlemesi gerekir.
appmintNative.watchIdle appmintNative#
import { watchIdle } from '@/lib/appmintNative'
Örnek
Kullanıcı boşta kaldığında veya ekranı kilitlediğinde ve geri döndüğünde uygulamaya haber verir (can('idle')). İlk seferde bir dokunuşla başlatın - izin isteği bunu gerektirir.
Döndürür: durdurma işlevinin Promise'i. Geri çağırma başlangıçta ve her değişiklikte { user: 'active' | 'idle', screen: 'locked' | 'unlocked' } alır. code 'permission-denied' veya 'unavailable' ile reddeder. Gerekenler: hiçbir şey, Android izni yok.
import { useRef } from 'react';
import { can, watchIdle } from '@/lib/appmintNative';
export function AwayStatus({ setAway }: { setAway: (away: boolean) => void }) {
const stop = useRef<(() => void) | null>(null);
if (can('idle') === 'none') return null;
return (
<button onClick={async () => {
stop.current?.();
stop.current = await watchIdle((s) => setAway(s.user === 'idle' || s.screen === 'locked'), { thresholdMs: 5 * 60000 });
}}>Show me as away when idle</button>
);
}Uzakta durumunu gösterin ve bileşen kaldırıldığında izlemeyi bırakın; reddedilen izin şununla bildirilir code:
import { useEffect, useRef, useState } from 'react';
import { can, watchIdle } from '@/lib/appmintNative';
export function useIdleWatch() {
const stop = useRef<(() => void) | null>(null);
const [state, setState] = useState<'off' | 'active' | 'idle' | 'denied'>('off');
useEffect(() => () => stop.current?.(), []);
// Call this from a tap: the first permission request needs a gesture.
const start = async () => {
if (can('idle') === 'none') return;
try {
stop.current = await watchIdle((s) => setState(s.user === 'idle' || s.screen === 'locked' ? 'idle' : 'active'));
} catch (e) {
if ((e as { code?: string }).code === 'permission-denied') setState('denied');
}
};
return { state, start };
}Kullanıcı uzaktayken bir yoklama zamanlayıcısını duraklatın ve geri döndüğünde yenileyin:
import { watchIdle } from '@/lib/appmintNative';
export async function pausePollingWhenIdle(refresh: () => void) {
let timer: ReturnType<typeof setInterval> | undefined = setInterval(refresh, 30000);
const stop = await watchIdle((s) => {
const away = s.user === 'idle' || s.screen === 'locked';
if (away && timer) { clearInterval(timer); timer = undefined; }
if (!away && !timer) { refresh(); timer = setInterval(refresh, 30000); }
});
return () => { stop(); if (timer) clearInterval(timer); };
}Notlar: eşik en az bir dakikadır (spesifikasyonun alt sınırı).
appmintNative.widget appmintNative#
widget.set(card) / widget.clear() / widget.placedCount() / widget.requestPin()
Örnek
Uygulamanın ana ekran widget'ını doldurur - bir başlık, büyük bir değer, bir açıklama, on iki satıra kadar (her biri isteğe bağlı renk, işaret ve rota ile) ve uygulamayı bir rotada açan bir dokunuş içeren tek bir kart.
Döndürür: set(card) → Promise<boolean> (kaydedildiğinde true); clear() → Promise<void>; placedCount() → Promise<number> (kullanıcının yerleştirdiği kopya sayısı); requestPin() → Promise<boolean> (başlatıcının "widget ekle" akışı açıldıysa true). Hiçbiri reddetmez. Gerekenler: Android uygulamasında hiçbir şey (widget her zaman derlemenin parçasıdır).
Kullanıcının ana ekranda görmek isteyeceği her değişiklikten sonra kartı güncelleyin:
import { can, widget } from '@/lib/appmintNative';
export async function updateTodayWidget(tasks: { title: string; done: boolean }[]) {
if (can('widget') === 'none') return; // on the web
const open = tasks.filter((t) => !t.done);
await widget.set({
title: 'Today',
value: `${open.length} left`,
lines: open.slice(0, 6).map((t) => '• ' + t.title),
route: '/today', // a tap opens the app here
});
}Yeniden boyutlandırılmış bir widget'ı dolduran, şeffaf arka planlı ve gecikmiş bir satırı kırmızı gösteren bir ajanda:
import { widget, type WidgetLine } from '@/lib/appmintNative';
export async function updateAgendaWidget(events: { day: string; time: string; title: string; overdue: boolean }[]) {
const lines: WidgetLine[] = events.slice(0, 12).map((e) => e.overdue
? { text: `Overdue: ${e.title}`, color: '#FF5252', marker: '#FF5252', bold: true, route: `/day/${e.day}` }
: { text: `${e.time} ${e.title}`, marker: '#29B6F6', route: `/day/${e.day}` });
await widget.set({
title: 'Agenda', value: 'Today', caption: `${events.length} events`,
bg: 'rgba(20, 38, 74, 0.6)', textSize: 'auto', lines, route: '/agenda',
});
}Bir "Ana ekrana ekle" düğmesi:
import { can, widget, toast } from '@/lib/appmintNative';
export function AddWidgetButton() {
if (can('widget') === 'none') return null;
return (
<button
onClick={async () => {
if (!(await widget.requestPin())) toast('Long-press your home screen, tap Widgets, and pick this app');
}}
>
Add widget to home screen
</button>
);
}Kullanıcı oturumu kapattığında kartı boşaltın ve ana ekranda kaç kopya olduğunu gösterin:
import { useEffect, useState } from 'react';
import { can, widget } from '@/lib/appmintNative';
export function WidgetStatus({ signedIn }: { signedIn: boolean }) {
const [placed, setPlaced] = useState(0);
useEffect(() => {
if (can('widget') === 'none') return;
if (!signedIn) void widget.clear();
widget.placedCount().then(setPlaced);
}, [signedIn]);
return <p>{placed === 0 ? 'Not on your home screen yet' : `On your home screen (${placed})`}</p>;
}Notlar:
- Kart cihaza kaydedilir, yeniden başlatmadan sonra da kalır ve yerleştirilen her kopya yenilenir.
0değerindeplacedCount()henüz ana ekranda kopya olmadığı anlamına gelir. - Kart alanları:
title(boş = uygulama adı),value(accentiçinde büyük gösterilir, boşsa gizlenir),caption(değerin altındaki küçük metin),lines(en fazla 12; altısı her zaman görünür, widget yeterince uzunsa daha fazlası; bir satır bir dize veya{ text, color?, marker?, bold?, wrap?, route? }olur),route(varsayılan/),textSize('auto'metni her satır sığdığı sürece widget ile büyütür, uzun satırları iki satıra sarar;'small' | 'normal' | 'large' | 'xl'veya 0.6-3 arası bir sayı),align('start' | 'center' | 'end'),wrap(uzun satırlar iki satır alır) vebg/ink/accent. Renkler varsayılan olarak--bg,--ink,--accentCSS belirteçlerini kullanır;#RRGGBB,#AARRGGBB(alfa ÖNCE:#9914264A%60 opaktır),#RGB,rgb()/rgba()veya bir renk adı olmalıdır -hsl()/oklch()değeri varsayılan renklere döner.set(), bilinmeyen birtextSize/alignveya hatalı bir satır rengi içinfalsedöndürür. - Bir dokunuş uygulamayı
appmint-shortcut://<package><route>ile açar; şununla okuyunlaunchUrl()/onDeepLink(). requestPin()Android 8+ ve bunu destekleyen bir başlatıcı gerektirir.clear()boş "Henüz gösterilecek bir şey yok" kartını gösterir.- Yalnızca Android uygulaması:
can('widget')web'de'none'olur ve oradaset()ilerequestPin()şunu döndürürfalse.