RuStore Remote Config

This commit is contained in:
2024-12-22 16:39:43 +03:00
parent 86e539daf1
commit 0cda6f9a48
27 changed files with 693 additions and 380 deletions

View File

@@ -0,0 +1,5 @@
import 'package:flutter_rustore_remoteconfig/rustore_remote_config.dart';
class ReverseNNRemoteConfigParameters extends StaticParameters {
}

View File

@@ -0,0 +1,131 @@
import 'dart:convert';
import 'dart:developer';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:flutter_rustore_remoteconfig/flutter_rustore_remoteconfig.dart';
import 'package:flutter_rustore_remoteconfig/rustore_remote_config.dart';
import 'package:reverse_nn/application/dto/rule.dart';
import 'package:reverse_nn/application/dto/schedule.dart';
import 'package:reverse_nn/configuration.dart';
import 'package:reverse_nn/application/services/remote_config/parameters.dart';
class ReverseNNRuStoreRemoteConfig {
void create() {
FlutterRustoreRemoteconfig.create(
Configuration.ruStoreRemoteConfigAppID,
PluginUpdateBehavior.defaultBehavior,
15,
ReverseNNRemoteConfigParameters(),
onInitComplete: () {
if(kDebugMode) { log('RuStore Remote Config - onInitComplete'); }
},
onFirstLoadComplete: () {
if(kDebugMode) { log('RuStore Remote Config - onFirstLoadComplete'); }
},
onMemoryCacheUpdated: () {
if(kDebugMode) { log('RuStore Remote Config - onMemoryCacheUpdated'); }
}
);
}
Future<DateTime> getStartScheduleDate() async {
try {
FlutterRustoreRemoteconfig.init();
final remoteConfig = await FlutterRustoreRemoteconfig.getRemoteConfig();
final startScheduleDate = remoteConfig.getString('startScheduleDate');
return startScheduleDate != null
? DateTime.parse(startScheduleDate)
: DateTime(2024, 8, 1);
}
catch (error) {
if(kDebugMode) {
log('ReverseNNRuStoreRemoteConfig.getStartScheduleDate - error - ${error.toString()}');
}
return DateTime(2024, 8, 1);
}
}
Future<DateTime> getEndScheduleDate() async {
try {
FlutterRustoreRemoteconfig.init();
final remoteConfig = await FlutterRustoreRemoteconfig.getRemoteConfig();
final endScheduleDate = remoteConfig.getString('endScheduleDate');
return endScheduleDate != null
? DateTime.parse(endScheduleDate)
: DateTime(2025, 12, 31, 23, 59, 59, 999, 999);
}
catch (error) {
if(kDebugMode) {
log('ReverseNNRuStoreRemoteConfig.getEndScheduleDate - error - ${error.toString()}');
}
return DateTime(2025, 12, 31, 23, 59, 59, 999, 999);
}
}
Future<List<Rule>?> getRules() async {
try {
FlutterRustoreRemoteconfig.init();
final remoteConfig = await FlutterRustoreRemoteconfig.getRemoteConfig();
final rules = remoteConfig.getString('scheduleRules');
return rules != null
? (jsonDecode(rules) as List<dynamic>).map((el) => Rule.fromJson(el)).toList()
: await _getRulesFromStorage();
}
catch(error) {
if(kDebugMode) {
log('ReverseNNRuStoreRemoteConfig.getRules - error - ${error.toString()}');
}
return await _getRulesFromStorage();
}
}
Future<List<Rule>?> _getRulesFromStorage() async {
final Map<String, dynamic>? schema = await _getSchemaFromStorage();
if(schema == null || !schema.containsKey('rules')) return null;
return (schema['rules'] as List<dynamic>)
.map((el) => Rule.fromJson(el))
.toList();
}
Future<List<ScheduleItem>?> getScheduleByKey(String key) async {
try {
FlutterRustoreRemoteconfig.init();
final remoteConfig = await FlutterRustoreRemoteconfig.getRemoteConfig();
final schedule = remoteConfig.getString('schedules-$key');
return schedule != null
? (jsonDecode(schedule) as List<dynamic>)
.map((el) => ScheduleItem.fromJson(el))
.toList()
: await _getScheduleByKeyFromStorage(key);
}
catch(error) {
if(kDebugMode) {
log('ReverseNNRuStoreRemoteConfig.getScheduleByKey($key) - error - ${error.toString()}');
}
return await _getScheduleByKeyFromStorage(key);
}
}
Future<List<ScheduleItem>?> _getScheduleByKeyFromStorage(String key) async {
final Map<String, dynamic>? schema = await _getSchemaFromStorage();
if(
schema == null
|| !schema.containsKey('schedules')
|| !(schema['schedules'] as Map<String, dynamic>).containsKey(key)
) return null;
return (schema['schedules'][key] as List<dynamic>)
.map((el) => ScheduleItem.fromJson(el))
.toList();
}
Future<Map<String, dynamic>?> _getSchemaFromStorage() async {
return jsonDecode(await rootBundle.loadString('assets/schedule.json'));
}
}

