React Native / Push Notification үндэс

Push Notification үндэс

"Таны захиалга хүргэгдлээ", "Шинэ мессеж ирлээ" — эдгээр мэдэгдлийг push notification гэнэ. Апп нээлгүй байхад ч хэрэглэгчид мэдэгдэл хүргэх боломжийг expo-notifications package олгодог. Энэ хичээлд локал notification (өөрийн төхөөрөмж дотор) болон push notification-н суурийг сурна.

expo-notifications суулгах

bash
npx expo install expo-notifications

app.json-д notification тохиргоо нэмнэ:

json
{
  "expo": {
    "plugins": [
      [
        "expo-notifications",
        {
          "icon": "./assets/notification-icon.png",
          "color": "#22d3ee",
          "sounds": []
        }
      ]
    ]
  }
}

Зөвшөөрөл авах ба локал notification

Notification илгээхийн өмнө хэрэглэгчийн зөвшөөрөл авах шаардлагатай. iOS дээр заавал асуудаг. Android 13+ дээр ч асуудаг болсон:

jsx
import { useEffect, useRef, useState } from "react";
import * as Notifications from "expo-notifications";
import {
  View,
  Text,
  TouchableOpacity,
  Platform,
  StyleSheet,
} from "react-native";

// Notification-г апп нээлттэй байх үед харуулах тохиргоо
Notifications.setNotificationHandler({
  handleNotification: async () => ({
    shouldShowAlert: true,
    shouldPlaySound: true,
    shouldSetBadge: false,
  }),
});

async function requestPermissions() {
  if (Platform.OS === "android") {
    await Notifications.setNotificationChannelAsync("default", {
      name: "Үндсэн",
      importance: Notifications.AndroidImportance.HIGH,
      sound: "default",
    });
  }

  const { status } = await Notifications.requestPermissionsAsync();
  return status === "granted";
}

export default function NotificationDemo() {
  const [permissionGranted, setPermissionGranted] = useState(false);

  useEffect(() => {
    requestPermissions().then(setPermissionGranted);
  }, []);

  const sendLocalNotification = async () => {
    await Notifications.scheduleNotificationAsync({
      content: {
        title: "🎉 Амжилттай!",
        body: "Таны захиалга баталгаажлаа. Удахгүй хүргэгдэнэ.",
        data: { orderId: "ORD-001" },
      },
      trigger: null, // null = шууд илгээнэ
    });
  };

  const sendDelayedNotification = async () => {
    await Notifications.scheduleNotificationAsync({
      content: {
        title: "⏰ Сануулга",
        body: "5 секундын дараа энэ мэдэгдэл ирнэ!",
      },
      trigger: {
        seconds: 5, // 5 секундын дараа
      },
    });
  };

  return (
    <View style={styles.container}>
      <Text style={styles.heading}>Push Notification</Text>

      <Text style={styles.status}>
        Зөвшөөрөл: {permissionGranted ? "✅ Олгосон" : "❌ Олгоогүй"}
      </Text>

      <TouchableOpacity
        style={[styles.btn, !permissionGranted && styles.btnDisabled]}
        onPress={sendLocalNotification}
        disabled={!permissionGranted}
      >
        <Text style={styles.btnText}>Шууд мэдэгдэл илгээх</Text>
      </TouchableOpacity>

      <TouchableOpacity
        style={[styles.secondaryBtn, !permissionGranted && styles.btnDisabled]}
        onPress={sendDelayedNotification}
        disabled={!permissionGranted}
      >
        <Text style={styles.secondaryText}>5 секундын дараа илгээх</Text>
      </TouchableOpacity>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: "center",
    alignItems: "center",
    backgroundColor: "#0b1120",
    padding: 24,
    gap: 16,
  },
  heading: {
    fontSize: 24,
    fontWeight: "bold",
    color: "#f1f5f9",
    marginBottom: 8,
  },
  status: {
    color: "#94a3b8",
    fontSize: 14,
    marginBottom: 8,
  },
  btn: {
    backgroundColor: "#22d3ee",
    paddingHorizontal: 24,
    paddingVertical: 14,
    borderRadius: 12,
    width: "100%",
    alignItems: "center",
  },
  btnDisabled: {
    opacity: 0.4,
  },
  btnText: {
    color: "#0b1120",
    fontWeight: "700",
    fontSize: 15,
  },
  secondaryBtn: {
    backgroundColor: "#1e293b",
    paddingHorizontal: 24,
    paddingVertical: 14,
    borderRadius: 12,
    width: "100%",
    alignItems: "center",
    borderWidth: 1,
    borderColor: "#475569",
  },
  secondaryText: {
    color: "#f1f5f9",
    fontWeight: "600",
    fontSize: 15,
  },
});

