Skip to content

Commit

Permalink
wip: getting data on schedule screen and wip schedule state mgmt
Browse files Browse the repository at this point in the history
  • Loading branch information
glitchedmob committed Oct 8, 2024
1 parent a15a2d0 commit 8e8adc3
Show file tree
Hide file tree
Showing 10 changed files with 297 additions and 21 deletions.
12 changes: 11 additions & 1 deletion lib/app.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import 'package:flutter/material.dart';
import 'package:method_conf_app/providers/conference_provider.dart';
import 'package:method_conf_app/providers/schedule_provider.dart';
import 'package:method_conf_app/providers/schedule_state_provider.dart';
import 'package:method_conf_app/providers/sponsor_provider.dart';
import 'package:provider/provider.dart';

Expand All @@ -24,7 +26,15 @@ class App extends StatelessWidget {
ChangeNotifierProvider(create: (context) {
var c = Provider.of<ConferenceProvider>(context, listen: false);
return SponsorProvider(conferenceProvider: c);
})
}),
ChangeNotifierProvider(create: (context) {
var c = Provider.of<ConferenceProvider>(context, listen: false);
return ScheduleProvider(conferenceProvider: c);
}),
ChangeNotifierProvider(create: (context) {
var s = Provider.of<ScheduleProvider>(context, listen: false);
return ScheduleStateProvider(scheduleProvider: s);
}),
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
Expand Down
15 changes: 15 additions & 0 deletions lib/data/get_schedule_grid.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import 'dart:convert';

import 'package:http/http.dart' as http;
import 'package:method_conf_app/data/umbraco/models/schedule_grid.dart';

import 'package:method_conf_app/env.dart';

Future<List<List<String?>>> getScheduleGrid(String conferenceId) async {
var url = Uri.parse(
'${Env.umbracoBaseUrl}/api/v1/conference/$conferenceId/schedule');

final res = await http.get(url);

return ScheduleGrid.fromJson(json.decode(res.body)).scheduleGrid;
}
28 changes: 28 additions & 0 deletions lib/data/get_schedule_items.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import 'package:method_conf_app/data/umbraco/get_child_nodes_of_type.dart';
import 'package:method_conf_app/data/umbraco/get_items.dart';
import 'package:method_conf_app/data/umbraco/models/api_content_model_base.dart';
import 'package:method_conf_app/data/umbraco/models/session.dart';
import 'package:method_conf_app/data/umbraco/models/track.dart';

const maximumScheduleItems = 100;