View File

@@ -5,43 +5,25 @@ import 'package:flutter/material.dart';
import 'package:intl/intl.dart' show DateFormat;
import 'package:flutter/services.dart';
import 'package:reverse_nn/application/dto/rule.dart';
import 'package:reverse_nn/application/dto/schedule.dart';
import 'package:reverse_nn/application/services/remote_config/service.dart';
class ScheduleService {
static const Duration dayOffset = Duration(hours: 4);
Future<Map<String, dynamic>> getSchema() async {
final source = await rootBundle
.loadString('assets/schedule.json');
return jsonDecode(source);
}
Future<Rule?> getRuleByDate(DateTime date) async {
final List<Rule>? rules = await ReverseNNRuStoreRemoteConfig().getRules();
if(rules == null) return null;
Future<Map<String, dynamic>?> getRule(DateTime day) async {
final Map<String, dynamic> schema = await getSchema();
if(!schema.containsKey('rules')) return null;
for(final Map<String, dynamic> rule in (schema['rules'] as List<dynamic>)) {
final DateTime after = DateTime.parse(rule['after']);
final DateTime? before = rule['before'] != null
? DateTime.parse(rule['before'])
: null;
if(day.isAfter(after) && (before == null || day.isBefore(before)))
for(final Rule rule in rules) {
if(date.isAfter(rule.after) && (rule.before == null || date.isBefore(rule.before!)))
{ return rule; }
}
return null;
}
Future<Map<String, String>?> getDataset(DateTime day) async {
final Map<String, dynamic>? rule = await getRule(day);
if(rule == null || !rule.containsKey('dataset')) return null;
return (rule['dataset'] as Map<String, dynamic>).map((key, value) {
return MapEntry(key, value.toString());
});
}
String datetimeWeekdayToDatasetKey(int weekday) {
switch(weekday){
case DateTime.monday: return 'monday';
@@ -56,17 +38,7 @@ class ScheduleService {
return 'monday';
}
Future<List<Map<String, dynamic>>?> getScheduleByKey(String key) async {
final Map<String, dynamic> schema = await getSchema();
if(!schema.containsKey('schedules')) return null;
if(!(schema['schedules'] as Map<String, dynamic>).containsKey(key)) return null;
return (schema['schedules'][key] as List<dynamic>).map((element) {
return element as Map<String, dynamic>;
}).toList();
}
Future<List<Map<String, dynamic>>?> getScheduleByDate(DateTime datetime, {bool force = false}) async {
Future<List<ScheduleItem>?> getScheduleByDate(DateTime datetime, {bool force = false}) async {
final dateStart = datetime.copyWith(hour: 0, minute: 0, second: 0, microsecond: 0, millisecond: 0);
final dateEnd = datetime.copyWith(hour: 23, minute: 59, second: 59, microsecond: 999, millisecond: 999);
final datetimeStartWithOffset = dateStart.copyWith().add(dayOffset);
@@ -75,7 +47,7 @@ class ScheduleService {
? datetime
: datetime.copyWith().subtract(const Duration(days: 1));
final Map<String, String>? dataset = await getDataset(usedScheduleDatetime);
final Map<String, String>? dataset = (await getRuleByDate(usedScheduleDatetime))?.dataset;
if(dataset == null) return null;
final String dayKey = DateFormat('y-M-d').format(usedScheduleDatetime);
@@ -87,74 +59,55 @@ class ScheduleService {
if(scheduleKey == null) return null;
List<Map<String, dynamic>>? schedule = await getScheduleByKey(scheduleKey);
List<ScheduleItem>? schedule = await ReverseNNRuStoreRemoteConfig().getScheduleByKey(scheduleKey);
if(schedule == null) return null;
int durationOffset = 0;
for (var i = 0; i < schedule.length; i++) {
final scheduleItem = schedule[i];
final int duration = (scheduleItem['duration'] as int);
final ScheduleItem scheduleItem = schedule[i];
DateTime start = datetimeStartWithOffset.copyWith().add(Duration(minutes: durationOffset));
DateTime end = datetimeStartWithOffset.copyWith().add(Duration(minutes: durationOffset + duration));
durationOffset += duration;
DateTime end = datetimeStartWithOffset.copyWith().add(Duration(minutes: durationOffset + scheduleItem.duration));
durationOffset += scheduleItem.duration;
if(!force) {
if(i == 0) {
final schedulePrevDay = await getScheduleByDate(datetime.copyWith().subtract(const Duration(days: 1)), force: true);
final lastScheduleElement = schedulePrevDay?.last;
if(lastScheduleElement != null && (lastScheduleElement['direction'] as String) == (scheduleItem['direction'] as String)) {
start = start.subtract(Duration(minutes: lastScheduleElement['duration'] as int));
if(lastScheduleElement != null && lastScheduleElement.direction == scheduleItem.direction) {
start = start.subtract(Duration(minutes: lastScheduleElement.duration));
}
}
if(i == (schedule.length -1)) {
final scheduleNextDay = await getScheduleByDate(datetime.copyWith().add(const Duration(days: 1)), force: true);
final firstScheduleElement = scheduleNextDay?.first;
if(firstScheduleElement != null && (firstScheduleElement['direction'] as String) == (scheduleItem['direction'] as String)) {
end = end.add(Duration(minutes: firstScheduleElement['duration'] as int));
if(firstScheduleElement != null && firstScheduleElement.direction == scheduleItem.direction) {
end = end.add(Duration(minutes: firstScheduleElement.duration));
}
}
}
schedule[i]['start'] = start;
schedule[i]['show_start_date'] = !(start.isAfter(dateStart) && start.isBefore(dateEnd));
schedule[i]['end'] = end;
schedule[i]['show_end_date'] = !(end.isAfter(dateStart) && end.isBefore(dateEnd));
schedule[i] = scheduleItem.fillAdditional(
start: start,
end: end,
showStartDate: !(start.isAfter(dateStart) && start.isBefore(dateEnd)),
showEndDate: !(end.isAfter(dateStart) && end.isBefore(dateEnd))
);
}
return schedule;
}
Future<Map<String, dynamic>?> getCurrentStatus() async {
Future<ScheduleItem?> getCurrentStatus() async {
final DateTime now = DateTime.now();
final List<Map<String, dynamic>>? schedule = await getScheduleByDate(now);
final List<ScheduleItem>? schedule = await getScheduleByDate(now);
if(schedule == null) return null;
for(var i = 0; i < schedule.length; i++) {
final start = schedule[i]['start'];
final end = schedule[i]['end'];
if(now.isAfter(start) && now.isBefore(end)) {
return schedule[i];
}
for(final ScheduleItem item in schedule) {
if(item.start == null || item.end == null) continue;
if(now.isAfter(item.start!) && now.isBefore(item.end!)) return item;
}
return null;
}
static String formatDirection(String direction) {
switch(direction) {
case 'in_city': return 'В город';
case 'out_city': return 'Из города';
default: return 'Переключение';
}
}
static IconData getIconByDirection(String? direction) {
switch(direction) {
case 'in_city': return Icons.input_outlined;
case 'out_city': return Icons.output_outlined;
default: return Icons.change_circle_outlined;
}
}
}