Resolve merge conflict between main and vypq
This commit is contained in:
@@ -1,7 +1,13 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:sfm_app/product/cache/local_manager.dart';
|
||||
import 'package:sfm_app/product/constant/app/api_path_constant.dart';
|
||||
import 'package:sfm_app/product/constant/enums/local_keys_enums.dart';
|
||||
import 'package:sfm_app/product/network/network_manager.dart';
|
||||
import '../product/base/bloc/base_bloc.dart';
|
||||
import '../product/services/api_services.dart';
|
||||
import '../feature/bell/bell_model.dart';
|
||||
@@ -41,4 +47,32 @@ class MainBloc extends BlocBase {
|
||||
User user = User.fromJson(jsonDecode(data));
|
||||
sinkUserProfile.add(user);
|
||||
}
|
||||
|
||||
getFCMTokenAndPresentations() async {
|
||||
|
||||
String? firebaseAppToken = await FirebaseMessaging.instance.getToken();
|
||||
|
||||
if (firebaseAppToken != null) {
|
||||
log("FCM TOKEN: $firebaseAppToken");
|
||||
sendNotificationToken(firebaseAppToken);
|
||||
} else {
|
||||
log("FCM TOKEN: null");
|
||||
}
|
||||
}
|
||||
|
||||
Future<int> sendNotificationToken(String token) async{
|
||||
String uid = await getUID();
|
||||
Map<String,dynamic> body = {
|
||||
"user_id": uid,
|
||||
"app_token": token
|
||||
};
|
||||
int statusCode = await NetworkManager.instance!.updateDataInServer(
|
||||
APIPathConstants.NOTIFICATION_TOKEN_PATH, body);
|
||||
return statusCode;
|
||||
}
|
||||
|
||||
Future<String> getUID() async {
|
||||
String uid = LocaleManager.instance.getStringValue(PreferencesKeys.UID);
|
||||
return uid;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,9 +6,11 @@ import 'package:firebase_core/firebase_core.dart';
|
||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:badges/badges.dart' as badges;
|
||||
import 'package:persistent_bottom_nav_bar/persistent_bottom_nav_bar.dart';
|
||||
import 'package:sfm_app/product/utils/permission_handler.dart';
|
||||
import '../../product/permission/notification_permission.dart';
|
||||
import '../../product/services/notification_services.dart';
|
||||
import '../settings/profile/profile_model.dart';
|
||||
@@ -46,20 +48,20 @@ class MainScreen extends StatefulWidget {
|
||||
|
||||
PersistentTabController controller = PersistentTabController(initialIndex: 0);
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {
|
||||
log("Full background message payload: ${message.toMap()}");
|
||||
await Firebase.initializeApp();
|
||||
final notificationServices = NotificationServices();
|
||||
await notificationServices.initLocalNotifications(controller);
|
||||
await notificationServices.showNotification(message);
|
||||
log("Background message handled: ${message.data['title']}");
|
||||
}
|
||||
// @pragma('vm:entry-point')
|
||||
// Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {
|
||||
// log("Full background message payload: ${message.toMap()}");
|
||||
// await Firebase.initializeApp();
|
||||
// final notificationServices = NotificationServices();
|
||||
// await notificationServices.initLocalNotifications(controller);
|
||||
// await notificationServices.showNotification(message);
|
||||
// log("Background message handled: ${message.data['title']}");
|
||||
// }
|
||||
|
||||
|
||||
class _MainScreenState extends State<MainScreen> with WidgetsBindingObserver {
|
||||
APIServices apiServices = APIServices();
|
||||
final NotificationServices notificationServices = NotificationServices();
|
||||
// final NotificationServices notificationServices = NotificationServices();
|
||||
late MainBloc mainBloc;
|
||||
bool isVN = true;
|
||||
bool isLight = true;
|
||||
@@ -86,7 +88,7 @@ class _MainScreenState extends State<MainScreen> with WidgetsBindingObserver {
|
||||
}
|
||||
mainBloc.sinkIsVNIcon.add(isVN);
|
||||
mainBloc.sinkThemeMode.add(isLight);
|
||||
LocationPermissionRequest.instance.checkLocationPermission(context);
|
||||
checkAndRequestPermission();
|
||||
NotificationPermission.instance.checkNotificationPermission(context);
|
||||
}
|
||||
|
||||
@@ -94,13 +96,38 @@ class _MainScreenState extends State<MainScreen> with WidgetsBindingObserver {
|
||||
void initState() {
|
||||
super.initState();
|
||||
mainBloc = BlocProvider.of(context);
|
||||
mainBloc.getFCMTokenAndPresentations();
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
initialCheck();
|
||||
getBellNotification();
|
||||
mainBloc.getUserProfile();
|
||||
notificationServices.initLocalNotifications(controller);
|
||||
notificationServices.firebaseInit(context);
|
||||
NotificationServices().setupInteractMessage(controller);
|
||||
|
||||
FirebaseMessaging.instance.onTokenRefresh.listen((newToken) {
|
||||
log("New FCM Token: $newToken");
|
||||
// Gửi token mới lên server
|
||||
mainBloc.sendNotificationToken(newToken);
|
||||
});
|
||||
|
||||
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
|
||||
RemoteNotification? notification = message.notification;
|
||||
AndroidNotification? android = message.notification?.android;
|
||||
if (notification != null && android != null ) {
|
||||
const AndroidNotificationDetails androidPlatformChannelSpecifics =
|
||||
AndroidNotificationDetails(
|
||||
'your channel id', 'your channel name',
|
||||
importance: Importance.max,
|
||||
priority: Priority.high,
|
||||
ticker: 'ticker');
|
||||
const NotificationDetails platformChannelSpecifics =
|
||||
NotificationDetails(android: androidPlatformChannelSpecifics);
|
||||
flutterLocalNotificationsPlugin.show(
|
||||
notification.hashCode, notification.title, notification.body, platformChannelSpecifics,
|
||||
);
|
||||
}
|
||||
});
|
||||
// notificationServices.initLocalNotifications(controller);
|
||||
// notificationServices.firebaseInit(context);
|
||||
// NotificationServices().setupInteractMessage(controller);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -110,7 +137,6 @@ class _MainScreenState extends State<MainScreen> with WidgetsBindingObserver {
|
||||
log("App Inactive");
|
||||
} else if (state == AppLifecycleState.resumed) {
|
||||
log("App Resumed");
|
||||
LocationPermissionRequest.instance.checkLocationPermission(context);
|
||||
} else if (state == AppLifecycleState.paused) {
|
||||
log("App paused");
|
||||
} else if (state == AppLifecycleState.detached) {
|
||||
@@ -220,7 +246,7 @@ class _MainScreenState extends State<MainScreen> with WidgetsBindingObserver {
|
||||
SizedBox(
|
||||
width: context.lowValue,
|
||||
),
|
||||
Text(userSnapshot.data?.name ?? "")
|
||||
Flexible( child: Text(userSnapshot.data?.name ?? ""))
|
||||
],
|
||||
);
|
||||
}),
|
||||
|
||||
@@ -15,6 +15,7 @@ import 'package:sfm_app/product/base/bloc/base_bloc.dart';
|
||||
import 'package:sfm_app/product/constant/icon/icon_constants.dart';
|
||||
import 'package:sfm_app/product/permission/location_permission.dart';
|
||||
import 'package:sfm_app/product/services/api_services.dart';
|
||||
import 'package:sfm_app/product/utils/permission_handler.dart';
|
||||
import '../../product/constant/enums/app_theme_enums.dart';
|
||||
|
||||
class MapScreen extends StatefulWidget {
|
||||
@@ -181,8 +182,8 @@ class _MapScreenState extends State<MapScreen> with WidgetsBindingObserver {
|
||||
),
|
||||
position: cluster.location,
|
||||
onTap: () async {
|
||||
bool check = await checkLocationPermission(context);
|
||||
if (check == true) {
|
||||
LocationPermission permission = await checkAndRequestPermission();
|
||||
if (permission == LocationPermission.whileInUse || permission == LocationPermission.always) {
|
||||
Position position = await Geolocator.getCurrentPosition();
|
||||
onTapMarker(
|
||||
// ignore: use_build_context_synchronously
|
||||
@@ -284,9 +285,9 @@ class _MapScreenState extends State<MapScreen> with WidgetsBindingObserver {
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> checkLocationPermission(context) async {
|
||||
bool check = await LocationPermissionRequest.instance
|
||||
.checkLocationPermission(context);
|
||||
return check;
|
||||
}
|
||||
// Future<bool> checkLocationPermission(context) async {
|
||||
// bool check = await LocationPermissionRequest.instance
|
||||
// .checkLocationPermission(context);
|
||||
// return check;
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ showNearPlacesSideSheet(
|
||||
padding: context.paddingLow,
|
||||
width: screenWidth,
|
||||
height: screenHeight / 3,
|
||||
color: Colors.white,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
|
||||
122
lib/main.dart
122
lib/main.dart
@@ -2,9 +2,11 @@ import 'dart:developer';
|
||||
|
||||
import 'package:firebase_core/firebase_core.dart';
|
||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import 'feature/main/main_screen.dart';
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
import 'package:sfm_app/firebase_options.dart';
|
||||
import 'product/services/api_services.dart';
|
||||
import 'product/services/notification_services.dart';
|
||||
import 'product/services/theme_services.dart';
|
||||
@@ -13,12 +15,89 @@ import 'bloc/main_bloc.dart';
|
||||
import 'product/base/bloc/base_bloc.dart';
|
||||
import 'product/constant/navigation/navigation_router.dart';
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
|
||||
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
|
||||
await setupFlutterNotifications();
|
||||
showFlutterNotification(message);
|
||||
// If you're going to use other Firebase services in the background, such as Firestore,
|
||||
// make sure you call `initializeApp` before using other Firebase services.
|
||||
print('Handling a background message ${message.messageId}');
|
||||
}
|
||||
|
||||
|
||||
/// Create a [AndroidNotificationChannel] for heads up notifications
|
||||
late AndroidNotificationChannel channel;
|
||||
|
||||
bool isFlutterLocalNotificationsInitialized = false;
|
||||
|
||||
Future<void> setupFlutterNotifications() async {
|
||||
if (isFlutterLocalNotificationsInitialized) {
|
||||
return;
|
||||
}
|
||||
channel = const AndroidNotificationChannel(
|
||||
'high_importance_channel', // id
|
||||
'High Importance Notifications', // title
|
||||
description:
|
||||
'This channel is used for important notifications.', // description
|
||||
importance: Importance.high,
|
||||
);
|
||||
|
||||
flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
|
||||
|
||||
/// Create an Android Notification Channel.
|
||||
///
|
||||
/// We use this channel in the `AndroidManifest.xml` file to override the
|
||||
/// default FCM channel to enable heads up notifications.
|
||||
await flutterLocalNotificationsPlugin
|
||||
.resolvePlatformSpecificImplementation<
|
||||
AndroidFlutterLocalNotificationsPlugin>()
|
||||
?.createNotificationChannel(channel);
|
||||
|
||||
/// Update the iOS foreground notification presentation options to allow
|
||||
/// heads up notifications.
|
||||
await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
|
||||
alert: true,
|
||||
badge: true,
|
||||
sound: true,
|
||||
);
|
||||
isFlutterLocalNotificationsInitialized = true;
|
||||
}
|
||||
|
||||
void showFlutterNotification(RemoteMessage message) {
|
||||
RemoteNotification? notification = message.notification;
|
||||
AndroidNotification? android = message.notification?.android;
|
||||
if (notification != null && android != null && !kIsWeb) {
|
||||
flutterLocalNotificationsPlugin.show(
|
||||
notification.hashCode,
|
||||
notification.title,
|
||||
notification.body,
|
||||
NotificationDetails(
|
||||
android: AndroidNotificationDetails(
|
||||
channel.id,
|
||||
channel.name,
|
||||
channelDescription: channel.description,
|
||||
// TODO add a proper drawable resource to android, for now using
|
||||
// one that already exists in example app.
|
||||
icon: 'launch_background',
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize the [FlutterLocalNotificationsPlugin] package.
|
||||
late FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin;
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await Firebase.initializeApp();
|
||||
FirebaseMessaging
|
||||
.onBackgroundMessage(firebaseMessagingBackgroundHandler);
|
||||
// NotificationServices().setupInteractMessage();
|
||||
// Set the background messaging handler early on, as a named top-level function
|
||||
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
|
||||
|
||||
if (!kIsWeb) {
|
||||
await setupFlutterNotifications();
|
||||
}
|
||||
|
||||
runApp(
|
||||
BlocProvider(
|
||||
@@ -28,15 +107,7 @@ void main() async {
|
||||
);
|
||||
}
|
||||
|
||||
// @pragma('vm:entry-point')
|
||||
// Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
|
||||
// log("Full background message payload: ${message.toMap()}");
|
||||
// await Firebase.initializeApp();
|
||||
// final notificationServices = NotificationServices();
|
||||
// await notificationServices.initLocalNotifications();
|
||||
// await notificationServices.showNotification(message);
|
||||
// log("Background message handled: ${message.data['title']}");
|
||||
// }
|
||||
|
||||
class MyApp extends StatefulWidget {
|
||||
const MyApp({super.key});
|
||||
|
||||
@@ -59,7 +130,7 @@ class _MyAppState extends State<MyApp> {
|
||||
late MainBloc mainBloc;
|
||||
LanguageServices languageServices = LanguageServices();
|
||||
ThemeServices themeServices = ThemeServices();
|
||||
final NotificationServices notificationServices = NotificationServices();
|
||||
// final NotificationServices notificationServices = NotificationServices();
|
||||
APIServices apiServices = APIServices();
|
||||
setLocale(Locale locale) {
|
||||
_locale = locale;
|
||||
@@ -70,23 +141,24 @@ class _MyAppState extends State<MyApp> {
|
||||
_themeData = theme;
|
||||
mainBloc.sinkTheme.add(_themeData);
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
mainBloc = BlocProvider.of(context);
|
||||
// notificationServices.initLocalNotifications();
|
||||
// notificationServices.firebaseInit(context);
|
||||
// notificationServices.setupInteractMessage();
|
||||
notificationServices.getDeviceToken().then((token){
|
||||
print("Firebase Token: $token");
|
||||
sendNotificationToken(token);
|
||||
});
|
||||
// // notificationServices.initLocalNotifications();
|
||||
// // notificationServices.firebaseInit(context);
|
||||
// // notificationServices.setupInteractMessage();
|
||||
// notificationServices.getDeviceToken().then((token){
|
||||
// print("Firebase Token: $token");
|
||||
// sendNotificationToken(token);
|
||||
// });
|
||||
}
|
||||
|
||||
void sendNotificationToken (String token) async {
|
||||
int statusCode = await apiServices.sendNotificationToken(token);
|
||||
log("Notification Send StatusCode : $statusCode");
|
||||
}
|
||||
// void sendNotificationToken (String token) async {
|
||||
// int statusCode = await apiServices.sendNotificationToken(token);
|
||||
// log("Notification Send StatusCode : $statusCode");
|
||||
// }
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
|
||||
@@ -87,8 +87,8 @@ class RequestPermissionDialog {
|
||||
child: Text(
|
||||
appLocalization(showCupertinoDialogContext).allow_message),
|
||||
onPressed: () {
|
||||
Navigator.pop(showCupertinoDialogContext);
|
||||
AppSettings.openAppSettings(type: appSettingsType);
|
||||
Navigator.pop(showCupertinoDialogContext);
|
||||
},
|
||||
),
|
||||
],
|
||||
|
||||
@@ -12,17 +12,20 @@ class LocationPermissionRequest {
|
||||
static LocationPermissionRequest get instance =>
|
||||
_instance ??= LocationPermissionRequest._init();
|
||||
|
||||
Future<bool> checkLocationPermission(context) async {
|
||||
var status = await Permission.location.status;
|
||||
log("Status: $status");
|
||||
if (status.isDenied || status.isPermanentlyDenied) {
|
||||
requestLocationPermisson(context, Icons.location_on_outlined,
|
||||
Permission.location, AppSettingsType.location, "Location");
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Future<bool> checkLocationPermission(context) async {
|
||||
// var status = await Permission.location.status;
|
||||
// log("Status1: $status");
|
||||
// log("Status2: ${status.isDenied}");
|
||||
// log("Status3: ${status.isPermanentlyDenied}");
|
||||
// log("Status4: ${status.isGranted}");
|
||||
// if (status.isDenied || status.isPermanentlyDenied) {
|
||||
// requestLocationPermisson(context, Icons.location_on_outlined,
|
||||
// Permission.location, AppSettingsType.location, "Location");
|
||||
// return false;
|
||||
// } else {
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
|
||||
void requestLocationPermisson(
|
||||
context,
|
||||
|
||||
@@ -43,21 +43,22 @@ class MapServices {
|
||||
List<LatLng> polylineCoordinates = [];
|
||||
PolylinePoints polylinePoints = PolylinePoints();
|
||||
|
||||
PolylineResult result = await polylinePoints.getRouteBetweenCoordinates(
|
||||
ApplicationConstants.MAP_KEY,
|
||||
PointLatLng(origin.latitude, origin.longitude),
|
||||
PointLatLng(destination.latitude, destination.longitude),
|
||||
travelMode: TravelMode.driving,
|
||||
optimizeWaypoints: true);
|
||||
if (result.points.isNotEmpty) {
|
||||
for (var point in result.points) {
|
||||
polylineCoordinates.add(LatLng(point.latitude, point.longitude));
|
||||
}
|
||||
return polylineCoordinates;
|
||||
} else {
|
||||
log("Lỗi khi tìm đường");
|
||||
return [];
|
||||
}
|
||||
// PolylineResult result = await polylinePoints.getRouteBetweenCoordinates(
|
||||
// ApplicationConstants.MAP_KEY,
|
||||
// PointLatLng(origin.latitude, origin.longitude),
|
||||
// PointLatLng(destination.latitude, destination.longitude),
|
||||
// travelMode: TravelMode.driving,
|
||||
// optimizeWaypoints: true);
|
||||
// if (result.points.isNotEmpty) {
|
||||
// for (var point in result.points) {
|
||||
// polylineCoordinates.add(LatLng(point.latitude, point.longitude));
|
||||
// }
|
||||
// return polylineCoordinates;
|
||||
// } else {
|
||||
// log("Lỗi khi tìm đường");
|
||||
// return [];
|
||||
// }
|
||||
return [];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -39,7 +39,9 @@ class NotificationServices {
|
||||
}
|
||||
|
||||
Future<String> getDeviceToken() async {
|
||||
String? token = await messaging.getToken();
|
||||
print("GET FB TOKEN");
|
||||
String? token = await messaging.getAPNSToken();
|
||||
print("GET FB: ${token}");
|
||||
return token!;
|
||||
}
|
||||
|
||||
@@ -50,6 +52,9 @@ class NotificationServices {
|
||||
}
|
||||
|
||||
Future<void> showNotification(RemoteMessage message) async {
|
||||
dev.log(message.toString());
|
||||
dev.log(message.data.toString());
|
||||
dev.log(message.data["notification"].toString());
|
||||
String? title = message.data['title'];
|
||||
String? body = message.data['body'];
|
||||
String type = message.data['type'] ?? "normal";
|
||||
|
||||
62
lib/product/utils/permission_handler.dart
Normal file
62
lib/product/utils/permission_handler.dart
Normal file
@@ -0,0 +1,62 @@
|
||||
import 'package:geolocator/geolocator.dart';
|
||||
|
||||
/// Kiểm tra xem dịch vụ vị trí có được bật không
|
||||
Future<bool> isLocationServiceEnabled() async {
|
||||
bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
|
||||
if (!serviceEnabled) {
|
||||
print('Vui lòng bật dịch vụ vị trí');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Kiểm tra và yêu cầu quyền truy cập vị trí
|
||||
Future<LocationPermission> checkAndRequestPermission() async {
|
||||
LocationPermission permission = await Geolocator.checkPermission();
|
||||
|
||||
if (permission == LocationPermission.denied) {
|
||||
permission = await Geolocator.requestPermission();
|
||||
if (permission == LocationPermission.denied) {
|
||||
print('Quyền truy cập vị trí bị từ chối');
|
||||
return permission;
|
||||
}
|
||||
}
|
||||
|
||||
if (permission == LocationPermission.deniedForever) {
|
||||
print('Quyền truy cập vị trí bị từ chối vĩnh viễn. Vui lòng cấp quyền trong cài đặt.');
|
||||
return permission;
|
||||
}
|
||||
|
||||
return permission;
|
||||
}
|
||||
|
||||
/// Lấy vị trí hiện tại của người dùng
|
||||
Future<Position?> getCurrentPosition() async {
|
||||
try {
|
||||
Position position = await Geolocator.getCurrentPosition(
|
||||
desiredAccuracy: LocationAccuracy.high,
|
||||
);
|
||||
print('Vị trí hiện tại: ${position.latitude}, ${position.longitude}');
|
||||
return position;
|
||||
} catch (e) {
|
||||
print('Lỗi khi lấy vị trí: $e');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Hàm chính để xử lý toàn bộ quy trình yêu cầu vị trí
|
||||
Future<void> requestLocationPermission() async {
|
||||
// Bước 1: Kiểm tra dịch vụ vị trí
|
||||
bool isServiceEnabled = await isLocationServiceEnabled();
|
||||
if (!isServiceEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Bước 2: Kiểm tra và yêu cầu quyền
|
||||
LocationPermission permission = await checkAndRequestPermission();
|
||||
|
||||
// Bước 3: Nếu quyền được cấp, lấy vị trí
|
||||
if (permission == LocationPermission.whileInUse || permission == LocationPermission.always) {
|
||||
await getCurrentPosition();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user