Skip to content

Commit ca5e635

Browse files
committed
#34: Provide a page to display a list of communication URLs
1 parent de1334a commit ca5e635

File tree

5 files changed

+184
-4
lines changed

5 files changed

+184
-4
lines changed

lib/controls/nav_bar.dart

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'package:flutter/material.dart';
22
import 'package:iccm_eu_app/data/appProviders/page_index_provider.dart';
33
import 'package:iccm_eu_app/pages/about_page.dart';
4+
import 'package:iccm_eu_app/pages/communication_page.dart';
45
import 'package:iccm_eu_app/pages/home_page.dart';
56
import 'package:iccm_eu_app/pages/preferences_page.dart';
67
import 'package:iccm_eu_app/pages/rooms_page.dart';
@@ -19,8 +20,9 @@ enum PageList {
1920
rooms, // 4
2021
preferences, // 5
2122
travelInformation, // 6
22-
about, // 7
23-
share,// 8
23+
communication, // 7
24+
about, // 8
25+
share,// 9
2426
}
2527

2628
class NavBar extends StatefulWidget {
@@ -36,8 +38,9 @@ class NavBar extends StatefulWidget {
3638
RoomsPage(), // 4
3739
PreferencesPage(), // 5
3840
const TravelPage(), // 6
39-
const AboutPage(), // 7
40-
const SharePage(url: 'https://github.com/ICCM-EU/iccm_eu_app'), // 8
41+
const CommunicationPage(), // 7
42+
const AboutPage(), // 8
43+
const SharePage(url: 'https://github.com/ICCM-EU/iccm_eu_app'), // 9
4144
];
4245

4346
List<Widget> get widgetOptions => _widgetOptions;
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import 'dart:convert';
2+
3+
import 'package:flutter/material.dart';
4+
import 'package:iccm_eu_app/data/dataProviders/gsheets_provider.dart';
5+
import 'package:iccm_eu_app/data/model/communication_data.dart';
6+
import 'package:iccm_eu_app/utils/debug.dart';
7+
import 'package:shared_preferences/shared_preferences.dart';
8+
9+
class CommunicationProvider with ChangeNotifier {
10+
static String get worksheetTitle => "Communication";
11+
12+
String get _cacheTitle => "_communicationDataCache";
13+
final GsheetsProvider _gsheetsProvider;
14+
15+
final List<CommunicationData> _cache = [];
16+
17+
final List<CommunicationData> _items = [];
18+
19+
List<CommunicationData> items() {
20+
return _items;
21+
}
22+
23+
CommunicationProvider({
24+
required GsheetsProvider gsheetsProvider,
25+
}) : _gsheetsProvider = gsheetsProvider {
26+
_gsheetsProvider.addListener(updateCache);
27+
_loadCache();
28+
_populateItemsFromCache();
29+
}
30+
31+
void updateCache() {
32+
// Process raw data from GsheetsProvider and update _tracks
33+
var data = _gsheetsProvider.getHomeData();
34+
if (data != null && data.isNotEmpty) {
35+
_cacheClear();
36+
for (final itemData in data) {
37+
_cacheAdd(CommunicationData.fromItemData(itemData));
38+
}
39+
_commit();
40+
}
41+
}
42+
43+
Future<void> _loadCache() async {
44+
final prefs = await SharedPreferences.getInstance();
45+
final cacheJson = prefs.getString(_cacheTitle);
46+
if (cacheJson != null && cacheJson.isNotEmpty) {
47+
_cacheClear();
48+
final List<dynamic> jsonList = jsonDecode(cacheJson);
49+
jsonList.map((json) => CommunicationData.fromJson(json)).toList().forEach((item) {
50+
_cacheAdd(item);
51+
});
52+
Debug.msg('Cache loaded: Home');
53+
_commit();
54+
} else {
55+
Debug.msg('Cache OMITTED: Home');
56+
}
57+
}
58+
59+
void _cacheAdd(CommunicationData item) {
60+
_cache.add(item);
61+
}
62+
63+
void _cacheClear() {
64+
_cache.clear();
65+
}
66+
67+
void _commit() {
68+
_cache.sort((a, b) => a.title.compareTo(b.title));
69+
_saveCache();
70+
_populateItemsFromCache();
71+
notifyListeners();
72+
}
73+
74+
void _populateItemsFromCache() {
75+
if (_cache.isNotEmpty) {
76+
_items.clear();
77+
for (var item in _cache) {
78+
_items.add(item);
79+
}
80+
}
81+
}
82+
83+
Future<void> _saveCache() async {
84+
final prefs = await SharedPreferences.getInstance();
85+
final cacheJson = jsonEncode(_cache); // Convert _cache to JSON string
86+
await prefs.setString(_cacheTitle, cacheJson); // Save to SharedPreferences
87+
}
88+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
class CommunicationData {
2+
final String title;
3+
final String url;
4+
5+
CommunicationData._({
6+
required this.title,
7+
required this.url,
8+
});
9+
10+
factory CommunicationData.fromItemData(Map<String, dynamic> itemData) {
11+
return CommunicationData._(
12+
title: itemData['Title'] ?? '',
13+
url: itemData['URL'] ?? '',
14+
);
15+
}
16+
17+
factory CommunicationData.fromJson(Map<String, dynamic> json) {
18+
return CommunicationData._(
19+
title: json['title'] as String? ?? '',
20+
url: json['url'] as String? ?? '',
21+
);
22+
}
23+
24+
Map<String, dynamic> toJson() {
25+
return {
26+
'url': url,
27+
'title': title.toString(),
28+
};
29+
}
30+
}

lib/main.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import 'package:iccm_eu_app/data/appProviders/error_provider.dart';
22
import 'package:iccm_eu_app/data/appProviders/expand_content_provider.dart';
3+
import 'package:iccm_eu_app/data/dataProviders/communication_provider.dart';
34
import 'package:iccm_eu_app/data/dataProviders/events_provider.dart';
45
import 'package:iccm_eu_app/data/dataProviders/favorites_provider.dart';
56
import 'package:iccm_eu_app/data/dataProviders/gsheets_provider.dart';
@@ -92,6 +93,12 @@ void main() async {
9293
),
9394
update: (context, gsheetsProvider, thisProvider) => thisProvider!..updateCache(),
9495
),
96+
ChangeNotifierProxyProvider<GsheetsProvider, CommunicationProvider>(
97+
create: (context) => CommunicationProvider(
98+
gsheetsProvider: Provider.of<GsheetsProvider>(context, listen: false),
99+
),
100+
update: (context, gsheetsProvider, thisProvider) => thisProvider!..updateCache(),
101+
),
95102
ChangeNotifierProxyProvider<GsheetsProvider, TravelProvider>(
96103
create: (context) => TravelProvider(
97104
gsheetsProvider: Provider.of<GsheetsProvider>(context, listen: false),

lib/pages/communication_page.dart

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:iccm_eu_app/components/url_button.dart';
3+
import 'package:iccm_eu_app/data/dataProviders/communication_provider.dart';
4+
import 'package:iccm_eu_app/data/model/communication_data.dart';
5+
import 'package:provider/provider.dart';
6+
7+
class CommunicationPage extends StatelessWidget {
8+
9+
const CommunicationPage({
10+
super.key,
11+
});
12+
13+
@override
14+
Widget build(BuildContext context) {
15+
return Scaffold(
16+
appBar: AppBar(
17+
title: const Text('Communication'),
18+
),
19+
body: SliverToBoxAdapter(
20+
child: Consumer<CommunicationProvider>(
21+
builder: (context, itemProvider, child) {
22+
final itemList = itemProvider.items();
23+
if (itemList.isEmpty) {
24+
return const Center(
25+
child: Text('Loading dynamic content...'),
26+
);
27+
}
28+
return ListView.builder(
29+
shrinkWrap: true,
30+
physics: ClampingScrollPhysics(),
31+
itemCount: itemList.length,
32+
itemBuilder: (context, index) {
33+
CommunicationData item = itemList[index];
34+
if (item.title.isEmpty ||
35+
!item.url.startsWith('https://')) {
36+
return SizedBox.shrink();
37+
}
38+
return Padding(
39+
padding: const EdgeInsets.all(16.0),
40+
child: UrlButton(
41+
title: item.title,
42+
url: item.url,
43+
),
44+
);
45+
},
46+
);
47+
},
48+
),
49+
),
50+
);
51+
}
52+
}

0 commit comments

Comments
 (0)