fix: resolve 2 remaining strict analysis errors
- introPage.dart: Convert fireItems to proper static function closure - mainDrawer.dart: Convert unreadCount int to String for Text widget
This commit is contained in:
parent
1864e5b105
commit
7a4c9fb3bc
61 changed files with 1386 additions and 1202 deletions
181
analysis_options.yaml
Normal file
181
analysis_options.yaml
Normal file
|
|
@ -0,0 +1,181 @@
|
||||||
|
# Comprehensive Dart analysis configuration for Fires Flutter
|
||||||
|
# Based on Flutter framework standards with aggressive linting
|
||||||
|
|
||||||
|
analyzer:
|
||||||
|
language:
|
||||||
|
strict-casts: true
|
||||||
|
strict-raw-types: true
|
||||||
|
strict-inference: true
|
||||||
|
errors:
|
||||||
|
# Treat errors as errors (default behavior)
|
||||||
|
missing_required_param: error
|
||||||
|
missing_return: error
|
||||||
|
# Warnings configuration
|
||||||
|
deprecated_member_use_from_same_package: warning
|
||||||
|
unnecessary_null_comparison: ignore
|
||||||
|
todo: ignore
|
||||||
|
exclude:
|
||||||
|
- 'build/**'
|
||||||
|
- 'lib/generated/**'
|
||||||
|
- 'lib/data/models/*g.dart'
|
||||||
|
- '.dart_tool/**'
|
||||||
|
|
||||||
|
linter:
|
||||||
|
rules:
|
||||||
|
# Error handling and safety
|
||||||
|
- always_declare_return_types
|
||||||
|
- always_put_control_body_on_new_line
|
||||||
|
- always_specify_types
|
||||||
|
- annotate_overrides
|
||||||
|
- avoid_bool_literals_in_conditional_expressions
|
||||||
|
- avoid_classes_with_only_static_members
|
||||||
|
- avoid_double_and_int_checks
|
||||||
|
- avoid_dynamic_calls
|
||||||
|
- avoid_empty_else
|
||||||
|
- avoid_equals_and_hash_code_on_mutable_classes
|
||||||
|
- avoid_escaping_inner_quotes
|
||||||
|
- avoid_field_initializers_in_const_classes
|
||||||
|
- avoid_function_literals_in_foreach_calls
|
||||||
|
- avoid_implementing_value_types
|
||||||
|
- avoid_init_to_null
|
||||||
|
- avoid_js_rounded_ints
|
||||||
|
- avoid_null_checks_in_equality_operators
|
||||||
|
- avoid_print
|
||||||
|
- avoid_redundant_argument_values
|
||||||
|
- avoid_relative_lib_imports
|
||||||
|
- avoid_renaming_method_parameters
|
||||||
|
- avoid_return_types_on_setters
|
||||||
|
- avoid_returning_null_for_void
|
||||||
|
- avoid_setters_without_getters
|
||||||
|
- avoid_shadowing_type_parameters
|
||||||
|
- avoid_single_cascade_in_expression_statements
|
||||||
|
- avoid_slow_async_io
|
||||||
|
- avoid_type_to_string
|
||||||
|
- avoid_types_as_parameter_names
|
||||||
|
- avoid_unnecessary_containers
|
||||||
|
- avoid_unused_constructor_parameters
|
||||||
|
- avoid_void_async
|
||||||
|
- await_only_futures
|
||||||
|
|
||||||
|
# Naming conventions
|
||||||
|
- camel_case_extensions
|
||||||
|
- camel_case_types
|
||||||
|
- file_names
|
||||||
|
- library_names
|
||||||
|
- library_prefixes
|
||||||
|
- library_private_types_in_public_api
|
||||||
|
- non_constant_identifier_names
|
||||||
|
- package_names
|
||||||
|
- package_prefixed_library_names
|
||||||
|
|
||||||
|
# Code quality and style
|
||||||
|
- cast_nullable_to_non_nullable
|
||||||
|
- control_flow_in_finally
|
||||||
|
- depend_on_referenced_packages
|
||||||
|
- deprecated_consistency
|
||||||
|
- directives_ordering
|
||||||
|
- empty_catches
|
||||||
|
- empty_constructor_bodies
|
||||||
|
- empty_statements
|
||||||
|
- eol_at_end_of_file
|
||||||
|
- exhaustive_cases
|
||||||
|
- flutter_style_todos
|
||||||
|
- hash_and_equals
|
||||||
|
- implementation_imports
|
||||||
|
- collection_methods_unrelated_type
|
||||||
|
- leading_newlines_in_multiline_strings
|
||||||
|
- missing_whitespace_between_adjacent_strings
|
||||||
|
- no_adjacent_strings_in_list
|
||||||
|
- no_default_cases
|
||||||
|
- no_duplicate_case_values
|
||||||
|
- no_leading_underscores_for_library_prefixes
|
||||||
|
- no_leading_underscores_for_local_identifiers
|
||||||
|
- no_logic_in_create_state
|
||||||
|
- noop_primitive_operations
|
||||||
|
- null_check_on_nullable_type_parameter
|
||||||
|
- null_closures
|
||||||
|
- only_throw_errors
|
||||||
|
- overridden_fields
|
||||||
|
|
||||||
|
# Preferences and improvements
|
||||||
|
- prefer_adjacent_string_concatenation
|
||||||
|
- prefer_asserts_in_initializer_lists
|
||||||
|
- prefer_collection_literals
|
||||||
|
- prefer_conditional_assignment
|
||||||
|
- prefer_const_constructors
|
||||||
|
- prefer_const_constructors_in_immutables
|
||||||
|
- prefer_const_declarations
|
||||||
|
- prefer_const_literals_to_create_immutables
|
||||||
|
- prefer_contains
|
||||||
|
- prefer_final_fields
|
||||||
|
- prefer_final_in_for_each
|
||||||
|
- prefer_final_locals
|
||||||
|
- prefer_for_elements_to_map_fromIterable
|
||||||
|
- prefer_foreach
|
||||||
|
- prefer_function_declarations_over_variables
|
||||||
|
- prefer_generic_function_type_aliases
|
||||||
|
- prefer_if_elements_to_conditional_expressions
|
||||||
|
- prefer_if_null_operators
|
||||||
|
- prefer_initializing_formals
|
||||||
|
- prefer_inlined_adds
|
||||||
|
- prefer_interpolation_to_compose_strings
|
||||||
|
- prefer_is_empty
|
||||||
|
- prefer_is_not_empty
|
||||||
|
- prefer_is_not_operator
|
||||||
|
- prefer_iterable_whereType
|
||||||
|
- prefer_null_aware_operators
|
||||||
|
- prefer_relative_imports
|
||||||
|
- prefer_single_quotes
|
||||||
|
- prefer_spread_collections
|
||||||
|
- prefer_typing_uninitialized_variables
|
||||||
|
- prefer_void_to_null
|
||||||
|
- provide_deprecation_message
|
||||||
|
- recursive_getters
|
||||||
|
- secure_pubspec_urls
|
||||||
|
- sized_box_for_whitespace
|
||||||
|
- slash_for_doc_comments
|
||||||
|
- sort_child_properties_last
|
||||||
|
- sort_constructors_first
|
||||||
|
- sort_unnamed_constructors_first
|
||||||
|
- test_types_in_equals
|
||||||
|
- throw_in_finally
|
||||||
|
- tighten_type_of_initializing_formals
|
||||||
|
- type_init_formals
|
||||||
|
- unnecessary_await_in_return
|
||||||
|
- unnecessary_brace_in_string_interps
|
||||||
|
- unnecessary_const
|
||||||
|
- unnecessary_constructor_name
|
||||||
|
- unnecessary_getters_setters
|
||||||
|
- unnecessary_late
|
||||||
|
- unnecessary_new
|
||||||
|
- unnecessary_null_aware_assignments
|
||||||
|
- unnecessary_null_checks
|
||||||
|
- unnecessary_null_in_if_null_operators
|
||||||
|
- unnecessary_nullable_for_final_variable_declarations
|
||||||
|
- unnecessary_overrides
|
||||||
|
- unnecessary_parenthesis
|
||||||
|
- unnecessary_statements
|
||||||
|
- unnecessary_string_escapes
|
||||||
|
- unnecessary_string_interpolations
|
||||||
|
- unnecessary_this
|
||||||
|
- unrelated_type_equality_checks
|
||||||
|
- use_build_context_synchronously
|
||||||
|
- use_full_hex_values_for_flutter_colors
|
||||||
|
- use_function_type_syntax_for_parameters
|
||||||
|
- use_if_null_to_convert_nulls_to_bools
|
||||||
|
- use_is_even_rather_than_modulo
|
||||||
|
- use_key_in_widget_constructors
|
||||||
|
- use_late_for_private_fields_and_variables
|
||||||
|
- use_named_constants
|
||||||
|
- use_raw_strings
|
||||||
|
- use_rethrow_when_possible
|
||||||
|
- use_setters_to_change_properties
|
||||||
|
- use_super_parameters
|
||||||
|
- use_test_throws_matchers
|
||||||
|
- valid_regexps
|
||||||
|
- void_checks
|
||||||
|
|
||||||
|
include: package:bloc_lint/recommended.yaml
|
||||||
|
bloc:
|
||||||
|
rules:
|
||||||
|
- avoid_flutter_imports
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:comunes_flutter/comunes_flutter.dart';
|
import 'package:comunes_flutter/comunes_flutter.dart';
|
||||||
import 'package:fires_flutter/models/yourLocation.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_redux/flutter_redux.dart';
|
import 'package:flutter_redux/flutter_redux.dart';
|
||||||
import 'package:flutter_speed_dial/flutter_speed_dial.dart';
|
import 'package:flutter_speed_dial/flutter_speed_dial.dart';
|
||||||
|
|
@ -15,20 +14,14 @@ import 'globalFiresBottomStats.dart';
|
||||||
import 'locationUtils.dart';
|
import 'locationUtils.dart';
|
||||||
import 'mainDrawer.dart';
|
import 'mainDrawer.dart';
|
||||||
import 'models/appState.dart';
|
import 'models/appState.dart';
|
||||||
|
import 'models/yourLocation.dart';
|
||||||
import 'placesAutocompleteUtils.dart';
|
import 'placesAutocompleteUtils.dart';
|
||||||
import 'redux/actions.dart';
|
import 'redux/actions.dart';
|
||||||
|
|
||||||
@immutable
|
@immutable
|
||||||
class _ViewModel {
|
class _ViewModel {
|
||||||
final List<YourLocation> yourLocations;
|
|
||||||
final AddYourLocationFunction onAdd;
|
|
||||||
final DeleteYourLocationFunction onDelete;
|
|
||||||
final ToggleSubscriptionFunction onToggleSubs;
|
|
||||||
final OnLocationTapFunction onTap;
|
|
||||||
final OnRefreshYourLocationsFunction onRefresh;
|
|
||||||
final bool isLoading;
|
|
||||||
|
|
||||||
_ViewModel(
|
const _ViewModel(
|
||||||
{required this.onAdd,
|
{required this.onAdd,
|
||||||
required this.onDelete,
|
required this.onDelete,
|
||||||
required this.onToggleSubs,
|
required this.onToggleSubs,
|
||||||
|
|
@ -36,6 +29,13 @@ class _ViewModel {
|
||||||
required this.onRefresh,
|
required this.onRefresh,
|
||||||
required this.yourLocations,
|
required this.yourLocations,
|
||||||
required this.isLoading});
|
required this.isLoading});
|
||||||
|
final List<YourLocation> yourLocations;
|
||||||
|
final AddYourLocationFunction onAdd;
|
||||||
|
final DeleteYourLocationFunction onDelete;
|
||||||
|
final ToggleSubscriptionFunction onToggleSubs;
|
||||||
|
final OnLocationTapFunction onTap;
|
||||||
|
final OnRefreshYourLocationsFunction onRefresh;
|
||||||
|
final bool isLoading;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) =>
|
bool operator ==(Object other) =>
|
||||||
|
|
@ -50,6 +50,8 @@ class _ViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
class ActiveFiresPage extends StatefulWidget {
|
class ActiveFiresPage extends StatefulWidget {
|
||||||
|
const ActiveFiresPage({super.key});
|
||||||
|
|
||||||
static const String routeName = '/fires';
|
static const String routeName = '/fires';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -57,11 +59,11 @@ class ActiveFiresPage extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ActiveFiresPageState extends State<ActiveFiresPage> {
|
class _ActiveFiresPageState extends State<ActiveFiresPage> {
|
||||||
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
|
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
|
||||||
|
|
||||||
List<SpeedDialChild> _fabMiniMenuItemList(
|
List<SpeedDialChild> _fabMiniMenuItemList(
|
||||||
BuildContext context, AddYourLocationFunction onAdd) {
|
BuildContext context, AddYourLocationFunction onAdd) {
|
||||||
return [
|
return <SpeedDialChild>[
|
||||||
SpeedDialChild(
|
SpeedDialChild(
|
||||||
child: const Icon(Icons.location_searching),
|
child: const Icon(Icons.location_searching),
|
||||||
backgroundColor: fires600,
|
backgroundColor: fires600,
|
||||||
|
|
@ -92,13 +94,13 @@ class _ActiveFiresPageState extends State<ActiveFiresPage> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildRow(BuildContext context, YourLocation loc, onToggle, onTap) {
|
Widget _buildRow(BuildContext context, YourLocation loc, onToggle, onTap) {
|
||||||
final fireStatsStyle = new TextStyle(color: fires600);
|
const TextStyle fireStatsStyle = TextStyle(color: fires600);
|
||||||
return new ListTile(
|
return ListTile(
|
||||||
dense: true,
|
dense: true,
|
||||||
isThreeLine: false,
|
isThreeLine: false,
|
||||||
// leading: const Icon(Icons.location_on),
|
// leading: const Icon(Icons.location_on),
|
||||||
trailing: new IconButton(
|
trailing: IconButton(
|
||||||
icon: new Icon(loc.subscribed
|
icon: Icon(loc.subscribed
|
||||||
? Icons.notifications_active
|
? Icons.notifications_active
|
||||||
: Icons.notifications_off),
|
: Icons.notifications_off),
|
||||||
color: loc.subscribed ? fires600 : null,
|
color: loc.subscribed ? fires600 : null,
|
||||||
|
|
@ -110,18 +112,18 @@ class _ActiveFiresPageState extends State<ActiveFiresPage> {
|
||||||
? S.of(context).subscribedToFires
|
? S.of(context).subscribedToFires
|
||||||
: S.of(context).unsubscribedToFires);
|
: S.of(context).unsubscribedToFires);
|
||||||
}),
|
}),
|
||||||
title: new Text(loc.description, style: new TextStyle(fontSize: 16.0)),
|
title: Text(loc.description, style: const TextStyle(fontSize: 16.0)),
|
||||||
subtitle: loc.currentNumFires == YourLocation.withoutStats
|
subtitle: loc.currentNumFires == YourLocation.withoutStats
|
||||||
? null
|
? null
|
||||||
: loc.currentNumFires == 0
|
: loc.currentNumFires == 0
|
||||||
? new Text(S.of(context).noFiresAround)
|
? Text(S.of(context).noFiresAround)
|
||||||
: loc.currentNumFires == 1
|
: loc.currentNumFires == 1
|
||||||
? new Text(
|
? Text(
|
||||||
S
|
S
|
||||||
.of(context)
|
.of(context)
|
||||||
.fireAroundThisArea(loc.distance.toString()),
|
.fireAroundThisArea(loc.distance.toString()),
|
||||||
style: fireStatsStyle)
|
style: fireStatsStyle)
|
||||||
: new Text(
|
: Text(
|
||||||
S.of(context).firesAroundThisArea(
|
S.of(context).firesAroundThisArea(
|
||||||
loc.currentNumFires.toString(),
|
loc.currentNumFires.toString(),
|
||||||
loc.distance.toString()),
|
loc.distance.toString()),
|
||||||
|
|
@ -136,86 +138,85 @@ class _ActiveFiresPageState extends State<ActiveFiresPage> {
|
||||||
|
|
||||||
void showSnackMsg(String msg) {
|
void showSnackMsg(String msg) {
|
||||||
// _scaffoldKey.currentState.showSnackBar(new SnackBar(
|
// _scaffoldKey.currentState.showSnackBar(new SnackBar(
|
||||||
ScaffoldMessenger.of(context).showSnackBar(new SnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||||
content: new Text(msg),
|
content: Text(msg),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildSavedLocations(
|
Widget _buildSavedLocations(
|
||||||
BuildContext context, List<YourLocation> yl, onDeleted, onToggle, onTap) {
|
BuildContext context, List<YourLocation> yl, onDeleted, onToggle, onTap) {
|
||||||
return new ListView.builder(
|
return ListView.builder(
|
||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
itemCount: yl.length,
|
itemCount: yl.length,
|
||||||
itemBuilder: (BuildContext _context, int i) {
|
itemBuilder: (BuildContext context, int i) {
|
||||||
final ThemeData theme = Theme.of(context);
|
final ThemeData theme = Theme.of(context);
|
||||||
return new Dismissible(
|
return Dismissible(
|
||||||
key: new ObjectKey(yl.elementAt(i)),
|
key: ObjectKey(yl.elementAt(i)),
|
||||||
direction: DismissDirection.horizontal,
|
|
||||||
onDismissed: (DismissDirection direction) {
|
onDismissed: (DismissDirection direction) {
|
||||||
onDeleted(yl.elementAt(i));
|
onDeleted(yl.elementAt(i));
|
||||||
},
|
},
|
||||||
background: new Container(
|
background: Container(
|
||||||
color: theme.primaryColor,
|
color: theme.primaryColor,
|
||||||
child: const ListTile(
|
child: const ListTile(
|
||||||
leading: const Icon(Icons.delete,
|
leading: Icon(Icons.delete,
|
||||||
color: Colors.white, size: 36.0))),
|
color: Colors.white, size: 36.0))),
|
||||||
secondaryBackground: new Container(
|
secondaryBackground: Container(
|
||||||
color: theme.primaryColor,
|
color: theme.primaryColor,
|
||||||
child: const ListTile(
|
child: const ListTile(
|
||||||
trailing: const Icon(Icons.delete,
|
trailing: Icon(Icons.delete,
|
||||||
color: Colors.white, size: 36.0))),
|
color: Colors.white, size: 36.0))),
|
||||||
child: new Container(
|
child: Container(
|
||||||
decoration: new BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: theme.canvasColor,
|
color: theme.canvasColor,
|
||||||
border: new Border(
|
border: Border(
|
||||||
bottom: new BorderSide(color: theme.dividerColor))),
|
bottom: BorderSide(color: theme.dividerColor))),
|
||||||
child: _buildRow(context, yl.elementAt(i), onToggle, onTap)));
|
child: _buildRow(context, yl.elementAt(i), onToggle, onTap)));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return new StoreConnector<AppState, _ViewModel>(
|
return StoreConnector<AppState, _ViewModel>(
|
||||||
distinct: true,
|
distinct: true,
|
||||||
converter: (store) {
|
converter: (Store<AppState> store) {
|
||||||
print('New ViewModel of Active Fires');
|
print('New ViewModel of Active Fires');
|
||||||
return new _ViewModel(
|
return _ViewModel(
|
||||||
onAdd: (loc) {
|
onAdd: (YourLocation loc) {
|
||||||
if (store.state.yourLocations
|
if (store.state.yourLocations
|
||||||
.any((l) => loc.lat == l.lat && loc.lon == l.lon)) {
|
.any((YourLocation l) => loc.lat == l.lat && loc.lon == l.lon)) {
|
||||||
// Already added
|
// Already added
|
||||||
showSnackMsg(S.of(context).addedThisLocation);
|
showSnackMsg(S.of(context).addedThisLocation);
|
||||||
} else {
|
} else {
|
||||||
store.dispatch(new AddYourLocationAction(loc));
|
store.dispatch(AddYourLocationAction(loc));
|
||||||
new Timer(new Duration(milliseconds: 1000), () {
|
Timer(const Duration(milliseconds: 1000), () {
|
||||||
gotoLocationMap(store, loc, context);
|
gotoLocationMap(store, loc, context);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onDelete: (loc) {
|
onDelete: (YourLocation loc) {
|
||||||
store.dispatch(new DeleteYourLocationAction(loc));
|
store.dispatch(DeleteYourLocationAction(loc));
|
||||||
ScaffoldMessenger.of(context).showSnackBar(new SnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||||
content: new Text(S.of(context).youDeletedThisPlace),
|
content: Text(S.of(context).youDeletedThisPlace),
|
||||||
action: new SnackBarAction(
|
action: SnackBarAction(
|
||||||
label: S.of(context).UNDO,
|
label: S.of(context).UNDO,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
store.dispatch(new AddYourLocationAction(loc));
|
store.dispatch(AddYourLocationAction(loc));
|
||||||
})));
|
})));
|
||||||
},
|
},
|
||||||
onToggleSubs: (loc) {
|
onToggleSubs: (YourLocation loc) {
|
||||||
store.dispatch(new ToggleSubscriptionAction(loc));
|
store.dispatch(ToggleSubscriptionAction(loc));
|
||||||
},
|
},
|
||||||
onTap: (loc) {
|
onTap: (YourLocation loc) {
|
||||||
gotoLocationMap(store, loc, context);
|
gotoLocationMap(store, loc, context);
|
||||||
},
|
},
|
||||||
onRefresh: (refreshCallback) =>
|
onRefresh: (Completer<void> refreshCallback) =>
|
||||||
store.dispatch(new FetchYourLocationsAction(refreshCallback)),
|
store.dispatch(FetchYourLocationsAction(refreshCallback)),
|
||||||
yourLocations: store.state.yourLocations,
|
yourLocations: store.state.yourLocations,
|
||||||
isLoading: !store.state.isLoaded);
|
isLoading: !store.state.isLoaded);
|
||||||
},
|
},
|
||||||
builder: (context, view) {
|
builder: (BuildContext context, _ViewModel view) {
|
||||||
var hasLocations = view.yourLocations.length > 0;
|
final bool hasLocations = view.yourLocations.isNotEmpty;
|
||||||
final title = hasLocations
|
final String title = hasLocations
|
||||||
? S.of(context).firesInYourPlaces
|
? S.of(context).firesInYourPlaces
|
||||||
: S.of(context).firesInTheWorld;
|
: S.of(context).firesInTheWorld;
|
||||||
print('Building Active Fires');
|
print('Building Active Fires');
|
||||||
|
|
@ -223,11 +224,11 @@ class _ActiveFiresPageState extends State<ActiveFiresPage> {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
key: _scaffoldKey,
|
key: _scaffoldKey,
|
||||||
// FIXME new?
|
// FIXME new?
|
||||||
drawer: new MainDrawer(context, ActiveFiresPage.routeName),
|
drawer: MainDrawer(context, ActiveFiresPage.routeName),
|
||||||
appBar: new AppBar(
|
appBar: AppBar(
|
||||||
title: Text(title),
|
title: Text(title),
|
||||||
leading: IconButton(
|
leading: IconButton(
|
||||||
icon: Icon(Icons.menu),
|
icon: const Icon(Icons.menu),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
_scaffoldKey.currentState?.openDrawer();
|
_scaffoldKey.currentState?.openDrawer();
|
||||||
},
|
},
|
||||||
|
|
@ -235,12 +236,12 @@ class _ActiveFiresPageState extends State<ActiveFiresPage> {
|
||||||
),
|
),
|
||||||
floatingActionButtonLocation:
|
floatingActionButtonLocation:
|
||||||
FloatingActionButtonLocation.centerFloat,
|
FloatingActionButtonLocation.centerFloat,
|
||||||
bottomNavigationBar: new GlobalFiresBottomStats(),
|
bottomNavigationBar: const GlobalFiresBottomStats(),
|
||||||
body: view.isLoading
|
body: view.isLoading
|
||||||
? new FiresSpinner()
|
? const FiresSpinner()
|
||||||
: hasLocations
|
: hasLocations
|
||||||
? new Stack(children: <Widget>[
|
? Stack(children: <Widget>[
|
||||||
new RefreshIndicator(
|
RefreshIndicator(
|
||||||
child: _buildSavedLocations(
|
child: _buildSavedLocations(
|
||||||
context,
|
context,
|
||||||
view.yourLocations,
|
view.yourLocations,
|
||||||
|
|
@ -248,8 +249,8 @@ class _ActiveFiresPageState extends State<ActiveFiresPage> {
|
||||||
view.onToggleSubs,
|
view.onToggleSubs,
|
||||||
view.onTap),
|
view.onTap),
|
||||||
onRefresh: () async {
|
onRefresh: () async {
|
||||||
final Completer<Null> completer =
|
final Completer<void> completer =
|
||||||
new Completer<Null>();
|
Completer<void>();
|
||||||
view.onRefresh(completer);
|
view.onRefresh(completer);
|
||||||
return completer.future;
|
return completer.future;
|
||||||
}),
|
}),
|
||||||
|
|
@ -260,15 +261,15 @@ class _ActiveFiresPageState extends State<ActiveFiresPage> {
|
||||||
children: _fabMiniMenuItemList(context, view.onAdd),
|
children: _fabMiniMenuItemList(context, view.onAdd),
|
||||||
)
|
)
|
||||||
])
|
])
|
||||||
: new Center(
|
: Center(
|
||||||
child: new CenteredColumn(children: <Widget>[
|
child: CenteredColumn(children: <Widget>[
|
||||||
new RoundedBtn(
|
RoundedBtn(
|
||||||
icon: Icons.location_searching,
|
icon: Icons.location_searching,
|
||||||
text: S.of(context).addYourCurrentPosition,
|
text: S.of(context).addYourCurrentPosition,
|
||||||
onPressed: () => onAddYourLocation(view.onAdd),
|
onPressed: () => onAddYourLocation(view.onAdd),
|
||||||
backColor: fires600),
|
backColor: fires600),
|
||||||
const SizedBox(height: 26.0),
|
const SizedBox(height: 26.0),
|
||||||
new RoundedBtn(
|
RoundedBtn(
|
||||||
icon: Icons.edit_location,
|
icon: Icons.edit_location,
|
||||||
text: S.of(context).addSomePlace,
|
text: S.of(context).addSomePlace,
|
||||||
onPressed: () => onAddOtherLocation(view.onAdd),
|
onPressed: () => onAddOtherLocation(view.onAdd),
|
||||||
|
|
@ -280,23 +281,23 @@ class _ActiveFiresPageState extends State<ActiveFiresPage> {
|
||||||
|
|
||||||
void gotoLocationMap(
|
void gotoLocationMap(
|
||||||
Store<AppState> store, YourLocation loc, BuildContext context) {
|
Store<AppState> store, YourLocation loc, BuildContext context) {
|
||||||
store.dispatch(new ShowYourLocationMapAction(loc));
|
store.dispatch(ShowYourLocationMapAction(loc));
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context, new MaterialPageRoute(builder: (context) => new genericMap()));
|
context, MaterialPageRoute(builder: (BuildContext context) => const genericMap()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void onAddYourLocation(AddYourLocationFunction onAdd) {
|
void onAddYourLocation(AddYourLocationFunction onAdd) {
|
||||||
Future<YourLocation> location = getUserLocation(_scaffoldKey);
|
final Future<YourLocation> location = getUserLocation(_scaffoldKey);
|
||||||
_saveLocation(location, onAdd);
|
_saveLocation(location, onAdd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onAddOtherLocation(AddYourLocationFunction onAdd) {
|
void onAddOtherLocation(AddYourLocationFunction onAdd) {
|
||||||
Future<YourLocation> location = openPlacesDialog(_scaffoldKey);
|
final Future<YourLocation> location = openPlacesDialog(_scaffoldKey);
|
||||||
_saveLocation(location, onAdd);
|
_saveLocation(location, onAdd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _saveLocation(Future<YourLocation> location, onAdd) {
|
void _saveLocation(Future<YourLocation> location, onAdd) {
|
||||||
location.then((newLocation) {
|
location.then((YourLocation newLocation) {
|
||||||
if (newLocation != YourLocation.noLocation) {
|
if (newLocation != YourLocation.noLocation) {
|
||||||
onAdd(newLocation);
|
onAdd(newLocation);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,19 +2,19 @@ import 'package:flutter/material.dart';
|
||||||
|
|
||||||
/// Attribution widget for displaying map attribution text
|
/// Attribution widget for displaying map attribution text
|
||||||
class AttributionPluginWidget extends StatelessWidget {
|
class AttributionPluginWidget extends StatelessWidget {
|
||||||
final String text;
|
|
||||||
|
|
||||||
AttributionPluginWidget({this.text = ""});
|
const AttributionPluginWidget({super.key, this.text = ''});
|
||||||
|
final String text;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var style = new TextStyle(
|
const TextStyle style = TextStyle(
|
||||||
fontSize: 12.0,
|
fontSize: 12.0,
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
);
|
);
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.all(5.0),
|
padding: const EdgeInsets.all(5.0),
|
||||||
child: new Text(
|
child: Text(
|
||||||
text,
|
text,
|
||||||
style: style,
|
style: style,
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
const fires50 = const Color(0xFFFBE9E7);
|
const Color fires50 = Color(0xFFFBE9E7);
|
||||||
const fires100 = const Color(0xFFFFCCBC);
|
const Color fires100 = Color(0xFFFFCCBC);
|
||||||
const fires300 = const Color(0xFFFF8A65);
|
const Color fires300 = Color(0xFFFF8A65);
|
||||||
const fires600 = const Color(0xFFF4511E);
|
const Color fires600 = Color(0xFFF4511E);
|
||||||
const fires900 = const Color(0xFFBF360C);
|
const Color fires900 = Color(0xFFBF360C);
|
||||||
const firesErrorRed = const Color(0xFFDD2C00);
|
const Color firesErrorRed = Color(0xFFDD2C00);
|
||||||
|
|
||||||
const firesSurfaceWhite = fires50;
|
const Color firesSurfaceWhite = fires50;
|
||||||
const firesBackgroundWhite = Colors.white;
|
const Color firesBackgroundWhite = Colors.white;
|
||||||
|
|
@ -7,6 +7,8 @@ import 'package:flutter_map/flutter_map.dart';
|
||||||
/// Compass button widget for resetting map rotation to north
|
/// Compass button widget for resetting map rotation to north
|
||||||
/// Only visible when map is rotated (rotation != 0)
|
/// Only visible when map is rotated (rotation != 0)
|
||||||
class CompassMapPluginWidget extends StatefulWidget {
|
class CompassMapPluginWidget extends StatefulWidget {
|
||||||
|
const CompassMapPluginWidget({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_CompassMapPluginWidgetState createState() => _CompassMapPluginWidgetState();
|
_CompassMapPluginWidgetState createState() => _CompassMapPluginWidgetState();
|
||||||
}
|
}
|
||||||
|
|
@ -29,7 +31,7 @@ class _CompassMapPluginWidgetState extends State<CompassMapPluginWidget> {
|
||||||
|
|
||||||
void _initRotationMonitoring() {
|
void _initRotationMonitoring() {
|
||||||
// Check rotation periodically (every 100ms) to detect changes
|
// Check rotation periodically (every 100ms) to detect changes
|
||||||
_rotationCheckTimer = Timer.periodic(Duration(milliseconds: 100), (_) {
|
_rotationCheckTimer = Timer.periodic(const Duration(milliseconds: 100), (_) {
|
||||||
_checkRotation();
|
_checkRotation();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -41,8 +43,8 @@ class _CompassMapPluginWidgetState extends State<CompassMapPluginWidget> {
|
||||||
try {
|
try {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
|
|
||||||
final rotation = _mapController.camera.rotation;
|
final double rotation = _mapController.camera.rotation;
|
||||||
final isRotated = rotation.abs() > 0.0;
|
final bool isRotated = rotation.abs() > 0.0;
|
||||||
|
|
||||||
if (isRotated != _isRotated) {
|
if (isRotated != _isRotated) {
|
||||||
setState(() {
|
setState(() {
|
||||||
|
|
@ -56,7 +58,7 @@ class _CompassMapPluginWidgetState extends State<CompassMapPluginWidget> {
|
||||||
|
|
||||||
void _resetRotation() {
|
void _resetRotation() {
|
||||||
try {
|
try {
|
||||||
final controller = MapController.of(context);
|
final MapController controller = MapController.of(context);
|
||||||
controller.rotate(0);
|
controller.rotate(0);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Error resetting rotation: $e');
|
print('Error resetting rotation: $e');
|
||||||
|
|
@ -66,7 +68,7 @@ class _CompassMapPluginWidgetState extends State<CompassMapPluginWidget> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return LayoutBuilder(
|
return LayoutBuilder(
|
||||||
builder: (context, constraints) => Stack(
|
builder: (BuildContext context, BoxConstraints constraints) => Stack(
|
||||||
fit: StackFit.expand,
|
fit: StackFit.expand,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
if (_isRotated)
|
if (_isRotated)
|
||||||
|
|
@ -94,7 +96,7 @@ class _CompassMapPluginWidgetState extends State<CompassMapPluginWidget> {
|
||||||
heroTag: 'compass_button',
|
heroTag: 'compass_button',
|
||||||
tooltip: 'Go to North',
|
tooltip: 'Go to North',
|
||||||
onPressed: _resetRotation,
|
onPressed: _resetRotation,
|
||||||
child: Icon(Icons.explore),
|
child: const Icon(Icons.explore),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class CustomBottomAppBar extends StatelessWidget {
|
class CustomBottomAppBar extends StatelessWidget {
|
||||||
const CustomBottomAppBar(
|
const CustomBottomAppBar(
|
||||||
{this.fabLocation,
|
{super.key, this.fabLocation,
|
||||||
this.showNotch = false,
|
this.showNotch = false,
|
||||||
this.height = 56.0,
|
this.height = 56.0,
|
||||||
this.color = Colors.black45,
|
this.color = Colors.black45,
|
||||||
|
|
@ -24,7 +24,7 @@ class CustomBottomAppBar extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final List<Widget> rowContents = <Widget>[new SizedBox(height: height)];
|
final List<Widget> rowContents = <Widget>[SizedBox(height: height)];
|
||||||
|
|
||||||
if (kCenterLocations.contains(fabLocation)) {
|
if (kCenterLocations.contains(fabLocation)) {
|
||||||
/* rowContents.add(
|
/* rowContents.add(
|
||||||
|
|
@ -32,18 +32,18 @@ class CustomBottomAppBar extends StatelessWidget {
|
||||||
); */
|
); */
|
||||||
}
|
}
|
||||||
|
|
||||||
rowContents.addAll(this.actions);
|
rowContents.addAll(actions);
|
||||||
|
|
||||||
return showNotch
|
return showNotch
|
||||||
? new BottomAppBar(
|
? BottomAppBar(
|
||||||
color: color,
|
color: color,
|
||||||
shape: CircularNotchedRectangle(),
|
shape: const CircularNotchedRectangle(),
|
||||||
child: new Row(
|
child: Row(
|
||||||
mainAxisAlignment: mainAxisAlignment, children: rowContents),
|
mainAxisAlignment: mainAxisAlignment, children: rowContents),
|
||||||
)
|
)
|
||||||
: new BottomAppBar(
|
: BottomAppBar(
|
||||||
color: color,
|
color: color,
|
||||||
child: new Row(
|
child: Row(
|
||||||
mainAxisAlignment: mainAxisAlignment, children: rowContents),
|
mainAxisAlignment: mainAxisAlignment, children: rowContents),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
// Copyright (c) 2016, rinukkusu. All rights reserved. Use of this source code
|
// Copyright (c) 2016, rinukkusu. All rights reserved. Use of this source code
|
||||||
// is governed by a BSD-style license that can be found in the LICENSE file.
|
// is governed by a BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
import 'generated/i18n.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'generated/i18n.dart';
|
||||||
|
|
||||||
class Moment {
|
class Moment {
|
||||||
late DateTime _date;
|
|
||||||
|
|
||||||
Moment.now() {
|
Moment.now() {
|
||||||
_date = new DateTime.now();
|
_date = DateTime.now();
|
||||||
}
|
}
|
||||||
|
|
||||||
Moment.fromDate(DateTime date) {
|
Moment.fromDate(DateTime date) {
|
||||||
|
|
@ -17,27 +17,29 @@ class Moment {
|
||||||
|
|
||||||
Moment.fromMillisecondsSinceEpoch(int millisecondsSinceEpoch,
|
Moment.fromMillisecondsSinceEpoch(int millisecondsSinceEpoch,
|
||||||
{bool isUtc = false}) {
|
{bool isUtc = false}) {
|
||||||
_date = new DateTime.fromMillisecondsSinceEpoch(millisecondsSinceEpoch,
|
_date = DateTime.fromMillisecondsSinceEpoch(millisecondsSinceEpoch,
|
||||||
isUtc: isUtc);
|
isUtc: isUtc);
|
||||||
}
|
}
|
||||||
|
late DateTime _date;
|
||||||
|
|
||||||
static Moment parse(String date) {
|
static Moment parse(String date) {
|
||||||
return new Moment.fromDate(DateTime.parse(date));
|
return Moment.fromDate(DateTime.parse(date));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return _date.toString();
|
return _date.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
String fromNow(BuildContext context, [bool withoutPrefixOrSuffix = false]) {
|
String fromNow(BuildContext context, [bool withoutPrefixOrSuffix = false]) {
|
||||||
return from(context, new DateTime.now(), withoutPrefixOrSuffix);
|
return from(context, DateTime.now(), withoutPrefixOrSuffix);
|
||||||
}
|
}
|
||||||
|
|
||||||
String from(BuildContext context, DateTime date,
|
String from(BuildContext context, DateTime date,
|
||||||
[bool withoutPrefixOrSuffix = false]) {
|
[bool withoutPrefixOrSuffix = false]) {
|
||||||
Duration diff = date.difference(_date);
|
final Duration diff = date.difference(_date);
|
||||||
|
|
||||||
String timeString = "";
|
String timeString = '';
|
||||||
|
|
||||||
if (diff.inSeconds.abs() < 45)
|
if (diff.inSeconds.abs() < 45)
|
||||||
timeString = S.of(context).aF3wSeconds;
|
timeString = S.of(context).aF3wSeconds;
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ enum CustomStepperType {
|
||||||
horizontal,
|
horizontal,
|
||||||
}
|
}
|
||||||
|
|
||||||
const TextStyle _kCustomStepStyle = const TextStyle(
|
const TextStyle _kCustomStepStyle = TextStyle(
|
||||||
fontSize: 12.0,
|
fontSize: 12.0,
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
);
|
);
|
||||||
|
|
@ -120,16 +120,15 @@ class CustomStepper extends StatefulWidget {
|
||||||
/// new one.
|
/// new one.
|
||||||
///
|
///
|
||||||
/// The [steps], [type], and [currentCustomStep] arguments must not be null.
|
/// The [steps], [type], and [currentCustomStep] arguments must not be null.
|
||||||
CustomStepper({
|
const CustomStepper({
|
||||||
Key? key,
|
super.key,
|
||||||
required this.steps,
|
required this.steps,
|
||||||
this.type = CustomStepperType.vertical,
|
this.type = CustomStepperType.vertical,
|
||||||
this.currentCustomStep = 0,
|
this.currentCustomStep = 0,
|
||||||
this.onCustomStepTapped,
|
this.onCustomStepTapped,
|
||||||
this.onCustomStepContinue,
|
this.onCustomStepContinue,
|
||||||
this.onCustomStepCancel,
|
this.onCustomStepCancel,
|
||||||
}) : assert(0 <= currentCustomStep && currentCustomStep < steps.length),
|
}) : assert(0 <= currentCustomStep && currentCustomStep < steps.length);
|
||||||
super(key: key);
|
|
||||||
|
|
||||||
/// The steps of the stepper whose titles, subtitles, icons always get shown.
|
/// The steps of the stepper whose titles, subtitles, icons always get shown.
|
||||||
///
|
///
|
||||||
|
|
@ -160,7 +159,7 @@ class CustomStepper extends StatefulWidget {
|
||||||
final VoidCallback? onCustomStepCancel;
|
final VoidCallback? onCustomStepCancel;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_CustomStepperState createState() => new _CustomStepperState();
|
_CustomStepperState createState() => _CustomStepperState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _CustomStepperState extends State<CustomStepper> {
|
class _CustomStepperState extends State<CustomStepper> {
|
||||||
|
|
@ -170,9 +169,9 @@ class _CustomStepperState extends State<CustomStepper> {
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_keys = new List<GlobalKey>.generate(
|
_keys = List<GlobalKey>.generate(
|
||||||
widget.steps.length,
|
widget.steps.length,
|
||||||
(int i) => new GlobalKey(),
|
(int i) => GlobalKey(),
|
||||||
);
|
);
|
||||||
|
|
||||||
for (int i = 0; i < widget.steps.length; i += 1)
|
for (int i = 0; i < widget.steps.length; i += 1)
|
||||||
|
|
@ -205,7 +204,7 @@ class _CustomStepperState extends State<CustomStepper> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildLine(bool visible) {
|
Widget _buildLine(bool visible) {
|
||||||
return new Container(
|
return Container(
|
||||||
width: visible ? 1.0 : 0.0,
|
width: visible ? 1.0 : 0.0,
|
||||||
height: 16.0,
|
height: 16.0,
|
||||||
color: Colors.grey.shade400,
|
color: Colors.grey.shade400,
|
||||||
|
|
@ -219,19 +218,19 @@ class _CustomStepperState extends State<CustomStepper> {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case CustomStepState.indexed:
|
case CustomStepState.indexed:
|
||||||
case CustomStepState.disabled:
|
case CustomStepState.disabled:
|
||||||
return new Text(
|
return Text(
|
||||||
'${index + 1}',
|
'${index + 1}',
|
||||||
style: isDarkActive
|
style: isDarkActive
|
||||||
? _kCustomStepStyle.copyWith(color: Colors.black87)
|
? _kCustomStepStyle.copyWith(color: Colors.black87)
|
||||||
: _kCustomStepStyle,
|
: _kCustomStepStyle,
|
||||||
);
|
);
|
||||||
case CustomStepState.editing:
|
case CustomStepState.editing:
|
||||||
return new Icon(
|
return Icon(
|
||||||
Icons.edit,
|
Icons.edit,
|
||||||
color: isDarkActive ? _kCircleActiveDark : _kCircleActiveLight,
|
color: isDarkActive ? _kCircleActiveDark : _kCircleActiveLight,
|
||||||
);
|
);
|
||||||
case CustomStepState.complete:
|
case CustomStepState.complete:
|
||||||
return new Icon(
|
return Icon(
|
||||||
Icons.check,
|
Icons.check,
|
||||||
color: isDarkActive ? _kCircleActiveDark : _kCircleActiveLight,
|
color: isDarkActive ? _kCircleActiveDark : _kCircleActiveLight,
|
||||||
);
|
);
|
||||||
|
|
@ -255,18 +254,18 @@ class _CustomStepperState extends State<CustomStepper> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildCircle(int index, bool oldState) {
|
Widget _buildCircle(int index, bool oldState) {
|
||||||
return new Container(
|
return Container(
|
||||||
margin: const EdgeInsets.symmetric(vertical: 8.0),
|
margin: const EdgeInsets.symmetric(vertical: 8.0),
|
||||||
width: _kCustomStepSize,
|
width: _kCustomStepSize,
|
||||||
height: _kCustomStepSize,
|
height: _kCustomStepSize,
|
||||||
child: new AnimatedContainer(
|
child: AnimatedContainer(
|
||||||
curve: Curves.fastOutSlowIn,
|
curve: Curves.fastOutSlowIn,
|
||||||
duration: kThemeAnimationDuration,
|
duration: kThemeAnimationDuration,
|
||||||
decoration: new BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: _circleColor(index),
|
color: _circleColor(index),
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
child: new Center(
|
child: Center(
|
||||||
child: _buildCircleChild(index,
|
child: _buildCircleChild(index,
|
||||||
oldState && widget.steps[index].state == CustomStepState.error),
|
oldState && widget.steps[index].state == CustomStepState.error),
|
||||||
),
|
),
|
||||||
|
|
@ -275,20 +274,20 @@ class _CustomStepperState extends State<CustomStepper> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildTriangle(int index, bool oldState) {
|
Widget _buildTriangle(int index, bool oldState) {
|
||||||
return new Container(
|
return Container(
|
||||||
margin: const EdgeInsets.symmetric(vertical: 8.0),
|
margin: const EdgeInsets.symmetric(vertical: 8.0),
|
||||||
width: _kCustomStepSize,
|
width: _kCustomStepSize,
|
||||||
height: _kCustomStepSize,
|
height: _kCustomStepSize,
|
||||||
child: new Center(
|
child: Center(
|
||||||
child: new SizedBox(
|
child: SizedBox(
|
||||||
width: _kCustomStepSize,
|
width: _kCustomStepSize,
|
||||||
height:
|
height:
|
||||||
_kTriangleHeight, // Height of 24dp-long-sided equilateral triangle.
|
_kTriangleHeight, // Height of 24dp-long-sided equilateral triangle.
|
||||||
child: new CustomPaint(
|
child: CustomPaint(
|
||||||
painter: new _TrianglePainter(
|
painter: _TrianglePainter(
|
||||||
color: _isDark() ? _kErrorDark : _kErrorLight,
|
color: _isDark() ? _kErrorDark : _kErrorLight,
|
||||||
),
|
),
|
||||||
child: new Align(
|
child: Align(
|
||||||
alignment: const Alignment(
|
alignment: const Alignment(
|
||||||
0.0, 0.8), // 0.8 looks better than the geometrical 0.33.
|
0.0, 0.8), // 0.8 looks better than the geometrical 0.33.
|
||||||
child: _buildCircleChild(
|
child: _buildCircleChild(
|
||||||
|
|
@ -304,7 +303,7 @@ class _CustomStepperState extends State<CustomStepper> {
|
||||||
|
|
||||||
Widget _buildIcon(int index) {
|
Widget _buildIcon(int index) {
|
||||||
if (widget.steps[index].state != _oldStates[index]) {
|
if (widget.steps[index].state != _oldStates[index]) {
|
||||||
return new AnimatedCrossFade(
|
return AnimatedCrossFade(
|
||||||
firstChild: _buildCircle(index, true),
|
firstChild: _buildCircle(index, true),
|
||||||
secondChild: _buildTriangle(index, true),
|
secondChild: _buildTriangle(index, true),
|
||||||
firstCurve: const Interval(0.0, 0.6, curve: Curves.fastOutSlowIn),
|
firstCurve: const Interval(0.0, 0.6, curve: Curves.fastOutSlowIn),
|
||||||
|
|
@ -339,29 +338,12 @@ class _CustomStepperState extends State<CustomStepper> {
|
||||||
// final MaterialLocalizations localizations =
|
// final MaterialLocalizations localizations =
|
||||||
// MaterialLocalizations.of(context);
|
// MaterialLocalizations.of(context);
|
||||||
|
|
||||||
return new Container(
|
return Container(
|
||||||
margin: const EdgeInsets.only(top: 16.0),
|
margin: const EdgeInsets.only(top: 16.0),
|
||||||
child: new ConstrainedBox(
|
child: ConstrainedBox(
|
||||||
constraints: const BoxConstraints.tightFor(height: 48.0),
|
constraints: const BoxConstraints.tightFor(height: 48.0),
|
||||||
child: new Row(
|
child: const Row(
|
||||||
children: <Widget>[
|
|
||||||
/* new FlatButton(
|
|
||||||
onPressed: widget.onCustomStepContinue,
|
|
||||||
color: _isDark() ? themeData.backgroundColor : themeData.primaryColor,
|
|
||||||
textColor: Colors.white,
|
|
||||||
textTheme: ButtonTextTheme.normal,
|
|
||||||
child: new Text(localizations.continueButtonLabel),
|
|
||||||
),
|
|
||||||
new Container(
|
|
||||||
margin: const EdgeInsetsDirectional.only(start: 8.0),
|
|
||||||
child: new FlatButton(
|
|
||||||
onPressed: widget.onCustomStepCancel,
|
|
||||||
textColor: cancelColor,
|
|
||||||
textTheme: ButtonTextTheme.normal,
|
|
||||||
child: new Text(localizations.cancelButtonLabel),
|
|
||||||
),
|
|
||||||
), */
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -407,7 +389,7 @@ class _CustomStepperState extends State<CustomStepper> {
|
||||||
|
|
||||||
Widget _buildHeaderText(int index) {
|
Widget _buildHeaderText(int index) {
|
||||||
final List<Widget> children = <Widget>[
|
final List<Widget> children = <Widget>[
|
||||||
new AnimatedDefaultTextStyle(
|
AnimatedDefaultTextStyle(
|
||||||
style: _titleStyle(index),
|
style: _titleStyle(index),
|
||||||
duration: kThemeAnimationDuration,
|
duration: kThemeAnimationDuration,
|
||||||
curve: Curves.fastOutSlowIn,
|
curve: Curves.fastOutSlowIn,
|
||||||
|
|
@ -417,9 +399,9 @@ class _CustomStepperState extends State<CustomStepper> {
|
||||||
|
|
||||||
if (widget.steps[index].subtitle != null) {
|
if (widget.steps[index].subtitle != null) {
|
||||||
children.add(
|
children.add(
|
||||||
new Container(
|
Container(
|
||||||
margin: const EdgeInsets.only(top: 2.0),
|
margin: const EdgeInsets.only(top: 2.0),
|
||||||
child: new AnimatedDefaultTextStyle(
|
child: AnimatedDefaultTextStyle(
|
||||||
style: _subtitleStyle(index),
|
style: _subtitleStyle(index),
|
||||||
duration: kThemeAnimationDuration,
|
duration: kThemeAnimationDuration,
|
||||||
curve: Curves.fastOutSlowIn,
|
curve: Curves.fastOutSlowIn,
|
||||||
|
|
@ -429,57 +411,57 @@ class _CustomStepperState extends State<CustomStepper> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: children);
|
children: children);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildVerticalHeader(int index) {
|
Widget _buildVerticalHeader(int index) {
|
||||||
return new Container(
|
return Container(
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 24.0),
|
margin: const EdgeInsets.symmetric(horizontal: 24.0),
|
||||||
child: new Row(children: <Widget>[
|
child: Row(children: <Widget>[
|
||||||
new Column(children: <Widget>[
|
Column(children: <Widget>[
|
||||||
// Line parts are always added in order for the ink splash to
|
// Line parts are always added in order for the ink splash to
|
||||||
// flood the tips of the connector lines.
|
// flood the tips of the connector lines.
|
||||||
_buildLine(!_isFirst(index)),
|
_buildLine(!_isFirst(index)),
|
||||||
_buildIcon(index),
|
_buildIcon(index),
|
||||||
_buildLine(!_isLast(index)),
|
_buildLine(!_isLast(index)),
|
||||||
]),
|
]),
|
||||||
new Container(
|
Container(
|
||||||
margin: const EdgeInsetsDirectional.only(start: 12.0),
|
margin: const EdgeInsetsDirectional.only(start: 12.0),
|
||||||
child: _buildHeaderText(index))
|
child: _buildHeaderText(index))
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildVerticalBody(int index) {
|
Widget _buildVerticalBody(int index) {
|
||||||
return new Stack(
|
return Stack(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
new PositionedDirectional(
|
PositionedDirectional(
|
||||||
start: 24.0,
|
start: 24.0,
|
||||||
top: 0.0,
|
top: 0.0,
|
||||||
bottom: 0.0,
|
bottom: 0.0,
|
||||||
child: new SizedBox(
|
child: SizedBox(
|
||||||
width: 24.0,
|
width: 24.0,
|
||||||
child: new Center(
|
child: Center(
|
||||||
child: new SizedBox(
|
child: SizedBox(
|
||||||
width: _isLast(index) ? 0.0 : 1.0,
|
width: _isLast(index) ? 0.0 : 1.0,
|
||||||
child: new Container(
|
child: Container(
|
||||||
color: Colors.grey.shade400,
|
color: Colors.grey.shade400,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
new AnimatedCrossFade(
|
AnimatedCrossFade(
|
||||||
firstChild: new Container(height: 0.0),
|
firstChild: Container(height: 0.0),
|
||||||
secondChild: new Container(
|
secondChild: Container(
|
||||||
margin: const EdgeInsetsDirectional.only(
|
margin: const EdgeInsetsDirectional.only(
|
||||||
start: 60.0,
|
start: 60.0,
|
||||||
end: 24.0,
|
end: 24.0,
|
||||||
bottom: 24.0,
|
bottom: 24.0,
|
||||||
),
|
),
|
||||||
child: new Column(
|
child: Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
widget.steps[index].content,
|
widget.steps[index].content,
|
||||||
_buildVerticalControls(),
|
_buildVerticalControls(),
|
||||||
|
|
@ -502,8 +484,8 @@ class _CustomStepperState extends State<CustomStepper> {
|
||||||
final List<Widget> children = <Widget>[];
|
final List<Widget> children = <Widget>[];
|
||||||
|
|
||||||
for (int i = 0; i < widget.steps.length; i += 1) {
|
for (int i = 0; i < widget.steps.length; i += 1) {
|
||||||
children.add(new Column(key: _keys[i], children: <Widget>[
|
children.add(Column(key: _keys[i], children: <Widget>[
|
||||||
new InkWell(
|
InkWell(
|
||||||
onTap: widget.steps[i].state != CustomStepState.disabled
|
onTap: widget.steps[i].state != CustomStepState.disabled
|
||||||
? () {
|
? () {
|
||||||
// In the vertical case we need to scroll to the newly tapped
|
// In the vertical case we need to scroll to the newly tapped
|
||||||
|
|
@ -522,7 +504,7 @@ class _CustomStepperState extends State<CustomStepper> {
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ListView(
|
return ListView(
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
children: children,
|
children: children,
|
||||||
);
|
);
|
||||||
|
|
@ -533,21 +515,21 @@ class _CustomStepperState extends State<CustomStepper> {
|
||||||
|
|
||||||
for (int i = 0; i < widget.steps.length; i += 1) {
|
for (int i = 0; i < widget.steps.length; i += 1) {
|
||||||
children.add(
|
children.add(
|
||||||
new InkResponse(
|
InkResponse(
|
||||||
onTap: widget.steps[i].state != CustomStepState.disabled
|
onTap: widget.steps[i].state != CustomStepState.disabled
|
||||||
? () {
|
? () {
|
||||||
widget.onCustomStepTapped?.call(i);
|
widget.onCustomStepTapped?.call(i);
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
child: new Row(
|
child: Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
new Container(
|
SizedBox(
|
||||||
height: 72.0,
|
height: 72.0,
|
||||||
child: new Center(
|
child: Center(
|
||||||
child: _buildIcon(i),
|
child: _buildIcon(i),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
new Container(
|
Container(
|
||||||
margin: const EdgeInsetsDirectional.only(start: 12.0),
|
margin: const EdgeInsetsDirectional.only(start: 12.0),
|
||||||
child: _buildHeaderText(i),
|
child: _buildHeaderText(i),
|
||||||
),
|
),
|
||||||
|
|
@ -558,8 +540,8 @@ class _CustomStepperState extends State<CustomStepper> {
|
||||||
|
|
||||||
if (!_isLast(i)) {
|
if (!_isLast(i)) {
|
||||||
children.add(
|
children.add(
|
||||||
new Expanded(
|
Expanded(
|
||||||
child: new Container(
|
child: Container(
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 8.0),
|
margin: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||||
height: 1.0,
|
height: 1.0,
|
||||||
color: Colors.grey.shade400,
|
color: Colors.grey.shade400,
|
||||||
|
|
@ -569,22 +551,22 @@ class _CustomStepperState extends State<CustomStepper> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Column(
|
return Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
new Material(
|
Material(
|
||||||
elevation: 2.0,
|
elevation: 2.0,
|
||||||
child: new Container(
|
child: Container(
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 24.0),
|
margin: const EdgeInsets.symmetric(horizontal: 24.0),
|
||||||
child: new Row(
|
child: Row(
|
||||||
children: children,
|
children: children,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
new Expanded(
|
Expanded(
|
||||||
child: new ListView(
|
child: ListView(
|
||||||
padding: const EdgeInsets.all(24.0),
|
padding: const EdgeInsets.all(24.0),
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
new AnimatedSize(
|
AnimatedSize(
|
||||||
curve: Curves.fastOutSlowIn,
|
curve: Curves.fastOutSlowIn,
|
||||||
duration: kThemeAnimationDuration,
|
duration: kThemeAnimationDuration,
|
||||||
child: widget.steps[widget.currentCustomStep].content,
|
child: widget.steps[widget.currentCustomStep].content,
|
||||||
|
|
@ -602,7 +584,7 @@ class _CustomStepperState extends State<CustomStepper> {
|
||||||
assert(debugCheckHasMaterial(context));
|
assert(debugCheckHasMaterial(context));
|
||||||
assert(() {
|
assert(() {
|
||||||
if (context.findAncestorWidgetOfExactType<CustomStepper>() != null)
|
if (context.findAncestorWidgetOfExactType<CustomStepper>() != null)
|
||||||
throw new FlutterError(
|
throw FlutterError(
|
||||||
'CustomSteppers must not be nested. The material specification advises '
|
'CustomSteppers must not be nested. The material specification advises '
|
||||||
'that one should avoid embedding steppers within steppers. '
|
'that one should avoid embedding steppers within steppers. '
|
||||||
'https://material.google.com/components/steppers.html#steppers-usage\n');
|
'https://material.google.com/components/steppers.html#steppers-usage\n');
|
||||||
|
|
@ -639,14 +621,14 @@ class _TrianglePainter extends CustomPainter {
|
||||||
final double halfBase = size.width / 2.0;
|
final double halfBase = size.width / 2.0;
|
||||||
final double height = size.height;
|
final double height = size.height;
|
||||||
final List<Offset> points = <Offset>[
|
final List<Offset> points = <Offset>[
|
||||||
new Offset(0.0, height),
|
Offset(0.0, height),
|
||||||
new Offset(base, height),
|
Offset(base, height),
|
||||||
new Offset(halfBase, 0.0),
|
Offset(halfBase, 0.0),
|
||||||
];
|
];
|
||||||
|
|
||||||
canvas.drawPath(
|
canvas.drawPath(
|
||||||
new Path()..addPolygon(points, true),
|
Path()..addPolygon(points, true),
|
||||||
new Paint()..color = color,
|
Paint()..color = color,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@ import 'package:flutter/material.dart';
|
||||||
|
|
||||||
/// Dummy/placeholder widget for map plugins
|
/// Dummy/placeholder widget for map plugins
|
||||||
class DummyMapPluginWidget extends StatelessWidget {
|
class DummyMapPluginWidget extends StatelessWidget {
|
||||||
|
const DummyMapPluginWidget({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return const SizedBox();
|
return const SizedBox();
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/services.dart' show rootBundle;
|
import 'package:flutter/services.dart' show rootBundle;
|
||||||
|
|
||||||
final esRegExp = new RegExp("^es-");
|
final RegExp esRegExp = RegExp('^es-');
|
||||||
|
|
||||||
String getFallbackLang(String lang) {
|
String getFallbackLang(String lang) {
|
||||||
if (lang == 'ast' ||
|
if (lang == 'ast' ||
|
||||||
|
|
@ -10,7 +10,7 @@ String getFallbackLang(String lang) {
|
||||||
lang == 'eu' ||
|
lang == 'eu' ||
|
||||||
lang == 'es' ||
|
lang == 'es' ||
|
||||||
lang == 'ca' ||
|
lang == 'ca' ||
|
||||||
(esRegExp).allMatches(lang).length > 0) {
|
esRegExp.allMatches(lang).isNotEmpty) {
|
||||||
return 'es';
|
return 'es';
|
||||||
}
|
}
|
||||||
return 'en';
|
return 'en';
|
||||||
|
|
@ -21,8 +21,8 @@ Future<String> getFileNameOfLang(
|
||||||
required String fileName,
|
required String fileName,
|
||||||
required String ext,
|
required String ext,
|
||||||
required String lang}) async {
|
required String lang}) async {
|
||||||
String base = '$dir/$fileName';
|
final String base = '$dir/$fileName';
|
||||||
String fallback = getFallbackLang(lang);
|
final String fallback = getFallbackLang(lang);
|
||||||
String file = '$base-$lang.$ext';
|
String file = '$base-$lang.$ext';
|
||||||
if (await assetNotExists(file)) {
|
if (await assetNotExists(file)) {
|
||||||
file = '$base-$fallback.$ext';
|
file = '$base-$fallback.$ext';
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,12 @@ import 'package:url_launcher/url_launcher.dart';
|
||||||
import 'customStepper.dart';
|
import 'customStepper.dart';
|
||||||
import 'generated/i18n.dart';
|
import 'generated/i18n.dart';
|
||||||
import 'mainDrawer.dart';
|
import 'mainDrawer.dart';
|
||||||
|
import 'models/yourLocation.dart';
|
||||||
import 'placesAutocompleteUtils.dart';
|
import 'placesAutocompleteUtils.dart';
|
||||||
|
|
||||||
class FireAlert extends StatefulWidget {
|
class FireAlert extends StatefulWidget {
|
||||||
|
const FireAlert({super.key});
|
||||||
|
|
||||||
static const String routeName = '/fireAlert';
|
static const String routeName = '/fireAlert';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -17,31 +20,31 @@ class FireAlert extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _FireAlertState extends State<FireAlert> {
|
class _FireAlertState extends State<FireAlert> {
|
||||||
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
|
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
|
||||||
int _currentStep = 0;
|
int _currentStep = 0;
|
||||||
|
|
||||||
Widget buildCallButton() {
|
Widget buildCallButton() {
|
||||||
return new Align(
|
return Align(
|
||||||
alignment: const Alignment(0.0, -0.2),
|
alignment: const Alignment(0.0, -0.2),
|
||||||
child: new FloatingActionButton(
|
child: FloatingActionButton(
|
||||||
heroTag: 'callAction',
|
heroTag: 'callAction',
|
||||||
child: const Icon(Icons.call),
|
child: const Icon(Icons.call),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
launch("tel://112");
|
launch('tel://112');
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget buildNotifyNeighboursButton() {
|
Widget buildNotifyNeighboursButton() {
|
||||||
return new Align(
|
return Align(
|
||||||
alignment: const Alignment(0.0, -0.2),
|
alignment: const Alignment(0.0, -0.2),
|
||||||
child: new FloatingActionButton(
|
child: FloatingActionButton(
|
||||||
heroTag: 'neighAction',
|
heroTag: 'neighAction',
|
||||||
child: const Icon(CommunityMaterialIcons.bullhorn),
|
child: const Icon(CommunityMaterialIcons.bullhorn),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(new SnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||||
content: new Text(S.of(context).inDevelopment),
|
content: Text(S.of(context).inDevelopment),
|
||||||
));
|
));
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
@ -49,26 +52,26 @@ class _FireAlertState extends State<FireAlert> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget buildTweetButton() {
|
Widget buildTweetButton() {
|
||||||
return new Align(
|
return Align(
|
||||||
alignment: const Alignment(0.0, -0.2),
|
alignment: const Alignment(0.0, -0.2),
|
||||||
child: new FloatingActionButton(
|
child: FloatingActionButton(
|
||||||
heroTag: 'tweetAction',
|
heroTag: 'tweetAction',
|
||||||
child: const Icon(CommunityMaterialIcons.twitter),
|
child: const Icon(CommunityMaterialIcons.twitter),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// In Android you can choose with app to use with setPackage but seems it's not implemented here
|
// In Android you can choose with app to use with setPackage but seems it's not implemented here
|
||||||
// https://stackoverflow.com/questions/6814268/android-share-on-facebook-twitter-mail-ecc
|
// https://stackoverflow.com/questions/6814268/android-share-on-facebook-twitter-mail-ecc
|
||||||
openPlacesDialog(_scaffoldKey).then((yourLocation) {
|
openPlacesDialog(_scaffoldKey).then((YourLocation yourLocation) {
|
||||||
String where =
|
final String where =
|
||||||
yourLocation.description.replaceAll(' ', '').split(',')[0];
|
yourLocation.description.replaceAll(' ', '').split(',')[0];
|
||||||
print(where);
|
print(where);
|
||||||
Share.shareWithResult(S
|
Share.shareWithResult(S
|
||||||
.of(context)
|
.of(context)
|
||||||
.tweetAboutSelf(yourLocation.description, '#IF$where'));
|
.tweetAboutSelf(yourLocation.description, '#IF$where'));
|
||||||
}).catchError((onError) {
|
}).catchError((onError) {
|
||||||
final ctx = _scaffoldKey.currentContext;
|
final BuildContext? ctx = _scaffoldKey.currentContext;
|
||||||
if (ctx != null) {
|
if (ctx != null) {
|
||||||
ScaffoldMessenger.of(ctx).showSnackBar(new SnackBar(
|
ScaffoldMessenger.of(ctx).showSnackBar(SnackBar(
|
||||||
content: new Text(S.of(ctx).errorFirePlaceDialog)));
|
content: Text(S.of(ctx).errorFirePlaceDialog)));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
@ -81,40 +84,40 @@ class _FireAlertState extends State<FireAlert> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
List<CustomStep> fireSteps = listWithoutNulls(<CustomStep>[
|
final List<CustomStep> fireSteps = listWithoutNulls(<CustomStep>[
|
||||||
new CustomStep(
|
CustomStep(
|
||||||
title: new Text(S.of(context).callEmergencyServicesTitle),
|
title: Text(S.of(context).callEmergencyServicesTitle),
|
||||||
content: new Column(children: <Widget>[
|
content: Column(children: <Widget>[
|
||||||
new Text(S.of(context).callEmergencyServicesDescription),
|
Text(S.of(context).callEmergencyServicesDescription),
|
||||||
new SizedBox(height: 20.0),
|
const SizedBox(height: 20.0),
|
||||||
buildCallButton()
|
buildCallButton()
|
||||||
])),
|
])),
|
||||||
new CustomStep(
|
CustomStep(
|
||||||
title: new Text(S.of(context).notifyNeighbours),
|
title: Text(S.of(context).notifyNeighbours),
|
||||||
// state: CustomStepState.disabled,
|
// state: CustomStepState.disabled,
|
||||||
content: new Column(children: <Widget>[
|
content: Column(children: <Widget>[
|
||||||
new Text(S.of(context).notifyNeighboursDescription),
|
Text(S.of(context).notifyNeighboursDescription),
|
||||||
new SizedBox(height: 20.0),
|
const SizedBox(height: 20.0),
|
||||||
buildNotifyNeighboursButton()
|
buildNotifyNeighboursButton()
|
||||||
])),
|
])),
|
||||||
// TODO conditional: this only in Spain
|
// TODOconditional: this only in Spain
|
||||||
new CustomStep(
|
CustomStep(
|
||||||
title: new Text(S.of(context).tweetAboutAFireTitle),
|
title: Text(S.of(context).tweetAboutAFireTitle),
|
||||||
// subtitle: new Text(S.of(context).tweetAboutAFireDescription),
|
// subtitle: new Text(S.of(context).tweetAboutAFireDescription),
|
||||||
content: new Column(children: <Widget>[
|
content: Column(children: <Widget>[
|
||||||
new Text(S.of(context).tweetAboutAFireDescription),
|
Text(S.of(context).tweetAboutAFireDescription),
|
||||||
new SizedBox(height: 20.0),
|
const SizedBox(height: 20.0),
|
||||||
buildTweetButton()
|
buildTweetButton()
|
||||||
])),
|
])),
|
||||||
]);
|
]);
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
key: _scaffoldKey,
|
key: _scaffoldKey,
|
||||||
appBar: new AppBar(title: new Text(S.of(context).notifyAFire)),
|
appBar: AppBar(title: Text(S.of(context).notifyAFire)),
|
||||||
drawer: new MainDrawer(context, FireAlert.routeName),
|
drawer: MainDrawer(context, FireAlert.routeName),
|
||||||
body: new CustomStepper(
|
body: CustomStepper(
|
||||||
currentCustomStep: _currentStep,
|
currentCustomStep: _currentStep,
|
||||||
// type: StepperType.horizontal,
|
// type: StepperType.horizontal,
|
||||||
onCustomStepTapped: (num) => setState(() {
|
onCustomStepTapped: (int num) => setState(() {
|
||||||
_currentStep = num;
|
_currentStep = num;
|
||||||
}),
|
}),
|
||||||
steps: fireSteps));
|
steps: fireSteps));
|
||||||
|
|
|
||||||
|
|
@ -31,16 +31,16 @@ Marker FireMarker(
|
||||||
Offset _getAnchorOffset(FireMarkType type) {
|
Offset _getAnchorOffset(FireMarkType type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case FireMarkType.position:
|
case FireMarkType.position:
|
||||||
return Offset(40.0, 20.0);
|
return const Offset(40.0, 20.0);
|
||||||
case FireMarkType.fire:
|
case FireMarkType.fire:
|
||||||
return Offset(40.0, 24.0);
|
return const Offset(40.0, 24.0);
|
||||||
case FireMarkType.pixel:
|
case FireMarkType.pixel:
|
||||||
// Auto-calculate based on marker size
|
// Auto-calculate based on marker size
|
||||||
return Offset(40.0, 40.0);
|
return const Offset(40.0, 40.0);
|
||||||
case FireMarkType.falsePos:
|
case FireMarkType.falsePos:
|
||||||
case FireMarkType.industry:
|
case FireMarkType.industry:
|
||||||
return Offset(40.0, 35.0);
|
return const Offset(40.0, 35.0);
|
||||||
default:
|
default:
|
||||||
return Offset(40.0, 40.0);
|
return const Offset(40.0, 40.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,27 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'fireMarkType.dart';
|
|
||||||
import 'colors.dart';
|
import 'colors.dart';
|
||||||
|
import 'fireMarkType.dart';
|
||||||
|
|
||||||
class FireMarkerIcon extends StatelessWidget {
|
class FireMarkerIcon extends StatelessWidget {
|
||||||
final FireMarkType type;
|
|
||||||
|
|
||||||
FireMarkerIcon(this.type);
|
const FireMarkerIcon(this.type, {super.key});
|
||||||
|
final FireMarkType type;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case FireMarkType.position:
|
case FireMarkType.position:
|
||||||
return new Icon(Icons.location_on, color: fires600, size: 50.0);
|
return const Icon(Icons.location_on, color: fires600, size: 50.0);
|
||||||
case FireMarkType.pixel:
|
case FireMarkType.pixel:
|
||||||
return new Icon(Icons.brightness_1, color: fires900, size: 3.0);
|
return const Icon(Icons.brightness_1, color: fires900, size: 3.0);
|
||||||
case FireMarkType.fire:
|
case FireMarkType.fire:
|
||||||
return new Image.asset('images/fire-marker-l.png');
|
return Image.asset('images/fire-marker-l.png');
|
||||||
case FireMarkType.industry:
|
case FireMarkType.industry:
|
||||||
return new Image.asset('images/industry-marker-reg.png');
|
return Image.asset('images/industry-marker-reg.png');
|
||||||
case FireMarkType.falsePos:
|
case FireMarkType.falsePos:
|
||||||
default:
|
default:
|
||||||
return new Image.asset('images/industry-marker.png');
|
return Image.asset('images/industry-marker.png');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:comunes_flutter/comunes_flutter.dart';
|
import 'package:comunes_flutter/comunes_flutter.dart';
|
||||||
import 'package:fires_flutter/models/fireNotification.dart';
|
|
||||||
import 'package:fires_flutter/models/yourLocation.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_redux/flutter_redux.dart';
|
import 'package:flutter_redux/flutter_redux.dart';
|
||||||
import 'package:redux/redux.dart';
|
import 'package:redux/redux.dart';
|
||||||
|
|
@ -13,19 +11,14 @@ import 'generated/i18n.dart';
|
||||||
import 'genericMap.dart';
|
import 'genericMap.dart';
|
||||||
import 'mainDrawer.dart';
|
import 'mainDrawer.dart';
|
||||||
import 'models/appState.dart';
|
import 'models/appState.dart';
|
||||||
|
import 'models/fireNotification.dart';
|
||||||
|
import 'models/yourLocation.dart';
|
||||||
import 'redux/actions.dart';
|
import 'redux/actions.dart';
|
||||||
|
|
||||||
@immutable
|
@immutable
|
||||||
class _ViewModel {
|
class _ViewModel {
|
||||||
final bool isLoaded;
|
|
||||||
final List<FireNotification> fireNotifications;
|
|
||||||
final int fireNotificationsUnread;
|
|
||||||
final List<YourLocation> yourLocations;
|
|
||||||
final TapFireNotificationFunction onTap;
|
|
||||||
final DeleteFireNotificationFunction onDelete;
|
|
||||||
final DeleteAllFireNotificationFunction onDeleteAll;
|
|
||||||
|
|
||||||
_ViewModel(
|
const _ViewModel(
|
||||||
{required this.isLoaded,
|
{required this.isLoaded,
|
||||||
required this.onTap,
|
required this.onTap,
|
||||||
required this.onDelete,
|
required this.onDelete,
|
||||||
|
|
@ -33,6 +26,13 @@ class _ViewModel {
|
||||||
required this.fireNotifications,
|
required this.fireNotifications,
|
||||||
required this.yourLocations,
|
required this.yourLocations,
|
||||||
required this.fireNotificationsUnread});
|
required this.fireNotificationsUnread});
|
||||||
|
final bool isLoaded;
|
||||||
|
final List<FireNotification> fireNotifications;
|
||||||
|
final int fireNotificationsUnread;
|
||||||
|
final List<YourLocation> yourLocations;
|
||||||
|
final TapFireNotificationFunction onTap;
|
||||||
|
final DeleteFireNotificationFunction onDelete;
|
||||||
|
final DeleteAllFireNotificationFunction onDeleteAll;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) =>
|
bool operator ==(Object other) =>
|
||||||
|
|
@ -53,6 +53,8 @@ class _ViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
class FireNotificationList extends StatefulWidget {
|
class FireNotificationList extends StatefulWidget {
|
||||||
|
const FireNotificationList({super.key});
|
||||||
|
|
||||||
static const String routeName = '/fireNotifications';
|
static const String routeName = '/fireNotifications';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -60,23 +62,23 @@ class FireNotificationList extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _FireNotificationListState extends State<FireNotificationList> {
|
class _FireNotificationListState extends State<FireNotificationList> {
|
||||||
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
|
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
|
||||||
|
|
||||||
Widget _buildRow(BuildContext context, List<YourLocation> yourLocations,
|
Widget _buildRow(BuildContext context, List<YourLocation> yourLocations,
|
||||||
FireNotification notif, onDeleted, onTap) {
|
FireNotification notif, onDeleted, onTap) {
|
||||||
String prefix = "";
|
String prefix = '';
|
||||||
|
|
||||||
// FIXME (this can fails if you don't have a location for this notif, for instance during tests)
|
// FIXME (this can fails if you don't have a location for this notif, for instance during tests)
|
||||||
YourLocation yl = yourLocations.singleWhere((yl) => yl.id == notif.subsId);
|
final YourLocation yl = yourLocations.singleWhere((YourLocation yl) => yl.id == notif.subsId);
|
||||||
prefix = '${yl.description}. ';
|
prefix = '${yl.description}. ';
|
||||||
|
|
||||||
return new ListTile(
|
return ListTile(
|
||||||
dense: true,
|
dense: true,
|
||||||
leading: const Icon(Icons.whatshot),
|
leading: const Icon(Icons.whatshot),
|
||||||
title: new Text('$prefix${notif.description}',
|
title: Text('$prefix${notif.description}',
|
||||||
style: new TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: notif.read ? FontWeight.normal : FontWeight.bold)),
|
fontWeight: notif.read ? FontWeight.normal : FontWeight.bold)),
|
||||||
subtitle: new Text(Moment.now().from(context, notif.when)),
|
subtitle: Text(Moment.now().from(context, notif.when)),
|
||||||
onLongPress: () {
|
onLongPress: () {
|
||||||
showSnackMsg(S.of(context).toDeleteThisNotification);
|
showSnackMsg(S.of(context).toDeleteThisNotification);
|
||||||
},
|
},
|
||||||
|
|
@ -86,8 +88,8 @@ class _FireNotificationListState extends State<FireNotificationList> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void showSnackMsg(String msg) {
|
void showSnackMsg(String msg) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(new SnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||||
content: new Text(msg),
|
content: Text(msg),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -97,78 +99,77 @@ class _FireNotificationListState extends State<FireNotificationList> {
|
||||||
List<FireNotification> notifList,
|
List<FireNotification> notifList,
|
||||||
onDeleted,
|
onDeleted,
|
||||||
onTap) {
|
onTap) {
|
||||||
return new RefreshIndicator(
|
return RefreshIndicator(
|
||||||
child: new ListView.builder(
|
onRefresh: _handleRefresh,
|
||||||
|
child: ListView.builder(
|
||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
// reverse: true,
|
// reverse: true,
|
||||||
// shrinkWrap: true,
|
// shrinkWrap: true,
|
||||||
itemCount: notifList.length,
|
itemCount: notifList.length,
|
||||||
itemBuilder: (BuildContext _context, int i) {
|
itemBuilder: (BuildContext context, int i) {
|
||||||
final ThemeData theme = Theme.of(context);
|
final ThemeData theme = Theme.of(context);
|
||||||
return new Dismissible(
|
return Dismissible(
|
||||||
key: new ObjectKey(notifList.elementAt(i)),
|
key: ObjectKey(notifList.elementAt(i)),
|
||||||
direction: DismissDirection.horizontal,
|
|
||||||
onDismissed: (DismissDirection direction) {
|
onDismissed: (DismissDirection direction) {
|
||||||
onDeleted(notifList.elementAt(i));
|
onDeleted(notifList.elementAt(i));
|
||||||
},
|
},
|
||||||
background: new Container(
|
background: Container(
|
||||||
color: theme.primaryColor,
|
color: theme.primaryColor,
|
||||||
child: const ListTile(
|
child: const ListTile(
|
||||||
leading: const Icon(Icons.delete,
|
leading: Icon(Icons.delete,
|
||||||
color: Colors.white, size: 36.0))),
|
color: Colors.white, size: 36.0))),
|
||||||
secondaryBackground: new Container(
|
secondaryBackground: Container(
|
||||||
color: theme.primaryColor,
|
color: theme.primaryColor,
|
||||||
child: const ListTile(
|
child: const ListTile(
|
||||||
trailing: const Icon(Icons.delete,
|
trailing: Icon(Icons.delete,
|
||||||
color: Colors.white, size: 36.0))),
|
color: Colors.white, size: 36.0))),
|
||||||
child: new Container(
|
child: Container(
|
||||||
decoration: new BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: theme.canvasColor,
|
color: theme.canvasColor,
|
||||||
border: new Border(
|
border: Border(
|
||||||
bottom:
|
bottom:
|
||||||
new BorderSide(color: theme.dividerColor))),
|
BorderSide(color: theme.dividerColor))),
|
||||||
child: _buildRow(context, yourLocations,
|
child: _buildRow(context, yourLocations,
|
||||||
notifList.elementAt(i), onDeleted, onTap)));
|
notifList.elementAt(i), onDeleted, onTap)));
|
||||||
}),
|
}));
|
||||||
onRefresh: _handleRefresh);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Null> _handleRefresh() async {
|
Future<void> _handleRefresh() async {
|
||||||
await new Future.delayed(new Duration(seconds: 1));
|
await Future.delayed(const Duration(seconds: 1));
|
||||||
|
|
||||||
setState(() {});
|
setState(() {});
|
||||||
|
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return new StoreConnector<AppState, _ViewModel>(
|
return StoreConnector<AppState, _ViewModel>(
|
||||||
distinct: true,
|
distinct: true,
|
||||||
converter: (store) {
|
converter: (Store<AppState> store) {
|
||||||
print(
|
print(
|
||||||
'New ViewModel of Fires Notifications (unread: ${store.state.fireNotificationsUnread})');
|
'New ViewModel of Fires Notifications (unread: ${store.state.fireNotificationsUnread})');
|
||||||
return new _ViewModel(
|
return _ViewModel(
|
||||||
isLoaded: store.state.isLoaded,
|
isLoaded: store.state.isLoaded,
|
||||||
onDeleteAll: () {
|
onDeleteAll: () {
|
||||||
store.dispatch(new DeleteAllFireNotificationAction());
|
store.dispatch(DeleteAllFireNotificationAction());
|
||||||
},
|
},
|
||||||
onDelete: (notif) {
|
onDelete: (FireNotification notif) {
|
||||||
store.dispatch(new DeleteFireNotificationAction(notif));
|
store.dispatch(DeleteFireNotificationAction(notif));
|
||||||
ScaffoldMessenger.of(context).showSnackBar(new SnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||||
content: new Text(S.of(context).youDeletedThisNotification),
|
content: Text(S.of(context).youDeletedThisNotification),
|
||||||
action: new SnackBarAction(
|
action: SnackBarAction(
|
||||||
label: S.of(context).UNDO,
|
label: S.of(context).UNDO,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
store.dispatch(new AddFireNotificationAction(notif));
|
store.dispatch(AddFireNotificationAction(notif));
|
||||||
})));
|
})));
|
||||||
},
|
},
|
||||||
onTap: (notif) {
|
onTap: (FireNotification notif) {
|
||||||
if (!notif.read) {
|
if (!notif.read) {
|
||||||
store.dispatch(new ReadFireNotificationAction(
|
store.dispatch(ReadFireNotificationAction(
|
||||||
notif.copyWith(read: true)));
|
notif.copyWith(read: true)));
|
||||||
}
|
}
|
||||||
new Timer(new Duration(milliseconds: 500), () {
|
Timer(const Duration(milliseconds: 500), () {
|
||||||
gotoMap(store, notif, context);
|
gotoMap(store, notif, context);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
@ -176,48 +177,46 @@ class _FireNotificationListState extends State<FireNotificationList> {
|
||||||
fireNotifications: store.state.fireNotifications,
|
fireNotifications: store.state.fireNotifications,
|
||||||
fireNotificationsUnread: store.state.fireNotificationsUnread);
|
fireNotificationsUnread: store.state.fireNotificationsUnread);
|
||||||
},
|
},
|
||||||
builder: (context, view) {
|
builder: (BuildContext context, _ViewModel view) {
|
||||||
var hasFireNotifications = view.fireNotifications.length > 0;
|
final bool hasFireNotifications = view.fireNotifications.isNotEmpty;
|
||||||
final title = S.of(context).fireNotificationsTitle;
|
final String title = S.of(context).fireNotificationsTitle;
|
||||||
print('Building Fire Notifications List');
|
print('Building Fire Notifications List');
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
key: _scaffoldKey,
|
key: _scaffoldKey,
|
||||||
drawer: new MainDrawer(context, FireNotificationList.routeName),
|
drawer: MainDrawer(context, FireNotificationList.routeName),
|
||||||
appBar: new AppBar(
|
appBar: AppBar(
|
||||||
title: Text(title),
|
title: Text(title),
|
||||||
leading: IconButton(
|
leading: IconButton(
|
||||||
icon: Icon(Icons.menu),
|
icon: const Icon(Icons.menu),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
_scaffoldKey.currentState?.openDrawer();
|
_scaffoldKey.currentState?.openDrawer();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
actions: (<Widget?>[
|
actions: <Widget?>[
|
||||||
hasFireNotifications
|
if (hasFireNotifications) IconButton(
|
||||||
? IconButton(
|
icon: const Icon(Icons.delete),
|
||||||
icon: Icon(Icons.delete),
|
onPressed: () => _showConfirmDialog(view)) else null
|
||||||
onPressed: () => _showConfirmDialog(view))
|
].where((Widget? w) => w != null).cast<Widget>().toList()),
|
||||||
: null
|
|
||||||
]).where((w) => w != null).cast<Widget>().toList()),
|
|
||||||
body: !view.isLoaded
|
body: !view.isLoaded
|
||||||
? new FiresSpinner()
|
? const FiresSpinner()
|
||||||
: !hasFireNotifications
|
: !hasFireNotifications
|
||||||
? Padding(
|
? Padding(
|
||||||
padding: const EdgeInsets.all(20.0),
|
padding: const EdgeInsets.all(20.0),
|
||||||
child: new Card(
|
child: Card(
|
||||||
child: new Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(20.0),
|
padding: const EdgeInsets.all(20.0),
|
||||||
child: new CenteredColumn(children: <Widget>[
|
child: CenteredColumn(children: <Widget>[
|
||||||
new Icon(Icons.notifications_none,
|
const Icon(Icons.notifications_none,
|
||||||
size: 150.0, color: Colors.black26),
|
size: 150.0, color: Colors.black26),
|
||||||
new SizedBox(height: 20.0),
|
const SizedBox(height: 20.0),
|
||||||
new Text(
|
Text(
|
||||||
S
|
S
|
||||||
.of(context)
|
.of(context)
|
||||||
.fireNotificationsDescription,
|
.fireNotificationsDescription,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
textScaleFactor: 1.1,
|
textScaleFactor: 1.1,
|
||||||
style: new TextStyle(
|
style: const TextStyle(
|
||||||
height: 1.3, color: Colors.black45))
|
height: 1.3, color: Colors.black45))
|
||||||
]))))
|
]))))
|
||||||
: _buildSavedFireNotifications(
|
: _buildSavedFireNotifications(
|
||||||
|
|
@ -231,29 +230,29 @@ class _FireNotificationListState extends State<FireNotificationList> {
|
||||||
|
|
||||||
void gotoMap(
|
void gotoMap(
|
||||||
Store<AppState> store, FireNotification notif, BuildContext context) {
|
Store<AppState> store, FireNotification notif, BuildContext context) {
|
||||||
store.dispatch(new ShowFireNotificationMapAction(notif));
|
store.dispatch(ShowFireNotificationMapAction(notif));
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context, new MaterialPageRoute(builder: (context) => new genericMap()));
|
context, MaterialPageRoute(builder: (BuildContext context) => const genericMap()));
|
||||||
}
|
}
|
||||||
|
|
||||||
_showConfirmDialog(_ViewModel view) {
|
Future<void> _showConfirmDialog(_ViewModel view) {
|
||||||
return showDialog<Null>(
|
return showDialog<void>(
|
||||||
context: context,
|
context: context,
|
||||||
barrierDismissible: false, // user must tap button!
|
barrierDismissible: false, // user must tap button!
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return new AlertDialog(
|
return AlertDialog(
|
||||||
title: new Text(S.of(context).areYouSureTitle),
|
title: Text(S.of(context).areYouSureTitle),
|
||||||
content: new SingleChildScrollView(
|
content: SingleChildScrollView(
|
||||||
child: new ListBody(
|
child: ListBody(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
new Text(
|
Text(
|
||||||
S.of(context).deleteAllFireNotificationsAlertDescription)
|
S.of(context).deleteAllFireNotificationsAlertDescription)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
actions: <Widget>[
|
actions: <Widget>[
|
||||||
new TextButton(
|
TextButton(
|
||||||
child: new Text(S.of(context).DELETE),
|
child: Text(S.of(context).DELETE),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
view.onDeleteAll();
|
view.onDeleteAll();
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ import 'theme.dart';
|
||||||
import 'themeDev.dart';
|
import 'themeDev.dart';
|
||||||
|
|
||||||
class FiresApp extends StatefulWidget {
|
class FiresApp extends StatefulWidget {
|
||||||
FiresApp(this.store);
|
const FiresApp(this.store, {super.key});
|
||||||
|
|
||||||
final Store<AppState> store;
|
final Store<AppState> store;
|
||||||
|
|
||||||
|
|
@ -29,48 +29,48 @@ class FiresApp extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _FiresAppState extends State<FiresApp> {
|
class _FiresAppState extends State<FiresApp> {
|
||||||
|
|
||||||
|
// globals.getIt.registerSingleton
|
||||||
|
_FiresAppState(this.store);
|
||||||
final GlobalKey<NavigatorState> navigatorKey =
|
final GlobalKey<NavigatorState> navigatorKey =
|
||||||
new GlobalKey<NavigatorState>();
|
GlobalKey<NavigatorState>();
|
||||||
static final WidgetBuilder introWidget = (context) => new IntroPage();
|
static Widget introWidget(BuildContext context) => IntroPage();
|
||||||
static final WidgetBuilder continueWidget = (context) => new HomePage();
|
static Widget continueWidget(BuildContext context) => const HomePage();
|
||||||
|
|
||||||
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
|
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
|
||||||
IntroPage.routeName: introWidget,
|
IntroPage.routeName: introWidget,
|
||||||
HomePage.routeName: continueWidget,
|
HomePage.routeName: continueWidget,
|
||||||
PrivacyPage.routeName: (BuildContext context) => new PrivacyPage(context),
|
PrivacyPage.routeName: (BuildContext context) => PrivacyPage(context),
|
||||||
ActiveFiresPage.routeName: (BuildContext context) => new ActiveFiresPage(),
|
ActiveFiresPage.routeName: (BuildContext context) => const ActiveFiresPage(),
|
||||||
Sandbox.routeName: (BuildContext context) => new Sandbox(),
|
Sandbox.routeName: (BuildContext context) => const Sandbox(),
|
||||||
FireAlert.routeName: (BuildContext context) => new FireAlert(),
|
FireAlert.routeName: (BuildContext context) => const FireAlert(),
|
||||||
SupportPage.routeName: (BuildContext context) => new SupportPage(),
|
SupportPage.routeName: (BuildContext context) => const SupportPage(),
|
||||||
FireNotificationList.routeName: (BuildContext context) =>
|
FireNotificationList.routeName: (BuildContext context) =>
|
||||||
new FireNotificationList(),
|
const FireNotificationList(),
|
||||||
MonitoredAreasPage.routeName: (BuildContext context) =>
|
MonitoredAreasPage.routeName: (BuildContext context) =>
|
||||||
new MonitoredAreasPage()
|
const MonitoredAreasPage()
|
||||||
};
|
};
|
||||||
|
|
||||||
final Store<AppState> store;
|
final Store<AppState> store;
|
||||||
|
|
||||||
// globals.getIt.registerSingleton
|
|
||||||
_FiresAppState(this.store);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
StatefulWidget home = new MaterialAppWithIntroHome(
|
final StatefulWidget home = MaterialAppWithIntroHome(
|
||||||
introWidget, continueWidget, 'showInitialWizard-2018-06-27-01');
|
introWidget, continueWidget, 'showInitialWizard-2018-06-27-01');
|
||||||
return new StoreProvider<AppState>(
|
return StoreProvider<AppState>(
|
||||||
store: this.store,
|
store: store,
|
||||||
child: new MaterialApp(
|
child: MaterialApp(
|
||||||
navigatorKey: navigatorKey,
|
navigatorKey: navigatorKey,
|
||||||
localizationsDelegates: [
|
localizationsDelegates: <LocalizationsDelegate<dynamic>> [
|
||||||
S.delegate,
|
S.delegate,
|
||||||
GlobalMaterialLocalizations.delegate,
|
GlobalMaterialLocalizations.delegate,
|
||||||
GlobalWidgetsLocalizations.delegate,
|
GlobalWidgetsLocalizations.delegate,
|
||||||
],
|
],
|
||||||
supportedLocales: S.delegate.supportedLocales,
|
supportedLocales: S.delegate.supportedLocales,
|
||||||
localeResolutionCallback:
|
localeResolutionCallback:
|
||||||
S.delegate.resolution(fallback: new Locale("en", "")),
|
S.delegate.resolution(fallback: const Locale('en', '')),
|
||||||
home: home,
|
home: home,
|
||||||
onGenerateTitle: (context) {
|
onGenerateTitle: (BuildContext context) {
|
||||||
print('MaterialApp onGenerateTitle');
|
print('MaterialApp onGenerateTitle');
|
||||||
return S.of(context).appName;
|
return S.of(context).appName;
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,10 @@ import 'package:flutter_spinkit/flutter_spinkit.dart';
|
||||||
import 'colors.dart';
|
import 'colors.dart';
|
||||||
|
|
||||||
class FiresSpinner extends StatelessWidget {
|
class FiresSpinner extends StatelessWidget {
|
||||||
|
const FiresSpinner({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return new SpinKitPulse(color: fires600);
|
return const SpinKitPulse(color: fires600);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
import 'dart:core';
|
import 'dart:core';
|
||||||
|
|
||||||
import 'package:comunes_flutter/comunes_flutter.dart';
|
import 'package:comunes_flutter/comunes_flutter.dart';
|
||||||
import 'package:fires_flutter/models/yourLocation.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_map/flutter_map.dart';
|
import 'package:flutter_map/flutter_map.dart';
|
||||||
import 'package:flutter_redux/flutter_redux.dart';
|
import 'package:flutter_redux/flutter_redux.dart';
|
||||||
import 'package:latlong2/latlong.dart';
|
import 'package:latlong2/latlong.dart';
|
||||||
|
import 'package:redux/src/store.dart';
|
||||||
import 'package:share_plus/share_plus.dart';
|
import 'package:share_plus/share_plus.dart';
|
||||||
|
|
||||||
import 'attributionMapPlugin.dart';
|
import 'attributionMapPlugin.dart';
|
||||||
|
|
@ -21,6 +21,9 @@ import 'globals.dart' as globals;
|
||||||
import 'layerSelectorMapPlugin.dart';
|
import 'layerSelectorMapPlugin.dart';
|
||||||
import 'locationUtils.dart';
|
import 'locationUtils.dart';
|
||||||
import 'models/appState.dart';
|
import 'models/appState.dart';
|
||||||
|
import 'models/falsePositiveTypes.dart';
|
||||||
|
import 'models/fireNotification.dart';
|
||||||
|
import 'models/yourLocation.dart';
|
||||||
import 'redux/actions.dart';
|
import 'redux/actions.dart';
|
||||||
import 'sentryReport.dart';
|
import 'sentryReport.dart';
|
||||||
import 'slider.dart';
|
import 'slider.dart';
|
||||||
|
|
@ -28,21 +31,8 @@ import 'zoomMapPlugin.dart';
|
||||||
|
|
||||||
@immutable
|
@immutable
|
||||||
class _ViewModel {
|
class _ViewModel {
|
||||||
final String serverUrl;
|
|
||||||
final String lang;
|
|
||||||
final FireMapState mapState;
|
|
||||||
final OnSubscribeFunction onSubs;
|
|
||||||
final OnSubscribeConfirmedFunction onSubsConfirmed;
|
|
||||||
final OnUnSubscribeFunction onUnSubs;
|
|
||||||
final OnSubscribeDistanceChangeFunction onSlide;
|
|
||||||
final OnLocationEdit onEdit;
|
|
||||||
final OnLocationEditConfirm onEditConfirm;
|
|
||||||
final OnLocationEditCancel onEditCancel;
|
|
||||||
final OnLocationEditing onEditing;
|
|
||||||
final OnFalsePositive onFalsePositive;
|
|
||||||
final OnFirePressedInMap onFirePressed;
|
|
||||||
|
|
||||||
_ViewModel(
|
const _ViewModel(
|
||||||
{required this.mapState,
|
{required this.mapState,
|
||||||
required this.serverUrl,
|
required this.serverUrl,
|
||||||
required this.lang,
|
required this.lang,
|
||||||
|
|
@ -56,6 +46,19 @@ class _ViewModel {
|
||||||
required this.onFalsePositive,
|
required this.onFalsePositive,
|
||||||
required this.onFirePressed,
|
required this.onFirePressed,
|
||||||
required this.onEditCancel});
|
required this.onEditCancel});
|
||||||
|
final String serverUrl;
|
||||||
|
final String lang;
|
||||||
|
final FireMapState mapState;
|
||||||
|
final OnSubscribeFunction onSubs;
|
||||||
|
final OnSubscribeConfirmedFunction onSubsConfirmed;
|
||||||
|
final OnUnSubscribeFunction onUnSubs;
|
||||||
|
final OnSubscribeDistanceChangeFunction onSlide;
|
||||||
|
final OnLocationEdit onEdit;
|
||||||
|
final OnLocationEditConfirm onEditConfirm;
|
||||||
|
final OnLocationEditCancel onEditCancel;
|
||||||
|
final OnLocationEditing onEditing;
|
||||||
|
final OnFalsePositive onFalsePositive;
|
||||||
|
final OnFirePressedInMap onFirePressed;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) =>
|
bool operator ==(Object other) =>
|
||||||
|
|
@ -71,6 +74,8 @@ class _ViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
class genericMap extends StatefulWidget {
|
class genericMap extends StatefulWidget {
|
||||||
|
const genericMap({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_genericMapState createState() => _genericMapState();
|
_genericMapState createState() => _genericMapState();
|
||||||
}
|
}
|
||||||
|
|
@ -78,49 +83,49 @@ class genericMap extends StatefulWidget {
|
||||||
class _genericMapState extends State<genericMap> {
|
class _genericMapState extends State<genericMap> {
|
||||||
// This needs to be stateful so when resizes don't get a new globalkey
|
// This needs to be stateful so when resizes don't get a new globalkey
|
||||||
// https://github.com/flutter/flutter/issues/1632#issuecomment-180478202
|
// https://github.com/flutter/flutter/issues/1632#issuecomment-180478202
|
||||||
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
|
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
|
||||||
|
|
||||||
YourLocation? _location;
|
YourLocation? _location;
|
||||||
YourLocation? _initialLocation;
|
YourLocation? _initialLocation;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return new StoreConnector<AppState, _ViewModel>(
|
return StoreConnector<AppState, _ViewModel>(
|
||||||
distinct: true,
|
distinct: true,
|
||||||
onInitialBuild: (store) {
|
onInitialBuild: (_ViewModel store) {
|
||||||
_initialLocation = _location?.copyWith();
|
_initialLocation = _location?.copyWith();
|
||||||
},
|
},
|
||||||
converter: (store) {
|
converter: (Store<AppState> store) {
|
||||||
print('New map viewer');
|
print('New map viewer');
|
||||||
return new _ViewModel(
|
return _ViewModel(
|
||||||
onSubs: (loc) {
|
onSubs: (YourLocation loc) {
|
||||||
store.dispatch(new SubscribeAction());
|
store.dispatch(SubscribeAction());
|
||||||
},
|
},
|
||||||
onSubsConfirmed: (loc) {
|
onSubsConfirmed: (YourLocation loc) {
|
||||||
loc.subscribed = true;
|
loc.subscribed = true;
|
||||||
store.dispatch(new SubscribeConfirmAction(loc));
|
store.dispatch(SubscribeConfirmAction(loc));
|
||||||
},
|
},
|
||||||
onUnSubs: (loc) {
|
onUnSubs: (YourLocation loc) {
|
||||||
loc.subscribed = false;
|
loc.subscribed = false;
|
||||||
store.dispatch(new UnSubscribeAction(loc));
|
store.dispatch(UnSubscribeAction(loc));
|
||||||
},
|
},
|
||||||
onSlide: (loc) {
|
onSlide: (YourLocation loc) {
|
||||||
store.dispatch(new UpdateYourLocationMapAction(loc));
|
store.dispatch(UpdateYourLocationMapAction(loc));
|
||||||
},
|
},
|
||||||
onEdit: (loc) => store.dispatch(new EditYourLocationAction(loc)),
|
onEdit: (YourLocation loc) => store.dispatch(EditYourLocationAction(loc)),
|
||||||
onEditing: (loc) {
|
onEditing: (YourLocation loc) {
|
||||||
store.dispatch(new UpdateYourLocationMapAction(loc));
|
store.dispatch(UpdateYourLocationMapAction(loc));
|
||||||
},
|
},
|
||||||
onEditCancel: (loc) =>
|
onEditCancel: (YourLocation loc) =>
|
||||||
store.dispatch(new EditCancelYourLocationAction(loc)),
|
store.dispatch(EditCancelYourLocationAction(loc)),
|
||||||
onEditConfirm: (loc) {
|
onEditConfirm: (YourLocation loc) {
|
||||||
store.dispatch(new UpdateYourLocationAction(loc));
|
store.dispatch(UpdateYourLocationAction(loc));
|
||||||
store.dispatch(new UpdateYourLocationMapAction(loc));
|
store.dispatch(UpdateYourLocationMapAction(loc));
|
||||||
store.dispatch(new EditConfirmYourLocationAction(loc));
|
store.dispatch(EditConfirmYourLocationAction(loc));
|
||||||
},
|
},
|
||||||
onFalsePositive: (notif, type) => store
|
onFalsePositive: (FireNotification notif, FalsePositiveType type) => store
|
||||||
.dispatch(new MarkFireAsFalsePositiveAction(notif, type)),
|
.dispatch(MarkFireAsFalsePositiveAction(notif, type)),
|
||||||
onFirePressed: (LatLng latLng, DateTime when, type) {
|
onFirePressed: (LatLng latLng, DateTime when, String type) {
|
||||||
_showFireDialog(latLng, when, type);
|
_showFireDialog(latLng, when, type);
|
||||||
},
|
},
|
||||||
serverUrl: store.state.serverUrl,
|
serverUrl: store.state.serverUrl,
|
||||||
|
|
@ -128,24 +133,23 @@ class _genericMapState extends State<genericMap> {
|
||||||
lang: store.state.user.lang,
|
lang: store.state.user.lang,
|
||||||
mapState: store.state.fireMapState);
|
mapState: store.state.fireMapState);
|
||||||
},
|
},
|
||||||
builder: (context, view) {
|
builder: (BuildContext context, _ViewModel view) {
|
||||||
YourLocation? location = view.mapState.yourLocation;
|
final YourLocation? location = view.mapState.yourLocation;
|
||||||
_location = location?.copyWith();
|
_location = location?.copyWith();
|
||||||
print('New map builder with ${_location?.description}');
|
print('New map builder with ${_location?.description}');
|
||||||
|
|
||||||
FireMapState mapState = view.mapState;
|
final FireMapState mapState = view.mapState;
|
||||||
FireMapStatus status = mapState.status;
|
final FireMapStatus status = mapState.status;
|
||||||
FireMapLayer layer = mapState.layer;
|
final FireMapLayer layer = mapState.layer;
|
||||||
print('Build map with status: $status and layer $layer');
|
print('Build map with status: $status and layer $layer');
|
||||||
|
|
||||||
double maxZoom = 18.0; // works?
|
const double maxZoom = 18.0; // works?
|
||||||
|
|
||||||
MapOptions mapOptions = new MapOptions(
|
final MapOptions mapOptions = MapOptions(
|
||||||
initialCenter:
|
initialCenter:
|
||||||
new LatLng(_location?.lat ?? 0.0, _location?.lon ?? 0.0),
|
LatLng(_location?.lat ?? 0.0, _location?.lon ?? 0.0),
|
||||||
initialZoom: 13.0,
|
|
||||||
maxZoom: maxZoom,
|
maxZoom: maxZoom,
|
||||||
onTap: (tapPosition, latLng) {
|
onTap: (TapPosition tapPosition, LatLng latLng) {
|
||||||
if (status == FireMapStatus.edit && _location != null) {
|
if (status == FireMapStatus.edit && _location != null) {
|
||||||
_location = _location!
|
_location = _location!
|
||||||
.copyWith(lat: latLng.latitude, lon: latLng.longitude);
|
.copyWith(lat: latLng.latitude, lon: latLng.longitude);
|
||||||
|
|
@ -161,19 +165,19 @@ class _genericMapState extends State<genericMap> {
|
||||||
// mapController.fitBounds(bounds);
|
// mapController.fitBounds(bounds);
|
||||||
// mapController.center
|
// mapController.center
|
||||||
|
|
||||||
final btnText = status == FireMapStatus.view
|
final String btnText = status == FireMapStatus.view
|
||||||
? S.of(context).toFiresNotifications
|
? S.of(context).toFiresNotifications
|
||||||
: status == FireMapStatus.subscriptionConfirm
|
: status == FireMapStatus.subscriptionConfirm
|
||||||
? S.of(context).confirm
|
? S.of(context).confirm
|
||||||
: S.of(context).unsubscribe;
|
: S.of(context).unsubscribe;
|
||||||
final btnIcon = status == FireMapStatus.view
|
final IconData btnIcon = status == FireMapStatus.view
|
||||||
? Icons.notifications_active
|
? Icons.notifications_active
|
||||||
: status == FireMapStatus.subscriptionConfirm
|
: status == FireMapStatus.subscriptionConfirm
|
||||||
? Icons.check
|
? Icons.check
|
||||||
: Icons.notifications_off;
|
: Icons.notifications_off;
|
||||||
|
|
||||||
String baseLayer;
|
String baseLayer;
|
||||||
List<String> subdomains = [];
|
List<String> subdomains = <String>[];
|
||||||
String attribution;
|
String attribution;
|
||||||
switch (layer) {
|
switch (layer) {
|
||||||
case FireMapLayer.osmc:
|
case FireMapLayer.osmc:
|
||||||
|
|
@ -188,14 +192,14 @@ class _genericMapState extends State<genericMap> {
|
||||||
/* tiles from: https://github.com/dceejay/RedMap/blob/1914ed3b9ce4e8a496049849a93282730b4fff02/worldmap/index.html */
|
/* tiles from: https://github.com/dceejay/RedMap/blob/1914ed3b9ce4e8a496049849a93282730b4fff02/worldmap/index.html */
|
||||||
switch (layer) {
|
switch (layer) {
|
||||||
case FireMapLayer.osmc:
|
case FireMapLayer.osmc:
|
||||||
subdomains = ['a', 'b', 'c'];
|
subdomains = <String>['a', 'b', 'c'];
|
||||||
baseLayer =
|
baseLayer =
|
||||||
'https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png';
|
'https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png';
|
||||||
break;
|
break;
|
||||||
case FireMapLayer.osmcGrey:
|
case FireMapLayer.osmcGrey:
|
||||||
baseLayer =
|
baseLayer =
|
||||||
'https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_all/{z}/{x}/{y}.png';
|
'https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_all/{z}/{x}/{y}.png';
|
||||||
subdomains = ['a', 'b', 'c', 'd'];
|
subdomains = <String>['a', 'b', 'c', 'd'];
|
||||||
break;
|
break;
|
||||||
case FireMapLayer.esri:
|
case FireMapLayer.esri:
|
||||||
baseLayer =
|
baseLayer =
|
||||||
|
|
@ -209,29 +213,27 @@ class _genericMapState extends State<genericMap> {
|
||||||
baseLayer =
|
baseLayer =
|
||||||
'https://server.arcgisonline.com/ArcGIS/rest/services/World_Shaded_Relief/MapServer/tile/{z}/{y}/{x}';
|
'https://server.arcgisonline.com/ArcGIS/rest/services/World_Shaded_Relief/MapServer/tile/{z}/{y}/{x}';
|
||||||
}
|
}
|
||||||
FlutterMap map = new FlutterMap(
|
final FlutterMap map = FlutterMap(
|
||||||
options: mapOptions,
|
options: mapOptions,
|
||||||
children: [
|
children: <Widget>[
|
||||||
new TileLayer(
|
TileLayer(
|
||||||
maxZoom: maxZoom,
|
maxZoom: maxZoom,
|
||||||
urlTemplate: baseLayer,
|
urlTemplate: baseLayer,
|
||||||
subdomains: subdomains,
|
subdomains: subdomains,
|
||||||
userAgentPackageName: 'com.example.fires_flutter',
|
userAgentPackageName: 'com.example.fires_flutter',
|
||||||
additionalOptions: {
|
additionalOptions: const <String, String> {
|
||||||
// 'opacity': '0.1',
|
// 'opacity': '0.1',
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
globals.isDevelopment
|
if (globals.isDevelopment) const ZoomMapPluginWidget() else const DummyMapPluginWidget(),
|
||||||
? new ZoomMapPluginWidget()
|
const CompassMapPluginWidget(),
|
||||||
: new DummyMapPluginWidget(),
|
MarkerLayer(
|
||||||
new CompassMapPluginWidget(),
|
|
||||||
new MarkerLayer(
|
|
||||||
markers: buildMarkers(
|
markers: buildMarkers(
|
||||||
mapState.status == FireMapStatus.viewFireNotification &&
|
mapState.status == FireMapStatus.viewFireNotification &&
|
||||||
mapState.fireNotification != null
|
mapState.fireNotification != null
|
||||||
? new LatLng(mapState.fireNotification!.lat,
|
? LatLng(mapState.fireNotification!.lat,
|
||||||
mapState.fireNotification!.lon)
|
mapState.fireNotification!.lon)
|
||||||
: new LatLng(_location!.lat, _location!.lon),
|
: LatLng(_location!.lat, _location!.lon),
|
||||||
mapState.fires,
|
mapState.fires,
|
||||||
mapState.industries,
|
mapState.industries,
|
||||||
mapState.falsePos,
|
mapState.falsePos,
|
||||||
|
|
@ -239,8 +241,8 @@ class _genericMapState extends State<genericMap> {
|
||||||
view.onFirePressed),
|
view.onFirePressed),
|
||||||
),
|
),
|
||||||
// new AttributionPluginWidget(text: "© OpenStreetMap contributors"),
|
// new AttributionPluginWidget(text: "© OpenStreetMap contributors"),
|
||||||
new LayerSelectorMapPluginWidget(),
|
const LayerSelectorMapPluginWidget(),
|
||||||
new AttributionPluginWidget(text: attribution),
|
AttributionPluginWidget(text: attribution),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
// mapController.
|
// mapController.
|
||||||
|
|
@ -250,35 +252,33 @@ class _genericMapState extends State<genericMap> {
|
||||||
;
|
;
|
||||||
}); */
|
}); */
|
||||||
// Do something with it
|
// Do something with it
|
||||||
return new Scaffold(
|
return Scaffold(
|
||||||
key: _scaffoldKey,
|
key: _scaffoldKey,
|
||||||
resizeToAvoidBottomInset: true,
|
resizeToAvoidBottomInset: true,
|
||||||
appBar: new AppBar(
|
appBar: AppBar(
|
||||||
title: status == FireMapStatus.edit
|
title: status == FireMapStatus.edit
|
||||||
? new TextField(
|
? TextField(
|
||||||
// autofocus: true,
|
// autofocus: true,
|
||||||
key: new Key('LocationDescField'),
|
key: const Key('LocationDescField'),
|
||||||
keyboardType: TextInputType.text,
|
keyboardType: TextInputType.text,
|
||||||
|
controller: TextEditingController.fromValue(
|
||||||
decoration: new InputDecoration(),
|
TextEditingValue(
|
||||||
controller: new TextEditingController.fromValue(
|
|
||||||
new TextEditingValue(
|
|
||||||
text: _location!.description,
|
text: _location!.description,
|
||||||
selection: new TextSelection.collapsed(
|
selection: TextSelection.collapsed(
|
||||||
offset: _location!.description.length))),
|
offset: _location!.description.length))),
|
||||||
onChanged: (newDesc) {
|
onChanged: (String newDesc) {
|
||||||
debugPrint("OnChanged");
|
debugPrint('OnChanged');
|
||||||
_location = _location!.copyWith(description: newDesc);
|
_location = _location!.copyWith(description: newDesc);
|
||||||
},
|
},
|
||||||
onSubmitted: (newDesc) {
|
onSubmitted: (String newDesc) {
|
||||||
debugPrint("OnSubmitted");
|
debugPrint('OnSubmitted');
|
||||||
_location = _location!.copyWith(description: newDesc);
|
_location = _location!.copyWith(description: newDesc);
|
||||||
view.onEditConfirm(_location!);
|
view.onEditConfirm(_location!);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
: status == FireMapStatus.viewFireNotification
|
: status == FireMapStatus.viewFireNotification
|
||||||
? new Text(S.of(context).fireNotificationTitle)
|
? Text(S.of(context).fireNotificationTitle)
|
||||||
: new Text(_location!.description),
|
: Text(_location!.description),
|
||||||
actions: buildAppBarActions(status, view, _location!),
|
actions: buildAppBarActions(status, view, _location!),
|
||||||
),
|
),
|
||||||
floatingActionButton: status == FireMapStatus.edit ||
|
floatingActionButton: status == FireMapStatus.edit ||
|
||||||
|
|
@ -302,9 +302,9 @@ class _genericMapState extends State<genericMap> {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// https://github.com/flutter/flutter/issues/17583
|
// https://github.com/flutter/flutter/issues/17583
|
||||||
heroTag: "firesmap" + _location!.id.hexString,
|
heroTag: 'firesmap${_location!.id.hexString}',
|
||||||
icon: new Icon(btnIcon, color: fires600),
|
icon: Icon(btnIcon, color: fires600),
|
||||||
label: new Text(
|
label: Text(
|
||||||
btnText,
|
btnText,
|
||||||
style: const TextStyle(color: fires600),
|
style: const TextStyle(color: fires600),
|
||||||
),
|
),
|
||||||
|
|
@ -312,20 +312,20 @@ class _genericMapState extends State<genericMap> {
|
||||||
),
|
),
|
||||||
floatingActionButtonLocation:
|
floatingActionButtonLocation:
|
||||||
FloatingActionButtonLocation.centerFloat,
|
FloatingActionButtonLocation.centerFloat,
|
||||||
bottomNavigationBar: new GenericMapBottom(
|
bottomNavigationBar: GenericMapBottom(
|
||||||
onSave: () => view.onEditConfirm(_location!),
|
onSave: () => view.onEditConfirm(_location!),
|
||||||
onCancel: () =>
|
onCancel: () =>
|
||||||
view.onEditCancel(_initialLocation ?? _location!),
|
view.onEditCancel(_initialLocation ?? _location!),
|
||||||
onFalsePositive: (sealed, type) =>
|
onFalsePositive: (FireNotification sealed, FalsePositiveType type) =>
|
||||||
view.onFalsePositive(sealed, type),
|
view.onFalsePositive(sealed, type),
|
||||||
state: view.mapState,
|
state: view.mapState,
|
||||||
scaffoldKey: _scaffoldKey,
|
scaffoldKey: _scaffoldKey,
|
||||||
),
|
),
|
||||||
body: LayoutBuilder(
|
body: LayoutBuilder(
|
||||||
builder: (context, constraints) =>
|
builder: (BuildContext context, BoxConstraints constraints) =>
|
||||||
Stack(fit: StackFit.expand, children: <Widget>[
|
Stack(fit: StackFit.expand, children: <Widget>[
|
||||||
// Material(color: Colors.yellowAccent),
|
// Material(color: Colors.yellowAccent),
|
||||||
new Opacity(
|
Opacity(
|
||||||
opacity:
|
opacity:
|
||||||
status == FireMapStatus.subscriptionConfirm ||
|
status == FireMapStatus.subscriptionConfirm ||
|
||||||
status == FireMapStatus.edit
|
status == FireMapStatus.edit
|
||||||
|
|
@ -336,7 +336,7 @@ class _genericMapState extends State<genericMap> {
|
||||||
top: constraints.maxHeight - 200,
|
top: constraints.maxHeight - 200,
|
||||||
right: 10.0,
|
right: 10.0,
|
||||||
left: 10.0,
|
left: 10.0,
|
||||||
child: new CenteredRow(
|
child: CenteredRow(
|
||||||
// Fit sample:
|
// Fit sample:
|
||||||
// https://github.com/apptreesoftware/flutter_map/blob/master/flutter_map_example/lib/pages/map_controller.dart
|
// https://github.com/apptreesoftware/flutter_map/blob/master/flutter_map_example/lib/pages/map_controller.dart
|
||||||
children: status ==
|
children: status ==
|
||||||
|
|
@ -345,18 +345,18 @@ class _genericMapState extends State<genericMap> {
|
||||||
_location != null &&
|
_location != null &&
|
||||||
_location!.subscribed)
|
_location!.subscribed)
|
||||||
? <Widget>[
|
? <Widget>[
|
||||||
new FireDistanceSlider(
|
FireDistanceSlider(
|
||||||
initialValue:
|
initialValue:
|
||||||
(_location?.distance ?? 0)
|
_location?.distance ?? 0
|
||||||
.round(),
|
,
|
||||||
onSlide: (distance) {
|
onSlide: (int distance) {
|
||||||
if (_location != null) {
|
if (_location != null) {
|
||||||
_location!.distance = distance;
|
_location!.distance = distance;
|
||||||
view.onSlide(_location!);
|
view.onSlide(_location!);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
: []),
|
: <Widget>[]),
|
||||||
)
|
)
|
||||||
])));
|
])));
|
||||||
});
|
});
|
||||||
|
|
@ -368,20 +368,20 @@ class _genericMapState extends State<genericMap> {
|
||||||
case FireMapStatus.view:
|
case FireMapStatus.view:
|
||||||
case FireMapStatus.unsubscribe:
|
case FireMapStatus.unsubscribe:
|
||||||
return <Widget>[
|
return <Widget>[
|
||||||
new IconButton(
|
IconButton(
|
||||||
icon: new Icon(Icons.edit),
|
icon: const Icon(Icons.edit),
|
||||||
onPressed: () => view.onEdit(location))
|
onPressed: () => view.onEdit(location))
|
||||||
];
|
];
|
||||||
case FireMapStatus.edit:
|
case FireMapStatus.edit:
|
||||||
return <Widget>[
|
return <Widget>[
|
||||||
new IconButton(
|
IconButton(
|
||||||
icon: new Icon(Icons.save),
|
icon: const Icon(Icons.save),
|
||||||
onPressed: () => view.onEditConfirm(_location!))
|
onPressed: () => view.onEditConfirm(_location!))
|
||||||
];
|
];
|
||||||
case FireMapStatus.viewFireNotification:
|
case FireMapStatus.viewFireNotification:
|
||||||
return <Widget>[
|
return <Widget>[
|
||||||
new IconButton(
|
IconButton(
|
||||||
icon: new Icon(Icons.share),
|
icon: const Icon(Icons.share),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Share.share(
|
Share.share(
|
||||||
'${view.mapState.fireNotification?.description ?? 'Fire'}. ${view.serverUrl}fire/${view.mapState.fireNotification?.sealed ?? ''}');
|
'${view.mapState.fireNotification?.description ?? 'Fire'}. ${view.serverUrl}fire/${view.mapState.fireNotification?.sealed ?? ''}');
|
||||||
|
|
@ -399,15 +399,15 @@ class _genericMapState extends State<genericMap> {
|
||||||
List<dynamic> industries,
|
List<dynamic> industries,
|
||||||
bool isNotif,
|
bool isNotif,
|
||||||
OnFirePressedInMap onFirePressed) {
|
OnFirePressedInMap onFirePressed) {
|
||||||
List<Marker> markers = [];
|
final List<Marker> markers = <Marker>[];
|
||||||
print(
|
print(
|
||||||
'building markers: fires: ${fires.length} falsePos: ${falsePosList.length} industries: ${industries.length}, isNotif: $isNotif ');
|
'building markers: fires: ${fires.length} falsePos: ${falsePosList.length} industries: ${industries.length}, isNotif: $isNotif ');
|
||||||
// const calibrate = false; // useful when we change the fire icons size
|
// const calibrate = false; // useful when we change the fire icons size
|
||||||
falsePosList.forEach((falsePos) {
|
for (final falsePos in falsePosList) {
|
||||||
try {
|
try {
|
||||||
var coords = falsePos['geo']['coordinates'] as List<dynamic>;
|
final List<dynamic> coords = falsePos['geo']['coordinates'] as List<dynamic>;
|
||||||
// print('false pos: ${coords}');
|
// print('false pos: ${coords}');
|
||||||
var loc = LatLng(
|
final LatLng loc = LatLng(
|
||||||
(coords[1] as num).toDouble(), (coords[0] as num).toDouble());
|
(coords[1] as num).toDouble(), (coords[0] as num).toDouble());
|
||||||
markers.add(FireMarker(loc, FireMarkType.falsePos, () {
|
markers.add(FireMarker(loc, FireMarkType.falsePos, () {
|
||||||
_showFalsePositiveDialog(loc);
|
_showFalsePositiveDialog(loc);
|
||||||
|
|
@ -417,12 +417,12 @@ class _genericMapState extends State<genericMap> {
|
||||||
print('Failed to process $falsePos');
|
print('Failed to process $falsePos');
|
||||||
reportError(e, stackTrace);
|
reportError(e, stackTrace);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
industries.forEach((industry) {
|
for (final industry in industries) {
|
||||||
try {
|
try {
|
||||||
// print(fire['geo']['coordinates']);
|
// print(fire['geo']['coordinates']);
|
||||||
var coords = industry['geo']['coordinates'] as List<dynamic>;
|
final List<dynamic> coords = industry['geo']['coordinates'] as List<dynamic>;
|
||||||
var loc = LatLng(
|
final LatLng loc = LatLng(
|
||||||
(coords[1] as num).toDouble(), (coords[0] as num).toDouble());
|
(coords[1] as num).toDouble(), (coords[0] as num).toDouble());
|
||||||
markers.add(FireMarker(loc, FireMarkType.industry, () {
|
markers.add(FireMarker(loc, FireMarkType.industry, () {
|
||||||
_showIndustryDialog(loc);
|
_showIndustryDialog(loc);
|
||||||
|
|
@ -432,14 +432,14 @@ class _genericMapState extends State<genericMap> {
|
||||||
print('Failed to process $industry');
|
print('Failed to process $industry');
|
||||||
reportError(e, stackTrace);
|
reportError(e, stackTrace);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
fires.forEach((fire) {
|
for (final fire in fires) {
|
||||||
try {
|
try {
|
||||||
var loc = new LatLng(
|
final LatLng loc = LatLng(
|
||||||
(fire['lat'] as num).toDouble(), (fire['lon'] as num).toDouble());
|
(fire['lat'] as num).toDouble(), (fire['lon'] as num).toDouble());
|
||||||
markers.add(FireMarker(loc, FireMarkType.fire, () {
|
markers.add(FireMarker(loc, FireMarkType.fire, () {
|
||||||
onFirePressed(loc, DateTime.parse(fire['when'].toString()),
|
onFirePressed(loc, DateTime.parse(fire['when'].toString()),
|
||||||
(fire['type'] as String));
|
fire['type'] as String);
|
||||||
print('fire $fire pressed');
|
print('fire $fire pressed');
|
||||||
}));
|
}));
|
||||||
markers.add(FireMarker(loc, FireMarkType.pixel));
|
markers.add(FireMarker(loc, FireMarkType.pixel));
|
||||||
|
|
@ -447,7 +447,7 @@ class _genericMapState extends State<genericMap> {
|
||||||
print('Failed to process $fire');
|
print('Failed to process $fire');
|
||||||
reportError(e, stackTrace);
|
reportError(e, stackTrace);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
markers.add(
|
markers.add(
|
||||||
FireMarker(pos, isNotif ? FireMarkType.fire : FireMarkType.position));
|
FireMarker(pos, isNotif ? FireMarkType.fire : FireMarkType.position));
|
||||||
// if (calibrate) markers.add(FireMarker(pos, FireMarkType.pixel));
|
// if (calibrate) markers.add(FireMarker(pos, FireMarkType.pixel));
|
||||||
|
|
@ -455,20 +455,20 @@ class _genericMapState extends State<genericMap> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showFireDialog(LatLng pos, DateTime date, String type) {
|
void _showFireDialog(LatLng pos, DateTime date, String type) {
|
||||||
var when = Moment.fromDate(date).fromNow(context);
|
final String when = Moment.fromDate(date).fromNow(context);
|
||||||
getReverseLocation(lat: pos.latitude, lon: pos.longitude)
|
getReverseLocation(lat: pos.latitude, lon: pos.longitude)
|
||||||
.then((reverseLoc) {
|
.then((String reverseLoc) {
|
||||||
String by = type == 'vecinal'
|
final String by = type == 'vecinal'
|
||||||
? S.of(context).byOurUsers
|
? S.of(context).byOurUsers
|
||||||
: S.of(context).byNASAsatellites;
|
: S.of(context).byNASAsatellites;
|
||||||
String fireDesc =
|
final String fireDesc =
|
||||||
S.of(context).additionalInfoAboutFire(reverseLoc, when, by);
|
S.of(context).additionalInfoAboutFire(reverseLoc, when, by);
|
||||||
showDialog<bool>(
|
showDialog<bool>(
|
||||||
context: _scaffoldKey.currentContext!,
|
context: _scaffoldKey.currentContext!,
|
||||||
builder: (_) => new AlertDialog(
|
builder: (_) => AlertDialog(
|
||||||
content: new Text(fireDesc),
|
content: Text(fireDesc),
|
||||||
actions: <Widget>[
|
actions: <Widget>[
|
||||||
new TextButton(
|
TextButton(
|
||||||
child: Text(S.of(context).CLOSE),
|
child: Text(S.of(context).CLOSE),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
|
|
@ -481,17 +481,17 @@ class _genericMapState extends State<genericMap> {
|
||||||
|
|
||||||
void _showIndustryDialog(LatLng pos) {
|
void _showIndustryDialog(LatLng pos) {
|
||||||
getReverseLocation(lat: pos.latitude, lon: pos.longitude)
|
getReverseLocation(lat: pos.latitude, lon: pos.longitude)
|
||||||
.then((reverseLoc) {
|
.then((String reverseLoc) {
|
||||||
String industryDesc = '${S.of(context).itSeemsAIndustry}\n\n'
|
final String industryDesc = '${S.of(context).itSeemsAIndustry}\n\n'
|
||||||
'Type: Industry\n'
|
'Type: Industry\n'
|
||||||
'Location: $reverseLoc';
|
'Location: $reverseLoc';
|
||||||
showDialog<bool>(
|
showDialog<bool>(
|
||||||
context: _scaffoldKey.currentContext!,
|
context: _scaffoldKey.currentContext!,
|
||||||
builder: (_) => new AlertDialog(
|
builder: (_) => AlertDialog(
|
||||||
title: Text(S.of(context).notAWildfire),
|
title: Text(S.of(context).notAWildfire),
|
||||||
content: new Text(industryDesc),
|
content: Text(industryDesc),
|
||||||
actions: <Widget>[
|
actions: <Widget>[
|
||||||
new TextButton(
|
TextButton(
|
||||||
child: Text(S.of(context).CLOSE),
|
child: Text(S.of(context).CLOSE),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
|
|
@ -504,17 +504,17 @@ class _genericMapState extends State<genericMap> {
|
||||||
|
|
||||||
void _showFalsePositiveDialog(LatLng pos) {
|
void _showFalsePositiveDialog(LatLng pos) {
|
||||||
getReverseLocation(lat: pos.latitude, lon: pos.longitude)
|
getReverseLocation(lat: pos.latitude, lon: pos.longitude)
|
||||||
.then((reverseLoc) {
|
.then((String reverseLoc) {
|
||||||
String falseDesc = '${S.of(context).itSeemsNotAtForesFire}\n\n'
|
final String falseDesc = '${S.of(context).itSeemsNotAtForesFire}\n\n'
|
||||||
'Type: False Positive\n'
|
'Type: False Positive\n'
|
||||||
'Location: $reverseLoc';
|
'Location: $reverseLoc';
|
||||||
showDialog<bool>(
|
showDialog<bool>(
|
||||||
context: _scaffoldKey.currentContext!,
|
context: _scaffoldKey.currentContext!,
|
||||||
builder: (_) => new AlertDialog(
|
builder: (_) => AlertDialog(
|
||||||
title: Text(S.of(context).notAWildfire),
|
title: Text(S.of(context).notAWildfire),
|
||||||
content: new Text(falseDesc),
|
content: Text(falseDesc),
|
||||||
actions: <Widget>[
|
actions: <Widget>[
|
||||||
new TextButton(
|
TextButton(
|
||||||
child: Text(S.of(context).CLOSE),
|
child: Text(S.of(context).CLOSE),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:comunes_flutter/comunes_flutter.dart';
|
import 'package:comunes_flutter/comunes_flutter.dart';
|
||||||
import 'package:fires_flutter/models/fireNotification.dart';
|
|
||||||
import 'package:fires_flutter/models/yourLocation.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'colors.dart';
|
import 'colors.dart';
|
||||||
|
|
@ -12,90 +10,87 @@ import 'generated/i18n.dart';
|
||||||
import 'models/appState.dart';
|
import 'models/appState.dart';
|
||||||
import 'models/falsePositiveTypes.dart';
|
import 'models/falsePositiveTypes.dart';
|
||||||
import 'models/fireMapState.dart';
|
import 'models/fireMapState.dart';
|
||||||
|
import 'models/fireNotification.dart';
|
||||||
|
import 'models/yourLocation.dart';
|
||||||
|
|
||||||
typedef void OnSave();
|
typedef OnSave = void Function();
|
||||||
typedef void OnCancel();
|
typedef OnCancel = void Function();
|
||||||
typedef void OnFalsePositive(FireNotification notif, FalsePositiveType type);
|
typedef OnFalsePositive = void Function(FireNotification notif, FalsePositiveType type);
|
||||||
|
|
||||||
class GenericMapBottom extends StatelessWidget {
|
class GenericMapBottom extends StatelessWidget {
|
||||||
|
|
||||||
|
const GenericMapBottom(
|
||||||
|
{super.key, required this.onSave,
|
||||||
|
required this.onCancel,
|
||||||
|
required this.onFalsePositive,
|
||||||
|
required this.state,
|
||||||
|
required this.scaffoldKey});
|
||||||
final OnSave onSave;
|
final OnSave onSave;
|
||||||
final OnCancel onCancel;
|
final OnCancel onCancel;
|
||||||
final OnFalsePositive onFalsePositive;
|
final OnFalsePositive onFalsePositive;
|
||||||
final FireMapState state;
|
final FireMapState state;
|
||||||
final GlobalKey<ScaffoldState> scaffoldKey;
|
final GlobalKey<ScaffoldState> scaffoldKey;
|
||||||
|
|
||||||
GenericMapBottom(
|
|
||||||
{required this.onSave,
|
|
||||||
required this.onCancel,
|
|
||||||
required this.onFalsePositive,
|
|
||||||
required this.state,
|
|
||||||
required this.scaffoldKey});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final YourLocation? locOrNull = state.yourLocation;
|
final YourLocation? locOrNull = state.yourLocation;
|
||||||
if (locOrNull == null) {
|
if (locOrNull == null) {
|
||||||
return SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
final YourLocation loc = locOrNull;
|
final YourLocation loc = locOrNull;
|
||||||
int kmAround = loc.distance;
|
final int kmAround = loc.distance;
|
||||||
return new CustomBottomAppBar(
|
return CustomBottomAppBar(
|
||||||
fabLocation: FloatingActionButtonLocation.centerFloat,
|
fabLocation: FloatingActionButtonLocation.centerFloat,
|
||||||
showNotch: false,
|
|
||||||
color: fires100,
|
color: fires100,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
actions: buildActionList(loc, context, kmAround, scaffoldKey));
|
actions: buildActionList(loc, context, kmAround, scaffoldKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Widget> buildActionList(YourLocation loc, BuildContext context,
|
List<Widget> buildActionList(YourLocation loc, BuildContext context,
|
||||||
int kmAround, GlobalKey<ScaffoldState> scaffoldState) {
|
int kmAround, GlobalKey<ScaffoldState> scaffoldState) {
|
||||||
List<Widget> actionList = [];
|
final List<Widget> actionList = <Widget>[];
|
||||||
switch (state.status) {
|
switch (state.status) {
|
||||||
case FireMapStatus.edit:
|
case FireMapStatus.edit:
|
||||||
actionList.add(TextButton(
|
actionList.add(TextButton(
|
||||||
onPressed: onSave,
|
onPressed: onSave,
|
||||||
child: new Text(S.of(context).SAVE,
|
child: Text(S.of(context).SAVE,
|
||||||
style: Theme.of(context).textTheme.labelLarge)));
|
style: Theme.of(context).textTheme.labelLarge)));
|
||||||
actionList.add(TextButton(
|
actionList.add(TextButton(
|
||||||
onPressed: onCancel,
|
onPressed: onCancel,
|
||||||
child: new Text(S.of(context).CANCEL,
|
child: Text(S.of(context).CANCEL,
|
||||||
style: Theme.of(context).textTheme.labelLarge)));
|
style: Theme.of(context).textTheme.labelLarge)));
|
||||||
break;
|
break;
|
||||||
case FireMapStatus.subscriptionConfirm:
|
case FireMapStatus.subscriptionConfirm:
|
||||||
break;
|
break;
|
||||||
case FireMapStatus.viewFireNotification:
|
case FireMapStatus.viewFireNotification:
|
||||||
final notif = state.fireNotification;
|
final FireNotification? notif = state.fireNotification;
|
||||||
if (notif != null) {
|
if (notif != null) {
|
||||||
actionList.add(new Flexible(
|
actionList.add(Flexible(
|
||||||
child: new Padding(
|
child: Padding(
|
||||||
padding: new EdgeInsets.all(10.0),
|
padding: const EdgeInsets.all(10.0),
|
||||||
child: new Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
|
||||||
children: listWithoutNulls(<Widget?>[
|
children: listWithoutNulls(<Widget?>[
|
||||||
new Text(notif.description),
|
Text(notif.description),
|
||||||
// TODO fire type (neighbout, NASA, etc)
|
// TODOfire type (neighbout, NASA, etc)
|
||||||
new SizedBox(height: 5.0),
|
const SizedBox(height: 5.0),
|
||||||
new Row(
|
Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
new Icon(Icons.access_time),
|
const Icon(Icons.access_time),
|
||||||
new SizedBox(width: 5.0),
|
const SizedBox(width: 5.0),
|
||||||
new Text(Moment.now().from(context, notif.when)),
|
Text(Moment.now().from(context, notif.when)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
state.industries.length > 0 || state.falsePos.length > 0
|
if (state.industries.isNotEmpty || state.falsePos.isNotEmpty) Padding(
|
||||||
? new Padding(
|
padding: const EdgeInsets.only(top: 10.0),
|
||||||
padding: new EdgeInsets.only(top: 10.0),
|
child: Text(
|
||||||
child: new Text(
|
|
||||||
S.of(context).itSeemsNotAtForesFire,
|
S.of(context).itSeemsNotAtForesFire,
|
||||||
style: new TextStyle(color: fires600)))
|
style: const TextStyle(color: fires600))) else null,
|
||||||
: null,
|
DropdownButton<FalsePositiveType>(
|
||||||
new DropdownButton<FalsePositiveType>(
|
style: const TextStyle(
|
||||||
style: new TextStyle(
|
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
// fontSize: 18.0,
|
// fontSize: 18.0,
|
||||||
),
|
),
|
||||||
hint: new Text(S.of(context).notAWildfire),
|
hint: Text(S.of(context).notAWildfire),
|
||||||
items: FalsePositiveType.values
|
items: FalsePositiveType.values
|
||||||
.map((FalsePositiveType value) {
|
.map((FalsePositiveType value) {
|
||||||
String menuText;
|
String menuText;
|
||||||
|
|
@ -110,18 +105,18 @@ class GenericMapBottom extends StatelessWidget {
|
||||||
case FalsePositiveType.falsealarm:
|
case FalsePositiveType.falsealarm:
|
||||||
menuText = S.of(context).itSeemsAFalseAlarm;
|
menuText = S.of(context).itSeemsAFalseAlarm;
|
||||||
}
|
}
|
||||||
return new DropdownMenuItem<FalsePositiveType>(
|
return DropdownMenuItem<FalsePositiveType>(
|
||||||
value: value, child: new Text(menuText));
|
value: value, child: Text(menuText));
|
||||||
}).toList(),
|
}).toList(),
|
||||||
onChanged: (FalsePositiveType? value) async {
|
onChanged: (FalsePositiveType? value) async {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
onFalsePositive(notif, value);
|
onFalsePositive(notif, value);
|
||||||
}
|
}
|
||||||
await new Future.delayed(
|
await Future.delayed(
|
||||||
new Duration(milliseconds: 500));
|
const Duration(milliseconds: 500));
|
||||||
ScaffoldMessenger.of(context)
|
ScaffoldMessenger.of(context)
|
||||||
.showSnackBar(new SnackBar(
|
.showSnackBar(SnackBar(
|
||||||
content: new Text(
|
content: Text(
|
||||||
S.of(context).thanksForParticipating),
|
S.of(context).thanksForParticipating),
|
||||||
));
|
));
|
||||||
}),
|
}),
|
||||||
|
|
@ -130,7 +125,7 @@ class GenericMapBottom extends StatelessWidget {
|
||||||
break;
|
break;
|
||||||
case FireMapStatus.unsubscribe:
|
case FireMapStatus.unsubscribe:
|
||||||
case FireMapStatus.view:
|
case FireMapStatus.view:
|
||||||
actionList.add(new Text(state.numFires > 0
|
actionList.add(Text(state.numFires > 0
|
||||||
? loc.currentNumFires == 1
|
? loc.currentNumFires == 1
|
||||||
? S.of(context).fireAroundThisArea(loc.distance.toString())
|
? S.of(context).fireAroundThisArea(loc.distance.toString())
|
||||||
: S.of(context).firesAroundThisArea(
|
: S.of(context).firesAroundThisArea(
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,8 @@ import 'customMoment.dart';
|
||||||
import 'generated/i18n.dart';
|
import 'generated/i18n.dart';
|
||||||
|
|
||||||
class GlobalFiresBottomStats extends StatefulWidget {
|
class GlobalFiresBottomStats extends StatefulWidget {
|
||||||
|
const GlobalFiresBottomStats({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_GlobalFiresBottomStatsState createState() => _GlobalFiresBottomStatsState();
|
_GlobalFiresBottomStatsState createState() => _GlobalFiresBottomStatsState();
|
||||||
}
|
}
|
||||||
|
|
@ -17,20 +19,20 @@ class GlobalFiresBottomStats extends StatefulWidget {
|
||||||
class _GlobalFiresBottomStatsState extends State<GlobalFiresBottomStats> {
|
class _GlobalFiresBottomStatsState extends State<GlobalFiresBottomStats> {
|
||||||
late String lastCheck;
|
late String lastCheck;
|
||||||
int activeFires = 0;
|
int activeFires = 0;
|
||||||
final firesApiUrl = GetIt.instance<String>(instanceName: "firesApiUrl");
|
final String firesApiUrl = GetIt.instance<String>(instanceName: 'firesApiUrl');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
http.read(Uri.parse('${firesApiUrl}status/last-fire-check')).then((result) {
|
http.read(Uri.parse('${firesApiUrl}status/last-fire-check')).then((String result) {
|
||||||
try {
|
try {
|
||||||
var now = Moment.now();
|
final Moment now = Moment.now();
|
||||||
var last = DateTime.parse(json.decode(result)['value'] as String);
|
final DateTime last = DateTime.parse(json.decode(result)['value'] as String);
|
||||||
http
|
http
|
||||||
.read(Uri.parse('${firesApiUrl}status/active-fires-count'))
|
.read(Uri.parse('${firesApiUrl}status/active-fires-count'))
|
||||||
.then((result) {
|
.then((String result) {
|
||||||
try {
|
try {
|
||||||
int count = (json.decode(result)['total'] as num).toInt();
|
final int count = (json.decode(result)['total'] as num).toInt();
|
||||||
setState(() {
|
setState(() {
|
||||||
lastCheck = now.from(context, last);
|
lastCheck = now.from(context, last);
|
||||||
activeFires = count;
|
activeFires = count;
|
||||||
|
|
@ -51,21 +53,19 @@ class _GlobalFiresBottomStatsState extends State<GlobalFiresBottomStats> {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final List<Widget> actionWidgets = <Widget>[];
|
final List<Widget> actionWidgets = <Widget>[];
|
||||||
if (activeFires > 0) {
|
if (activeFires > 0) {
|
||||||
actionWidgets.add(new Column(
|
actionWidgets.add(Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
children: <Widget>[
|
||||||
children: [
|
Text(
|
||||||
new Text(
|
|
||||||
S.of(context).activeFiresWorldWide(activeFires.toString())),
|
S.of(context).activeFiresWorldWide(activeFires.toString())),
|
||||||
new Text(S.of(context).updatedLastCheck(lastCheck))
|
Text(S.of(context).updatedLastCheck(lastCheck))
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new CustomBottomAppBar(
|
return CustomBottomAppBar(
|
||||||
fabLocation: FloatingActionButtonLocation.centerDocked,
|
fabLocation: FloatingActionButtonLocation.centerDocked,
|
||||||
showNotch: true,
|
showNotch: true,
|
||||||
color: fires100,
|
color: fires100,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
actions: actionWidgets);
|
actions: actionWidgets);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,6 @@ import 'dart:async';
|
||||||
|
|
||||||
import 'package:comunes_flutter/comunes_flutter.dart';
|
import 'package:comunes_flutter/comunes_flutter.dart';
|
||||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||||
import 'package:fires_flutter/models/fireNotification.dart';
|
|
||||||
import 'package:fires_flutter/objectIdUtils.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_redux/flutter_redux.dart';
|
import 'package:flutter_redux/flutter_redux.dart';
|
||||||
import 'package:get_it/get_it.dart';
|
import 'package:get_it/get_it.dart';
|
||||||
|
|
@ -17,12 +15,14 @@ import 'firesSpinner.dart';
|
||||||
import 'generated/i18n.dart';
|
import 'generated/i18n.dart';
|
||||||
import 'mainDrawer.dart';
|
import 'mainDrawer.dart';
|
||||||
import 'models/appState.dart';
|
import 'models/appState.dart';
|
||||||
|
import 'models/fireNotification.dart';
|
||||||
|
import 'objectIdUtils.dart';
|
||||||
import 'redux/actions.dart';
|
import 'redux/actions.dart';
|
||||||
|
|
||||||
class _ViewModel {
|
class _ViewModel {
|
||||||
final bool isLoaded;
|
|
||||||
|
|
||||||
_ViewModel({required this.isLoaded});
|
_ViewModel({required this.isLoaded});
|
||||||
|
final bool isLoaded;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) =>
|
bool operator ==(Object other) =>
|
||||||
|
|
@ -36,21 +36,21 @@ class _ViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
class HomePage extends StatefulWidget {
|
class HomePage extends StatefulWidget {
|
||||||
static const String routeName = '/home';
|
|
||||||
|
|
||||||
HomePage();
|
const HomePage({super.key});
|
||||||
|
static const String routeName = '/home';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_HomePageState createState() => _HomePageState();
|
_HomePageState createState() => _HomePageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _HomePageState extends State<HomePage> {
|
class _HomePageState extends State<HomePage> {
|
||||||
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
|
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
|
||||||
final Store<AppState> store = GetIt.instance<Store<AppState>>();
|
final Store<AppState> store = GetIt.instance<Store<AppState>>();
|
||||||
final List<FireNotification> newNotifications = [];
|
final List<FireNotification> newNotifications = <FireNotification>[];
|
||||||
|
|
||||||
// Platform messages are asynchronous, so we initialize in an async method.
|
// Platform messages are asynchronous, so we initialize in an async method.
|
||||||
Future<Null> initConnectivity() async {
|
Future<void> initConnectivity() async {
|
||||||
// Connectivity checking removed - no longer needed
|
// Connectivity checking removed - no longer needed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -70,9 +70,9 @@ class _HomePageState extends State<HomePage> {
|
||||||
// Listen for messages when app is in foreground
|
// Listen for messages when app is in foreground
|
||||||
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
|
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
|
||||||
debugPrint(
|
debugPrint(
|
||||||
"onMessage in fireApp (isLoaded: ${store.state.isLoaded}): $message");
|
'onMessage in fireApp (isLoaded: ${store.state.isLoaded}): $message');
|
||||||
if (message.data.isNotEmpty) {
|
if (message.data.isNotEmpty) {
|
||||||
final notif = _notifForMessage(message.data, store.state.isLoaded);
|
final FireNotification? notif = _notifForMessage(message.data, store.state.isLoaded);
|
||||||
if (notif != null) {
|
if (notif != null) {
|
||||||
_showItemDialog(message.data, notif);
|
_showItemDialog(message.data, notif);
|
||||||
}
|
}
|
||||||
|
|
@ -82,7 +82,7 @@ class _HomePageState extends State<HomePage> {
|
||||||
// Listen for messages when app is opened from background
|
// Listen for messages when app is opened from background
|
||||||
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
|
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
|
||||||
debugPrint(
|
debugPrint(
|
||||||
"onMessageOpenedApp (isLoaded: ${store.state.isLoaded}): $message");
|
'onMessageOpenedApp (isLoaded: ${store.state.isLoaded}): $message');
|
||||||
if (message.data.isNotEmpty) {
|
if (message.data.isNotEmpty) {
|
||||||
_notifForMessage(message.data, store.state.isLoaded);
|
_notifForMessage(message.data, store.state.isLoaded);
|
||||||
_navigateToItemDetail(message.data);
|
_navigateToItemDetail(message.data);
|
||||||
|
|
@ -92,7 +92,7 @@ class _HomePageState extends State<HomePage> {
|
||||||
// Check if app was terminated and opened by notification tap
|
// Check if app was terminated and opened by notification tap
|
||||||
_firebaseMessaging.getInitialMessage().then((RemoteMessage? message) {
|
_firebaseMessaging.getInitialMessage().then((RemoteMessage? message) {
|
||||||
if (message != null) {
|
if (message != null) {
|
||||||
debugPrint("App opened by notification: $message");
|
debugPrint('App opened by notification: $message');
|
||||||
if (message.data.isNotEmpty) {
|
if (message.data.isNotEmpty) {
|
||||||
_navigateToItemDetail(message.data);
|
_navigateToItemDetail(message.data);
|
||||||
}
|
}
|
||||||
|
|
@ -103,39 +103,39 @@ class _HomePageState extends State<HomePage> {
|
||||||
void _getFirebaseToken() {
|
void _getFirebaseToken() {
|
||||||
_firebaseMessaging.getToken().then((String? token) {
|
_firebaseMessaging.getToken().then((String? token) {
|
||||||
if (token != null) {
|
if (token != null) {
|
||||||
store.dispatch(new OnUserTokenAction(token));
|
store.dispatch(OnUserTokenAction(token));
|
||||||
setState(() {});
|
setState(() {});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
final _homeFont = const TextStyle(
|
final TextStyle _homeFont = const TextStyle(
|
||||||
fontSize: 50.0,
|
fontSize: 50.0,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
);
|
);
|
||||||
final _btnFont = const TextStyle(
|
final TextStyle _btnFont = const TextStyle(
|
||||||
fontSize: 20.0,
|
fontSize: 20.0,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return new StoreConnector<AppState, _ViewModel>(
|
return StoreConnector<AppState, _ViewModel>(
|
||||||
distinct: true,
|
distinct: true,
|
||||||
converter: (store) {
|
converter: (Store<AppState> store) {
|
||||||
bool isLoaded = store.state.isLoaded;
|
final bool isLoaded = store.state.isLoaded;
|
||||||
if (isLoaded && newNotifications.isNotEmpty) {
|
if (isLoaded && newNotifications.isNotEmpty) {
|
||||||
newNotifications.forEach((notif) {
|
for (final FireNotification notif in newNotifications) {
|
||||||
store.dispatch(new AddFireNotificationAction(notif));
|
store.dispatch(AddFireNotificationAction(notif));
|
||||||
});
|
}
|
||||||
newNotifications.clear();
|
newNotifications.clear();
|
||||||
}
|
}
|
||||||
return new _ViewModel(isLoaded: store.state.isLoaded);
|
return _ViewModel(isLoaded: store.state.isLoaded);
|
||||||
},
|
},
|
||||||
builder: (context, view) {
|
builder: (BuildContext context, _ViewModel view) {
|
||||||
return new Scaffold(
|
return Scaffold(
|
||||||
key: _scaffoldKey,
|
key: _scaffoldKey,
|
||||||
drawer: new MainDrawer(context, HomePage.routeName),
|
drawer: MainDrawer(context, HomePage.routeName),
|
||||||
floatingActionButtonLocation:
|
floatingActionButtonLocation:
|
||||||
FloatingActionButtonLocation.centerFloat,
|
FloatingActionButtonLocation.centerFloat,
|
||||||
floatingActionButton: Column(
|
floatingActionButton: Column(
|
||||||
|
|
@ -160,7 +160,7 @@ class _HomePageState extends State<HomePage> {
|
||||||
},
|
},
|
||||||
heroTag: 'notifyFire',
|
heroTag: 'notifyFire',
|
||||||
backgroundColor: fires600,
|
backgroundColor: fires600,
|
||||||
label: new Text(S.of(context).notifyAFire,
|
label: Text(S.of(context).notifyAFire,
|
||||||
style: _btnFont),
|
style: _btnFont),
|
||||||
icon:
|
icon:
|
||||||
const Icon(Icons.notifications_active, size: 32.0),
|
const Icon(Icons.notifications_active, size: 32.0),
|
||||||
|
|
@ -168,41 +168,40 @@ class _HomePageState extends State<HomePage> {
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
body: !view.isLoaded
|
body: !view.isLoaded
|
||||||
? new FiresSpinner()
|
? const FiresSpinner()
|
||||||
: new SafeArea(
|
: SafeArea(
|
||||||
child: Center(
|
child: Center(
|
||||||
child: new CenteredColumn(children: <Widget>[
|
child: CenteredColumn(children: <Widget>[
|
||||||
new Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
new IconButton(
|
IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
_scaffoldKey.currentState?.openDrawer();
|
_scaffoldKey.currentState?.openDrawer();
|
||||||
},
|
},
|
||||||
icon: new Icon(Icons.menu,
|
icon: const Icon(Icons.menu,
|
||||||
size: 30.0, color: Colors.black38)),
|
size: 30.0, color: Colors.black38)),
|
||||||
]),
|
]),
|
||||||
new Expanded(
|
Expanded(
|
||||||
child: new FractionallySizedBox(
|
child: FractionallySizedBox(
|
||||||
alignment: FractionalOffset.center,
|
alignment: FractionalOffset.center,
|
||||||
heightFactor: 0.7,
|
heightFactor: 0.7,
|
||||||
child: new Image.asset('images/logo-200.png',
|
child: Image.asset('images/logo-200.png',
|
||||||
fit: BoxFit.fitHeight))),
|
fit: BoxFit.fitHeight))),
|
||||||
new Expanded(
|
Expanded(
|
||||||
child: new FractionallySizedBox(
|
child: FractionallySizedBox(
|
||||||
alignment: FractionalOffset.topCenter,
|
alignment: FractionalOffset.topCenter,
|
||||||
heightFactor: 1.0,
|
heightFactor: 1.0,
|
||||||
child: new Column(
|
child: Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
new Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
vertical: 10.0, horizontal: 20.0),
|
vertical: 10.0, horizontal: 20.0),
|
||||||
child: FittedBox(
|
child: FittedBox(
|
||||||
child: new Text(S.of(context).appName,
|
fit: BoxFit.scaleDown,
|
||||||
|
child: Text(S.of(context).appName,
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: _homeFont),
|
style: _homeFont),
|
||||||
fit: BoxFit.scaleDown,
|
|
||||||
)),
|
)),
|
||||||
],
|
],
|
||||||
)))
|
)))
|
||||||
|
|
@ -212,14 +211,14 @@ class _HomePageState extends State<HomePage> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showDialog(String message) {
|
void _showDialog(String message) {
|
||||||
final context = _scaffoldKey.currentContext;
|
final BuildContext? context = _scaffoldKey.currentContext;
|
||||||
if (context == null) return;
|
if (context == null) return;
|
||||||
showDialog<bool>(
|
showDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (_) => new AlertDialog(
|
builder: (_) => AlertDialog(
|
||||||
content: new Text(message),
|
content: Text(message),
|
||||||
actions: <Widget>[
|
actions: <Widget>[
|
||||||
new TextButton(
|
TextButton(
|
||||||
child: Text(S.of(context).CLOSE),
|
child: Text(S.of(context).CLOSE),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
|
|
@ -230,29 +229,29 @@ class _HomePageState extends State<HomePage> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showItemDialog(Map<String, dynamic> message, FireNotification notif) {
|
void _showItemDialog(Map<String, dynamic> message, FireNotification notif) {
|
||||||
final context = _scaffoldKey.currentContext;
|
final BuildContext? context = _scaffoldKey.currentContext;
|
||||||
if (context == null) return;
|
if (context == null) return;
|
||||||
showDialog<bool>(
|
showDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (_) => _buildDialog(context, notif),
|
builder: (_) => _buildDialog(context, notif),
|
||||||
).then((bool? shouldNavigate) {
|
).then((bool? shouldNavigate) {
|
||||||
if (shouldNavigate == true) {
|
if (shouldNavigate ?? false) {
|
||||||
_navigateToItemDetail(message);
|
_navigateToItemDetail(message);
|
||||||
}
|
}
|
||||||
}).catchError((e) => print("$e"));
|
}).catchError((e) => print('$e'));
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildDialog(BuildContext context, FireNotification item) {
|
Widget _buildDialog(BuildContext context, FireNotification item) {
|
||||||
return new AlertDialog(
|
return AlertDialog(
|
||||||
content: new Text(item.description),
|
content: Text(item.description),
|
||||||
actions: <Widget>[
|
actions: <Widget>[
|
||||||
new TextButton(
|
TextButton(
|
||||||
child: Text(S.of(context).CLOSE),
|
child: Text(S.of(context).CLOSE),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.pop(context, false);
|
Navigator.pop(context, false);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
new TextButton(
|
TextButton(
|
||||||
child: Text(S.of(context).SHOW),
|
child: Text(S.of(context).SHOW),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.pop(context, true);
|
Navigator.pop(context, true);
|
||||||
|
|
@ -263,7 +262,7 @@ class _HomePageState extends State<HomePage> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _navigateToItemDetail(Map<String, dynamic> message) {
|
void _navigateToItemDetail(Map<String, dynamic> message) {
|
||||||
final context = _scaffoldKey.currentContext;
|
final BuildContext? context = _scaffoldKey.currentContext;
|
||||||
if (context == null) return;
|
if (context == null) return;
|
||||||
// Clear away dialogs
|
// Clear away dialogs
|
||||||
Navigator.popUntil(context, (Route<dynamic> route) => route is PageRoute);
|
Navigator.popUntil(context, (Route<dynamic> route) => route is PageRoute);
|
||||||
|
|
@ -280,7 +279,7 @@ class _HomePageState extends State<HomePage> {
|
||||||
Map<String, dynamic> message, bool isLoaded) {
|
Map<String, dynamic> message, bool isLoaded) {
|
||||||
FireNotification? notif;
|
FireNotification? notif;
|
||||||
try {
|
try {
|
||||||
notif = new FireNotification(
|
notif = FireNotification(
|
||||||
id: objectIdFromJson(message['id'] as String),
|
id: objectIdFromJson(message['id'] as String),
|
||||||
subsId: objectIdFromJson(message['subsId'] as String),
|
subsId: objectIdFromJson(message['subsId'] as String),
|
||||||
lat: double.parse(message['lat'] as String),
|
lat: double.parse(message['lat'] as String),
|
||||||
|
|
@ -296,7 +295,7 @@ class _HomePageState extends State<HomePage> {
|
||||||
// if our store is loaded, we just dispatch the notification, if not, we wait til is loaded
|
// if our store is loaded, we just dispatch the notification, if not, we wait til is loaded
|
||||||
if (notif != null) {
|
if (notif != null) {
|
||||||
if (isLoaded) {
|
if (isLoaded) {
|
||||||
store.dispatch(new AddFireNotificationAction(notif));
|
store.dispatch(AddFireNotificationAction(notif));
|
||||||
} else {
|
} else {
|
||||||
newNotifications.add(notif);
|
newNotifications.add(notif);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,18 @@
|
||||||
import 'package:comunes_flutter/comunes_flutter.dart';
|
import 'package:comunes_flutter/comunes_flutter.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'homePage.dart';
|
|
||||||
import 'generated/i18n.dart';
|
import 'generated/i18n.dart';
|
||||||
|
import 'homePage.dart';
|
||||||
|
|
||||||
class IntroPage extends AppIntroPage {
|
class IntroPage extends AppIntroPage {
|
||||||
|
IntroPage({super.key})
|
||||||
|
: super(
|
||||||
|
items: _fireItems,
|
||||||
|
onIntroFinish: (BuildContext context) =>
|
||||||
|
Navigator.pushNamed(context, HomePage.routeName));
|
||||||
static const String routeName = '/intro';
|
static const String routeName = '/intro';
|
||||||
|
|
||||||
static final fireItems = (BuildContext context) => <AppIntroItem>[
|
static final _fireItems = (BuildContext context) => <AppIntroItem>[
|
||||||
AppIntroItem(
|
AppIntroItem(
|
||||||
icon: Icons.location_on, title: S.of(context).chooseAPlace),
|
icon: Icons.location_on, title: S.of(context).chooseAPlace),
|
||||||
AppIntroItem(
|
AppIntroItem(
|
||||||
|
|
@ -20,11 +25,4 @@ class IntroPage extends AppIntroPage {
|
||||||
icon: Icons.notifications_active,
|
icon: Icons.notifications_active,
|
||||||
title: S.of(context).alertWhenThereIsAFire)
|
title: S.of(context).alertWhenThereIsAFire)
|
||||||
];
|
];
|
||||||
|
|
||||||
IntroPage({Key? key})
|
|
||||||
: super(
|
|
||||||
items: fireItems,
|
|
||||||
onIntroFinish: (context) =>
|
|
||||||
Navigator.pushNamed(context, HomePage.routeName),
|
|
||||||
key: key);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,23 +4,24 @@ import 'package:get_it/get_it.dart';
|
||||||
import 'package:redux/redux.dart';
|
import 'package:redux/redux.dart';
|
||||||
|
|
||||||
import 'models/appState.dart';
|
import 'models/appState.dart';
|
||||||
import 'models/fireMapState.dart';
|
|
||||||
import 'redux/fireMapActions.dart';
|
import 'redux/fireMapActions.dart';
|
||||||
|
|
||||||
/// Layer selector widget for changing map layers
|
/// Layer selector widget for changing map layers
|
||||||
class LayerSelectorMapPluginWidget extends StatelessWidget {
|
class LayerSelectorMapPluginWidget extends StatelessWidget {
|
||||||
|
const LayerSelectorMapPluginWidget({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Store<AppState> store = GetIt.instance<Store<AppState>>();
|
final Store<AppState> store = GetIt.instance<Store<AppState>>();
|
||||||
return LayoutBuilder(
|
return LayoutBuilder(
|
||||||
builder: (context, constraints) =>
|
builder: (BuildContext context, BoxConstraints constraints) =>
|
||||||
Stack(fit: StackFit.expand, children: <Widget>[
|
Stack(fit: StackFit.expand, children: <Widget>[
|
||||||
Positioned(
|
Positioned(
|
||||||
top: constraints.maxHeight - 60,
|
top: constraints.maxHeight - 60,
|
||||||
left: 10.0,
|
left: 10.0,
|
||||||
child: new CenteredRow(
|
child: CenteredRow(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
new Column(
|
Column(
|
||||||
children: <Widget>[_LayerSelectorButton(store)],
|
children: <Widget>[_LayerSelectorButton(store)],
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
|
@ -30,7 +31,7 @@ class LayerSelectorMapPluginWidget extends StatelessWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _LayerSelectorButton(Store<AppState> store) {
|
Widget _LayerSelectorButton(Store<AppState> store) {
|
||||||
final key = GlobalKey<PopupMenuButtonState<FireMapLayer>>();
|
final GlobalKey<PopupMenuButtonState<FireMapLayer>> key = GlobalKey<PopupMenuButtonState<FireMapLayer>>();
|
||||||
|
|
||||||
return PopupMenuButton<FireMapLayer>(
|
return PopupMenuButton<FireMapLayer>(
|
||||||
key: key,
|
key: key,
|
||||||
|
|
@ -40,17 +41,17 @@ class LayerSelectorMapPluginWidget extends StatelessWidget {
|
||||||
},
|
},
|
||||||
itemBuilder: (BuildContext context) {
|
itemBuilder: (BuildContext context) {
|
||||||
return FireMapLayer.values.map((FireMapLayer layer) {
|
return FireMapLayer.values.map((FireMapLayer layer) {
|
||||||
final isSelected = store.state.fireMapState.layer == layer;
|
final bool isSelected = store.state.fireMapState.layer == layer;
|
||||||
return PopupMenuItem<FireMapLayer>(
|
return PopupMenuItem<FireMapLayer>(
|
||||||
value: layer,
|
value: layer,
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: <Widget>[
|
||||||
Icon(
|
Icon(
|
||||||
isSelected ? Icons.check : Icons.check,
|
isSelected ? Icons.check : Icons.check,
|
||||||
color: isSelected ? Colors.blue : Colors.transparent,
|
color: isSelected ? Colors.blue : Colors.transparent,
|
||||||
size: 20,
|
size: 20,
|
||||||
),
|
),
|
||||||
SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Text(
|
Text(
|
||||||
_getLayerName(layer),
|
_getLayerName(layer),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
|
|
@ -66,7 +67,7 @@ class LayerSelectorMapPluginWidget extends StatelessWidget {
|
||||||
child: FloatingActionButton(
|
child: FloatingActionButton(
|
||||||
backgroundColor: Colors.black26,
|
backgroundColor: Colors.black26,
|
||||||
mini: true,
|
mini: true,
|
||||||
child: Icon(Icons.layers),
|
child: const Icon(Icons.layers),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
key.currentState?.showButtonMenu();
|
key.currentState?.showButtonMenu();
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:fires_flutter/models/yourLocation.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:geocoding/geocoding.dart' as geo;
|
import 'package:geocoding/geocoding.dart' as geo;
|
||||||
|
|
@ -8,42 +7,43 @@ import 'package:location/location.dart';
|
||||||
import 'package:objectid/objectid.dart';
|
import 'package:objectid/objectid.dart';
|
||||||
|
|
||||||
import 'generated/i18n.dart';
|
import 'generated/i18n.dart';
|
||||||
|
import 'models/yourLocation.dart';
|
||||||
|
|
||||||
Future<YourLocation> getUserLocation(
|
Future<YourLocation> getUserLocation(
|
||||||
GlobalKey<ScaffoldState> scaffoldKey) async {
|
GlobalKey<ScaffoldState> scaffoldKey) async {
|
||||||
// Platform messages may fail, so we use a try/catch PlatformException.
|
// Platform messages may fail, so we use a try/catch PlatformException.
|
||||||
try {
|
try {
|
||||||
Location _location = new Location();
|
final Location location0 = Location();
|
||||||
|
|
||||||
LocationData location = await _location.getLocation();
|
final LocationData location = await location0.getLocation();
|
||||||
|
|
||||||
// It seems that the lib fails with lat/lon values
|
// It seems that the lib fails with lat/lon values
|
||||||
var yl = new YourLocation(
|
final YourLocation yl = YourLocation(
|
||||||
id: ObjectId(), lat: location.latitude!, lon: location.longitude!);
|
id: ObjectId(), lat: location.latitude!, lon: location.longitude!);
|
||||||
var address;
|
String address;
|
||||||
try {
|
try {
|
||||||
address = await getReverseLocation(lat: yl.lat, lon: yl.lon);
|
address = await getReverseLocation(lat: yl.lat, lon: yl.lon);
|
||||||
yl.description = address as String;
|
yl.description = address;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
try {
|
try {
|
||||||
address =
|
address =
|
||||||
await getReverseLocation(lat: yl.lat, lon: yl.lon, external: true);
|
await getReverseLocation(lat: yl.lat, lon: yl.lon, external: true);
|
||||||
yl.description = address as String;
|
yl.description = address;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('We cannot reverse geolocate');
|
print('We cannot reverse geolocate');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return yl;
|
return yl;
|
||||||
} on PlatformException catch (e) {
|
} on PlatformException catch (e) {
|
||||||
final context = scaffoldKey.currentContext;
|
final BuildContext? context = scaffoldKey.currentContext;
|
||||||
if (context != null) {
|
if (context != null) {
|
||||||
if (e.code == 'PERMISSION_DENIED') {
|
if (e.code == 'PERMISSION_DENIED') {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(new SnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||||
content: new Text(S.of(context).notPermsUbication),
|
content: Text(S.of(context).notPermsUbication),
|
||||||
));
|
));
|
||||||
} else if (e.code == 'PERMISSION_DENIED_NEVER_ASK') {}
|
} else if (e.code == 'PERMISSION_DENIED_NEVER_ASK') {}
|
||||||
ScaffoldMessenger.of(context).showSnackBar(new SnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||||
content: new Text(S.of(context).isYourUbicationEnabled),
|
content: Text(S.of(context).isYourUbicationEnabled),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
return YourLocation.noLocation;
|
return YourLocation.noLocation;
|
||||||
|
|
@ -53,19 +53,19 @@ Future<YourLocation> getUserLocation(
|
||||||
Future<String> getReverseLocation(
|
Future<String> getReverseLocation(
|
||||||
{required double lat, required double lon, bool external = false}) async {
|
{required double lat, required double lon, bool external = false}) async {
|
||||||
try {
|
try {
|
||||||
List<geo.Placemark> placemarks =
|
final List<geo.Placemark> placemarks =
|
||||||
await geo.placemarkFromCoordinates(lat, lon);
|
await geo.placemarkFromCoordinates(lat, lon);
|
||||||
if (placemarks.isNotEmpty) {
|
if (placemarks.isNotEmpty) {
|
||||||
var first = placemarks.first;
|
final geo.Placemark first = placemarks.first;
|
||||||
String address =
|
final String address =
|
||||||
'${first.street}, ${first.locality}, ${first.administrativeArea}, ${first.country}';
|
'${first.street}, ${first.locality}, ${first.administrativeArea}, ${first.country}';
|
||||||
print("${first.name} : $address");
|
print('${first.name} : $address');
|
||||||
return address;
|
return address;
|
||||||
} else {
|
} else {
|
||||||
return 'Unable to determine address';
|
return 'Unable to determine address';
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print("Error in reverse geocoding: $e");
|
print('Error in reverse geocoding: $e');
|
||||||
throw Exception("Failed to reverse geocode: $e");
|
throw Exception('Failed to reverse geocode: $e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,12 +16,12 @@ import 'redux/reducers.dart';
|
||||||
import 'sentryReport.dart';
|
import 'sentryReport.dart';
|
||||||
|
|
||||||
Future<PackageInfo> loadPackageInfo() async {
|
Future<PackageInfo> loadPackageInfo() async {
|
||||||
PackageInfo packageInfo = await PackageInfo.fromPlatform();
|
final PackageInfo packageInfo = await PackageInfo.fromPlatform();
|
||||||
return packageInfo;
|
return packageInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Map<String, dynamic>> loadSecrets() async {
|
Future<Map<String, dynamic>> loadSecrets() async {
|
||||||
return await SecretLoader(
|
return SecretLoader(
|
||||||
secretPath: globals.isDevelopment
|
secretPath: globals.isDevelopment
|
||||||
? 'assets/private-settings-dev.json'
|
? 'assets/private-settings-dev.json'
|
||||||
: 'assets/private-settings.json')
|
: 'assets/private-settings.json')
|
||||||
|
|
@ -34,29 +34,29 @@ Future<void> mainCommon(List<Middleware<AppState>> otherMiddleware) async {
|
||||||
// Initialize Firebase before any Firebase-dependent code runs
|
// Initialize Firebase before any Firebase-dependent code runs
|
||||||
await Firebase.initializeApp();
|
await Firebase.initializeApp();
|
||||||
|
|
||||||
final getIt = GetIt.instance;
|
final GetIt getIt = GetIt.instance;
|
||||||
getIt.registerSingleton<FiresApi>(FiresApi());
|
getIt.registerSingleton<FiresApi>(FiresApi());
|
||||||
loadPackageInfo().then((packageInfo) {
|
loadPackageInfo().then((PackageInfo packageInfo) {
|
||||||
globals.appVersion = packageInfo.version;
|
globals.appVersion = packageInfo.version;
|
||||||
print('Running version ${packageInfo.version}');
|
print('Running version ${packageInfo.version}');
|
||||||
loadSecrets().then((secrets) {
|
loadSecrets().then((Map<String, dynamic> secrets) {
|
||||||
final store = Store<AppState>(appStateReducer,
|
final Store<AppState> store = Store<AppState>(appStateReducer,
|
||||||
initialState: AppState(
|
initialState: AppState(
|
||||||
gmapKey: secrets['gmapKey'] as String,
|
gmapKey: secrets['gmapKey'] as String,
|
||||||
firesApiKey: secrets['firesApiKey'] as String,
|
firesApiKey: secrets['firesApiKey'] as String,
|
||||||
serverUrl: secrets['firesApiUrl'] as String,
|
serverUrl: secrets['firesApiUrl'] as String,
|
||||||
firesApiUrl: (secrets['firesApiUrl'] as String) + "api/v1/"),
|
firesApiUrl: "${secrets['firesApiUrl'] as String}api/v1/"),
|
||||||
middleware: List.from(otherMiddleware)..add(fetchDataMiddleware));
|
middleware: List.from(otherMiddleware)..add(fetchDataMiddleware));
|
||||||
|
|
||||||
getIt.registerSingleton<Store<AppState>>(store);
|
getIt.registerSingleton<Store<AppState>>(store);
|
||||||
getIt.registerSingleton<String>(store.state.firesApiUrl,
|
getIt.registerSingleton<String>(store.state.firesApiUrl,
|
||||||
instanceName: "firesApiUrl");
|
instanceName: 'firesApiUrl');
|
||||||
getIt.registerSingleton<String>(store.state.firesApiKey,
|
getIt.registerSingleton<String>(store.state.firesApiKey,
|
||||||
instanceName: "firesApiKey");
|
instanceName: 'firesApiKey');
|
||||||
getIt.registerSingleton<String>(store.state.serverUrl,
|
getIt.registerSingleton<String>(store.state.serverUrl,
|
||||||
instanceName: "serverUrl");
|
instanceName: 'serverUrl');
|
||||||
getIt.registerSingleton<String>(store.state.gmapKey,
|
getIt.registerSingleton<String>(store.state.gmapKey,
|
||||||
instanceName: "gmapKey");
|
instanceName: 'gmapKey');
|
||||||
|
|
||||||
// https://flutter.io/cookbook/maintenance/error-reporting/
|
// https://flutter.io/cookbook/maintenance/error-reporting/
|
||||||
runZonedGuarded<Future<void>>(() async {
|
runZonedGuarded<Future<void>>(() async {
|
||||||
|
|
@ -68,7 +68,7 @@ Future<void> mainCommon(List<Middleware<AppState>> otherMiddleware) async {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Listen to store changes, and re-render when the state is updated
|
// Listen to store changes, and re-render when the state is updated
|
||||||
store.onChange.listen((state) {
|
store.onChange.listen((AppState state) {
|
||||||
// print('Store onChange');
|
// print('Store onChange');
|
||||||
});
|
});
|
||||||
FlutterError.onError = (FlutterErrorDetails details) {
|
FlutterError.onError = (FlutterErrorDetails details) {
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ enum LogLevel { none, actions, all }
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
globals.isDevelopment = true;
|
globals.isDevelopment = true;
|
||||||
debugPrint("Is development!");
|
debugPrint('Is development!');
|
||||||
|
|
||||||
// Simple logging middleware para desarrollo
|
// Simple logging middleware para desarrollo
|
||||||
Middleware<dynamic> createLoggingMiddleware() {
|
Middleware<dynamic> createLoggingMiddleware() {
|
||||||
|
|
@ -18,10 +18,10 @@ void main() async {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
LogLevel logRedux = LogLevel.actions;
|
const LogLevel logRedux = LogLevel.actions;
|
||||||
|
|
||||||
List<Middleware<dynamic>> devMiddlewares =
|
final List<Middleware<dynamic>> devMiddlewares =
|
||||||
logRedux == LogLevel.actions ? [] : [createLoggingMiddleware()];
|
logRedux == LogLevel.actions ? <Middleware<dynamic>>[] : <Middleware<dynamic>>[createLoggingMiddleware()];
|
||||||
|
|
||||||
// In development, Sentry is disabled, so we skip initialization
|
// In development, Sentry is disabled, so we skip initialization
|
||||||
await mainCommon(devMiddlewares);
|
await mainCommon(devMiddlewares);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import 'package:badges/badges.dart' as badges_pkg;
|
import 'package:badges/badges.dart' as badges_pkg;
|
||||||
import 'package:comunes_flutter/comunes_flutter.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_redux/flutter_redux.dart';
|
import 'package:flutter_redux/flutter_redux.dart';
|
||||||
|
import 'package:redux/src/store.dart';
|
||||||
|
|
||||||
import 'activeFires.dart';
|
import 'activeFires.dart';
|
||||||
import 'colors.dart';
|
import 'colors.dart';
|
||||||
|
|
@ -17,11 +17,10 @@ import 'supportPage.dart';
|
||||||
|
|
||||||
@immutable
|
@immutable
|
||||||
class _ViewModel {
|
class _ViewModel {
|
||||||
final int unreadCount;
|
const _ViewModel({
|
||||||
|
|
||||||
_ViewModel({
|
|
||||||
required this.unreadCount,
|
required this.unreadCount,
|
||||||
});
|
});
|
||||||
|
final int unreadCount;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
|
|
@ -36,65 +35,65 @@ class _ViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
class MainDrawer extends Drawer {
|
class MainDrawer extends Drawer {
|
||||||
MainDrawer(BuildContext context, String currentRoute, {Key? key})
|
MainDrawer(BuildContext context, String currentRoute, {super.key})
|
||||||
: super(key: key, child: mainDrawer(context, currentRoute));
|
: super(child: mainDrawer(context, currentRoute));
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget mainDrawer(BuildContext context, String currentRoute) {
|
Widget mainDrawer(BuildContext context, String currentRoute) {
|
||||||
return new StoreConnector<AppState, _ViewModel>(
|
return StoreConnector<AppState, _ViewModel>(
|
||||||
distinct: true,
|
distinct: true,
|
||||||
converter: (store) {
|
converter: (Store<AppState> store) {
|
||||||
return new _ViewModel(unreadCount: store.state.fireNotificationsUnread);
|
return _ViewModel(unreadCount: store.state.fireNotificationsUnread);
|
||||||
},
|
},
|
||||||
builder: (context, view) {
|
builder: (BuildContext context, _ViewModel view) {
|
||||||
final bottomTextStyle =
|
const TextStyle bottomTextStyle =
|
||||||
new TextStyle(fontSize: 12.0, color: Colors.grey);
|
TextStyle(fontSize: 12.0, color: Colors.grey);
|
||||||
return new ListView(
|
return ListView(
|
||||||
// Important: Remove any padding from the ListView.
|
// Important: Remove any padding from the ListView.
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
children: (<Widget?>[
|
children: <Widget?>[
|
||||||
new GestureDetector(
|
GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.popAndPushNamed(context, '/');
|
Navigator.popAndPushNamed(context, '/');
|
||||||
},
|
},
|
||||||
child: new DrawerHeader(
|
child: DrawerHeader(
|
||||||
child: new Column(
|
decoration: const BoxDecoration(
|
||||||
|
color: fires300,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
new Image.asset(
|
Image.asset(
|
||||||
'images/logo-200.png',
|
'images/logo-200.png',
|
||||||
fit: BoxFit.scaleDown,
|
fit: BoxFit.scaleDown,
|
||||||
height: 80.0,
|
height: 80.0,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20.0),
|
const SizedBox(height: 20.0),
|
||||||
new Text(S.of(context).appName,
|
Text(S.of(context).appName,
|
||||||
style: new TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 24.0,
|
fontSize: 24.0,
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
)),
|
)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
decoration: new BoxDecoration(
|
|
||||||
color: fires300,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
new ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.whatshot),
|
leading: const Icon(Icons.whatshot),
|
||||||
title: new Text(S.of(context).activeFires),
|
title: Text(S.of(context).activeFires),
|
||||||
selected: currentRoute == ActiveFiresPage.routeName,
|
selected: currentRoute == ActiveFiresPage.routeName,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.popAndPushNamed(context, ActiveFiresPage.routeName);
|
Navigator.popAndPushNamed(context, ActiveFiresPage.routeName);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
new ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.notifications_active),
|
leading: const Icon(Icons.notifications_active),
|
||||||
selected: currentRoute == FireAlert.routeName,
|
selected: currentRoute == FireAlert.routeName,
|
||||||
title: new Text(S.of(context).notifyAFire),
|
title: Text(S.of(context).notifyAFire),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.popAndPushNamed(context, FireAlert.routeName);
|
Navigator.popAndPushNamed(context, FireAlert.routeName);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
new ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.notifications),
|
leading: const Icon(Icons.notifications),
|
||||||
selected: currentRoute == FireNotificationList.routeName,
|
selected: currentRoute == FireNotificationList.routeName,
|
||||||
title: Text(S.of(context).fireNotificationsTitleShort),
|
title: Text(S.of(context).fireNotificationsTitleShort),
|
||||||
|
|
@ -107,10 +106,10 @@ Widget mainDrawer(BuildContext context, String currentRoute) {
|
||||||
position: badges_pkg.BadgePosition.topEnd(
|
position: badges_pkg.BadgePosition.topEnd(
|
||||||
top: -10, end: -12),
|
top: -10, end: -12),
|
||||||
badgeContent: Text(
|
badgeContent: Text(
|
||||||
'${view.unreadCount.toString()}',
|
view.unreadCount.toString(),
|
||||||
style: TextStyle(color: Colors.white),
|
style: const TextStyle(color: Colors.white),
|
||||||
),
|
),
|
||||||
child: Icon(Icons.notifications))
|
child: const Icon(Icons.notifications))
|
||||||
])),
|
])),
|
||||||
|
|
||||||
// Text(S.of(context).fireNotificationsTitleShort),
|
// Text(S.of(context).fireNotificationsTitleShort),
|
||||||
|
|
@ -119,43 +118,44 @@ Widget mainDrawer(BuildContext context, String currentRoute) {
|
||||||
context, FireNotificationList.routeName);
|
context, FireNotificationList.routeName);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
new ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.map),
|
leading: const Icon(Icons.map),
|
||||||
selected: currentRoute == MonitoredAreasPage.routeName,
|
selected: currentRoute == MonitoredAreasPage.routeName,
|
||||||
title: new Text(S.of(context).monitoredAreasTitle),
|
title: Text(S.of(context).monitoredAreasTitle),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.popAndPushNamed(
|
Navigator.popAndPushNamed(
|
||||||
context, MonitoredAreasPage.routeName);
|
context, MonitoredAreasPage.routeName);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
new Divider(),
|
const Divider(),
|
||||||
new ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.favorite),
|
leading: const Icon(Icons.favorite),
|
||||||
selected: currentRoute == SupportPage.routeName,
|
selected: currentRoute == SupportPage.routeName,
|
||||||
title: new Text(S.of(context).supportThisInitiative),
|
title: Text(S.of(context).supportThisInitiative),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.popAndPushNamed(context, SupportPage.routeName);
|
Navigator.popAndPushNamed(context, SupportPage.routeName);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
new ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.lock),
|
leading: const Icon(Icons.lock),
|
||||||
selected: currentRoute == PrivacyPage.routeName,
|
selected: currentRoute == PrivacyPage.routeName,
|
||||||
title: new Text(S.of(context).privacyPolicy),
|
title: Text(S.of(context).privacyPolicy),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.popAndPushNamed(context, PrivacyPage.routeName);
|
Navigator.popAndPushNamed(context, PrivacyPage.routeName);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
globals.isDevelopment
|
if (globals.isDevelopment)
|
||||||
? new ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.bug_report),
|
leading: const Icon(Icons.bug_report),
|
||||||
title: new Text('Sandbox'),
|
title: const Text('Sandbox'),
|
||||||
selected: currentRoute == Sandbox.routeName,
|
selected: currentRoute == Sandbox.routeName,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.popAndPushNamed(context, Sandbox.routeName);
|
Navigator.popAndPushNamed(context, Sandbox.routeName);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
: null,
|
else
|
||||||
new AboutListTile(
|
null,
|
||||||
|
AboutListTile(
|
||||||
icon: globals.appIcon,
|
icon: globals.appIcon,
|
||||||
applicationName: S.of(context).appName,
|
applicationName: S.of(context).appName,
|
||||||
applicationVersion: globals.appVersion,
|
applicationVersion: globals.appVersion,
|
||||||
|
|
@ -163,13 +163,13 @@ Widget mainDrawer(BuildContext context, String currentRoute) {
|
||||||
applicationLegalese:
|
applicationLegalese:
|
||||||
S.of(context).appLicense(DateTime.now().year.toString()),
|
S.of(context).appLicense(DateTime.now().year.toString()),
|
||||||
aboutBoxChildren: <Widget>[
|
aboutBoxChildren: <Widget>[
|
||||||
new SizedBox(height: 10.0),
|
const SizedBox(height: 10.0),
|
||||||
new Text(S.of(context).appMoto),
|
Text(S.of(context).appMoto),
|
||||||
// , style: new TextStyle(fontStyle: FontStyle.italic)),
|
// , style: new TextStyle(fontStyle: FontStyle.italic)),
|
||||||
new SizedBox(height: 10.0),
|
const SizedBox(height: 10.0),
|
||||||
new Text(S.of(context).NASAAck, style: bottomTextStyle),
|
Text(S.of(context).NASAAck, style: bottomTextStyle),
|
||||||
// More ?
|
// More ?
|
||||||
])
|
])
|
||||||
]).where((w) => w != null).cast<Widget>().toList());
|
].where((Widget? w) => w != null).cast<Widget>().toList());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,27 @@
|
||||||
import 'package:comunes_flutter/comunes_flutter.dart';
|
import 'package:comunes_flutter/comunes_flutter.dart';
|
||||||
|
import 'package:redux/src/store.dart';
|
||||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||||
|
|
||||||
import 'globals.dart' as globals;
|
import 'globals.dart' as globals;
|
||||||
import 'mainCommon.dart';
|
import 'mainCommon.dart';
|
||||||
|
import 'models/appState.dart';
|
||||||
|
|
||||||
Future<void> main() async {
|
Future<void> main() async {
|
||||||
globals.isDevelopment = false;
|
globals.isDevelopment = false;
|
||||||
|
|
||||||
// Load secrets to get Sentry DSN
|
// Load secrets to get Sentry DSN
|
||||||
final secrets = await loadSecrets();
|
final Map<String, dynamic> secrets = await loadSecrets();
|
||||||
|
|
||||||
await SentryFlutter.init(
|
await SentryFlutter.init(
|
||||||
(options) {
|
(SentryFlutterOptions options) {
|
||||||
options.dsn = secrets['sentryDSN'] as String?;
|
options.dsn = secrets['sentryDSN'] as String?;
|
||||||
},
|
},
|
||||||
appRunner: () => mainCommon([]),
|
appRunner: () => mainCommon(<Middleware<AppState>>[]),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Map<String, dynamic>> loadSecrets() async {
|
Future<Map<String, dynamic>> loadSecrets() async {
|
||||||
return await SecretLoader(
|
return SecretLoader(
|
||||||
secretPath: globals.isDevelopment
|
secretPath: globals.isDevelopment
|
||||||
? 'assets/private-settings-dev.json'
|
? 'assets/private-settings-dev.json'
|
||||||
: 'assets/private-settings.json')
|
: 'assets/private-settings.json')
|
||||||
|
|
|
||||||
|
|
@ -8,40 +8,40 @@ import 'globals.dart' as globals;
|
||||||
import 'mainDrawer.dart';
|
import 'mainDrawer.dart';
|
||||||
|
|
||||||
abstract class MarkdownPage extends StatefulWidget {
|
abstract class MarkdownPage extends StatefulWidget {
|
||||||
|
|
||||||
|
const MarkdownPage({super.key, required this.title, required this.route, required this.file});
|
||||||
final String title;
|
final String title;
|
||||||
final Future<String> file;
|
final Future<String> file;
|
||||||
final String route;
|
final String route;
|
||||||
|
|
||||||
MarkdownPage({required this.title, required this.route, required this.file});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_MarkdownPageState createState() =>
|
_MarkdownPageState createState() =>
|
||||||
_MarkdownPageState(title: this.title, file: this.file, route: this.route);
|
_MarkdownPageState(title: title, file: file, route: route);
|
||||||
}
|
}
|
||||||
|
|
||||||
class _MarkdownPageState extends State<MarkdownPage> {
|
class _MarkdownPageState extends State<MarkdownPage> {
|
||||||
final String title;
|
|
||||||
final String route;
|
|
||||||
final Future<String> file;
|
|
||||||
String pageData = "";
|
|
||||||
|
|
||||||
_MarkdownPageState(
|
_MarkdownPageState(
|
||||||
{required this.title, required this.file, required this.route});
|
{required this.title, required this.file, required this.route});
|
||||||
|
final String title;
|
||||||
|
final String route;
|
||||||
|
final Future<String> file;
|
||||||
|
String pageData = '';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
this.file.then((fileSync) {
|
file.then((String fileSync) {
|
||||||
rootBundle
|
rootBundle
|
||||||
.loadString(fileSync, cache: !globals.isDevelopment)
|
.loadString(fileSync, cache: !globals.isDevelopment)
|
||||||
.then((pageData) {
|
.then((String pageData) {
|
||||||
setState(() {
|
setState(() {
|
||||||
this.pageData = pageData;
|
this.pageData = pageData;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return new Scaffold(
|
return Scaffold(
|
||||||
appBar: new AppBar(title: new Text(title ?? '')),
|
appBar: AppBar(title: Text(title ?? '')),
|
||||||
drawer: new MainDrawer(context, route),
|
drawer: MainDrawer(context, route),
|
||||||
body: new Markdown(data: pageData));
|
body: Markdown(data: pageData));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:comunes_flutter/comunes_flutter.dart';
|
import 'package:comunes_flutter/comunes_flutter.dart';
|
||||||
import 'package:fires_flutter/models/fireNotification.dart';
|
|
||||||
import 'package:fires_flutter/models/yourLocation.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_map/flutter_map.dart';
|
import 'package:flutter_map/flutter_map.dart';
|
||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
@ -10,7 +8,9 @@ import 'package:latlong2/latlong.dart';
|
||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
|
|
||||||
import 'fireMapState.dart';
|
import 'fireMapState.dart';
|
||||||
|
import 'fireNotification.dart';
|
||||||
import 'user.dart';
|
import 'user.dart';
|
||||||
|
import 'yourLocation.dart';
|
||||||
|
|
||||||
export 'fireMapState.dart';
|
export 'fireMapState.dart';
|
||||||
|
|
||||||
|
|
@ -19,6 +19,25 @@ part 'appState.g.dart';
|
||||||
@immutable
|
@immutable
|
||||||
@JsonSerializable(nullable: false)
|
@JsonSerializable(nullable: false)
|
||||||
class AppState {
|
class AppState {
|
||||||
|
|
||||||
|
const AppState(
|
||||||
|
{this.yourLocations = const <YourLocation>[],
|
||||||
|
this.fireNotifications = const <FireNotification>[],
|
||||||
|
this.fireNotificationsUnread = 0,
|
||||||
|
this.user = const User.initial(),
|
||||||
|
this.isLoading = false,
|
||||||
|
this.isLoaded = false,
|
||||||
|
this.error = '',
|
||||||
|
this.gmapKey = '',
|
||||||
|
this.firesApiKey = '',
|
||||||
|
this.firesApiUrl = '',
|
||||||
|
this.serverUrl = '',
|
||||||
|
this.monitoredAreas = const <Polyline>[],
|
||||||
|
this.fireMapState = const FireMapState.initial()});
|
||||||
|
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
factory AppState.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$AppStateFromJson(json);
|
||||||
@JsonKey(ignore: true)
|
@JsonKey(ignore: true)
|
||||||
final bool isLoading;
|
final bool isLoading;
|
||||||
@JsonKey(ignore: true)
|
@JsonKey(ignore: true)
|
||||||
|
|
@ -44,25 +63,6 @@ class AppState {
|
||||||
@JsonKey(ignore: true)
|
@JsonKey(ignore: true)
|
||||||
final FireMapState fireMapState;
|
final FireMapState fireMapState;
|
||||||
|
|
||||||
@JsonKey(ignore: true)
|
|
||||||
factory AppState.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$AppStateFromJson(json);
|
|
||||||
|
|
||||||
AppState(
|
|
||||||
{this.yourLocations = const <YourLocation>[],
|
|
||||||
this.fireNotifications = const <FireNotification>[],
|
|
||||||
this.fireNotificationsUnread = 0,
|
|
||||||
this.user = const User.initial(),
|
|
||||||
this.isLoading = false,
|
|
||||||
this.isLoaded = false,
|
|
||||||
this.error = '',
|
|
||||||
this.gmapKey = '',
|
|
||||||
this.firesApiKey = '',
|
|
||||||
this.firesApiUrl = '',
|
|
||||||
this.serverUrl = '',
|
|
||||||
this.monitoredAreas = const <Polyline>[],
|
|
||||||
this.fireMapState = const FireMapState.initial()});
|
|
||||||
|
|
||||||
AppState copyWith(
|
AppState copyWith(
|
||||||
{bool? isLoading,
|
{bool? isLoading,
|
||||||
bool? isLoaded,
|
bool? isLoaded,
|
||||||
|
|
@ -77,7 +77,7 @@ class AppState {
|
||||||
int? fireNotificationsUnread,
|
int? fireNotificationsUnread,
|
||||||
FireMapState? fireMapState,
|
FireMapState? fireMapState,
|
||||||
List<Polyline>? monitoredAreas}) {
|
List<Polyline>? monitoredAreas}) {
|
||||||
return new AppState(
|
return AppState(
|
||||||
isLoading: isLoading ?? this.isLoading,
|
isLoading: isLoading ?? this.isLoading,
|
||||||
isLoaded: isLoaded ?? this.isLoaded,
|
isLoaded: isLoaded ?? this.isLoaded,
|
||||||
user: user ?? this.user,
|
user: user ?? this.user,
|
||||||
|
|
@ -100,24 +100,24 @@ class AppState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef void AddYourLocationFunction(YourLocation loc);
|
typedef AddYourLocationFunction = void Function(YourLocation loc);
|
||||||
typedef void DeleteYourLocationFunction(YourLocation loc);
|
typedef DeleteYourLocationFunction = void Function(YourLocation loc);
|
||||||
typedef void OnRefreshYourLocationsFunction(Completer<Null> callback);
|
typedef OnRefreshYourLocationsFunction = void Function(Completer<void> callback);
|
||||||
typedef void ToggleSubscriptionFunction(YourLocation loc);
|
typedef ToggleSubscriptionFunction = void Function(YourLocation loc);
|
||||||
typedef void OnLocationTapFunction(YourLocation loc);
|
typedef OnLocationTapFunction = void Function(YourLocation loc);
|
||||||
typedef void OnSubscribeFunction(YourLocation loc);
|
typedef OnSubscribeFunction = void Function(YourLocation loc);
|
||||||
typedef void OnSubscribeDistanceChangeFunction(YourLocation loc);
|
typedef OnSubscribeDistanceChangeFunction = void Function(YourLocation loc);
|
||||||
typedef void OnUnSubscribeFunction(YourLocation loc);
|
typedef OnUnSubscribeFunction = void Function(YourLocation loc);
|
||||||
typedef void OnSubscribeConfirmedFunction(YourLocation loc);
|
typedef OnSubscribeConfirmedFunction = void Function(YourLocation loc);
|
||||||
typedef void OnLocationEdit(YourLocation loc);
|
typedef OnLocationEdit = void Function(YourLocation loc);
|
||||||
typedef void OnLocationEditing(YourLocation loc);
|
typedef OnLocationEditing = void Function(YourLocation loc);
|
||||||
typedef void OnLocationEditConfirm(YourLocation loc);
|
typedef OnLocationEditConfirm = void Function(YourLocation loc);
|
||||||
typedef void OnLocationEditCancel(YourLocation loc);
|
typedef OnLocationEditCancel = void Function(YourLocation loc);
|
||||||
typedef void TapFireNotificationFunction(FireNotification notif);
|
typedef TapFireNotificationFunction = void Function(FireNotification notif);
|
||||||
typedef void OnFirePressedInMap(LatLng latLng, DateTime when, String source);
|
typedef OnFirePressedInMap = void Function(LatLng latLng, DateTime when, String source);
|
||||||
// typedef void OnReceivedFireNotificationFunction(FireNotification notif);
|
// typedef void OnReceivedFireNotificationFunction(FireNotification notif);
|
||||||
typedef void DeleteFireNotificationFunction(FireNotification notif);
|
typedef DeleteFireNotificationFunction = void Function(FireNotification notif);
|
||||||
typedef void DeleteAllFireNotificationFunction();
|
typedef DeleteAllFireNotificationFunction = void Function();
|
||||||
|
|
||||||
// unused
|
// unused
|
||||||
// typedef void UpdateYourLocationFunction(ObjectId id, YourLocation loc);
|
// typedef void UpdateYourLocationFunction(ObjectId id, YourLocation loc);
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,26 @@
|
||||||
class BasicLocation implements Comparable<BasicLocation> {
|
class BasicLocation implements Comparable<BasicLocation> {
|
||||||
|
|
||||||
|
// static BasicLocation noLocation = new BasicLocation(lat: 0.0, lon: 0.0);
|
||||||
|
|
||||||
|
BasicLocation({required this.lat, required this.lon, this.description});
|
||||||
|
|
||||||
|
BasicLocation.fromJson(Map<String, dynamic> json)
|
||||||
|
: lat = (json['lat'] as num).toDouble(),
|
||||||
|
lon = (json['lon'] as num).toDouble(),
|
||||||
|
description = json['description'] as String?;
|
||||||
final double lat;
|
final double lat;
|
||||||
final double lon;
|
final double lon;
|
||||||
final String? description;
|
final String? description;
|
||||||
|
|
||||||
// static BasicLocation noLocation = new BasicLocation(lat: 0.0, lon: 0.0);
|
@override
|
||||||
|
|
||||||
BasicLocation({required this.lat, required this.lon, this.description}) {}
|
|
||||||
|
|
||||||
int compareTo(BasicLocation other) {
|
int compareTo(BasicLocation other) {
|
||||||
return lat == other.lat && lon == other.lon ? 1 : 0;
|
return lat == other.lat && lon == other.lon ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator ==(o) => o is BasicLocation && o.lat == lat && o.lon == lon;
|
@override
|
||||||
|
bool operator ==(Object o) => o is BasicLocation && o.lat == lat && o.lon == lon;
|
||||||
|
|
||||||
|
@override
|
||||||
int get hashCode {
|
int get hashCode {
|
||||||
int hash = 1;
|
int hash = 1;
|
||||||
hash = hash * 17 + lat.hashCode;
|
hash = hash * 17 + lat.hashCode;
|
||||||
|
|
@ -20,12 +28,7 @@ class BasicLocation implements Comparable<BasicLocation> {
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
BasicLocation.fromJson(Map<String, dynamic> json)
|
Map<String, dynamic> toJson() => <String, dynamic>{
|
||||||
: lat = (json['lat'] as num).toDouble(),
|
|
||||||
lon = (json['lon'] as num).toDouble(),
|
|
||||||
description = json['description'] as String?;
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
|
||||||
'lat': lat,
|
'lat': lat,
|
||||||
'lon': lon,
|
'lon': lon,
|
||||||
'description': description,
|
'description': description,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
import 'package:fires_flutter/models/fireNotification.dart';
|
|
||||||
import 'package:fires_flutter/models/yourLocation.dart';
|
|
||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
|
|
||||||
|
import 'fireNotification.dart';
|
||||||
|
import 'yourLocation.dart';
|
||||||
|
|
||||||
enum FireMapStatus {
|
enum FireMapStatus {
|
||||||
view,
|
view,
|
||||||
subscriptionConfirm,
|
subscriptionConfirm,
|
||||||
|
|
@ -14,6 +15,26 @@ enum FireMapLayer { osmcGrey, esriSatellite, osmc, esri, esriTerrain }
|
||||||
|
|
||||||
@immutable
|
@immutable
|
||||||
class FireMapState {
|
class FireMapState {
|
||||||
|
|
||||||
|
const FireMapState(
|
||||||
|
{this.status = FireMapStatus.view,
|
||||||
|
this.layer = FireMapLayer.osmcGrey,
|
||||||
|
this.yourLocation,
|
||||||
|
this.numFires = 0,
|
||||||
|
this.fires = const <dynamic>[],
|
||||||
|
this.fireNotification,
|
||||||
|
this.falsePos = const <dynamic>[],
|
||||||
|
this.industries = const <dynamic>[]});
|
||||||
|
|
||||||
|
const FireMapState.initial()
|
||||||
|
: status = FireMapStatus.view,
|
||||||
|
layer = FireMapLayer.osmcGrey,
|
||||||
|
yourLocation = null,
|
||||||
|
fireNotification = null,
|
||||||
|
numFires = 0,
|
||||||
|
fires = const <dynamic>[],
|
||||||
|
falsePos = const <dynamic>[],
|
||||||
|
industries = const <dynamic>[];
|
||||||
final FireMapStatus status;
|
final FireMapStatus status;
|
||||||
final FireMapLayer layer;
|
final FireMapLayer layer;
|
||||||
final int numFires;
|
final int numFires;
|
||||||
|
|
@ -23,26 +44,6 @@ class FireMapState {
|
||||||
final List<dynamic> industries;
|
final List<dynamic> industries;
|
||||||
final YourLocation? yourLocation;
|
final YourLocation? yourLocation;
|
||||||
|
|
||||||
const FireMapState.initial()
|
|
||||||
: this.status = FireMapStatus.view,
|
|
||||||
this.layer = FireMapLayer.osmcGrey,
|
|
||||||
this.yourLocation = null,
|
|
||||||
this.fireNotification = null,
|
|
||||||
this.numFires = 0,
|
|
||||||
this.fires = const [],
|
|
||||||
this.falsePos = const [],
|
|
||||||
this.industries = const [];
|
|
||||||
|
|
||||||
FireMapState(
|
|
||||||
{this.status = FireMapStatus.view,
|
|
||||||
this.layer = FireMapLayer.osmcGrey,
|
|
||||||
this.yourLocation,
|
|
||||||
this.numFires = 0,
|
|
||||||
this.fires = const [],
|
|
||||||
this.fireNotification,
|
|
||||||
this.falsePos = const [],
|
|
||||||
this.industries = const []});
|
|
||||||
|
|
||||||
FireMapState copyWith({
|
FireMapState copyWith({
|
||||||
FireMapStatus? status,
|
FireMapStatus? status,
|
||||||
FireMapLayer? layer,
|
FireMapLayer? layer,
|
||||||
|
|
@ -53,9 +54,9 @@ class FireMapState {
|
||||||
List<dynamic>? falsePos,
|
List<dynamic>? falsePos,
|
||||||
List<dynamic>? industries,
|
List<dynamic>? industries,
|
||||||
}) {
|
}) {
|
||||||
return new FireMapState(
|
return FireMapState(
|
||||||
yourLocation: yourLocation ?? this.yourLocation,
|
yourLocation: yourLocation ?? this.yourLocation,
|
||||||
fireNotification: fireNotication ?? this.fireNotification,
|
fireNotification: fireNotication ?? fireNotification,
|
||||||
numFires: numFires ?? this.numFires,
|
numFires: numFires ?? this.numFires,
|
||||||
fires: fires ?? this.fires,
|
fires: fires ?? this.fires,
|
||||||
falsePos: falsePos ?? this.falsePos,
|
falsePos: falsePos ?? this.falsePos,
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,19 @@ part 'fireNotification.g.dart';
|
||||||
|
|
||||||
@JsonSerializable(nullable: false)
|
@JsonSerializable(nullable: false)
|
||||||
class FireNotification {
|
class FireNotification {
|
||||||
|
|
||||||
|
FireNotification(
|
||||||
|
{required this.id,
|
||||||
|
required this.lat,
|
||||||
|
required this.lon,
|
||||||
|
required this.description,
|
||||||
|
required this.when,
|
||||||
|
required this.read,
|
||||||
|
required this.sealed,
|
||||||
|
required this.subsId});
|
||||||
|
|
||||||
|
factory FireNotification.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$FireNotificationFromJson(json);
|
||||||
@JsonKey(toJson: objectIdToJson, fromJson: objectIdFromJson)
|
@JsonKey(toJson: objectIdToJson, fromJson: objectIdFromJson)
|
||||||
ObjectId id;
|
ObjectId id;
|
||||||
final double lat;
|
final double lat;
|
||||||
|
|
@ -20,19 +33,6 @@ class FireNotification {
|
||||||
final ObjectId subsId;
|
final ObjectId subsId;
|
||||||
final bool read;
|
final bool read;
|
||||||
|
|
||||||
factory FireNotification.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$FireNotificationFromJson(json);
|
|
||||||
|
|
||||||
FireNotification(
|
|
||||||
{required this.id,
|
|
||||||
required this.lat,
|
|
||||||
required this.lon,
|
|
||||||
required this.description,
|
|
||||||
required this.when,
|
|
||||||
required this.read,
|
|
||||||
required this.sealed,
|
|
||||||
required this.subsId}) {}
|
|
||||||
|
|
||||||
FireNotification copyWith(
|
FireNotification copyWith(
|
||||||
{ObjectId? id,
|
{ObjectId? id,
|
||||||
double? lat,
|
double? lat,
|
||||||
|
|
@ -42,7 +42,7 @@ class FireNotification {
|
||||||
DateTime? when,
|
DateTime? when,
|
||||||
String? sealed,
|
String? sealed,
|
||||||
ObjectId? subsId}) {
|
ObjectId? subsId}) {
|
||||||
return new FireNotification(
|
return FireNotification(
|
||||||
id: id ?? this.id,
|
id: id ?? this.id,
|
||||||
lat: lat ?? this.lat,
|
lat: lat ?? this.lat,
|
||||||
lon: lon ?? this.lon,
|
lon: lon ?? this.lon,
|
||||||
|
|
@ -83,7 +83,7 @@ class FireNotification {
|
||||||
return 'FireNotification {id: $id, lat: $lat, lon: $lon, when: $when, read: $read, subsId: $subsId, sealed ${ellipse(sealed)}';
|
return 'FireNotification {id: $id, lat: $lat, lon: $lon, when: $when, read: $read, subsId: $subsId, sealed ${ellipse(sealed)}';
|
||||||
}
|
}
|
||||||
|
|
||||||
static final Map<String, Route<Null>> routes = <String, Route<Null>>{};
|
static final Map<String, Route<void>> routes = <String, Route<void>>{};
|
||||||
Map<String, dynamic> toJson() => _$FireNotificationToJson(this);
|
Map<String, dynamic> toJson() => _$FireNotificationToJson(this);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -2,30 +2,31 @@ import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:comunes_flutter/comunes_flutter.dart';
|
import 'package:comunes_flutter/comunes_flutter.dart';
|
||||||
import 'package:fires_flutter/models/fireNotification.dart';
|
import 'package:shared_preferences/src/shared_preferences_legacy.dart';
|
||||||
|
|
||||||
import '../globals.dart' as globals;
|
import '../globals.dart' as globals;
|
||||||
|
import 'fireNotification.dart';
|
||||||
|
|
||||||
final String fireNotificationKey = 'fireNotifications';
|
const String fireNotificationKey = 'fireNotifications';
|
||||||
|
|
||||||
Future<List<FireNotification>> loadFireNotifications() async {
|
Future<List<FireNotification>> loadFireNotifications() async {
|
||||||
return await globals.prefs.then((prefs) {
|
return globals.prefs.then((SharedPreferences prefs) {
|
||||||
List<String>? FireNotifications = prefs.getStringList(fireNotificationKey);
|
final List<String>? FireNotifications = prefs.getStringList(fireNotificationKey);
|
||||||
List<FireNotification> persistedList = [];
|
final List<FireNotification> persistedList = <FireNotification>[];
|
||||||
(FireNotifications ?? []).forEach((notificationString) {
|
for (final String notificationString in (FireNotifications ?? <String>[])) {
|
||||||
Map<String, dynamic> notificationMap =
|
final Map<String, dynamic> notificationMap =
|
||||||
json.decode(notificationString) as Map<String, dynamic>;
|
json.decode(notificationString) as Map<String, dynamic>;
|
||||||
persistedList.add(FireNotification.fromJson(notificationMap));
|
persistedList.add(FireNotification.fromJson(notificationMap));
|
||||||
});
|
}
|
||||||
return persistedList;
|
return persistedList;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
persistFireNotifications(List<FireNotification> notif) {
|
void persistFireNotifications(List<FireNotification> notif) {
|
||||||
// print('Persisting $notif');
|
// print('Persisting $notif');
|
||||||
globals.prefs.then((prefs) {
|
globals.prefs.then((SharedPreferences prefs) {
|
||||||
List<String> notifAsString = [];
|
final List<String> notifAsString = <String>[];
|
||||||
notif.where(notNull).toList().forEach((notification) {
|
notif.where(notNull).toList().forEach((FireNotification notification) {
|
||||||
notifAsString.add(json.encode(notification.toJson()));
|
notifAsString.add(json.encode(notification.toJson()));
|
||||||
});
|
});
|
||||||
prefs.setStringList(fireNotificationKey, notifAsString);
|
prefs.setStringList(fireNotificationKey, notifAsString);
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:fires_flutter/models/yourLocation.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_map/flutter_map.dart';
|
import 'package:flutter_map/flutter_map.dart';
|
||||||
import 'package:latlong2/latlong.dart';
|
import 'package:latlong2/latlong.dart';
|
||||||
|
|
@ -12,49 +11,50 @@ import '../objectIdUtils.dart';
|
||||||
import '../redux/actions.dart';
|
import '../redux/actions.dart';
|
||||||
import 'appState.dart';
|
import 'appState.dart';
|
||||||
import 'falsePositiveTypes.dart';
|
import 'falsePositiveTypes.dart';
|
||||||
|
import 'yourLocation.dart';
|
||||||
|
|
||||||
class FiresApi {
|
class FiresApi {
|
||||||
late final Dio _dio;
|
|
||||||
|
|
||||||
FiresApi() {
|
FiresApi() {
|
||||||
_dio = Dio();
|
_dio = Dio();
|
||||||
}
|
}
|
||||||
|
late final Dio _dio;
|
||||||
|
|
||||||
Future<String> createUser(
|
Future<String> createUser(
|
||||||
AppState state, String mobileToken, String lang) async {
|
AppState state, String mobileToken, String lang) async {
|
||||||
final params = {
|
final Map<String, String> params = <String, String>{
|
||||||
"token": state.firesApiKey,
|
'token': state.firesApiKey,
|
||||||
"mobileToken": mobileToken,
|
'mobileToken': mobileToken,
|
||||||
"lang": lang
|
'lang': lang
|
||||||
};
|
};
|
||||||
final String url = '${state.firesApiUrl}mobile/users';
|
final String url = '${state.firesApiUrl}mobile/users';
|
||||||
try {
|
try {
|
||||||
final response = await _dio.post(url, data: params);
|
final Response<dynamic> response = await _dio.post(url, data: params);
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
return response.data['data']['userId'] as String;
|
return response.data['data']['userId'] as String;
|
||||||
} else {
|
} else {
|
||||||
throw "Unexpected error on create user";
|
throw 'Unexpected error on create user';
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw "Error creating user: $e";
|
throw 'Error creating user: $e';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<YourLocation>> fetchYourLocations(AppState state) async {
|
Future<List<YourLocation>> fetchYourLocations(AppState state) async {
|
||||||
final apiKey = state.firesApiKey;
|
final String apiKey = state.firesApiKey;
|
||||||
final mobileToken = state.user.token;
|
final String mobileToken = state.user.token;
|
||||||
final String url =
|
final String url =
|
||||||
'${state.firesApiUrl}mobile/subscriptions/all/$apiKey/$mobileToken';
|
'${state.firesApiUrl}mobile/subscriptions/all/$apiKey/$mobileToken';
|
||||||
try {
|
try {
|
||||||
final response = await _dio.get(url);
|
final Response<dynamic> response = await _dio.get(url);
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
final dataSubscriptions =
|
final List<dynamic> dataSubscriptions =
|
||||||
response.data['data']['subscriptions'] as List<dynamic>;
|
response.data['data']['subscriptions'] as List<dynamic>;
|
||||||
List<YourLocation> subscribed = [];
|
final List<YourLocation> subscribed = <YourLocation>[];
|
||||||
for (int i = 0; i < dataSubscriptions.length; i++) {
|
for (int i = 0; i < dataSubscriptions.length; i++) {
|
||||||
var el = dataSubscriptions[i] as Map<String, dynamic>;
|
final Map<String, dynamic> el = dataSubscriptions[i] as Map<String, dynamic>;
|
||||||
var lat = (el['location']['lat'] as num).toDouble();
|
final double lat = (el['location']['lat'] as num).toDouble();
|
||||||
var lon = (el['location']['lon'] as num).toDouble();
|
final double lon = (el['location']['lon'] as num).toDouble();
|
||||||
subscribed.add(YourLocation(
|
subscribed.add(YourLocation(
|
||||||
id: objectIdFromJson(el['_id']['_str'] as String),
|
id: objectIdFromJson(el['_id']['_str'] as String),
|
||||||
lat: lat,
|
lat: lat,
|
||||||
|
|
@ -64,50 +64,50 @@ class FiresApi {
|
||||||
}
|
}
|
||||||
return subscribed;
|
return subscribed;
|
||||||
} else {
|
} else {
|
||||||
throw "Unexpected error fetching your locations";
|
throw 'Unexpected error fetching your locations';
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw "Error fetching locations: $e";
|
throw 'Error fetching locations: $e';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> subscribe(AppState state, YourLocation loc) async {
|
Future<String> subscribe(AppState state, YourLocation loc) async {
|
||||||
final params = {
|
final Map<String, Object> params = <String, Object>{
|
||||||
"token": state.firesApiKey,
|
'token': state.firesApiKey,
|
||||||
"mobileToken": state.user.token,
|
'mobileToken': state.user.token,
|
||||||
"id": loc.id.hexString,
|
'id': loc.id.hexString,
|
||||||
"lat": loc.lat,
|
'lat': loc.lat,
|
||||||
"lon": loc.lon,
|
'lon': loc.lon,
|
||||||
"distance": loc.distance
|
'distance': loc.distance
|
||||||
};
|
};
|
||||||
final String url = '${state.firesApiUrl}mobile/subscriptions';
|
final String url = '${state.firesApiUrl}mobile/subscriptions';
|
||||||
try {
|
try {
|
||||||
final response = await _dio.post(url, data: params);
|
final Response<dynamic> response = await _dio.post(url, data: params);
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
return response.data['data']['subsId'] as String;
|
return response.data['data']['subsId'] as String;
|
||||||
} else {
|
} else {
|
||||||
print(response.data);
|
print(response.data);
|
||||||
throw "Unexpected error on subscribe";
|
throw 'Unexpected error on subscribe';
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw "Error subscribing: $e";
|
throw 'Error subscribing: $e';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> unsubscribe(AppState state, String subsId) async {
|
Future<bool> unsubscribe(AppState state, String subsId) async {
|
||||||
final apiKey = state.firesApiKey;
|
final String apiKey = state.firesApiKey;
|
||||||
final mobileToken = state.user.token;
|
final String mobileToken = state.user.token;
|
||||||
final String url =
|
final String url =
|
||||||
'${state.firesApiUrl}mobile/subscriptions/$apiKey/$mobileToken/$subsId';
|
'${state.firesApiUrl}mobile/subscriptions/$apiKey/$mobileToken/$subsId';
|
||||||
try {
|
try {
|
||||||
final response = await _dio.delete(url);
|
final Response<dynamic> response = await _dio.delete(url);
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
throw "Unexpected error on unsubscribe";
|
throw 'Unexpected error on unsubscribe';
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw "Error unsubscribing: $e";
|
throw 'Error unsubscribing: $e';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -116,22 +116,22 @@ class FiresApi {
|
||||||
required double lat,
|
required double lat,
|
||||||
required double lon,
|
required double lon,
|
||||||
required int distance}) async {
|
required int distance}) async {
|
||||||
var url =
|
final String url =
|
||||||
'${state.firesApiUrl}fires-in-full/${state.firesApiKey}/$lat/$lon/$distance';
|
'${state.firesApiUrl}fires-in-full/${state.firesApiKey}/$lat/$lon/$distance';
|
||||||
if (globals.isDevelopment) print(url);
|
if (globals.isDevelopment) print(url);
|
||||||
try {
|
try {
|
||||||
final response = await _dio.get(url);
|
final Response<dynamic> response = await _dio.get(url);
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
var resultDecoded = response.data;
|
final resultDecoded = response.data;
|
||||||
int numFires = (resultDecoded['real'] as num).toInt();
|
final int numFires = (resultDecoded['real'] as num).toInt();
|
||||||
List<dynamic> fires = resultDecoded['fires'] as List<dynamic>;
|
final List<dynamic> fires = resultDecoded['fires'] as List<dynamic>;
|
||||||
List<dynamic> falsePos = resultDecoded['falsePos'] as List<dynamic>;
|
final List<dynamic> falsePos = resultDecoded['falsePos'] as List<dynamic>;
|
||||||
List<dynamic> industries = resultDecoded['industries'] as List<dynamic>;
|
final List<dynamic> industries = resultDecoded['industries'] as List<dynamic>;
|
||||||
|
|
||||||
if (globals.isDevelopment) {
|
if (globals.isDevelopment) {
|
||||||
var firesCount = fires.length;
|
final int firesCount = fires.length;
|
||||||
var industriesCount = industries.length;
|
final int industriesCount = industries.length;
|
||||||
var falsePosCount = falsePos.length;
|
final int falsePosCount = falsePos.length;
|
||||||
print(
|
print(
|
||||||
'(Pos: $lat, $lon) real: $numFires, fire: $firesCount falsePos: $falsePosCount industries: $industriesCount');
|
'(Pos: $lat, $lon) real: $numFires, fire: $firesCount falsePos: $falsePosCount industries: $industriesCount');
|
||||||
}
|
}
|
||||||
|
|
@ -148,25 +148,25 @@ class FiresApi {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<Polyline>> getMonitoredAreas({required AppState state}) async {
|
Future<List<Polyline>> getMonitoredAreas({required AppState state}) async {
|
||||||
var url =
|
final String url =
|
||||||
'${state.firesApiUrl}status/subs-public-union/${state.firesApiKey}';
|
'${state.firesApiUrl}status/subs-public-union/${state.firesApiKey}';
|
||||||
var color = const Color(0xFF145A32);
|
const Color color = Color(0xFF145A32);
|
||||||
try {
|
try {
|
||||||
final response = await _dio.get(url);
|
final Response<dynamic> response = await _dio.get(url);
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
var resultDecoded = response.data;
|
final resultDecoded = response.data;
|
||||||
List<Polyline> union = [];
|
final List<Polyline> union = <Polyline>[];
|
||||||
final multipolygon =
|
final List<dynamic> multipolygon =
|
||||||
(json.decode(resultDecoded['data']['union']['value'] as String)
|
(json.decode(resultDecoded['data']['union']['value'] as String)
|
||||||
as Map<String, dynamic>)['geometry']['coordinates']
|
as Map<String, dynamic>)['geometry']['coordinates']
|
||||||
as List<dynamic>;
|
as List<dynamic>;
|
||||||
for (dynamic polygonDynamic in multipolygon) {
|
for (final dynamic polygonDynamic in multipolygon) {
|
||||||
var polygon = polygonDynamic as List<dynamic>;
|
final List<dynamic> polygon = polygonDynamic as List<dynamic>;
|
||||||
for (dynamic holeDynamic in polygon) {
|
for (final dynamic holeDynamic in polygon) {
|
||||||
var hole = holeDynamic as List<dynamic>;
|
final List<dynamic> hole = holeDynamic as List<dynamic>;
|
||||||
List<LatLng> points = [];
|
final List<LatLng> points = <LatLng>[];
|
||||||
for (dynamic pointDynamic in hole) {
|
for (final dynamic pointDynamic in hole) {
|
||||||
var point = pointDynamic as List<dynamic>;
|
final List<dynamic> point = pointDynamic as List<dynamic>;
|
||||||
points.add(LatLng(
|
points.add(LatLng(
|
||||||
(point[1] as num).toDouble(), (point[0] as num).toDouble()));
|
(point[1] as num).toDouble(), (point[0] as num).toDouble()));
|
||||||
}
|
}
|
||||||
|
|
@ -183,15 +183,15 @@ class FiresApi {
|
||||||
|
|
||||||
Future<bool> markFalsePositive(AppState state, String mobileToken,
|
Future<bool> markFalsePositive(AppState state, String mobileToken,
|
||||||
String sealed, FalsePositiveType type) async {
|
String sealed, FalsePositiveType type) async {
|
||||||
final params = {
|
final Map<String, String> params = <String, String>{
|
||||||
"token": state.firesApiKey,
|
'token': state.firesApiKey,
|
||||||
"mobileToken": mobileToken,
|
'mobileToken': mobileToken,
|
||||||
"sealed": sealed,
|
'sealed': sealed,
|
||||||
"type": type.toString().split('.')[1]
|
'type': type.toString().split('.')[1]
|
||||||
};
|
};
|
||||||
final String url = '${state.firesApiUrl}mobile/falsepositive';
|
final String url = '${state.firesApiUrl}mobile/falsepositive';
|
||||||
try {
|
try {
|
||||||
final response = await _dio.post(url, data: params);
|
final Response<dynamic> response = await _dio.post(url, data: params);
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
if (globals.isDevelopment) print(response.data['data']['upsert']);
|
if (globals.isDevelopment) print(response.data['data']['upsert']);
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -200,7 +200,7 @@ class FiresApi {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint("Error marking false positive: $e");
|
debugPrint('Error marking false positive: $e');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,21 @@
|
||||||
import 'package:meta/meta.dart';
|
|
||||||
import 'package:comunes_flutter/comunes_flutter.dart';
|
import 'package:comunes_flutter/comunes_flutter.dart';
|
||||||
|
import 'package:meta/meta.dart';
|
||||||
|
|
||||||
@immutable
|
@immutable
|
||||||
class User {
|
class User {
|
||||||
final String userId;
|
|
||||||
final String lang;
|
const User({required this.userId, required this.lang, required this.token});
|
||||||
final String token;
|
|
||||||
|
|
||||||
const User.initial()
|
const User.initial()
|
||||||
: userId = '',
|
: userId = '',
|
||||||
lang = '',
|
lang = '',
|
||||||
token = '';
|
token = '';
|
||||||
|
final String userId;
|
||||||
const User({required this.userId, required this.lang, required this.token});
|
final String lang;
|
||||||
|
final String token;
|
||||||
|
|
||||||
User copyWith({String? userId, String? lang, String? token}) {
|
User copyWith({String? userId, String? lang, String? token}) {
|
||||||
return new User(
|
return User(
|
||||||
userId: userId ?? this.userId,
|
userId: userId ?? this.userId,
|
||||||
token: token ?? this.token,
|
token: token ?? this.token,
|
||||||
lang: lang ?? this.lang);
|
lang: lang ?? this.lang);
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,12 @@
|
||||||
import 'package:fires_flutter/objectIdUtils.dart';
|
|
||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
import 'package:objectid/objectid.dart';
|
import 'package:objectid/objectid.dart';
|
||||||
|
|
||||||
|
import '../objectIdUtils.dart';
|
||||||
|
|
||||||
part 'yourLocation.g.dart';
|
part 'yourLocation.g.dart';
|
||||||
|
|
||||||
@JsonSerializable(nullable: false)
|
@JsonSerializable(nullable: false)
|
||||||
class YourLocation {
|
class YourLocation {
|
||||||
@JsonKey(toJson: objectIdToJson, fromJson: objectIdFromJson)
|
|
||||||
ObjectId id;
|
|
||||||
final double lat;
|
|
||||||
final double lon;
|
|
||||||
String description;
|
|
||||||
bool subscribed;
|
|
||||||
int distance;
|
|
||||||
late int currentNumFires;
|
|
||||||
|
|
||||||
factory YourLocation.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$YourLocationFromJson(json);
|
|
||||||
|
|
||||||
static late final YourLocation noLocation;
|
|
||||||
static const int? withoutStats = null;
|
|
||||||
|
|
||||||
static void _initNoLocation() {
|
|
||||||
noLocation = new YourLocation(
|
|
||||||
id: ObjectId(), lat: 0.0, lon: 0.0, description: '', subscribed: false);
|
|
||||||
}
|
|
||||||
|
|
||||||
YourLocation(
|
YourLocation(
|
||||||
{required this.id,
|
{required this.id,
|
||||||
|
|
@ -36,6 +18,25 @@ class YourLocation {
|
||||||
this.subscribed = false}) {
|
this.subscribed = false}) {
|
||||||
this.currentNumFires = currentNumFires ?? 0;
|
this.currentNumFires = currentNumFires ?? 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
factory YourLocation.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$YourLocationFromJson(json);
|
||||||
|
@JsonKey(toJson: objectIdToJson, fromJson: objectIdFromJson)
|
||||||
|
ObjectId id;
|
||||||
|
final double lat;
|
||||||
|
final double lon;
|
||||||
|
String description;
|
||||||
|
bool subscribed;
|
||||||
|
int distance;
|
||||||
|
late int currentNumFires;
|
||||||
|
|
||||||
|
static late final YourLocation noLocation;
|
||||||
|
static const int? withoutStats = null;
|
||||||
|
|
||||||
|
static void _initNoLocation() {
|
||||||
|
noLocation = YourLocation(
|
||||||
|
id: ObjectId(), lat: 0.0, lon: 0.0);
|
||||||
|
}
|
||||||
Map<String, dynamic> toJson() => _$YourLocationToJson(this);
|
Map<String, dynamic> toJson() => _$YourLocationToJson(this);
|
||||||
|
|
||||||
YourLocation copyWith(
|
YourLocation copyWith(
|
||||||
|
|
@ -46,7 +47,7 @@ class YourLocation {
|
||||||
int? distance,
|
int? distance,
|
||||||
int? currentNumFires,
|
int? currentNumFires,
|
||||||
bool? subscribed}) {
|
bool? subscribed}) {
|
||||||
return new YourLocation(
|
return YourLocation(
|
||||||
id: id ?? this.id,
|
id: id ?? this.id,
|
||||||
lat: lat ?? this.lat,
|
lat: lat ?? this.lat,
|
||||||
lon: lon ?? this.lon,
|
lon: lon ?? this.lon,
|
||||||
|
|
|
||||||
|
|
@ -2,30 +2,31 @@ import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:comunes_flutter/comunes_flutter.dart';
|
import 'package:comunes_flutter/comunes_flutter.dart';
|
||||||
import 'package:fires_flutter/models/yourLocation.dart';
|
import 'package:shared_preferences/src/shared_preferences_legacy.dart';
|
||||||
|
|
||||||
import '../globals.dart' as globals;
|
import '../globals.dart' as globals;
|
||||||
|
import 'yourLocation.dart';
|
||||||
|
|
||||||
final String locationKey = 'yourlocations';
|
const String locationKey = 'yourlocations';
|
||||||
|
|
||||||
Future<List<YourLocation>> loadYourLocations() async {
|
Future<List<YourLocation>> loadYourLocations() async {
|
||||||
return await globals.prefs.then((prefs) {
|
return globals.prefs.then((SharedPreferences prefs) {
|
||||||
List<String>? yourLocations = prefs.getStringList(locationKey);
|
final List<String>? yourLocations = prefs.getStringList(locationKey);
|
||||||
List<YourLocation> persistedList = [];
|
final List<YourLocation> persistedList = <YourLocation>[];
|
||||||
(yourLocations ?? []).forEach((locationString) {
|
for (final String locationString in (yourLocations ?? <String>[])) {
|
||||||
Map<String, dynamic> locationMap =
|
final Map<String, dynamic> locationMap =
|
||||||
json.decode(locationString) as Map<String, dynamic>;
|
json.decode(locationString) as Map<String, dynamic>;
|
||||||
persistedList.add(YourLocation.fromJson(locationMap));
|
persistedList.add(YourLocation.fromJson(locationMap));
|
||||||
});
|
}
|
||||||
return persistedList;
|
return persistedList;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
persistYourLocations(List<YourLocation> yl) {
|
void persistYourLocations(List<YourLocation> yl) {
|
||||||
// debugPrint('Persisting $yl');
|
// debugPrint('Persisting $yl');
|
||||||
globals.prefs.then((prefs) {
|
globals.prefs.then((SharedPreferences prefs) {
|
||||||
List<String> ylAsString = [];
|
final List<String> ylAsString = <String>[];
|
||||||
yl.where(notNull).toList().forEach((location) {
|
yl.where(notNull).toList().forEach((YourLocation location) {
|
||||||
ylAsString.add(json.encode(location.toJson()));
|
ylAsString.add(json.encode(location.toJson()));
|
||||||
});
|
});
|
||||||
prefs.setStringList(locationKey, ylAsString);
|
prefs.setStringList(locationKey, ylAsString);
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_map/flutter_map.dart';
|
import 'package:flutter_map/flutter_map.dart';
|
||||||
import 'package:flutter_redux/flutter_redux.dart';
|
import 'package:flutter_redux/flutter_redux.dart';
|
||||||
import 'package:latlong2/latlong.dart';
|
import 'package:latlong2/latlong.dart';
|
||||||
|
import 'package:redux/src/store.dart';
|
||||||
|
|
||||||
import 'colors.dart';
|
import 'colors.dart';
|
||||||
import 'compassMapPlugin.dart';
|
import 'compassMapPlugin.dart';
|
||||||
|
|
@ -9,12 +10,11 @@ import 'customBottomAppBar.dart';
|
||||||
import 'generated/i18n.dart';
|
import 'generated/i18n.dart';
|
||||||
import 'mainDrawer.dart';
|
import 'mainDrawer.dart';
|
||||||
import 'models/appState.dart';
|
import 'models/appState.dart';
|
||||||
import 'firesSpinner.dart';
|
|
||||||
|
|
||||||
class _ViewModel {
|
class _ViewModel {
|
||||||
List<Polyline> monitoredAreas;
|
|
||||||
|
|
||||||
_ViewModel(this.monitoredAreas);
|
_ViewModel(this.monitoredAreas);
|
||||||
|
List<Polyline> monitoredAreas;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) =>
|
bool operator ==(Object other) =>
|
||||||
|
|
@ -28,65 +28,63 @@ class _ViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
class MonitoredAreasPage extends StatelessWidget {
|
class MonitoredAreasPage extends StatelessWidget {
|
||||||
static const String routeName = "monitoredAreasMap";
|
const MonitoredAreasPage({super.key});
|
||||||
|
|
||||||
|
static const String routeName = 'monitoredAreasMap';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return new StoreConnector<AppState, _ViewModel>(
|
return StoreConnector<AppState, _ViewModel>(
|
||||||
distinct: true,
|
distinct: true,
|
||||||
converter: (store) {
|
converter: (Store<AppState> store) {
|
||||||
return new _ViewModel(store.state.monitoredAreas);
|
return _ViewModel(store.state.monitoredAreas);
|
||||||
},
|
},
|
||||||
builder: (context, view) {
|
builder: (BuildContext context, _ViewModel view) {
|
||||||
return new Scaffold(
|
return Scaffold(
|
||||||
appBar:
|
appBar:
|
||||||
new AppBar(title: new Text(S.of(context).monitoredAreasTitle)),
|
AppBar(title: Text(S.of(context).monitoredAreasTitle)),
|
||||||
drawer: new MainDrawer(context, MonitoredAreasPage.routeName),
|
drawer: MainDrawer(context, MonitoredAreasPage.routeName),
|
||||||
bottomNavigationBar: new CustomBottomAppBar(
|
bottomNavigationBar: CustomBottomAppBar(
|
||||||
fabLocation: FloatingActionButtonLocation.centerDocked,
|
fabLocation: FloatingActionButtonLocation.centerDocked,
|
||||||
showNotch: true,
|
showNotch: true,
|
||||||
color: fires100,
|
color: fires100,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
actions: <Widget>[
|
actions: <Widget>[
|
||||||
new Flexible(
|
Flexible(
|
||||||
child: new Padding(
|
child: Padding(
|
||||||
padding: new EdgeInsets.only(left: 10.0, right: 10.0),
|
padding: const EdgeInsets.only(left: 10.0, right: 10.0),
|
||||||
child: new Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
children: <Widget>[
|
||||||
children: [
|
Text(S.of(context).mapPrivacy,
|
||||||
new Text(S.of(context).mapPrivacy,
|
style: const TextStyle(
|
||||||
style: new TextStyle(
|
|
||||||
fontStyle: FontStyle.italic,
|
fontStyle: FontStyle.italic,
|
||||||
color: Colors.black38))
|
color: Colors.black38))
|
||||||
])))
|
])))
|
||||||
]),
|
]),
|
||||||
body: !(view.monitoredAreas is List)
|
body: Padding(
|
||||||
? new FiresSpinner()
|
padding: const EdgeInsets.all(10.0),
|
||||||
: new Padding(
|
child: Column(
|
||||||
padding: new EdgeInsets.all(10.0),
|
children: <Widget>[
|
||||||
child: new Column(
|
Padding(
|
||||||
children: [
|
padding: const EdgeInsets.only(
|
||||||
new Padding(
|
top: 8.0, bottom: 8.0),
|
||||||
padding: new EdgeInsets.only(
|
child: Text(S.of(context).inGreenMonitoredAreas),
|
||||||
top: 8.0, bottom: 8.0, left: 0.0, right: 0.0),
|
|
||||||
child: new Text(S.of(context).inGreenMonitoredAreas),
|
|
||||||
),
|
),
|
||||||
new Flexible(
|
Flexible(
|
||||||
child: new FlutterMap(
|
child: FlutterMap(
|
||||||
options: new MapOptions(
|
options: const MapOptions(
|
||||||
initialCenter: new LatLng(53.5775, 3.106111),
|
initialCenter: LatLng(53.5775, 3.106111),
|
||||||
initialZoom: 1.0,
|
initialZoom: 1.0,
|
||||||
),
|
),
|
||||||
children: [
|
children: <Widget>[
|
||||||
new TileLayer(
|
TileLayer(
|
||||||
urlTemplate:
|
urlTemplate:
|
||||||
"https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_all/{z}/{x}/{y}.png",
|
'https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_all/{z}/{x}/{y}.png',
|
||||||
subdomains: ['a', 'b', 'c', 'd'],
|
subdomains: const <String>['a', 'b', 'c', 'd'],
|
||||||
userAgentPackageName:
|
userAgentPackageName:
|
||||||
'com.example.fires_flutter'),
|
'com.example.fires_flutter'),
|
||||||
new CompassMapPluginWidget(),
|
const CompassMapPluginWidget(),
|
||||||
new PolylineLayer(
|
PolylineLayer(
|
||||||
polylines: view.monitoredAreas,
|
polylines: view.monitoredAreas,
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import 'package:objectid/objectid.dart';
|
import 'package:objectid/objectid.dart';
|
||||||
|
|
||||||
ObjectId objectIdFromJson(String json) {
|
ObjectId objectIdFromJson(String json) {
|
||||||
return new ObjectId.fromHexString(
|
return ObjectId.fromHexString(
|
||||||
json.replaceFirst("ObjectId(", "").replaceAll(")", ""));
|
json.replaceFirst('ObjectId(', '').replaceAll(')', ''));
|
||||||
}
|
}
|
||||||
|
|
||||||
objectIdToJson(ObjectId o) {
|
String objectIdToJson(ObjectId o) {
|
||||||
return o.toString();
|
return o.toString();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:fires_flutter/models/yourLocation.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'models/yourLocation.dart';
|
||||||
|
|
||||||
|
|
||||||
/// Open a places dialog for selecting a location.
|
/// Open a places dialog for selecting a location.
|
||||||
/// Currently returns a default location as the google_places_autocomplete package
|
/// Currently returns a default location as the google_places_autocomplete package
|
||||||
|
|
@ -10,9 +11,9 @@ import 'package:flutter/material.dart';
|
||||||
/// TODO: Implement with a modern null-safe places API integration
|
/// TODO: Implement with a modern null-safe places API integration
|
||||||
Future<YourLocation> openPlacesDialog(GlobalKey<ScaffoldState> sc) async {
|
Future<YourLocation> openPlacesDialog(GlobalKey<ScaffoldState> sc) async {
|
||||||
// Show a snackbar informing the user that this feature is not yet available
|
// Show a snackbar informing the user that this feature is not yet available
|
||||||
final messenger = ScaffoldMessenger.of(sc.currentContext!);
|
final ScaffoldMessengerState messenger = ScaffoldMessenger.of(sc.currentContext!);
|
||||||
messenger.showSnackBar(
|
messenger.showSnackBar(
|
||||||
SnackBar(
|
const SnackBar(
|
||||||
content: Text(
|
content: Text(
|
||||||
'Place selection is currently unavailable. Please use manual location entry.'),
|
'Place selection is currently unavailable. Please use manual location entry.'),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,8 @@ import 'generated/i18n.dart';
|
||||||
import 'markdownPage.dart';
|
import 'markdownPage.dart';
|
||||||
|
|
||||||
class PrivacyPage extends MarkdownPage {
|
class PrivacyPage extends MarkdownPage {
|
||||||
static const String routeName = '/privacy';
|
|
||||||
|
|
||||||
PrivacyPage(BuildContext context)
|
PrivacyPage(BuildContext context, {super.key})
|
||||||
: super(
|
: super(
|
||||||
title: S.of(context).privacyPolicy,
|
title: S.of(context).privacyPolicy,
|
||||||
route: routeName,
|
route: routeName,
|
||||||
|
|
@ -16,4 +15,5 @@ class PrivacyPage extends MarkdownPage {
|
||||||
fileName: 'privacy',
|
fileName: 'privacy',
|
||||||
ext: 'md',
|
ext: 'md',
|
||||||
lang: Localizations.localeOf(context).languageCode));
|
lang: Localizations.localeOf(context).languageCode));
|
||||||
|
static const String routeName = '/privacy';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
export 'appActions.dart';
|
export 'appActions.dart';
|
||||||
export 'yourLocationActions.dart';
|
|
||||||
export 'fireMapActions.dart';
|
export 'fireMapActions.dart';
|
||||||
export 'fireNotificationActions.dart';
|
export 'fireNotificationActions.dart';
|
||||||
|
export 'yourLocationActions.dart';
|
||||||
|
|
@ -1,23 +1,25 @@
|
||||||
import 'package:fires_flutter/models/yourLocation.dart';
|
|
||||||
import 'package:fires_flutter/models/fireNotification.dart';
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'package:flutter_map/flutter_map.dart';
|
|
||||||
import 'package:connectivity_plus/connectivity_plus.dart';
|
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||||
|
import 'package:flutter_map/flutter_map.dart';
|
||||||
|
|
||||||
|
import '../models/fireNotification.dart';
|
||||||
|
import '../models/yourLocation.dart';
|
||||||
|
|
||||||
abstract class AppActions {}
|
abstract class AppActions {}
|
||||||
|
|
||||||
class FetchYourLocationsAction extends AppActions {
|
class FetchYourLocationsAction extends AppActions {
|
||||||
Completer<Null>? refreshCallback;
|
|
||||||
|
|
||||||
FetchYourLocationsAction([this.refreshCallback]);
|
FetchYourLocationsAction([this.refreshCallback]);
|
||||||
|
Completer<void>? refreshCallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
class PersistAppStateAction extends AppActions {}
|
class PersistAppStateAction extends AppActions {}
|
||||||
|
|
||||||
class FetchYourLocationsSucceededAction extends AppActions {
|
class FetchYourLocationsSucceededAction extends AppActions {
|
||||||
final List<YourLocation> fetchedYourLocations;
|
|
||||||
|
|
||||||
FetchYourLocationsSucceededAction(this.fetchedYourLocations);
|
FetchYourLocationsSucceededAction(this.fetchedYourLocations);
|
||||||
|
final List<YourLocation> fetchedYourLocations;
|
||||||
}
|
}
|
||||||
|
|
||||||
class FetchFireNotificationsAction extends AppActions {}
|
class FetchFireNotificationsAction extends AppActions {}
|
||||||
|
|
@ -25,45 +27,45 @@ class FetchFireNotificationsAction extends AppActions {}
|
||||||
class FetchMonitoredAreasAction extends AppActions {}
|
class FetchMonitoredAreasAction extends AppActions {}
|
||||||
|
|
||||||
class FetchYourLocationsFailedAction extends AppActions {
|
class FetchYourLocationsFailedAction extends AppActions {
|
||||||
final Exception error;
|
|
||||||
|
|
||||||
FetchYourLocationsFailedAction(this.error);
|
FetchYourLocationsFailedAction(this.error);
|
||||||
|
final Exception error;
|
||||||
}
|
}
|
||||||
|
|
||||||
class OnUserTokenAction extends AppActions {
|
class OnUserTokenAction extends AppActions {
|
||||||
final String token;
|
|
||||||
|
|
||||||
OnUserTokenAction(this.token);
|
OnUserTokenAction(this.token);
|
||||||
|
final String token;
|
||||||
}
|
}
|
||||||
|
|
||||||
class OnUserCreatedAction extends AppActions {
|
class OnUserCreatedAction extends AppActions {
|
||||||
final String userId;
|
|
||||||
|
|
||||||
OnUserCreatedAction(this.userId);
|
OnUserCreatedAction(this.userId);
|
||||||
|
final String userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
class OnUserLangAction extends AppActions {
|
class OnUserLangAction extends AppActions {
|
||||||
final String lang;
|
|
||||||
|
|
||||||
OnUserLangAction(this.lang);
|
OnUserLangAction(this.lang);
|
||||||
|
final String lang;
|
||||||
}
|
}
|
||||||
|
|
||||||
class FetchFireNotificationsSucceededAction extends AppActions {
|
class FetchFireNotificationsSucceededAction extends AppActions {
|
||||||
final List<FireNotification> fetchedFireNotifications;
|
|
||||||
final int unreadCount;
|
|
||||||
|
|
||||||
FetchFireNotificationsSucceededAction(
|
FetchFireNotificationsSucceededAction(
|
||||||
this.fetchedFireNotifications, this.unreadCount);
|
this.fetchedFireNotifications, this.unreadCount);
|
||||||
|
final List<FireNotification> fetchedFireNotifications;
|
||||||
|
final int unreadCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
class FetchMonitoredAreasSucceededAction extends AppActions {
|
class FetchMonitoredAreasSucceededAction extends AppActions {
|
||||||
final List<Polyline> monitoredAreas;
|
|
||||||
|
|
||||||
FetchMonitoredAreasSucceededAction(this.monitoredAreas);
|
FetchMonitoredAreasSucceededAction(this.monitoredAreas);
|
||||||
|
final List<Polyline> monitoredAreas;
|
||||||
}
|
}
|
||||||
|
|
||||||
class OnConnectivityChanged extends AppActions {
|
class OnConnectivityChanged extends AppActions {
|
||||||
final List<ConnectivityResult> connectionStatus;
|
|
||||||
|
|
||||||
OnConnectivityChanged(this.connectionStatus);
|
OnConnectivityChanged(this.connectionStatus);
|
||||||
|
final List<ConnectivityResult> connectionStatus;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import 'actions.dart';
|
|
||||||
import '../models/appState.dart';
|
import '../models/appState.dart';
|
||||||
|
import 'actions.dart';
|
||||||
|
|
||||||
AppState appReducer(AppState state, action) {
|
AppState appReducer(AppState state, action) {
|
||||||
if (action is FetchYourLocationsSucceededAction) {
|
if (action is FetchYourLocationsSucceededAction) {
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,15 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:fires_flutter/models/fireNotification.dart';
|
import 'package:flutter_map/src/layer/polyline_layer.dart';
|
||||||
import 'package:fires_flutter/models/yourLocation.dart';
|
|
||||||
import 'package:get_it/get_it.dart';
|
import 'package:get_it/get_it.dart';
|
||||||
import 'package:objectid/objectid.dart';
|
import 'package:objectid/objectid.dart';
|
||||||
import 'package:redux/redux.dart';
|
import 'package:redux/redux.dart';
|
||||||
|
|
||||||
import '../models/appState.dart';
|
import '../models/appState.dart';
|
||||||
|
import '../models/fireNotification.dart';
|
||||||
import '../models/fireNotificationsPersist.dart';
|
import '../models/fireNotificationsPersist.dart';
|
||||||
import '../models/firesApi.dart';
|
import '../models/firesApi.dart';
|
||||||
|
import '../models/yourLocation.dart';
|
||||||
import '../models/yourLocationPersist.dart';
|
import '../models/yourLocationPersist.dart';
|
||||||
import '../objectIdUtils.dart';
|
import '../objectIdUtils.dart';
|
||||||
import 'actions.dart';
|
import 'actions.dart';
|
||||||
|
|
@ -55,20 +56,20 @@ void fetchDataMiddleware(Store<AppState> store, action, NextDispatcher next) {
|
||||||
|
|
||||||
if (action is AddYourLocationAction) {
|
if (action is AddYourLocationAction) {
|
||||||
if (action.loc.subscribed) {
|
if (action.loc.subscribed) {
|
||||||
subscribeViaApi(store, action.loc, (sub) {
|
subscribeViaApi(store, action.loc, (YourLocation sub) {
|
||||||
store.dispatch(new AddedYourLocationAction(sub));
|
store.dispatch(AddedYourLocationAction(sub));
|
||||||
persistYourLocations(store.state.yourLocations);
|
persistYourLocations(store.state.yourLocations);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// No subscribed (only local)
|
// No subscribed (only local)
|
||||||
store.dispatch(new AddedYourLocationAction(action.loc));
|
store.dispatch(AddedYourLocationAction(action.loc));
|
||||||
persistYourLocations(store.state.yourLocations);
|
persistYourLocations(store.state.yourLocations);
|
||||||
}
|
}
|
||||||
getFiresStatsInLocation(store, action.loc);
|
getFiresStatsInLocation(store, action.loc);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action is DeleteYourLocationAction) {
|
if (action is DeleteYourLocationAction) {
|
||||||
store.dispatch(new DeletedYourLocationAction(action.loc.id));
|
store.dispatch(DeletedYourLocationAction(action.loc.id));
|
||||||
if (action.loc.subscribed) {
|
if (action.loc.subscribed) {
|
||||||
unsubsViaApi(store, action.loc.id, () {
|
unsubsViaApi(store, action.loc.id, () {
|
||||||
persistYourLocations(store.state.yourLocations);
|
persistYourLocations(store.state.yourLocations);
|
||||||
|
|
@ -79,17 +80,17 @@ void fetchDataMiddleware(Store<AppState> store, action, NextDispatcher next) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action is DeleteFireNotificationAction) {
|
if (action is DeleteFireNotificationAction) {
|
||||||
store.dispatch(new DeletedFireNotificationAction(action.notif));
|
store.dispatch(DeletedFireNotificationAction(action.notif));
|
||||||
persistFireNotifications(store.state.fireNotifications);
|
persistFireNotifications(store.state.fireNotifications);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action is DeleteAllFireNotificationAction) {
|
if (action is DeleteAllFireNotificationAction) {
|
||||||
store.dispatch(new DeletedAllFireNotificationAction());
|
store.dispatch(DeletedAllFireNotificationAction());
|
||||||
persistFireNotifications(store.state.fireNotifications);
|
persistFireNotifications(store.state.fireNotifications);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action is AddFireNotificationAction) {
|
if (action is AddFireNotificationAction) {
|
||||||
store.dispatch(new AddedFireNotificationAction(action.notif));
|
store.dispatch(AddedFireNotificationAction(action.notif));
|
||||||
persistFireNotifications(store.state.fireNotifications);
|
persistFireNotifications(store.state.fireNotifications);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -104,28 +105,28 @@ void fetchDataMiddleware(Store<AppState> store, action, NextDispatcher next) {
|
||||||
if (action is UpdateYourLocationAction) {
|
if (action is UpdateYourLocationAction) {
|
||||||
if (action.loc.subscribed)
|
if (action.loc.subscribed)
|
||||||
debounceLocationUpdate(
|
debounceLocationUpdate(
|
||||||
Duration(seconds: 2),
|
const Duration(seconds: 2),
|
||||||
() => api
|
() => api
|
||||||
.getFiresInLocation(
|
.getFiresInLocation(
|
||||||
state: store.state,
|
state: store.state,
|
||||||
lat: action.loc.lat,
|
lat: action.loc.lat,
|
||||||
lon: action.loc.lon,
|
lon: action.loc.lon,
|
||||||
distance: action.loc.distance)
|
distance: action.loc.distance)
|
||||||
.then((result) => store.dispatch(result)));
|
.then((UpdateFireMapStatsAction result) => store.dispatch(result)));
|
||||||
store.dispatch(new UpdatedYourLocationAction(action.loc));
|
store.dispatch(UpdatedYourLocationAction(action.loc));
|
||||||
persistYourLocations(store.state.yourLocations);
|
persistYourLocations(store.state.yourLocations);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action is SubscribeConfirmAction) {
|
if (action is SubscribeConfirmAction) {
|
||||||
subscribeViaApi(store, action.loc, (sub) {
|
subscribeViaApi(store, action.loc, (YourLocation sub) {
|
||||||
store.dispatch(new UpdateYourLocationAction(action.loc));
|
store.dispatch(UpdateYourLocationAction(action.loc));
|
||||||
persistYourLocations(store.state.yourLocations);
|
persistYourLocations(store.state.yourLocations);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action is UnSubscribeAction) {
|
if (action is UnSubscribeAction) {
|
||||||
unsubsViaApi(store, action.loc.id, () {
|
unsubsViaApi(store, action.loc.id, () {
|
||||||
store.dispatch(new UpdateYourLocationAction(action.loc));
|
store.dispatch(UpdateYourLocationAction(action.loc));
|
||||||
persistYourLocations(store.state.yourLocations);
|
persistYourLocations(store.state.yourLocations);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -133,21 +134,21 @@ void fetchDataMiddleware(Store<AppState> store, action, NextDispatcher next) {
|
||||||
if (action is ToggleSubscriptionAction) {
|
if (action is ToggleSubscriptionAction) {
|
||||||
if (action.loc.subscribed) {
|
if (action.loc.subscribed) {
|
||||||
subscribeViaApi(store, action.loc,
|
subscribeViaApi(store, action.loc,
|
||||||
(sub) => store.dispatch(new ToggledSubscriptionAction(sub)));
|
(YourLocation sub) => store.dispatch(ToggledSubscriptionAction(sub)));
|
||||||
} else {
|
} else {
|
||||||
unsubsViaApi(store, action.loc.id,
|
unsubsViaApi(store, action.loc.id,
|
||||||
() => store.dispatch(new ToggledSubscriptionAction(action.loc)));
|
() => store.dispatch(ToggledSubscriptionAction(action.loc)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action is ReadFireNotificationAction) {
|
if (action is ReadFireNotificationAction) {
|
||||||
store.dispatch(new ReadedFireNotificationAction(action.notif));
|
store.dispatch(ReadedFireNotificationAction(action.notif));
|
||||||
persistFireNotifications(store.state.fireNotifications);
|
persistFireNotifications(store.state.fireNotifications);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action is FetchYourLocationsAction) {
|
if (action is FetchYourLocationsAction) {
|
||||||
// Use the api to fetch the YourLocations
|
// Use the api to fetch the YourLocations
|
||||||
loadYourLocations().then((localLocations) {
|
loadYourLocations().then((List<YourLocation> localLocations) {
|
||||||
api
|
api
|
||||||
.fetchYourLocations(store.state)
|
.fetchYourLocations(store.state)
|
||||||
.then((List<YourLocation> subscribedLocations) {
|
.then((List<YourLocation> subscribedLocations) {
|
||||||
|
|
@ -155,61 +156,63 @@ void fetchDataMiddleware(Store<AppState> store, action, NextDispatcher next) {
|
||||||
// Our reducer will then update the State using these YourLocations.
|
// Our reducer will then update the State using these YourLocations.
|
||||||
print('Subscribed to: ${subscribedLocations.length}');
|
print('Subscribed to: ${subscribedLocations.length}');
|
||||||
// unsubscribe all locally to sync the subs state
|
// unsubscribe all locally to sync the subs state
|
||||||
localLocations.forEach((location) => location.subscribed = false);
|
for (final YourLocation location in localLocations) {
|
||||||
|
location.subscribed = false;
|
||||||
|
}
|
||||||
print('Local persisted: ${localLocations.length}');
|
print('Local persisted: ${localLocations.length}');
|
||||||
subscribedLocations.forEach((subsLoc) {
|
for (final YourLocation subsLoc in subscribedLocations) {
|
||||||
var locSubs = localLocations.firstWhere(
|
final YourLocation locSubs = localLocations.firstWhere(
|
||||||
(localLocation) => localLocation.id == subsLoc.id, orElse: () {
|
(YourLocation localLocation) => localLocation.id == subsLoc.id, orElse: () {
|
||||||
localLocations.add(subsLoc);
|
localLocations.add(subsLoc);
|
||||||
return subsLoc;
|
return subsLoc;
|
||||||
});
|
});
|
||||||
locSubs.subscribed = true;
|
locSubs.subscribed = true;
|
||||||
});
|
}
|
||||||
|
|
||||||
store.dispatch(new FetchYourLocationsSucceededAction(localLocations));
|
store.dispatch(FetchYourLocationsSucceededAction(localLocations));
|
||||||
persistYourLocations(localLocations);
|
persistYourLocations(localLocations);
|
||||||
|
|
||||||
localLocations.forEach((yl) {
|
for (final YourLocation yl in localLocations) {
|
||||||
api
|
api
|
||||||
.getFiresInLocation(
|
.getFiresInLocation(
|
||||||
state: store.state,
|
state: store.state,
|
||||||
lat: yl.lat,
|
lat: yl.lat,
|
||||||
lon: yl.lon,
|
lon: yl.lon,
|
||||||
distance: yl.distance)
|
distance: yl.distance)
|
||||||
.then((value) {
|
.then((UpdateFireMapStatsAction value) {
|
||||||
yl.currentNumFires = value.numFires;
|
yl.currentNumFires = value.numFires;
|
||||||
store.dispatch(new UpdateYourLocationAction(yl));
|
store.dispatch(UpdateYourLocationAction(yl));
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
final Completer<Null>? completer = action.refreshCallback;
|
final Completer<void>? completer = action.refreshCallback;
|
||||||
completer?.complete(null);
|
completer?.complete(null);
|
||||||
});
|
});
|
||||||
}).catchError((Exception onError) {
|
}).catchError((Exception onError) {
|
||||||
// If it fails, dispatch a failure action. The reducer will
|
// If it fails, dispatch a failure action. The reducer will
|
||||||
// update the state with the error.
|
// update the state with the error.
|
||||||
store.dispatch(new FetchYourLocationsFailedAction(onError));
|
store.dispatch(FetchYourLocationsFailedAction(onError));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action is FetchFireNotificationsAction) {
|
if (action is FetchFireNotificationsAction) {
|
||||||
loadFireNotifications().then((fireNotifications) {
|
loadFireNotifications().then((List<FireNotification> fireNotifications) {
|
||||||
int unread = 0;
|
int unread = 0;
|
||||||
for (FireNotification notif in fireNotifications) {
|
for (final FireNotification notif in fireNotifications) {
|
||||||
if (!notif.read) {
|
if (!notif.read) {
|
||||||
unread++;
|
unread++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
store.dispatch(
|
store.dispatch(
|
||||||
new FetchFireNotificationsSucceededAction(fireNotifications, unread));
|
FetchFireNotificationsSucceededAction(fireNotifications, unread));
|
||||||
persistFireNotifications(fireNotifications);
|
persistFireNotifications(fireNotifications);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action is FetchMonitoredAreasAction) {
|
if (action is FetchMonitoredAreasAction) {
|
||||||
api.getMonitoredAreas(state: store.state).then((result) {
|
api.getMonitoredAreas(state: store.state).then((List<Polyline> result) {
|
||||||
// store.dispatch()
|
// store.dispatch()
|
||||||
store.dispatch(new FetchMonitoredAreasSucceededAction(result));
|
store.dispatch(FetchMonitoredAreasSucceededAction(result));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -217,11 +220,11 @@ void fetchDataMiddleware(Store<AppState> store, action, NextDispatcher next) {
|
||||||
api
|
api
|
||||||
.markFalsePositive(store.state, store.state.user.token,
|
.markFalsePositive(store.state, store.state.user.token,
|
||||||
action.notif.sealed, action.type)
|
action.notif.sealed, action.type)
|
||||||
.then((result) {
|
.then((bool result) {
|
||||||
if (result) {
|
if (result) {
|
||||||
// Not necessary
|
// Not necessary
|
||||||
getFiresStatsInFire(store, action.notif);
|
getFiresStatsInFire(store, action.notif);
|
||||||
store.dispatch(new UpdatedFireNotificationAction(action.notif));
|
store.dispatch(UpdatedFireNotificationAction(action.notif));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -237,10 +240,10 @@ void getFiresStatsInLocation(Store<AppState> store, YourLocation loc) {
|
||||||
lat: loc.lat,
|
lat: loc.lat,
|
||||||
lon: loc.lon,
|
lon: loc.lon,
|
||||||
distance: loc.distance)
|
distance: loc.distance)
|
||||||
.then((result) {
|
.then((UpdateFireMapStatsAction result) {
|
||||||
store.dispatch(result);
|
store.dispatch(result);
|
||||||
loc.currentNumFires = result.numFires;
|
loc.currentNumFires = result.numFires;
|
||||||
store.dispatch(new UpdateYourLocationAction(loc));
|
store.dispatch(UpdateYourLocationAction(loc));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -251,11 +254,11 @@ void getFiresStatsInFire(Store<AppState> store, FireNotification notif) {
|
||||||
lat: notif.lat,
|
lat: notif.lat,
|
||||||
lon: notif.lon,
|
lon: notif.lon,
|
||||||
distance: 1) // FalsePositive/server/publications.js
|
distance: 1) // FalsePositive/server/publications.js
|
||||||
.then((result) => store.dispatch(result));
|
.then((UpdateFireMapStatsAction result) => store.dispatch(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
void unsubsViaApi(Store<AppState> store, ObjectId id, onUnsubs) {
|
void unsubsViaApi(Store<AppState> store, ObjectId id, onUnsubs) {
|
||||||
api.unsubscribe(store.state, id.hexString).then((res) {
|
api.unsubscribe(store.state, id.hexString).then((bool res) {
|
||||||
onUnsubs();
|
onUnsubs();
|
||||||
persistYourLocations(store.state.yourLocations);
|
persistYourLocations(store.state.yourLocations);
|
||||||
});
|
});
|
||||||
|
|
@ -263,8 +266,8 @@ void unsubsViaApi(Store<AppState> store, ObjectId id, onUnsubs) {
|
||||||
|
|
||||||
void subscribeViaApi(
|
void subscribeViaApi(
|
||||||
Store<AppState> store, YourLocation loc, Function(YourLocation) onSubs) {
|
Store<AppState> store, YourLocation loc, Function(YourLocation) onSubs) {
|
||||||
api.subscribe(store.state, loc).then((subsId) {
|
api.subscribe(store.state, loc).then((String subsId) {
|
||||||
YourLocation sub = loc;
|
final YourLocation sub = loc;
|
||||||
// if (loc.id != subsId) {
|
// if (loc.id != subsId) {
|
||||||
sub.id = objectIdFromJson(subsId);
|
sub.id = objectIdFromJson(subsId);
|
||||||
// }
|
// }
|
||||||
|
|
@ -274,12 +277,12 @@ void subscribeViaApi(
|
||||||
}
|
}
|
||||||
|
|
||||||
void createUser(Store<AppState> store, String lang, String token) {
|
void createUser(Store<AppState> store, String lang, String token) {
|
||||||
assert(token != null, "User lang is null");
|
assert(token != null, 'User lang is null');
|
||||||
assert(token != null, "User mobile token is null");
|
assert(token != null, 'User mobile token is null');
|
||||||
api.createUser(store.state, token, lang).then((userId) {
|
api.createUser(store.state, token, lang).then((String userId) {
|
||||||
store.dispatch(new OnUserCreatedAction(userId));
|
store.dispatch(OnUserCreatedAction(userId));
|
||||||
store.dispatch(new FetchYourLocationsAction());
|
store.dispatch(FetchYourLocationsAction());
|
||||||
store.dispatch(new FetchFireNotificationsAction());
|
store.dispatch(FetchFireNotificationsAction());
|
||||||
store.dispatch(new FetchMonitoredAreasAction());
|
store.dispatch(FetchMonitoredAreasAction());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,61 +1,61 @@
|
||||||
import 'package:fires_flutter/models/yourLocation.dart';
|
import '../models/fireMapState.dart';
|
||||||
import 'package:fires_flutter/models/fireNotification.dart';
|
import '../models/fireNotification.dart';
|
||||||
import 'package:fires_flutter/models/fireMapState.dart';
|
import '../models/yourLocation.dart';
|
||||||
|
|
||||||
abstract class FiresMapActions {}
|
abstract class FiresMapActions {}
|
||||||
|
|
||||||
class UpdateYourLocationMapAction extends FiresMapActions {
|
class UpdateYourLocationMapAction extends FiresMapActions {
|
||||||
final YourLocation loc;
|
|
||||||
|
|
||||||
UpdateYourLocationMapAction(
|
UpdateYourLocationMapAction(
|
||||||
this.loc,
|
this.loc,
|
||||||
);
|
);
|
||||||
|
final YourLocation loc;
|
||||||
}
|
}
|
||||||
|
|
||||||
class UpdateFireMapStatsAction extends FiresMapActions {
|
class UpdateFireMapStatsAction extends FiresMapActions {
|
||||||
int numFires;
|
|
||||||
List<dynamic> fires = [];
|
|
||||||
List<dynamic> falsePos = [];
|
|
||||||
List<dynamic> industries = [];
|
|
||||||
|
|
||||||
UpdateFireMapStatsAction(
|
UpdateFireMapStatsAction(
|
||||||
{required this.numFires,
|
{required this.numFires,
|
||||||
required this.fires,
|
required this.fires,
|
||||||
required this.falsePos,
|
required this.falsePos,
|
||||||
required this.industries});
|
required this.industries});
|
||||||
|
int numFires;
|
||||||
|
List<dynamic> fires = <dynamic>[];
|
||||||
|
List<dynamic> falsePos = <dynamic>[];
|
||||||
|
List<dynamic> industries = <dynamic>[];
|
||||||
}
|
}
|
||||||
|
|
||||||
class ShowYourLocationMapAction extends FiresMapActions {
|
class ShowYourLocationMapAction extends FiresMapActions {
|
||||||
YourLocation loc;
|
|
||||||
|
|
||||||
ShowYourLocationMapAction(this.loc);
|
ShowYourLocationMapAction(this.loc);
|
||||||
|
YourLocation loc;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ShowFireNotificationMapAction extends FiresMapActions {
|
class ShowFireNotificationMapAction extends FiresMapActions {
|
||||||
FireNotification notif;
|
|
||||||
|
|
||||||
ShowFireNotificationMapAction(this.notif);
|
ShowFireNotificationMapAction(this.notif);
|
||||||
|
FireNotification notif;
|
||||||
}
|
}
|
||||||
|
|
||||||
class EditYourLocationAction extends FiresMapActions {
|
class EditYourLocationAction extends FiresMapActions {
|
||||||
YourLocation loc;
|
|
||||||
|
|
||||||
EditYourLocationAction(this.loc);
|
EditYourLocationAction(this.loc);
|
||||||
|
YourLocation loc;
|
||||||
}
|
}
|
||||||
|
|
||||||
class EditConfirmYourLocationAction extends FiresMapActions {
|
class EditConfirmYourLocationAction extends FiresMapActions {
|
||||||
YourLocation loc;
|
|
||||||
|
|
||||||
EditConfirmYourLocationAction(this.loc);
|
EditConfirmYourLocationAction(this.loc);
|
||||||
|
YourLocation loc;
|
||||||
}
|
}
|
||||||
|
|
||||||
class EditCancelYourLocationAction extends FiresMapActions {
|
class EditCancelYourLocationAction extends FiresMapActions {
|
||||||
YourLocation loc;
|
|
||||||
EditCancelYourLocationAction(this.loc);
|
EditCancelYourLocationAction(this.loc);
|
||||||
|
YourLocation loc;
|
||||||
}
|
}
|
||||||
|
|
||||||
class SelectMapLayerAction extends FiresMapActions {
|
class SelectMapLayerAction extends FiresMapActions {
|
||||||
final FireMapLayer layer;
|
|
||||||
|
|
||||||
SelectMapLayerAction(this.layer);
|
SelectMapLayerAction(this.layer);
|
||||||
|
final FireMapLayer layer;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,29 @@
|
||||||
import 'package:redux/redux.dart';
|
|
||||||
import 'package:fires_flutter/models/yourLocation.dart';
|
|
||||||
import 'package:objectid/objectid.dart';
|
import 'package:objectid/objectid.dart';
|
||||||
|
import 'package:redux/redux.dart';
|
||||||
|
|
||||||
import '../models/fireMapState.dart';
|
import '../models/fireMapState.dart';
|
||||||
|
import '../models/yourLocation.dart';
|
||||||
import 'actions.dart';
|
import 'actions.dart';
|
||||||
|
|
||||||
final fireMapReducer = combineReducers<FireMapState>([
|
final Reducer<FireMapState> fireMapReducer = combineReducers<FireMapState>(<Reducer<FireMapState>>[
|
||||||
new TypedReducer<FireMapState, ShowYourLocationMapAction>(
|
TypedReducer<FireMapState, ShowYourLocationMapAction>(
|
||||||
_showYourLocationMap),
|
_showYourLocationMap),
|
||||||
new TypedReducer<FireMapState, ShowFireNotificationMapAction>(
|
TypedReducer<FireMapState, ShowFireNotificationMapAction>(
|
||||||
_showFireNotificationMap),
|
_showFireNotificationMap),
|
||||||
new TypedReducer<FireMapState, UpdateFireMapStatsAction>(
|
TypedReducer<FireMapState, UpdateFireMapStatsAction>(
|
||||||
_updateYourLocationMapStats),
|
_updateYourLocationMapStats),
|
||||||
new TypedReducer<FireMapState, SubscribeAction>(_subscribeYourLocationMap),
|
TypedReducer<FireMapState, SubscribeAction>(_subscribeYourLocationMap),
|
||||||
new TypedReducer<FireMapState, SubscribeConfirmAction>(
|
TypedReducer<FireMapState, SubscribeConfirmAction>(
|
||||||
_subscribeConfirmYourLocationMap),
|
_subscribeConfirmYourLocationMap),
|
||||||
new TypedReducer<FireMapState, UnSubscribeAction>(
|
TypedReducer<FireMapState, UnSubscribeAction>(
|
||||||
_unsubscribeYourLocationMap),
|
_unsubscribeYourLocationMap),
|
||||||
new TypedReducer<FireMapState, EditYourLocationAction>(_editYourLocationMap),
|
TypedReducer<FireMapState, EditYourLocationAction>(_editYourLocationMap),
|
||||||
new TypedReducer<FireMapState, EditConfirmYourLocationAction>(
|
TypedReducer<FireMapState, EditConfirmYourLocationAction>(
|
||||||
_editConfirmYourLocationMap),
|
_editConfirmYourLocationMap),
|
||||||
new TypedReducer<FireMapState, EditCancelYourLocationAction>(
|
TypedReducer<FireMapState, EditCancelYourLocationAction>(
|
||||||
_editCancelYourLocationMap),
|
_editCancelYourLocationMap),
|
||||||
new TypedReducer<FireMapState, SelectMapLayerAction>(_selectMapLayer),
|
TypedReducer<FireMapState, SelectMapLayerAction>(_selectMapLayer),
|
||||||
new TypedReducer<FireMapState, UpdateYourLocationMapAction>(
|
TypedReducer<FireMapState, UpdateYourLocationMapAction>(
|
||||||
_updateYourLocationMap)
|
_updateYourLocationMap)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -46,14 +47,13 @@ FireMapState _showYourLocationMap(
|
||||||
status: action.loc.subscribed
|
status: action.loc.subscribed
|
||||||
? FireMapStatus.unsubscribe
|
? FireMapStatus.unsubscribe
|
||||||
: FireMapStatus.view,
|
: FireMapStatus.view,
|
||||||
yourLocation: action.loc,
|
yourLocation: action.loc);
|
||||||
fireNotication: null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FireMapState _showFireNotificationMap(
|
FireMapState _showFireNotificationMap(
|
||||||
FireMapState state, ShowFireNotificationMapAction action) {
|
FireMapState state, ShowFireNotificationMapAction action) {
|
||||||
// TODO: use here you real location?
|
// TODO: use here you real location?
|
||||||
YourLocation pseudoLoc = new YourLocation(
|
final YourLocation pseudoLoc = YourLocation(
|
||||||
id: ObjectId(),
|
id: ObjectId(),
|
||||||
lat: action.notif.lat,
|
lat: action.notif.lat,
|
||||||
lon: action.notif.lon,
|
lon: action.notif.lon,
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
import 'package:fires_flutter/models/fireNotification.dart';
|
|
||||||
import '../models/falsePositiveTypes.dart';
|
import '../models/falsePositiveTypes.dart';
|
||||||
|
import '../models/fireNotification.dart';
|
||||||
|
|
||||||
abstract class FireNotificationActions {}
|
abstract class FireNotificationActions {}
|
||||||
|
|
||||||
class DeleteFireNotificationAction extends FireNotificationActions {
|
class DeleteFireNotificationAction extends FireNotificationActions {
|
||||||
final FireNotification notif;
|
|
||||||
|
|
||||||
DeleteFireNotificationAction(this.notif);
|
DeleteFireNotificationAction(this.notif);
|
||||||
|
final FireNotification notif;
|
||||||
}
|
}
|
||||||
|
|
||||||
class DeleteAllFireNotificationAction extends FireNotificationActions {
|
class DeleteAllFireNotificationAction extends FireNotificationActions {
|
||||||
|
|
@ -14,15 +14,15 @@ class DeleteAllFireNotificationAction extends FireNotificationActions {
|
||||||
}
|
}
|
||||||
|
|
||||||
class AddFireNotificationAction extends FireNotificationActions {
|
class AddFireNotificationAction extends FireNotificationActions {
|
||||||
final FireNotification notif;
|
|
||||||
|
|
||||||
AddFireNotificationAction(this.notif);
|
AddFireNotificationAction(this.notif);
|
||||||
|
final FireNotification notif;
|
||||||
}
|
}
|
||||||
|
|
||||||
class DeletedFireNotificationAction extends FireNotificationActions {
|
class DeletedFireNotificationAction extends FireNotificationActions {
|
||||||
final FireNotification notif;
|
|
||||||
|
|
||||||
DeletedFireNotificationAction(this.notif);
|
DeletedFireNotificationAction(this.notif);
|
||||||
|
final FireNotification notif;
|
||||||
}
|
}
|
||||||
|
|
||||||
class DeletedAllFireNotificationAction extends FireNotificationActions {
|
class DeletedAllFireNotificationAction extends FireNotificationActions {
|
||||||
|
|
@ -30,32 +30,32 @@ class DeletedAllFireNotificationAction extends FireNotificationActions {
|
||||||
}
|
}
|
||||||
|
|
||||||
class AddedFireNotificationAction extends FireNotificationActions {
|
class AddedFireNotificationAction extends FireNotificationActions {
|
||||||
final FireNotification notif;
|
|
||||||
|
|
||||||
AddedFireNotificationAction(this.notif);
|
AddedFireNotificationAction(this.notif);
|
||||||
|
final FireNotification notif;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ReadFireNotificationAction extends FireNotificationActions {
|
class ReadFireNotificationAction extends FireNotificationActions {
|
||||||
final FireNotification notif;
|
|
||||||
|
|
||||||
ReadFireNotificationAction(this.notif);
|
ReadFireNotificationAction(this.notif);
|
||||||
|
final FireNotification notif;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ReadedFireNotificationAction extends FireNotificationActions {
|
class ReadedFireNotificationAction extends FireNotificationActions {
|
||||||
final FireNotification notif;
|
|
||||||
|
|
||||||
ReadedFireNotificationAction(this.notif);
|
ReadedFireNotificationAction(this.notif);
|
||||||
|
final FireNotification notif;
|
||||||
}
|
}
|
||||||
|
|
||||||
class MarkFireAsFalsePositiveAction extends FireNotificationActions {
|
class MarkFireAsFalsePositiveAction extends FireNotificationActions {
|
||||||
final FireNotification notif;
|
|
||||||
final FalsePositiveType type;
|
|
||||||
|
|
||||||
MarkFireAsFalsePositiveAction(this.notif, this.type);
|
MarkFireAsFalsePositiveAction(this.notif, this.type);
|
||||||
|
final FireNotification notif;
|
||||||
|
final FalsePositiveType type;
|
||||||
}
|
}
|
||||||
|
|
||||||
class UpdatedFireNotificationAction extends FireNotificationActions {
|
class UpdatedFireNotificationAction extends FireNotificationActions {
|
||||||
final FireNotification notif;
|
|
||||||
|
|
||||||
UpdatedFireNotificationAction(this.notif);
|
UpdatedFireNotificationAction(this.notif);
|
||||||
|
final FireNotification notif;
|
||||||
}
|
}
|
||||||
|
|
@ -1,44 +1,44 @@
|
||||||
import 'package:fires_flutter/models/fireNotification.dart';
|
|
||||||
import 'package:redux/redux.dart';
|
import 'package:redux/redux.dart';
|
||||||
|
|
||||||
|
import '../models/fireNotification.dart';
|
||||||
import 'actions.dart';
|
import 'actions.dart';
|
||||||
|
|
||||||
final fireNotificationReducer = combineReducers<List<FireNotification>>([
|
final Reducer<List<FireNotification>> fireNotificationReducer = combineReducers<List<FireNotification>>(<Reducer<List<FireNotification>>>[
|
||||||
new TypedReducer<List<FireNotification>, AddedFireNotificationAction>(
|
TypedReducer<List<FireNotification>, AddedFireNotificationAction>(
|
||||||
_addedFireNotification),
|
_addedFireNotification),
|
||||||
new TypedReducer<List<FireNotification>, DeletedFireNotificationAction>(
|
TypedReducer<List<FireNotification>, DeletedFireNotificationAction>(
|
||||||
_deletedFireNotification),
|
_deletedFireNotification),
|
||||||
new TypedReducer<List<FireNotification>, UpdatedFireNotificationAction>(
|
TypedReducer<List<FireNotification>, UpdatedFireNotificationAction>(
|
||||||
_updatedFireNotification),
|
_updatedFireNotification),
|
||||||
new TypedReducer<List<FireNotification>, DeletedAllFireNotificationAction>(
|
TypedReducer<List<FireNotification>, DeletedAllFireNotificationAction>(
|
||||||
_deletedAllFireNotifications),
|
_deletedAllFireNotifications),
|
||||||
new TypedReducer<List<FireNotification>, ReadedFireNotificationAction>(
|
TypedReducer<List<FireNotification>, ReadedFireNotificationAction>(
|
||||||
_readedFireNotification)
|
_readedFireNotification)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
List<FireNotification> _addedFireNotification(
|
List<FireNotification> _addedFireNotification(
|
||||||
List<FireNotification> notifications, AddedFireNotificationAction action) {
|
List<FireNotification> notifications, AddedFireNotificationAction action) {
|
||||||
return new List.from(notifications)..insert(0, action.notif);
|
return List.from(notifications)..insert(0, action.notif);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<FireNotification> _deletedFireNotification(
|
List<FireNotification> _deletedFireNotification(
|
||||||
List<FireNotification> notifications,
|
List<FireNotification> notifications,
|
||||||
DeletedFireNotificationAction action) {
|
DeletedFireNotificationAction action) {
|
||||||
return new List.from(notifications)..remove(action.notif);
|
return List.from(notifications)..remove(action.notif);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<FireNotification> _updatedFireNotification(
|
List<FireNotification> _updatedFireNotification(
|
||||||
List<FireNotification> notifications,
|
List<FireNotification> notifications,
|
||||||
UpdatedFireNotificationAction action) {
|
UpdatedFireNotificationAction action) {
|
||||||
return notifications
|
return notifications
|
||||||
.map((notif) => notif.id == action.notif.id ? action.notif : notif)
|
.map((FireNotification notif) => notif.id == action.notif.id ? action.notif : notif)
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
List<FireNotification> _readedFireNotification(
|
List<FireNotification> _readedFireNotification(
|
||||||
List<FireNotification> notifications, ReadedFireNotificationAction action) {
|
List<FireNotification> notifications, ReadedFireNotificationAction action) {
|
||||||
return notifications
|
return notifications
|
||||||
.map((yourLocation) =>
|
.map((FireNotification yourLocation) =>
|
||||||
yourLocation.id == action.notif.id ? action.notif : yourLocation)
|
yourLocation.id == action.notif.id ? action.notif : yourLocation)
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
@ -46,5 +46,5 @@ List<FireNotification> _readedFireNotification(
|
||||||
List<FireNotification> _deletedAllFireNotifications(
|
List<FireNotification> _deletedAllFireNotifications(
|
||||||
List<FireNotification> notifications,
|
List<FireNotification> notifications,
|
||||||
DeletedAllFireNotificationAction action) {
|
DeletedAllFireNotificationAction action) {
|
||||||
return [];
|
return <FireNotification>[];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import 'yourLocationsReducer.dart';
|
||||||
|
|
||||||
// We create the State reducer by combining many smaller reducers into one!
|
// We create the State reducer by combining many smaller reducers into one!
|
||||||
AppState appStateReducer(AppState prevState, action) {
|
AppState appStateReducer(AppState prevState, action) {
|
||||||
var state = appReducer(prevState, action);
|
final AppState state = appReducer(prevState, action);
|
||||||
return AppState(
|
return AppState(
|
||||||
yourLocations: yourLocationsReducer(state.yourLocations, action),
|
yourLocations: yourLocationsReducer(state.yourLocations, action),
|
||||||
fireNotifications:
|
fireNotifications:
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import 'actions.dart';
|
|
||||||
import '../models/user.dart';
|
import '../models/user.dart';
|
||||||
|
import 'actions.dart';
|
||||||
|
|
||||||
User userReducer(User user, action) {
|
User userReducer(User user, action) {
|
||||||
if (action is OnUserCreatedAction) return user.copyWith(userId: action.userId);
|
if (action is OnUserCreatedAction) return user.copyWith(userId: action.userId);
|
||||||
|
|
|
||||||
|
|
@ -1,66 +1,67 @@
|
||||||
import 'package:fires_flutter/models/yourLocation.dart';
|
|
||||||
import 'package:objectid/objectid.dart';
|
import 'package:objectid/objectid.dart';
|
||||||
|
|
||||||
|
import '../models/yourLocation.dart';
|
||||||
|
|
||||||
abstract class YourLocationActions {}
|
abstract class YourLocationActions {}
|
||||||
|
|
||||||
class AddYourLocationAction extends YourLocationActions {
|
class AddYourLocationAction extends YourLocationActions {
|
||||||
YourLocation loc;
|
|
||||||
|
|
||||||
AddYourLocationAction(this.loc);
|
AddYourLocationAction(this.loc);
|
||||||
|
YourLocation loc;
|
||||||
}
|
}
|
||||||
|
|
||||||
class AddedYourLocationAction extends YourLocationActions {
|
class AddedYourLocationAction extends YourLocationActions {
|
||||||
YourLocation loc;
|
|
||||||
|
|
||||||
AddedYourLocationAction(this.loc);
|
AddedYourLocationAction(this.loc);
|
||||||
|
YourLocation loc;
|
||||||
}
|
}
|
||||||
|
|
||||||
class DeleteYourLocationAction extends YourLocationActions {
|
class DeleteYourLocationAction extends YourLocationActions {
|
||||||
YourLocation loc;
|
|
||||||
|
|
||||||
DeleteYourLocationAction(this.loc);
|
DeleteYourLocationAction(this.loc);
|
||||||
|
YourLocation loc;
|
||||||
}
|
}
|
||||||
|
|
||||||
class DeletedYourLocationAction extends YourLocationActions {
|
class DeletedYourLocationAction extends YourLocationActions {
|
||||||
ObjectId id;
|
|
||||||
|
|
||||||
DeletedYourLocationAction(this.id);
|
DeletedYourLocationAction(this.id);
|
||||||
|
ObjectId id;
|
||||||
}
|
}
|
||||||
|
|
||||||
class UpdateYourLocationAction extends YourLocationActions {
|
class UpdateYourLocationAction extends YourLocationActions {
|
||||||
YourLocation loc;
|
|
||||||
|
|
||||||
UpdateYourLocationAction(this.loc);
|
UpdateYourLocationAction(this.loc);
|
||||||
|
YourLocation loc;
|
||||||
}
|
}
|
||||||
|
|
||||||
class UpdatedYourLocationAction extends YourLocationActions {
|
class UpdatedYourLocationAction extends YourLocationActions {
|
||||||
YourLocation loc;
|
|
||||||
|
|
||||||
UpdatedYourLocationAction(this.loc);
|
UpdatedYourLocationAction(this.loc);
|
||||||
|
YourLocation loc;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ToggleSubscriptionAction extends YourLocationActions {
|
class ToggleSubscriptionAction extends YourLocationActions {
|
||||||
YourLocation loc;
|
|
||||||
|
|
||||||
ToggleSubscriptionAction(this.loc);
|
ToggleSubscriptionAction(this.loc);
|
||||||
|
YourLocation loc;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ToggledSubscriptionAction extends YourLocationActions {
|
class ToggledSubscriptionAction extends YourLocationActions {
|
||||||
YourLocation loc;
|
|
||||||
|
|
||||||
ToggledSubscriptionAction(this.loc);
|
ToggledSubscriptionAction(this.loc);
|
||||||
|
YourLocation loc;
|
||||||
}
|
}
|
||||||
|
|
||||||
class SubscribeAction extends YourLocationActions {}
|
class SubscribeAction extends YourLocationActions {}
|
||||||
|
|
||||||
class SubscribeConfirmAction extends YourLocationActions {
|
class SubscribeConfirmAction extends YourLocationActions {
|
||||||
YourLocation loc;
|
|
||||||
|
|
||||||
SubscribeConfirmAction(this.loc);
|
SubscribeConfirmAction(this.loc);
|
||||||
|
YourLocation loc;
|
||||||
}
|
}
|
||||||
|
|
||||||
class UnSubscribeAction extends YourLocationActions {
|
class UnSubscribeAction extends YourLocationActions {
|
||||||
YourLocation loc;
|
|
||||||
|
|
||||||
UnSubscribeAction(this.loc);
|
UnSubscribeAction(this.loc);
|
||||||
|
YourLocation loc;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,35 +1,35 @@
|
||||||
import 'package:fires_flutter/models/yourLocation.dart';
|
|
||||||
import 'package:redux/redux.dart';
|
import 'package:redux/redux.dart';
|
||||||
|
|
||||||
|
import '../models/yourLocation.dart';
|
||||||
import 'actions.dart';
|
import 'actions.dart';
|
||||||
|
|
||||||
final yourLocationsReducer = combineReducers<List<YourLocation>>([
|
final Reducer<List<YourLocation>> yourLocationsReducer = combineReducers<List<YourLocation>>(<Reducer<List<YourLocation>>>[
|
||||||
new TypedReducer<List<YourLocation>, AddedYourLocationAction>(
|
TypedReducer<List<YourLocation>, AddedYourLocationAction>(
|
||||||
_addedYourLocation),
|
_addedYourLocation),
|
||||||
new TypedReducer<List<YourLocation>, DeletedYourLocationAction>(
|
TypedReducer<List<YourLocation>, DeletedYourLocationAction>(
|
||||||
_deletedYourLocation),
|
_deletedYourLocation),
|
||||||
new TypedReducer<List<YourLocation>, UpdatedYourLocationAction>(
|
TypedReducer<List<YourLocation>, UpdatedYourLocationAction>(
|
||||||
_updatedYourLocation),
|
_updatedYourLocation),
|
||||||
new TypedReducer<List<YourLocation>, ToggledSubscriptionAction>(
|
TypedReducer<List<YourLocation>, ToggledSubscriptionAction>(
|
||||||
_toggledSubscriptionAction)
|
_toggledSubscriptionAction)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
List<YourLocation> _addedYourLocation(
|
List<YourLocation> _addedYourLocation(
|
||||||
List<YourLocation> yourLocations, AddedYourLocationAction action) {
|
List<YourLocation> yourLocations, AddedYourLocationAction action) {
|
||||||
return new List.from(yourLocations)..add(action.loc);
|
return List.from(yourLocations)..add(action.loc);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<YourLocation> _deletedYourLocation(
|
List<YourLocation> _deletedYourLocation(
|
||||||
List<YourLocation> yourLocations, DeletedYourLocationAction action) {
|
List<YourLocation> yourLocations, DeletedYourLocationAction action) {
|
||||||
return yourLocations
|
return yourLocations
|
||||||
.where((yourLocation) => yourLocation.id != action.id)
|
.where((YourLocation yourLocation) => yourLocation.id != action.id)
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
List<YourLocation> _updatedYourLocation(
|
List<YourLocation> _updatedYourLocation(
|
||||||
List<YourLocation> yourLocations, UpdatedYourLocationAction action) {
|
List<YourLocation> yourLocations, UpdatedYourLocationAction action) {
|
||||||
return yourLocations
|
return yourLocations
|
||||||
.map((yourLocation) =>
|
.map((YourLocation yourLocation) =>
|
||||||
yourLocation.id == action.loc.id ? action.loc : yourLocation)
|
yourLocation.id == action.loc.id ? action.loc : yourLocation)
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
@ -37,7 +37,7 @@ List<YourLocation> _updatedYourLocation(
|
||||||
List<YourLocation> _toggledSubscriptionAction(
|
List<YourLocation> _toggledSubscriptionAction(
|
||||||
List<YourLocation> yourLocations, ToggledSubscriptionAction action) {
|
List<YourLocation> yourLocations, ToggledSubscriptionAction action) {
|
||||||
return yourLocations
|
return yourLocations
|
||||||
.map((yourLocation) =>
|
.map((YourLocation yourLocation) =>
|
||||||
yourLocation.id == action.loc.id ? action.loc : yourLocation)
|
yourLocation.id == action.loc.id ? action.loc : yourLocation)
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,31 +7,32 @@ new RandomColorBlock( child
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class Sandbox extends StatelessWidget {
|
class Sandbox extends StatelessWidget {
|
||||||
|
const Sandbox({super.key});
|
||||||
|
|
||||||
static const String routeName = '/sandbox';
|
static const String routeName = '/sandbox';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
//showDialog(context: context, child: builder(context));
|
//showDialog(context: context, child: builder(context));
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: new AppBar(
|
appBar: AppBar(
|
||||||
title: new TextField(
|
title: TextField(
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
controller: new TextEditingController(text: "kk"),
|
controller: TextEditingController(text: 'kk'),
|
||||||
decoration: new InputDecoration(),
|
onSubmitted: (String todoText) {},
|
||||||
onSubmitted: (todoText) {},
|
|
||||||
)),
|
)),
|
||||||
body: new ElevatedButton(
|
body: ElevatedButton(
|
||||||
child: new Text('Press'),
|
child: const Text('Press'),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) {
|
builder: (BuildContext context) {
|
||||||
return new SimpleDialog(
|
return const SimpleDialog(
|
||||||
title: new Text('title'),
|
title: Text('title'),
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
new Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(left: 16.0),
|
padding: EdgeInsets.only(left: 16.0),
|
||||||
child: new Text('hhh'),
|
child: Text('hhh'),
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,9 @@ import 'package:sentry_flutter/sentry_flutter.dart';
|
||||||
|
|
||||||
import 'globals.dart' as globals;
|
import 'globals.dart' as globals;
|
||||||
|
|
||||||
var useSentry = !globals.isDevelopment;
|
bool useSentry = !globals.isDevelopment;
|
||||||
|
|
||||||
Future<Null> reportError(dynamic error, dynamic stackTrace) async {
|
Future<void> reportError(dynamic error, dynamic stackTrace) async {
|
||||||
// Print the exception to the console
|
// Print the exception to the console
|
||||||
|
|
||||||
print('Caught error: $error');
|
print('Caught error: $error');
|
||||||
|
|
|
||||||
|
|
@ -4,61 +4,61 @@ import 'package:flutter/material.dart';
|
||||||
import 'colors.dart';
|
import 'colors.dart';
|
||||||
import 'generated/i18n.dart';
|
import 'generated/i18n.dart';
|
||||||
|
|
||||||
typedef void SlideCallback(int distance);
|
typedef SlideCallback = void Function(int distance);
|
||||||
|
|
||||||
class FireDistanceSlider extends StatefulWidget {
|
class FireDistanceSlider extends StatefulWidget {
|
||||||
|
|
||||||
|
const FireDistanceSlider({super.key, required this.initialValue, required this.onSlide});
|
||||||
final int initialValue;
|
final int initialValue;
|
||||||
final SlideCallback onSlide;
|
final SlideCallback onSlide;
|
||||||
|
|
||||||
FireDistanceSlider({required this.initialValue, required this.onSlide});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_FireDistanceSliderState createState() => new _FireDistanceSliderState(
|
_FireDistanceSliderState createState() => _FireDistanceSliderState(
|
||||||
initialValue: initialValue, onSlide: onSlide);
|
initialValue: initialValue, onSlide: onSlide);
|
||||||
}
|
}
|
||||||
|
|
||||||
class _FireDistanceSliderState extends State<FireDistanceSlider> {
|
class _FireDistanceSliderState extends State<FireDistanceSlider> {
|
||||||
|
|
||||||
|
_FireDistanceSliderState({int initialValue = 10, required this.onSlide}) {
|
||||||
|
_sliderValue = initialValue;
|
||||||
|
}
|
||||||
late int _sliderValue;
|
late int _sliderValue;
|
||||||
final SlideCallback onSlide;
|
final SlideCallback onSlide;
|
||||||
|
|
||||||
_FireDistanceSliderState({int initialValue = 10, required this.onSlide}) {
|
Widget sizeText(int sliderValue) => Text(
|
||||||
this._sliderValue = initialValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget sizeText(int sliderValue) => new Text(
|
|
||||||
S.of(context).subscribeToValueAroundThisArea(sliderValue.toString()),
|
S.of(context).subscribeToValueAroundThisArea(sliderValue.toString()),
|
||||||
style: new TextStyle(color: Colors.black87));
|
style: const TextStyle(color: Colors.black87));
|
||||||
|
|
||||||
Widget warningText(int sliderValue) => _sliderValue >= 50
|
Widget warningText(int sliderValue) => _sliderValue >= 50
|
||||||
? new Text(S.of(context).warningThisIsAVeryLargeArea,
|
? Text(S.of(context).warningThisIsAVeryLargeArea,
|
||||||
style: new TextStyle(color: fires900))
|
style: const TextStyle(color: fires900))
|
||||||
: new Text('');
|
: const Text('');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var slider = new Slider(
|
final Slider slider = Slider(
|
||||||
value: _sliderValue + 0.0,
|
value: _sliderValue + 0.0,
|
||||||
activeColor: fires900,
|
activeColor: fires900,
|
||||||
inactiveColor: Colors.black45,
|
inactiveColor: Colors.black45,
|
||||||
min: 1.0,
|
min: 1.0,
|
||||||
max: 100.0,
|
max: 100.0,
|
||||||
divisions: 99,
|
divisions: 99,
|
||||||
label: '${_sliderValue.round()}',
|
label: '$_sliderValue',
|
||||||
onChanged: (double value) {
|
onChanged: (double value) {
|
||||||
_sliderValue = value.round();
|
_sliderValue = value.round();
|
||||||
onSlide(_sliderValue);
|
onSlide(_sliderValue);
|
||||||
setState(() {});
|
setState(() {});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return new Column(
|
return Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: listWithoutNulls(<Widget>[
|
children: listWithoutNulls(<Widget>[
|
||||||
new SizedBox(height: 50.0),
|
const SizedBox(height: 50.0),
|
||||||
new Row(mainAxisSize: MainAxisSize.max, children: <Widget>[slider]),
|
Row(children: <Widget>[slider]),
|
||||||
// new SizedBox(height: 5.0),
|
// new SizedBox(height: 5.0),
|
||||||
new Column(children: <Widget>[
|
Column(children: <Widget>[
|
||||||
sizeText(_sliderValue),
|
sizeText(_sliderValue),
|
||||||
new SizedBox(height: 5.0),
|
const SizedBox(height: 5.0),
|
||||||
warningText(_sliderValue)
|
warningText(_sliderValue)
|
||||||
])
|
])
|
||||||
]),
|
]),
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,15 @@
|
||||||
import 'package:comunes_flutter/comunes_flutter.dart';
|
import 'package:comunes_flutter/comunes_flutter.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:in_app_review/in_app_review.dart';
|
import 'package:in_app_review/in_app_review.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
|
||||||
import 'package:share_plus/share_plus.dart';
|
import 'package:share_plus/share_plus.dart';
|
||||||
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
import 'generated/i18n.dart';
|
import 'generated/i18n.dart';
|
||||||
import 'mainDrawer.dart';
|
import 'mainDrawer.dart';
|
||||||
|
|
||||||
class SupportPage extends StatefulWidget {
|
class SupportPage extends StatefulWidget {
|
||||||
|
const SupportPage({super.key});
|
||||||
|
|
||||||
static const String routeName = '/support';
|
static const String routeName = '/support';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -15,27 +17,27 @@ class SupportPage extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _SupportPageState extends State<SupportPage> {
|
class _SupportPageState extends State<SupportPage> {
|
||||||
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
|
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
|
||||||
|
|
||||||
Widget buildSupportButton() {
|
Widget buildSupportButton() {
|
||||||
return new Align(
|
return Align(
|
||||||
alignment: const Alignment(0.0, -0.2),
|
alignment: const Alignment(0.0, -0.2),
|
||||||
child: new OutlinedButton.icon(
|
child: OutlinedButton.icon(
|
||||||
icon: const Icon(Icons.favorite_border),
|
icon: const Icon(Icons.favorite_border),
|
||||||
label: new Text(S.of(context).comunesSupportBtn),
|
label: Text(S.of(context).comunesSupportBtn),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
launch("https://comunes.org/");
|
launch('https://comunes.org/');
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget buildShareButton() {
|
Widget buildShareButton() {
|
||||||
return new Align(
|
return Align(
|
||||||
alignment: const Alignment(0.0, -0.2),
|
alignment: const Alignment(0.0, -0.2),
|
||||||
child: new OutlinedButton.icon(
|
child: OutlinedButton.icon(
|
||||||
icon: const Icon(Icons.share),
|
icon: const Icon(Icons.share),
|
||||||
label: new Text(S.of(context).shareAppBtn),
|
label: Text(S.of(context).shareAppBtn),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Share.shareWithResult(
|
Share.shareWithResult(
|
||||||
'https://play.google.com/store/apps/details?id=org.comunes.fires');
|
'https://play.google.com/store/apps/details?id=org.comunes.fires');
|
||||||
|
|
@ -45,11 +47,11 @@ class _SupportPageState extends State<SupportPage> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget buildStarButton() {
|
Widget buildStarButton() {
|
||||||
return new Align(
|
return Align(
|
||||||
alignment: const Alignment(0.0, -0.2),
|
alignment: const Alignment(0.0, -0.2),
|
||||||
child: new OutlinedButton.icon(
|
child: OutlinedButton.icon(
|
||||||
icon: const Icon(Icons.star_border),
|
icon: const Icon(Icons.star_border),
|
||||||
label: new Text(S.of(context).starAppBtn),
|
label: Text(S.of(context).starAppBtn),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
InAppReview.instance.requestReview();
|
InAppReview.instance.requestReview();
|
||||||
},
|
},
|
||||||
|
|
@ -58,14 +60,14 @@ class _SupportPageState extends State<SupportPage> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget buildTranslateButton() {
|
Widget buildTranslateButton() {
|
||||||
return new Align(
|
return Align(
|
||||||
alignment: const Alignment(0.0, -0.2),
|
alignment: const Alignment(0.0, -0.2),
|
||||||
child: new OutlinedButton.icon(
|
child: OutlinedButton.icon(
|
||||||
icon: const Icon(Icons.translate),
|
icon: const Icon(Icons.translate),
|
||||||
label: new Text(S.of(context).translateBtn),
|
label: Text(S.of(context).translateBtn),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
launch(
|
launch(
|
||||||
"https://translate.comunes.org/projects/todos-contra-el-fuego/");
|
'https://translate.comunes.org/projects/todos-contra-el-fuego/');
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -76,24 +78,24 @@ class _SupportPageState extends State<SupportPage> {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
key: _scaffoldKey,
|
key: _scaffoldKey,
|
||||||
appBar:
|
appBar:
|
||||||
new AppBar(title: new Text(S.of(context).supportThisInitiative)),
|
AppBar(title: Text(S.of(context).supportThisInitiative)),
|
||||||
drawer: new MainDrawer(context, SupportPage.routeName),
|
drawer: MainDrawer(context, SupportPage.routeName),
|
||||||
body: Padding(
|
body: Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: new CenteredColumn(children: <Widget>[
|
child: CenteredColumn(children: <Widget>[
|
||||||
new Flexible(
|
Flexible(
|
||||||
child: new CenteredColumn(children: <Widget>[
|
child: CenteredColumn(children: <Widget>[
|
||||||
new Text(
|
Text(
|
||||||
S.of(context).supportPageDescription,
|
S.of(context).supportPageDescription,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
new SizedBox(height: 20.0),
|
const SizedBox(height: 20.0),
|
||||||
buildSupportButton(),
|
buildSupportButton(),
|
||||||
new SizedBox(height: 20.0),
|
const SizedBox(height: 20.0),
|
||||||
buildTranslateButton(),
|
buildTranslateButton(),
|
||||||
new SizedBox(height: 20.0),
|
const SizedBox(height: 20.0),
|
||||||
buildStarButton(),
|
buildStarButton(),
|
||||||
new SizedBox(height: 20.0),
|
const SizedBox(height: 20.0),
|
||||||
buildShareButton()
|
buildShareButton()
|
||||||
]))
|
]))
|
||||||
]),
|
]),
|
||||||
|
|
|
||||||
|
|
@ -10,16 +10,14 @@ ThemeData _buildFiresTheme() {
|
||||||
primaryColor: fires600,
|
primaryColor: fires600,
|
||||||
scaffoldBackgroundColor: firesSurfaceWhite,
|
scaffoldBackgroundColor: firesSurfaceWhite,
|
||||||
cardColor: firesBackgroundWhite,
|
cardColor: firesBackgroundWhite,
|
||||||
textSelectionTheme: TextSelectionThemeData(selectionColor: fires300),
|
textSelectionTheme: const TextSelectionThemeData(selectionColor: fires300),
|
||||||
colorScheme: ColorScheme.light(
|
colorScheme: const ColorScheme.light(
|
||||||
primary: fires600,
|
primary: fires600,
|
||||||
secondary: fires900,
|
secondary: fires900,
|
||||||
error: firesErrorRed,
|
error: firesErrorRed,
|
||||||
surface: firesSurfaceWhite,
|
surface: firesSurfaceWhite,
|
||||||
onSurface: fires900,
|
onSurface: fires900,
|
||||||
onPrimary: Colors.white,
|
|
||||||
onSecondary: Colors.white,
|
onSecondary: Colors.white,
|
||||||
onError: Colors.white,
|
|
||||||
),
|
),
|
||||||
//TODO: Add the text themes (103)
|
//TODO: Add the text themes (103)
|
||||||
//TODO: Add the icon themes (103)
|
//TODO: Add the icon themes (103)
|
||||||
|
|
|
||||||
|
|
@ -10,16 +10,14 @@ ThemeData _buildFiresTheme() {
|
||||||
primaryColor: Colors.pink,
|
primaryColor: Colors.pink,
|
||||||
scaffoldBackgroundColor: firesSurfaceWhite,
|
scaffoldBackgroundColor: firesSurfaceWhite,
|
||||||
cardColor: firesBackgroundWhite,
|
cardColor: firesBackgroundWhite,
|
||||||
textSelectionTheme: TextSelectionThemeData(selectionColor: fires300),
|
textSelectionTheme: const TextSelectionThemeData(selectionColor: fires300),
|
||||||
colorScheme: ColorScheme.light(
|
colorScheme: const ColorScheme.light(
|
||||||
primary: Colors.pink,
|
primary: Colors.pink,
|
||||||
secondary: fires900,
|
secondary: fires900,
|
||||||
error: firesErrorRed,
|
error: firesErrorRed,
|
||||||
surface: firesSurfaceWhite,
|
surface: firesSurfaceWhite,
|
||||||
onSurface: fires900,
|
onSurface: fires900,
|
||||||
onPrimary: Colors.white,
|
|
||||||
onSecondary: Colors.white,
|
onSecondary: Colors.white,
|
||||||
onError: Colors.white,
|
|
||||||
),
|
),
|
||||||
//TODO: Add the text themes (103)
|
//TODO: Add the text themes (103)
|
||||||
//TODO: Add the icon themes (103)
|
//TODO: Add the icon themes (103)
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,23 @@
|
||||||
import 'package:comunes_flutter/comunes_flutter.dart';
|
import 'package:comunes_flutter/comunes_flutter.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_map/flutter_map.dart';
|
import 'package:flutter_map/flutter_map.dart';
|
||||||
|
import 'package:latlong2/latlong.dart';
|
||||||
|
|
||||||
/// Zoom control widget for flutter_map v6+
|
/// Zoom control widget for flutter_map v6+
|
||||||
class ZoomMapPluginWidget extends StatelessWidget {
|
class ZoomMapPluginWidget extends StatelessWidget {
|
||||||
|
const ZoomMapPluginWidget({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return LayoutBuilder(
|
return LayoutBuilder(
|
||||||
builder: (context, constraints) =>
|
builder: (BuildContext context, BoxConstraints constraints) =>
|
||||||
Stack(fit: StackFit.expand, children: <Widget>[
|
Stack(fit: StackFit.expand, children: <Widget>[
|
||||||
Positioned(
|
Positioned(
|
||||||
top: constraints.maxHeight - 100,
|
top: constraints.maxHeight - 100,
|
||||||
right: 10.0,
|
right: 10.0,
|
||||||
child: new CenteredRow(
|
child: CenteredRow(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
new Column(
|
Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
_zoomButton(context, Icons.zoom_in, 1),
|
_zoomButton(context, Icons.zoom_in, 1),
|
||||||
_zoomButton(context, Icons.zoom_out, -1)
|
_zoomButton(context, Icons.zoom_out, -1)
|
||||||
|
|
@ -27,12 +30,12 @@ class ZoomMapPluginWidget extends StatelessWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
IconButton _zoomButton(BuildContext context, IconData zoomIcon, int inc) {
|
IconButton _zoomButton(BuildContext context, IconData zoomIcon, int inc) {
|
||||||
return new IconButton(
|
return IconButton(
|
||||||
icon: Icon(zoomIcon),
|
icon: Icon(zoomIcon),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
final controller = MapController.of(context);
|
final MapController controller = MapController.of(context);
|
||||||
var currentZoom = controller.camera.zoom;
|
final double currentZoom = controller.camera.zoom;
|
||||||
var currentCenter = controller.camera.center;
|
final LatLng currentCenter = controller.camera.center;
|
||||||
controller.move(currentCenter, currentZoom + inc);
|
controller.move(currentCenter, currentZoom + inc);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,29 +1,29 @@
|
||||||
import 'package:test/test.dart';
|
|
||||||
import 'package:fires_flutter/fileUtils.dart';
|
import 'package:fires_flutter/fileUtils.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
test('test es-ES fallback', () {
|
test('test es-ES fallback', () {
|
||||||
String answer = getFallbackLang('es-ES');
|
final String answer = getFallbackLang('es-ES');
|
||||||
expect(answer, 'es');
|
expect(answer, 'es');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('test en-GB fallback', () {
|
test('test en-GB fallback', () {
|
||||||
String answer = getFallbackLang('en-GB');
|
final String answer = getFallbackLang('en-GB');
|
||||||
expect(answer, 'en');
|
expect(answer, 'en');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('test gl fallback', () {
|
test('test gl fallback', () {
|
||||||
String answer = getFallbackLang('gl');
|
final String answer = getFallbackLang('gl');
|
||||||
expect(answer, 'es');
|
expect(answer, 'es');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('test fr fallback', () {
|
test('test fr fallback', () {
|
||||||
String answer = getFallbackLang('fr');
|
final String answer = getFallbackLang('fr');
|
||||||
expect(answer, 'en');
|
expect(answer, 'en');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('test privacy English md page', () async {
|
test('test privacy English md page', () async {
|
||||||
String answer = await getFileNameOfLang(dir: 'assets/pages', fileName: 'privacy', ext: 'md', lang: 'en');
|
final String answer = await getFileNameOfLang(dir: 'assets/pages', fileName: 'privacy', ext: 'md', lang: 'en');
|
||||||
expect(answer, 'assets/pages/privacy-en.md');
|
expect(answer, 'assets/pages/privacy-en.md');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue