Files
mobile-flutter/covas_mobile_new/lib/pages/ListItemByOrganizers.dart
2025-08-31 18:22:40 +02:00

276 lines
8.9 KiB
Dart

import 'dart:convert';
import 'dart:io';
import "ItemMenu.dart";
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import '../classes/events.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:intl/intl.dart';
import 'package:intl/date_symbol_data_local.dart';
import '../variable/globals.dart' as globals;
import '../classes/MyDrawer.dart';
import '../classes/auth_service.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:covas_mobile/gen_l10n/app_localizations.dart';
import 'package:provider/provider.dart';
import '../locale_provider.dart'; //
import '../classes/notification_service.dart';
// app starting point
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await initializeDateFormatting("fr_FR", null);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: [
const Locale('fr', 'FR'),
],
home: const ListItemOrganizers(organizer: "default"),
debugShowCheckedModeBanner: false,
);
}
}
// homepage class
class ListItemOrganizers extends StatefulWidget {
const ListItemOrganizers({Key? key, required this.organizer})
: super(key: key);
final String organizer;
@override
State<ListItemOrganizers> createState() => _MyHomePageState();
}
// homepage state
class _MyHomePageState extends State<ListItemOrganizers> {
int _fetchCount = 0;
bool _isLoading = false;
late ScrollController _scrollController;
final AuthService _authService = AuthService();
void _incrementFetchCount() {
setState(() {
_fetchCount++;
});
}
void _scrollListener() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
_incrementFetchCount();
_fetchData();
}
}
Future<void> _fetchData() async {
if (_isLoading) return;
setState(() {
_isLoading = true;
});
await Future.delayed(Duration(seconds: 2));
getPosts(widget.organizer, count: _fetchCount);
setState(() {
_isLoading = false;
});
}
Future<Map<String, dynamic>> toggleInterested(String eventId) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
var accessToken = prefs.getString("access_token") ?? "";
final url = Uri.parse("${globals.api}/events/${eventId}/interest");
if (accessToken.isNotEmpty) {
final response = await http.post(
url,
headers: {
"Content-Type": "application/json",
HttpHeaders.cookieHeader: "access_token=$accessToken"
},
);
if (response.statusCode != 200) {
throw (AppLocalizations.of(context)?.toogle_interest ??
"Error toogle interest: ${response.statusCode}");
}
var event = json.decode(response.body);
return event;
}
return {"interested": false, "interested_count": 0};
}
// variable to call and store future list of posts
// function to fetch data from api and return future list of posts
static Future<List<Events>> getPosts(organizer, {count = 0}) async {
await initializeDateFormatting("fr_FR", null);
SharedPreferences prefs = await SharedPreferences.getInstance();
var accessToken = prefs.getString("access_token") ?? "";
final List<Events> body = [];
if (accessToken.isNotEmpty) {
DateTime currentDatetime = DateTime.now();
num limit = 20 * (count + 1);
var url = Uri.parse(
"${globals.api}/events?organizers=${organizer}&limit=${limit}&current_datetime=${currentDatetime.toString()}");
final response = await http.get(url, headers: {
"Content-Type": "application/json",
HttpHeaders.cookieHeader: "access_token=${accessToken}"
});
final List body = json.decode(utf8.decode(response.bodyBytes));
return body.map((e) => Events.fromJson(e)).toList();
}
return body;
}
@override
void initState() {
super.initState();
_authService.checkTokenStatus(context);
_scrollController = ScrollController();
_scrollController.addListener(_scrollListener);
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
// build function
@override
Widget build(BuildContext context) {
return Scaffold(
drawer: MyDrawer(),
body: Center(
// FutureBuilder
child: FutureBuilder<List<Events>>(
future: getPosts(widget.organizer),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
// until data is fetched, show loader
return const CircularProgressIndicator();
} else if (snapshot.hasData) {
// once data is fetched, display it on screen (call buildPosts())
final posts = snapshot.data!;
return buildPosts(posts);
} else {
// if no data, show simple Text
return Text(
AppLocalizations.of(context)?.no_data ?? "No data available");
}
},
),
),
);
}
// function to display fetched data on screen
Widget buildPosts(List<Events> posts) {
String organizer =
AppLocalizations.of(context)?.item_organizer ?? "Organizer : ";
// ListView Builder to show data in a list
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text("${organizer}${widget.organizer}",
overflow: TextOverflow.ellipsis),
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
),
body: ListView.separated(
controller: _scrollController,
itemCount: posts.isNotEmpty
? posts.length + (_isLoading ? 1 : 0) // Add 1 only if loading
: 0,
itemBuilder: (context, index) {
final post = posts[index];
final startDate = DateTime.parse(post.startDate!);
final locale = Provider.of<LocaleProvider>(context, listen: false)
.locale
?.toString() ??
'en_US';
final dateLongue =
DateFormat('EEEE d MMMM y', locale).format(startDate);
final countInterestedString =
AppLocalizations.of(context)?.count_interested ??
"Interested people number";
final countInterested =
"${countInterestedString} : ${post.interestedCount}";
return ListTile(
title: Text('${post.name!}'),
subtitle:
Text('${post.place!}\n${dateLongue}\n${countInterested}'),
trailing: IconButton(
onPressed: () async {
try {
final result = await toggleInterested(post.id!);
setState(() {
post.interested = result["interested"];
post.interestedCount = result["interested_count"];
});
if (result["interested"] == true) {
NotificationService.scheduleEventNotification(
eventId: post.id!,
title: "Rappel évènement",
body:
"Ton évènement '${post.name}' commence dans 1 heure !",
eventDate: DateTime.parse(post.startDate!),
);
} else {
NotificationService.cancel(post.id!);
}
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
AppLocalizations.of(context)?.error_update ??
"Error when updating")),
);
}
},
icon: Icon(
post.interested ?? false
? Icons.favorite
: Icons.favorite_border,
color:
post.interested ?? false ? Colors.red : Colors.grey)),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => ItemMenu(title: post.id!)));
});
},
separatorBuilder: (context, index) {
return Divider();
},
),
);
}
}