Future<List<ApiContentModelBase>> getScheduleItems(conferenceId) async {
final sessions = await getFirstChildNodeOfType(
nodeId: conferenceId,
type: 'sessions',
);

if (sessions == null) {
return [];
}

var response = await getItems(
fetch: 'descendants:${sessions.id}',
expand: 'properties[\$all]',
take: 100,
);

return response.items
.where((item) => item is Track || item is Session)
.toList();
}
4 changes: 2 additions & 2 deletions lib/data/umbraco/get_child_nodes_of_type.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ Future<List<ApiContentModelBase>> getChildNodesOfType({
return res.items;
}

Future<ApiContentModelBase> getFirstChildNodeOfType({
Future<ApiContentModelBase?> getFirstChildNodeOfType({
required String nodeId,
required String type,
}) async {
var items = await getChildNodesOfType(nodeId: nodeId, type: type);

return items.first;
return items.firstOrNull;
}
17 changes: 13 additions & 4 deletions lib/data/umbraco/get_items.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import 'dart:convert';

import 'package:http/http.dart' as http;
import 'package:method_conf_app/data/umbraco/models/paged_api_content_response_model.dart';

import 'package:method_conf_app/data/umbraco/models/paged_api_content_response_model.dart';
import 'package:method_conf_app/env.dart';

Future<PagedApiContentResponseModel> getItems(
{List<String> filter = const [], String? fetch, int? take}) async {
Future<PagedApiContentResponseModel> getItems({
List<String> filter = const [],
String? fetch,
String? expand,
int? take,
}) async {
var url = Uri.parse('${Env.umbracoBaseUrl}/umbraco/delivery/api/v2/content');

if (filter.isNotEmpty) {
Expand All @@ -19,12 +23,17 @@ Future<PagedApiContentResponseModel> getItems(
url.replace(queryParameters: {...url.queryParameters, 'fetch': fetch});
}

if (expand != null) {
url = url
.replace(queryParameters: {...url.queryParameters, 'expand': expand});
}

if (take != null) {
url = url.replace(
queryParameters: {...url.queryParameters, 'take': take.toString()});
}

var res = await http.get(url);
final res = await http.get(url);

return PagedApiContentResponseModel.fromJson(json.decode(res.body));
}
6 changes: 5 additions & 1 deletion lib/data/umbraco/models/api_content_model_base.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,11 @@ class ApiContentModelBase {
return obj;
}

Map<String, dynamic> toJson() => _$ApiContentModelBaseToJson(this);
Map<String, dynamic> toJson() {
final json = _$ApiContentModelBaseToJson(this);
json['properties'] = unParsedProperties;
return json;
}
}

@JsonSerializable()
Expand Down
5 changes: 5 additions & 0 deletions lib/data/umbraco/models/session.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ part 'session.g.dart';
class Session extends ApiContentModelBase {
SessionProperties? properties;

String get gridId {
final parts = route.path.split('/').where((part) => part != '');
return parts.lastOrNull ?? route.path;
}

Session({
required super.contentType,
required super.name,
Expand Down
118 changes: 118 additions & 0 deletions lib/providers/schedule_provider.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:method_conf_app/data/get_schedule_grid.dart';
import 'package:method_conf_app/data/get_schedule_items.dart';
import 'package:method_conf_app/data/umbraco/models/api_content_model_base.dart';
import 'package:method_conf_app/data/umbraco/models/session.dart';
import 'package:method_conf_app/data/umbraco/models/track.dart';
import 'package:method_conf_app/providers/conference_provider.dart';
import 'package:shared_preferences/shared_preferences.dart';

const _scheduleItemsStorageKey = 'app-schedule-items';
const _scheduleGridStorageKey = 'app-schedule-grid';

typedef Schedule = (List<ApiContentModelBase>, List<List<String?>>);

class ScheduleProvider extends ChangeNotifier {
ConferenceProvider conferenceProvider;

Schedule? _schedule;

Schedule? get schedule => _schedule;

set schedule(Schedule? value) {
_schedule = value;
notifyListeners();
}

List<List<String?>> get grid => schedule?.$2 ?? [];

List<Track> get tracks => schedule?.$1.whereType<Track>().toList() ?? [];

List<Session> get sessions =>
schedule?.$1.whereType<Session>().toList() ?? [];

ScheduleProvider({required this.conferenceProvider});

Future<void> init() async {
await conferenceProvider.init(enableBackgroundRefresh: false);

schedule ??= await load();

if (schedule == null) {
await refresh();
} else {
refresh();
}
}

Future<Schedule?> load() async {
var prefs = await SharedPreferences.getInstance();

final itemsJson = prefs.getStringList(_scheduleItemsStorageKey);
final gridJson = prefs.getStringList(_scheduleGridStorageKey);

final items = itemsJson
?.map((item) => ApiContentModelBase.fromJson(json.decode(item)))
.toList();

final grid =
gridJson?.map((item) => json.decode(item) as List<String?>).toList();

if (items == null || grid == null) {
return null;
}

return (items, grid);
}

Future<void> store(Schedule? newSchedule) async {
var prefs = await SharedPreferences.getInstance();

if (newSchedule == null) {
await Future.wait([
prefs.remove(_scheduleItemsStorageKey),
prefs.remove(_scheduleGridStorageKey),
]);
return;
}

final (items, grid) = newSchedule;

await Future.wait([
prefs.setStringList(
_scheduleItemsStorageKey,
items.map((item) => json.encode(item.toJson())).toList(),
),
prefs.setStringList(
_scheduleGridStorageKey,
grid.map((item) => json.encode(item)).toList(),
),
]);
}

Future<Schedule?> fetch() async {
var conference = conferenceProvider.conference;

if (conference == null) {
return null;
}

final itemsFuture = getScheduleItems(conference.id);
final gridFuture = getScheduleGrid(conference.id);

final items = await itemsFuture;
final grid = await gridFuture;

return (items, grid);
}

Future<void> refresh() async {
final newSchedule = await fetch();

schedule = newSchedule;

await store(newSchedule);
}
}
72 changes: 72 additions & 0 deletions lib/providers/schedule_state_provider.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:method_conf_app/data/umbraco/models/session.dart';
import 'package:method_conf_app/data/umbraco/models/track.dart';
import 'package:method_conf_app/providers/schedule_provider.dart';
import 'package:shared_preferences/shared_preferences.dart';

const _startColumnIndexStorageKey = 'app-schedule-start-column-index';

class ScheduleStateProvider extends ChangeNotifier {
ScheduleProvider scheduleProvider;

int _visibleColumns = 1;

int get visibleColumns => _visibleColumns;

set visibleColumns(int value) {
_visibleColumns = value;
notifyListeners();
}

int _startColumnIndex = 0;

int get startColumnIndex => _startColumnIndex;

set startColumnIndex(int value) {
_startColumnIndex = value;
notifyListeners();
store(_startColumnIndex);
}

Track? get currentTrack =>
scheduleProvider.tracks.elementAtOrNull(startColumnIndex);

List<Session> get currentSessions =>
scheduleProvider.grid
.elementAtOrNull(startColumnIndex)
?.toSet()
.whereType<String>()
.map((gridId) => scheduleProvider.sessions
.firstWhereOrNull((session) => session.gridId == gridId))
.whereType<Session>()
.toList() ??
[];

ScheduleStateProvider({required this.scheduleProvider});

Future<void> init() async {
final storedStartColumnIndex = await load();

if (storedStartColumnIndex != null) {
startColumnIndex = storedStartColumnIndex;
}
}

Future<int?> load() async {
var prefs = await SharedPreferences.getInstance();

return prefs.getInt(_startColumnIndexStorageKey);
}

Future<void> store(int? newStartColumnIndex) async {
var prefs = await SharedPreferences.getInstance();

if (newStartColumnIndex == null) {
await prefs.remove(_startColumnIndexStorageKey);
return;
}

await prefs.setInt(_startColumnIndexStorageKey, newStartColumnIndex);
}
}
Loading

0 comments on commit 8e8adc3

Please sign in to comment.