276 lines
8.9 KiB
Dart
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}¤t_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();
|
|
},
|
|
),
|
|
);
|
|
}
|
|
}
|