trigger: null гэдэг нь шууд илгээнэ гэсэн үг. trigger: { seconds: 5 } гэвэл 5 секундын дараа, trigger: { hour: 9, minute: 0, repeats: true } гэвэл өдөр бүр 09:00-д илгээнэ.

Notification-г сонсох — listener

Notification ирэх үед болон хэрэглэгч дарах үед event сонсоно. Энэ нь жишээ нь notification дарахад тодорхой дэлгэц нээх зорилгоор ашиглагдана:

jsx
import { useEffect, useRef } from "react";
import * as Notifications from "expo-notifications";

export default function useNotificationListener(onReceive, onPress) {
  const receiveListener = useRef(null);
  const responseListener = useRef(null);

  useEffect(() => {
    // Notification ирэх үед (апп нээлттэй байхад)
    receiveListener.current = Notifications.addNotificationReceivedListener(
      (notification) => {
        const { title, body, data } = notification.request.content;
        console.log("Мэдэгдэл ирлээ:", title);
        onReceive?.({ title, body, data });
      },
    );

    // Хэрэглэгч notification дарах үед
    responseListener.current =
      Notifications.addNotificationResponseReceivedListener((response) => {
        const data = response.notification.request.content.data;
        console.log("Мэдэгдэл дарагдлаа, data:", data);
        onPress?.(data);
      });

    return () => {
      receiveListener.current?.remove();
      responseListener.current?.remove();
    };
  }, []);
}

Push token авах — серверээс мэдэгдэл илгээхэд

Серверээс notification илгээхийн тулд тухайн төхөөрөмжийн push token-г авах шаардлагатай. Token нь утас бүрт өвөрмөц бөгөөд backend-д хадгална:

jsx
import * as Notifications from "expo-notifications";
import * as Device from "expo-device";
import Constants from "expo-constants";

async function getPushToken() {
  // Симулятор дээр push token ажиллахгүй — жинхэнэ төхөөрөмж хэрэгтэй
  if (!Device.isDevice) {
    console.warn("Push token зөвхөн жинхэнэ утсан дээр ажилладаг");
    return null;
  }

  const { status } = await Notifications.requestPermissionsAsync();
  if (status !== "granted") return null;

  const projectId = Constants.expoConfig?.extra?.eas?.projectId;
  const tokenData = await Notifications.getExpoPushTokenAsync({ projectId });
  return tokenData.data;
  // Буцаах утга жишээ:
  // "ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]"
}

// Хэрэглэх:
// const token = await getPushToken();
// await saveTokenToServer(token);  // backend-д хадгална

Push notification нь хэрэглэгчийг апп руу буцааж татах хамгийн үр дүнтэй арга. Монгол апп-д "Захиалга хүргэгдлээ", "Таны дансны үлдэгдэл өөрчлөгдлөө" зэрэг мэдэгдэл маш хэрэгтэй байдаг. Суурийг эзэмшивэл ямар ч мэдэгдэл илгээж чадна!

Дараагийн хичээлд:

Апп зөвшөөрөл — камер, галерей, байршил, микрофон зэрэг device зөвшөөрлийг нэгтгэн удирдах зөв арга сурна.