notif fix on firebase
This commit is contained in:
parent
d782fe2404
commit
79f0f28578
|
|
@ -1,69 +1,73 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>didvan</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(FLUTTER_BUILD_NAME)</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
<key>FirebaseAppDelegateProxyEnabled</key>
|
||||
<false/>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>We need to access to the user gallery to add user profile photo</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>Some message to describe why you need this permission</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>We need to access to the user gallery to add user profile photo</string>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>audio</string>
|
||||
<string>fetch</string>
|
||||
<string>remote-notification</string>
|
||||
</array>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
<key>UNNotificationServiceExtension</key>
|
||||
<array>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER).MyNotificationServiceExtension</string>
|
||||
</array>
|
||||
<key>NSAppleMusicUsageDescription</key>
|
||||
<string>This app requires access to Apple Music to [explain specific reason].</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>... explain why the app uses the microphone here ...</string>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true />
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>didvan</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(FLUTTER_BUILD_NAME)</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
<key>FirebaseAppDelegateProxyEnabled</key>
|
||||
<false />
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true />
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>We need to access to the user gallery to add user profile photo</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>Some message to describe why you need this permission</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>We need to access to the user gallery to add user profile photo</string>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true />
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>audio</string>
|
||||
<string>fetch</string>
|
||||
<string>remote-notification</string>
|
||||
</array>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false />
|
||||
<key>UNNotificationServiceExtension</key>
|
||||
<array>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER).MyNotificationServiceExtension</string>
|
||||
</array>
|
||||
<key>NSAppleMusicUsageDescription</key>
|
||||
<string>This app requires access to Apple Music to [explain specific reason].</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>... explain why the app uses the microphone here ...</string>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true />
|
||||
</dict>
|
||||
<key>BGTaskSchedulerPermittedIdentifiers</key>
|
||||
<array>
|
||||
<string>dev.flutter.background.refresh</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -15,6 +15,7 @@ import 'package:didvan/providers/user.dart';
|
|||
import 'package:didvan/routes/route_generator.dart';
|
||||
import 'package:didvan/routes/routes.dart';
|
||||
import 'package:didvan/services/app_home_widget/home_widget_repository.dart';
|
||||
import 'package:didvan/services/back_services.dart';
|
||||
import 'package:didvan/services/media/media.dart';
|
||||
import 'package:didvan/services/notification/firebase_api.dart';
|
||||
import 'package:didvan/services/notification/notification_service.dart';
|
||||
|
|
@ -32,24 +33,23 @@ import 'package:provider/provider.dart';
|
|||
|
||||
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
Future _initPushNotification(RemoteMessage message) async {
|
||||
if (!kIsWeb) {
|
||||
await NotificationService.initializeNotification();
|
||||
}
|
||||
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
|
||||
// if (kDebugMode) {
|
||||
print("background: ${NotificationData.fromJson(message.data).toJson()}");
|
||||
// }
|
||||
try {
|
||||
NotificationService.showFirebaseNotification(message);
|
||||
NotificationService.startListeningNotificationEvents();
|
||||
} catch (e) {
|
||||
// if (kDebugMode) {
|
||||
print(e);
|
||||
// }
|
||||
}
|
||||
}
|
||||
// @pragma('vm:entry-point')
|
||||
// Future _initPushNotification(RemoteMessage message) async {
|
||||
// if (!kIsWeb) {
|
||||
// await NotificationService.startListeningNotificationEvents();
|
||||
// }
|
||||
// await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
|
||||
// // if (kDebugMode) {
|
||||
// print("background: ${NotificationData.fromJson(message.data).toJson()}");
|
||||
// // }
|
||||
// try {
|
||||
// NotificationService.showFirebaseNotification(message);
|
||||
// } catch (e) {
|
||||
// // if (kDebugMode) {
|
||||
// print(e);
|
||||
// // }
|
||||
// }
|
||||
// }
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
Future<void> _backgroundCallbackHomeWidget(Uri? uri) async {
|
||||
|
|
@ -65,7 +65,6 @@ Future<void> _backgroundCallbackHomeWidget(Uri? uri) async {
|
|||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
try {
|
||||
if (!kIsWeb) {
|
||||
HomeWidget.registerBackgroundCallback(_backgroundCallbackHomeWidget);
|
||||
|
|
@ -73,7 +72,7 @@ void main() async {
|
|||
await NotificationService.initializeNotification();
|
||||
}
|
||||
|
||||
FirebaseMessaging.onBackgroundMessage(_initPushNotification);
|
||||
// FirebaseMessaging.onBackgroundMessage(_initPushNotification);
|
||||
await Firebase.initializeApp(
|
||||
options: DefaultFirebaseOptions.currentPlatform);
|
||||
await FirebaseApi().initNotification();
|
||||
|
|
|
|||
|
|
@ -65,9 +65,7 @@ class UserProvider extends CoreProvier {
|
|||
value: service.result['user']['end'],
|
||||
);
|
||||
|
||||
FirebaseApi()
|
||||
.initNotification()
|
||||
.then((value) => _registerFirebaseToken());
|
||||
await _registerFirebaseToken();
|
||||
|
||||
return true;
|
||||
} catch (e) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
import 'dart:async';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:didvan/firebase_options.dart';
|
||||
import 'package:didvan/models/notification_data.dart';
|
||||
import 'package:didvan/services/notification/notification_service.dart';
|
||||
import 'package:firebase_core/firebase_core.dart';
|
||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter_background_service/flutter_background_service.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
Future<void> initializeService() async {
|
||||
final service = FlutterBackgroundService();
|
||||
await service.configure(
|
||||
iosConfiguration: IosConfiguration(
|
||||
autoStart: true,
|
||||
onForeground: onStart,
|
||||
onBackground: onIosBackground),
|
||||
androidConfiguration: AndroidConfiguration(
|
||||
onStart: onStart,
|
||||
isForegroundMode: true,
|
||||
autoStart: true,
|
||||
autoStartOnBoot: true));
|
||||
}
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
Future<bool> onIosBackground(ServiceInstance service) async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
DartPluginRegistrant.ensureInitialized();
|
||||
return true;
|
||||
}
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
void onStart(ServiceInstance service) {
|
||||
DartPluginRegistrant.ensureInitialized();
|
||||
if (service is AndroidServiceInstance) {
|
||||
service.on('setAsForeground').listen((event) {
|
||||
service.setAsForegroundService();
|
||||
});
|
||||
service.on('setAsBackground').listen((event) {
|
||||
service.setAsBackgroundService();
|
||||
});
|
||||
}
|
||||
service.on('stopService').listen((event) {
|
||||
service.stopSelf();
|
||||
});
|
||||
Timer.periodic(const Duration(seconds: 1), (timer) async {
|
||||
if (service is AndroidServiceInstance) {
|
||||
if (await service.isForegroundService()) {}
|
||||
}
|
||||
|
||||
NotificationService.startListeningNotificationEvents();
|
||||
print('background service running');
|
||||
service.invoke('update');
|
||||
});
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ import 'package:didvan/services/notification/notification_service.dart';
|
|||
import 'package:didvan/services/storage/storage.dart';
|
||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class FirebaseApi {
|
||||
|
|
@ -33,11 +34,34 @@ class FirebaseApi {
|
|||
sound: true,
|
||||
);
|
||||
|
||||
FirebaseMessaging.instance
|
||||
.getInitialMessage()
|
||||
.asStream()
|
||||
.listen((event) async {});
|
||||
FirebaseMessaging.onMessageOpenedApp.listen((event) {});
|
||||
final initMsg = await FirebaseMessaging.instance.getInitialMessage();
|
||||
|
||||
if (initMsg != null) {
|
||||
try {
|
||||
NotificationMessage data = NotificationMessage.fromJson(initMsg.data);
|
||||
HomeWidgetRepository.data = data;
|
||||
print("data: ${HomeWidgetRepository.data}");
|
||||
await HomeWidgetRepository.decideWhereToGoNotif();
|
||||
await StorageService.delete(
|
||||
key: 'notification${AppInitializer.createNotificationId(data)}');
|
||||
} catch (e) {
|
||||
e.printError();
|
||||
}
|
||||
}
|
||||
|
||||
FirebaseMessaging.onMessageOpenedApp.listen((initMsg) async {
|
||||
try {
|
||||
NotificationMessage data = NotificationMessage.fromJson(initMsg.data);
|
||||
HomeWidgetRepository.data = data;
|
||||
print("data: ${HomeWidgetRepository.data}");
|
||||
await HomeWidgetRepository.decideWhereToGoNotif();
|
||||
await StorageService.delete(
|
||||
key: 'notification${AppInitializer.createNotificationId(data)}');
|
||||
} catch (e) {
|
||||
e.printError();
|
||||
}
|
||||
});
|
||||
|
||||
FirebaseMessaging.onMessage.listen((event) => handleMessage(event));
|
||||
}
|
||||
|
||||
|
|
@ -46,6 +70,13 @@ class FirebaseApi {
|
|||
//do ever you want with message
|
||||
// if (kDebugMode) {
|
||||
print("forground: ${NotificationData.fromJson(message.data).toJson()}");
|
||||
const platform = MethodChannel('com.didvan.didvanapp/notification');
|
||||
|
||||
await platform.invokeMethod('showNotification', {
|
||||
'title': message.notification!.title,
|
||||
'message': message.notification!.body
|
||||
});
|
||||
|
||||
// }
|
||||
try {
|
||||
await NotificationService.showFirebaseNotification(message);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import 'package:didvan/services/app_initalizer.dart';
|
|||
import 'package:didvan/services/storage/storage.dart';
|
||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class NotificationService {
|
||||
|
|
@ -163,3 +164,47 @@ class NotificationService {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NotificationHelper {
|
||||
static final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
|
||||
FlutterLocalNotificationsPlugin();
|
||||
|
||||
static Future<void> initialized() async {
|
||||
const AndroidInitializationSettings initializationSettingsAndroid =
|
||||
AndroidInitializationSettings('@mipmap/ic_launcher');
|
||||
final initializationSettingsIos = DarwinInitializationSettings(
|
||||
requestAlertPermission: true,
|
||||
requestBadgePermission: true,
|
||||
requestSoundPermission: true,
|
||||
onDidReceiveLocalNotification: (id, title, body, payload) async {},
|
||||
);
|
||||
|
||||
await flutterLocalNotificationsPlugin.initialize(
|
||||
InitializationSettings(
|
||||
android: initializationSettingsAndroid,
|
||||
iOS: initializationSettingsIos),
|
||||
onDidReceiveNotificationResponse: (details) {
|
||||
print("onDidReceiveNotificationResponse: ${details}");
|
||||
}, onDidReceiveBackgroundNotificationResponse: localBackgroundHandler);
|
||||
}
|
||||
|
||||
static void displayNotification(RemoteMessage message) async {
|
||||
try {
|
||||
final id = DateTime.now().millisecondsSinceEpoch ~/ 1000;
|
||||
const notifDetails = NotificationDetails(
|
||||
android: AndroidNotificationDetails(
|
||||
'push_notificatiion', 'push_notificatiion_channel',
|
||||
importance: Importance.max, priority: Priority.high));
|
||||
|
||||
await flutterLocalNotificationsPlugin.show(
|
||||
id, message.data['title'], message.data['body'], notifDetails);
|
||||
} on Exception catch (e) {
|
||||
e.printError();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
Future<void> localBackgroundHandler(NotificationResponse data) async {
|
||||
print("onDidReceiveBackgroundNotificationResponse: ${data}");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ class _WebViewState extends State<WebView> {
|
|||
void initState() {
|
||||
controller = WebViewController()
|
||||
..setJavaScriptMode(JavaScriptMode.unrestricted)
|
||||
..setBackgroundColor(Theme.of(context).colorScheme.background)
|
||||
..setNavigationDelegate(
|
||||
NavigationDelegate(
|
||||
onProgress: (int progress) {
|
||||
|
|
|
|||
|
|
@ -311,7 +311,7 @@ class _DidvanPageViewState extends State<DidvanPageView> {
|
|||
),
|
||||
);
|
||||
} else {
|
||||
AppInitializer.openWebLink(content, href,
|
||||
AppInitializer.openWebLink(context, href,
|
||||
mode: LaunchMode.inAppWebView);
|
||||
}
|
||||
},
|
||||
|
|
|
|||
64
pubspec.lock
64
pubspec.lock
|
|
@ -358,6 +358,38 @@ packages:
|
|||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_background_service:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_background_service
|
||||
sha256: d32f078ec57647c9cfd6e1a8da9297f7d8f021d4dcc204a35aaad2cdbfe255f0
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.0.10"
|
||||
flutter_background_service_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_background_service_android
|
||||
sha256: "39da42dddf877beeef82bc2583130d8bedb4d0765e99ca9e7b4a32e8c6abd239"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.2.7"
|
||||
flutter_background_service_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_background_service_ios
|
||||
sha256: "6037ffd45c4d019dab0975c7feb1d31012dd697e25edc05505a4a9b0c7dc9fba"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.0.3"
|
||||
flutter_background_service_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_background_service_platform_interface
|
||||
sha256: ca74aa95789a8304f4d3f57f07ba404faa86bed6e415f83e8edea6ad8b904a41
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.1.2"
|
||||
flutter_cache_manager:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -382,6 +414,30 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.3"
|
||||
flutter_local_notifications:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_local_notifications
|
||||
sha256: c500d5d9e7e553f06b61877ca6b9c8b92c570a4c8db371038702e8ce57f8a50f
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "17.2.2"
|
||||
flutter_local_notifications_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_local_notifications_linux
|
||||
sha256: c49bd06165cad9beeb79090b18cd1eb0296f4bf4b23b84426e37dd7c027fc3af
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.1"
|
||||
flutter_local_notifications_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_local_notifications_platform_interface
|
||||
sha256: "85f8d07fe708c1bdcf45037f2c0109753b26ae077e9d9e899d55971711a4ea66"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.2.0"
|
||||
flutter_localizations:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
|
|
@ -1098,6 +1154,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.1"
|
||||
timezone:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: timezone
|
||||
sha256: "2236ec079a174ce07434e89fcd3fcda430025eb7692244139a9cf54fdcf1fc7d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.9.4"
|
||||
toggle_switch:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
|
|||
|
|
@ -92,6 +92,8 @@ dependencies:
|
|||
mime: ^1.0.2
|
||||
path: any
|
||||
flutter_cache_manager: any
|
||||
flutter_local_notifications: ^17.2.2
|
||||
flutter_background_service: ^5.0.10
|
||||
# url_launcher: ^6.3.0
|
||||
|
||||
dev_dependencies:
|
||||
|
|
|
|||
|
|
@ -28,12 +28,16 @@
|
|||
|
||||
<!-- Favicon -->
|
||||
<link rel="icon" type="image/png" href="favicon.png" />
|
||||
<script>
|
||||
// Import the functions you need from the SDKs you need
|
||||
import { initializeApp } from "https://www.gstatic.com/firebasejs/9.9.1/firebase-app.js";
|
||||
import { getAnalytics } from "https://www.gstatic.com/firebasejs/9.9.1/firebase-analytics.js";
|
||||
import { getMessaging } from "https://www.gstatic.com/firebasejs/9.9.1/firebase-messagin.js";
|
||||
<script src="flutter_bootstrap.js" async>
|
||||
if ('serviceWorker' in navigator) {
|
||||
window.addEventListener('load', function () {
|
||||
navigator.serviceWorker.register('firebase-messaging-sw.js', {
|
||||
scope: '/firebase-cloud-messaging-push-scope',
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<title>Didvan</title>
|
||||
<link rel="manifest" href="manifest.json" />
|
||||
<style>
|
||||
|
|
|
|||
Loading…
Reference in New Issue