feat: add setting for auto-lock fix: fix sideswipe
This commit is contained in:
@@ -27,6 +27,8 @@ class _KoolTabAppState extends State<KoolTabApp> {
|
||||
|
||||
_lifecycleLockObserver = AppLifecycleLockObserver(
|
||||
onLock: () => context.read<PinLockViewModel>().lock(),
|
||||
shouldLock: () =>
|
||||
context.read<SettingsViewModel>().settings.autoLockEnabled,
|
||||
);
|
||||
WidgetsBinding.instance.addObserver(_lifecycleLockObserver);
|
||||
|
||||
|
||||
@@ -2,8 +2,9 @@ import 'package:flutter/material.dart';
|
||||
|
||||
class AppLifecycleLockObserver extends WidgetsBindingObserver {
|
||||
final VoidCallback onLock;
|
||||
final bool Function()? shouldLock;
|
||||
|
||||
AppLifecycleLockObserver({required this.onLock});
|
||||
AppLifecycleLockObserver({required this.onLock, this.shouldLock});
|
||||
|
||||
@override
|
||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||
@@ -12,7 +13,7 @@ class AppLifecycleLockObserver extends WidgetsBindingObserver {
|
||||
case AppLifecycleState.hidden:
|
||||
case AppLifecycleState.paused:
|
||||
case AppLifecycleState.detached:
|
||||
onLock();
|
||||
if (shouldLock?.call() ?? true) onLock();
|
||||
case AppLifecycleState.resumed:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -106,6 +106,9 @@ class AppSettingsTable extends Table {
|
||||
|
||||
BoolColumn get pinRequired => boolean().withDefault(const Constant(false))();
|
||||
|
||||
BoolColumn get autoLockEnabled =>
|
||||
boolean().withDefault(const Constant(true))();
|
||||
|
||||
TextColumn get themeMode => text().withDefault(const Constant('system'))();
|
||||
|
||||
TextColumn get language => text().withDefault(const Constant('system'))();
|
||||
@@ -132,7 +135,7 @@ class AppDatabase extends _$AppDatabase {
|
||||
AppDatabase([QueryExecutor? executor]) : super(executor ?? _openConnection());
|
||||
|
||||
@override
|
||||
int get schemaVersion => 8;
|
||||
int get schemaVersion => 9;
|
||||
|
||||
@override
|
||||
MigrationStrategy get migration {
|
||||
@@ -181,6 +184,13 @@ class AppDatabase extends _$AppDatabase {
|
||||
appSettingsTable.barGridRows,
|
||||
);
|
||||
}
|
||||
|
||||
if (from < 9 && !await hasSettingsColumn('auto_lock_enabled')) {
|
||||
await migrator.addColumn(
|
||||
appSettingsTable,
|
||||
appSettingsTable.autoLockEnabled,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2207,6 +2207,21 @@ class $AppSettingsTableTable extends AppSettingsTable
|
||||
),
|
||||
defaultValue: const Constant(false),
|
||||
);
|
||||
static const VerificationMeta _autoLockEnabledMeta = const VerificationMeta(
|
||||
'autoLockEnabled',
|
||||
);
|
||||
@override
|
||||
late final GeneratedColumn<bool> autoLockEnabled = GeneratedColumn<bool>(
|
||||
'auto_lock_enabled',
|
||||
aliasedName,
|
||||
false,
|
||||
type: DriftSqlType.bool,
|
||||
requiredDuringInsert: false,
|
||||
defaultConstraints: GeneratedColumn.constraintIsAlways(
|
||||
'CHECK ("auto_lock_enabled" IN (0, 1))',
|
||||
),
|
||||
defaultValue: const Constant(true),
|
||||
);
|
||||
static const VerificationMeta _themeModeMeta = const VerificationMeta(
|
||||
'themeMode',
|
||||
);
|
||||
@@ -2247,6 +2262,7 @@ class $AppSettingsTableTable extends AppSettingsTable
|
||||
List<GeneratedColumn> get $columns => [
|
||||
id,
|
||||
pinRequired,
|
||||
autoLockEnabled,
|
||||
themeMode,
|
||||
language,
|
||||
barGridRows,
|
||||
@@ -2277,6 +2293,15 @@ class $AppSettingsTableTable extends AppSettingsTable
|
||||
),
|
||||
);
|
||||
}
|
||||
if (data.containsKey('auto_lock_enabled')) {
|
||||
context.handle(
|
||||
_autoLockEnabledMeta,
|
||||
autoLockEnabled.isAcceptableOrUnknown(
|
||||
data['auto_lock_enabled']!,
|
||||
_autoLockEnabledMeta,
|
||||
),
|
||||
);
|
||||
}
|
||||
if (data.containsKey('theme_mode')) {
|
||||
context.handle(
|
||||
_themeModeMeta,
|
||||
@@ -2315,6 +2340,10 @@ class $AppSettingsTableTable extends AppSettingsTable
|
||||
DriftSqlType.bool,
|
||||
data['${effectivePrefix}pin_required'],
|
||||
)!,
|
||||
autoLockEnabled: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.bool,
|
||||
data['${effectivePrefix}auto_lock_enabled'],
|
||||
)!,
|
||||
themeMode: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.string,
|
||||
data['${effectivePrefix}theme_mode'],
|
||||
@@ -2339,12 +2368,14 @@ class $AppSettingsTableTable extends AppSettingsTable
|
||||
class AppSettingsRow extends DataClass implements Insertable<AppSettingsRow> {
|
||||
final String id;
|
||||
final bool pinRequired;
|
||||
final bool autoLockEnabled;
|
||||
final String themeMode;
|
||||
final String language;
|
||||
final int barGridRows;
|
||||
const AppSettingsRow({
|
||||
required this.id,
|
||||
required this.pinRequired,
|
||||
required this.autoLockEnabled,
|
||||
required this.themeMode,
|
||||
required this.language,
|
||||
required this.barGridRows,
|
||||
@@ -2354,6 +2385,7 @@ class AppSettingsRow extends DataClass implements Insertable<AppSettingsRow> {
|
||||
final map = <String, Expression>{};
|
||||
map['id'] = Variable<String>(id);
|
||||
map['pin_required'] = Variable<bool>(pinRequired);
|
||||
map['auto_lock_enabled'] = Variable<bool>(autoLockEnabled);
|
||||
map['theme_mode'] = Variable<String>(themeMode);
|
||||
map['language'] = Variable<String>(language);
|
||||
map['bar_grid_rows'] = Variable<int>(barGridRows);
|
||||
@@ -2364,6 +2396,7 @@ class AppSettingsRow extends DataClass implements Insertable<AppSettingsRow> {
|
||||
return AppSettingsTableCompanion(
|
||||
id: Value(id),
|
||||
pinRequired: Value(pinRequired),
|
||||
autoLockEnabled: Value(autoLockEnabled),
|
||||
themeMode: Value(themeMode),
|
||||
language: Value(language),
|
||||
barGridRows: Value(barGridRows),
|
||||
@@ -2378,6 +2411,7 @@ class AppSettingsRow extends DataClass implements Insertable<AppSettingsRow> {
|
||||
return AppSettingsRow(
|
||||
id: serializer.fromJson<String>(json['id']),
|
||||
pinRequired: serializer.fromJson<bool>(json['pinRequired']),
|
||||
autoLockEnabled: serializer.fromJson<bool>(json['autoLockEnabled']),
|
||||
themeMode: serializer.fromJson<String>(json['themeMode']),
|
||||
language: serializer.fromJson<String>(json['language']),
|
||||
barGridRows: serializer.fromJson<int>(json['barGridRows']),
|
||||
@@ -2389,6 +2423,7 @@ class AppSettingsRow extends DataClass implements Insertable<AppSettingsRow> {
|
||||
return <String, dynamic>{
|
||||
'id': serializer.toJson<String>(id),
|
||||
'pinRequired': serializer.toJson<bool>(pinRequired),
|
||||
'autoLockEnabled': serializer.toJson<bool>(autoLockEnabled),
|
||||
'themeMode': serializer.toJson<String>(themeMode),
|
||||
'language': serializer.toJson<String>(language),
|
||||
'barGridRows': serializer.toJson<int>(barGridRows),
|
||||
@@ -2398,12 +2433,14 @@ class AppSettingsRow extends DataClass implements Insertable<AppSettingsRow> {
|
||||
AppSettingsRow copyWith({
|
||||
String? id,
|
||||
bool? pinRequired,
|
||||
bool? autoLockEnabled,
|
||||
String? themeMode,
|
||||
String? language,
|
||||
int? barGridRows,
|
||||
}) => AppSettingsRow(
|
||||
id: id ?? this.id,
|
||||
pinRequired: pinRequired ?? this.pinRequired,
|
||||
autoLockEnabled: autoLockEnabled ?? this.autoLockEnabled,
|
||||
themeMode: themeMode ?? this.themeMode,
|
||||
language: language ?? this.language,
|
||||
barGridRows: barGridRows ?? this.barGridRows,
|
||||
@@ -2414,6 +2451,9 @@ class AppSettingsRow extends DataClass implements Insertable<AppSettingsRow> {
|
||||
pinRequired: data.pinRequired.present
|
||||
? data.pinRequired.value
|
||||
: this.pinRequired,
|
||||
autoLockEnabled: data.autoLockEnabled.present
|
||||
? data.autoLockEnabled.value
|
||||
: this.autoLockEnabled,
|
||||
themeMode: data.themeMode.present ? data.themeMode.value : this.themeMode,
|
||||
language: data.language.present ? data.language.value : this.language,
|
||||
barGridRows: data.barGridRows.present
|
||||
@@ -2427,6 +2467,7 @@ class AppSettingsRow extends DataClass implements Insertable<AppSettingsRow> {
|
||||
return (StringBuffer('AppSettingsRow(')
|
||||
..write('id: $id, ')
|
||||
..write('pinRequired: $pinRequired, ')
|
||||
..write('autoLockEnabled: $autoLockEnabled, ')
|
||||
..write('themeMode: $themeMode, ')
|
||||
..write('language: $language, ')
|
||||
..write('barGridRows: $barGridRows')
|
||||
@@ -2435,14 +2476,21 @@ class AppSettingsRow extends DataClass implements Insertable<AppSettingsRow> {
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
Object.hash(id, pinRequired, themeMode, language, barGridRows);
|
||||
int get hashCode => Object.hash(
|
||||
id,
|
||||
pinRequired,
|
||||
autoLockEnabled,
|
||||
themeMode,
|
||||
language,
|
||||
barGridRows,
|
||||
);
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
(other is AppSettingsRow &&
|
||||
other.id == this.id &&
|
||||
other.pinRequired == this.pinRequired &&
|
||||
other.autoLockEnabled == this.autoLockEnabled &&
|
||||
other.themeMode == this.themeMode &&
|
||||
other.language == this.language &&
|
||||
other.barGridRows == this.barGridRows);
|
||||
@@ -2451,6 +2499,7 @@ class AppSettingsRow extends DataClass implements Insertable<AppSettingsRow> {
|
||||
class AppSettingsTableCompanion extends UpdateCompanion<AppSettingsRow> {
|
||||
final Value<String> id;
|
||||
final Value<bool> pinRequired;
|
||||
final Value<bool> autoLockEnabled;
|
||||
final Value<String> themeMode;
|
||||
final Value<String> language;
|
||||
final Value<int> barGridRows;
|
||||
@@ -2458,6 +2507,7 @@ class AppSettingsTableCompanion extends UpdateCompanion<AppSettingsRow> {
|
||||
const AppSettingsTableCompanion({
|
||||
this.id = const Value.absent(),
|
||||
this.pinRequired = const Value.absent(),
|
||||
this.autoLockEnabled = const Value.absent(),
|
||||
this.themeMode = const Value.absent(),
|
||||
this.language = const Value.absent(),
|
||||
this.barGridRows = const Value.absent(),
|
||||
@@ -2466,6 +2516,7 @@ class AppSettingsTableCompanion extends UpdateCompanion<AppSettingsRow> {
|
||||
AppSettingsTableCompanion.insert({
|
||||
required String id,
|
||||
this.pinRequired = const Value.absent(),
|
||||
this.autoLockEnabled = const Value.absent(),
|
||||
this.themeMode = const Value.absent(),
|
||||
this.language = const Value.absent(),
|
||||
this.barGridRows = const Value.absent(),
|
||||
@@ -2474,6 +2525,7 @@ class AppSettingsTableCompanion extends UpdateCompanion<AppSettingsRow> {
|
||||
static Insertable<AppSettingsRow> custom({
|
||||
Expression<String>? id,
|
||||
Expression<bool>? pinRequired,
|
||||
Expression<bool>? autoLockEnabled,
|
||||
Expression<String>? themeMode,
|
||||
Expression<String>? language,
|
||||
Expression<int>? barGridRows,
|
||||
@@ -2482,6 +2534,7 @@ class AppSettingsTableCompanion extends UpdateCompanion<AppSettingsRow> {
|
||||
return RawValuesInsertable({
|
||||
if (id != null) 'id': id,
|
||||
if (pinRequired != null) 'pin_required': pinRequired,
|
||||
if (autoLockEnabled != null) 'auto_lock_enabled': autoLockEnabled,
|
||||
if (themeMode != null) 'theme_mode': themeMode,
|
||||
if (language != null) 'language': language,
|
||||
if (barGridRows != null) 'bar_grid_rows': barGridRows,
|
||||
@@ -2492,6 +2545,7 @@ class AppSettingsTableCompanion extends UpdateCompanion<AppSettingsRow> {
|
||||
AppSettingsTableCompanion copyWith({
|
||||
Value<String>? id,
|
||||
Value<bool>? pinRequired,
|
||||
Value<bool>? autoLockEnabled,
|
||||
Value<String>? themeMode,
|
||||
Value<String>? language,
|
||||
Value<int>? barGridRows,
|
||||
@@ -2500,6 +2554,7 @@ class AppSettingsTableCompanion extends UpdateCompanion<AppSettingsRow> {
|
||||
return AppSettingsTableCompanion(
|
||||
id: id ?? this.id,
|
||||
pinRequired: pinRequired ?? this.pinRequired,
|
||||
autoLockEnabled: autoLockEnabled ?? this.autoLockEnabled,
|
||||
themeMode: themeMode ?? this.themeMode,
|
||||
language: language ?? this.language,
|
||||
barGridRows: barGridRows ?? this.barGridRows,
|
||||
@@ -2516,6 +2571,9 @@ class AppSettingsTableCompanion extends UpdateCompanion<AppSettingsRow> {
|
||||
if (pinRequired.present) {
|
||||
map['pin_required'] = Variable<bool>(pinRequired.value);
|
||||
}
|
||||
if (autoLockEnabled.present) {
|
||||
map['auto_lock_enabled'] = Variable<bool>(autoLockEnabled.value);
|
||||
}
|
||||
if (themeMode.present) {
|
||||
map['theme_mode'] = Variable<String>(themeMode.value);
|
||||
}
|
||||
@@ -2536,6 +2594,7 @@ class AppSettingsTableCompanion extends UpdateCompanion<AppSettingsRow> {
|
||||
return (StringBuffer('AppSettingsTableCompanion(')
|
||||
..write('id: $id, ')
|
||||
..write('pinRequired: $pinRequired, ')
|
||||
..write('autoLockEnabled: $autoLockEnabled, ')
|
||||
..write('themeMode: $themeMode, ')
|
||||
..write('language: $language, ')
|
||||
..write('barGridRows: $barGridRows, ')
|
||||
@@ -4125,6 +4184,7 @@ typedef $$AppSettingsTableTableCreateCompanionBuilder =
|
||||
AppSettingsTableCompanion Function({
|
||||
required String id,
|
||||
Value<bool> pinRequired,
|
||||
Value<bool> autoLockEnabled,
|
||||
Value<String> themeMode,
|
||||
Value<String> language,
|
||||
Value<int> barGridRows,
|
||||
@@ -4134,6 +4194,7 @@ typedef $$AppSettingsTableTableUpdateCompanionBuilder =
|
||||
AppSettingsTableCompanion Function({
|
||||
Value<String> id,
|
||||
Value<bool> pinRequired,
|
||||
Value<bool> autoLockEnabled,
|
||||
Value<String> themeMode,
|
||||
Value<String> language,
|
||||
Value<int> barGridRows,
|
||||
@@ -4159,6 +4220,11 @@ class $$AppSettingsTableTableFilterComposer
|
||||
builder: (column) => ColumnFilters(column),
|
||||
);
|
||||
|
||||
ColumnFilters<bool> get autoLockEnabled => $composableBuilder(
|
||||
column: $table.autoLockEnabled,
|
||||
builder: (column) => ColumnFilters(column),
|
||||
);
|
||||
|
||||
ColumnFilters<String> get themeMode => $composableBuilder(
|
||||
column: $table.themeMode,
|
||||
builder: (column) => ColumnFilters(column),
|
||||
@@ -4194,6 +4260,11 @@ class $$AppSettingsTableTableOrderingComposer
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
);
|
||||
|
||||
ColumnOrderings<bool> get autoLockEnabled => $composableBuilder(
|
||||
column: $table.autoLockEnabled,
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
);
|
||||
|
||||
ColumnOrderings<String> get themeMode => $composableBuilder(
|
||||
column: $table.themeMode,
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
@@ -4227,6 +4298,11 @@ class $$AppSettingsTableTableAnnotationComposer
|
||||
builder: (column) => column,
|
||||
);
|
||||
|
||||
GeneratedColumn<bool> get autoLockEnabled => $composableBuilder(
|
||||
column: $table.autoLockEnabled,
|
||||
builder: (column) => column,
|
||||
);
|
||||
|
||||
GeneratedColumn<String> get themeMode =>
|
||||
$composableBuilder(column: $table.themeMode, builder: (column) => column);
|
||||
|
||||
@@ -4278,6 +4354,7 @@ class $$AppSettingsTableTableTableManager
|
||||
({
|
||||
Value<String> id = const Value.absent(),
|
||||
Value<bool> pinRequired = const Value.absent(),
|
||||
Value<bool> autoLockEnabled = const Value.absent(),
|
||||
Value<String> themeMode = const Value.absent(),
|
||||
Value<String> language = const Value.absent(),
|
||||
Value<int> barGridRows = const Value.absent(),
|
||||
@@ -4285,6 +4362,7 @@ class $$AppSettingsTableTableTableManager
|
||||
}) => AppSettingsTableCompanion(
|
||||
id: id,
|
||||
pinRequired: pinRequired,
|
||||
autoLockEnabled: autoLockEnabled,
|
||||
themeMode: themeMode,
|
||||
language: language,
|
||||
barGridRows: barGridRows,
|
||||
@@ -4294,6 +4372,7 @@ class $$AppSettingsTableTableTableManager
|
||||
({
|
||||
required String id,
|
||||
Value<bool> pinRequired = const Value.absent(),
|
||||
Value<bool> autoLockEnabled = const Value.absent(),
|
||||
Value<String> themeMode = const Value.absent(),
|
||||
Value<String> language = const Value.absent(),
|
||||
Value<int> barGridRows = const Value.absent(),
|
||||
@@ -4301,6 +4380,7 @@ class $$AppSettingsTableTableTableManager
|
||||
}) => AppSettingsTableCompanion.insert(
|
||||
id: id,
|
||||
pinRequired: pinRequired,
|
||||
autoLockEnabled: autoLockEnabled,
|
||||
themeMode: themeMode,
|
||||
language: language,
|
||||
barGridRows: barGridRows,
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"settingsTitle": "Settings",
|
||||
"security": "Security",
|
||||
"pinRequired": "PIN Required",
|
||||
"autoLockOnExit": "Auto-lock on phone lock or app exit",
|
||||
"changePin": "Change PIN",
|
||||
"appearance": "Appearance",
|
||||
"language": "Language",
|
||||
@@ -71,7 +72,12 @@
|
||||
"selectOrOpenTab": "Select or open a tab",
|
||||
"noOpenTabs": "No open tabs",
|
||||
"edit": "Edit",
|
||||
"editTab": "Edit tab",
|
||||
"close": "Close",
|
||||
"deleteTab": "Delete tab?",
|
||||
"deleteTabDescription": "This will permanently delete the tab and return all its items to stock.",
|
||||
"couldNotRenameTab": "Could not rename tab.",
|
||||
"couldNotDeleteTab": "Could not delete tab.",
|
||||
"tabItemSummary": "{count, plural, =1 {1 item - {total}} other {{count} items - {total}}}",
|
||||
"@tabItemSummary": {"placeholders": {"count": {"type": "int"}, "total": {"type": "String"}}},
|
||||
"tabItemCount": "{count, plural, =1 {1 item} other {{count} items}}",
|
||||
|
||||
@@ -242,6 +242,12 @@ abstract class AppLocalizations {
|
||||
/// **'PIN Required'**
|
||||
String get pinRequired;
|
||||
|
||||
/// No description provided for @autoLockOnExit.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Auto-lock on phone lock or app exit'**
|
||||
String get autoLockOnExit;
|
||||
|
||||
/// No description provided for @changePin.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
@@ -506,12 +512,42 @@ abstract class AppLocalizations {
|
||||
/// **'Edit'**
|
||||
String get edit;
|
||||
|
||||
/// No description provided for @editTab.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Edit tab'**
|
||||
String get editTab;
|
||||
|
||||
/// No description provided for @close.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Close'**
|
||||
String get close;
|
||||
|
||||
/// No description provided for @deleteTab.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Delete tab?'**
|
||||
String get deleteTab;
|
||||
|
||||
/// No description provided for @deleteTabDescription.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'This will permanently delete the tab and return all its items to stock.'**
|
||||
String get deleteTabDescription;
|
||||
|
||||
/// No description provided for @couldNotRenameTab.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Could not rename tab.'**
|
||||
String get couldNotRenameTab;
|
||||
|
||||
/// No description provided for @couldNotDeleteTab.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Could not delete tab.'**
|
||||
String get couldNotDeleteTab;
|
||||
|
||||
/// No description provided for @tabItemSummary.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
|
||||
@@ -82,6 +82,9 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
@override
|
||||
String get pinRequired => 'PIN Required';
|
||||
|
||||
@override
|
||||
String get autoLockOnExit => 'Auto-lock on phone lock or app exit';
|
||||
|
||||
@override
|
||||
String get changePin => 'Change PIN';
|
||||
|
||||
@@ -219,9 +222,25 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
@override
|
||||
String get edit => 'Edit';
|
||||
|
||||
@override
|
||||
String get editTab => 'Edit tab';
|
||||
|
||||
@override
|
||||
String get close => 'Close';
|
||||
|
||||
@override
|
||||
String get deleteTab => 'Delete tab?';
|
||||
|
||||
@override
|
||||
String get deleteTabDescription =>
|
||||
'This will permanently delete the tab and return all its items to stock.';
|
||||
|
||||
@override
|
||||
String get couldNotRenameTab => 'Could not rename tab.';
|
||||
|
||||
@override
|
||||
String get couldNotDeleteTab => 'Could not delete tab.';
|
||||
|
||||
@override
|
||||
String tabItemSummary(int count, String total) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
|
||||
@@ -23,6 +23,8 @@ extension AppLocalizationsHelpers on AppLocalizations {
|
||||
'Could not add product to tab.' => couldNotAddProductToTab,
|
||||
'Could not update item quantity.' => couldNotUpdateQuantity,
|
||||
'Could not close tab.' => couldNotCloseTab,
|
||||
'Could not rename tab.' => couldNotRenameTab,
|
||||
'Could not delete tab.' => couldNotDeleteTab,
|
||||
'Could not reload tabs.' => couldNotReloadTabs,
|
||||
'Could not load tab history.' => couldNotLoadHistory,
|
||||
'Could not load more tabs.' => couldNotLoadMoreTabs,
|
||||
|
||||
@@ -82,6 +82,10 @@ class AppLocalizationsNl extends AppLocalizations {
|
||||
@override
|
||||
String get pinRequired => 'PIN vereist';
|
||||
|
||||
@override
|
||||
String get autoLockOnExit =>
|
||||
'Automatisch vergrendelen bij telefoonslot of afsluiten';
|
||||
|
||||
@override
|
||||
String get changePin => 'PIN wijzigen';
|
||||
|
||||
@@ -219,9 +223,25 @@ class AppLocalizationsNl extends AppLocalizations {
|
||||
@override
|
||||
String get edit => 'Bewerken';
|
||||
|
||||
@override
|
||||
String get editTab => 'Poef bewerken';
|
||||
|
||||
@override
|
||||
String get close => 'Sluiten';
|
||||
|
||||
@override
|
||||
String get deleteTab => 'Poef verwijderen?';
|
||||
|
||||
@override
|
||||
String get deleteTabDescription =>
|
||||
'Deze poef wordt definitief verwijderd en alle items worden terug aan de voorraad toegevoegd.';
|
||||
|
||||
@override
|
||||
String get couldNotRenameTab => 'Poef kon niet worden hernoemd.';
|
||||
|
||||
@override
|
||||
String get couldNotDeleteTab => 'Poef kon niet worden verwijderd.';
|
||||
|
||||
@override
|
||||
String tabItemSummary(int count, String total) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"settingsTitle": "Instellingen",
|
||||
"security": "Beveiliging",
|
||||
"pinRequired": "PIN vereist",
|
||||
"autoLockOnExit": "Automatisch vergrendelen bij telefoonslot of afsluiten",
|
||||
"changePin": "PIN wijzigen",
|
||||
"appearance": "Uiterlijk",
|
||||
"language": "Taal",
|
||||
@@ -71,7 +72,12 @@
|
||||
"selectOrOpenTab": "Selecteer of open een poef",
|
||||
"noOpenTabs": "Geen open poefs",
|
||||
"edit": "Bewerken",
|
||||
"editTab": "Poef bewerken",
|
||||
"close": "Sluiten",
|
||||
"deleteTab": "Poef verwijderen?",
|
||||
"deleteTabDescription": "Deze poef wordt definitief verwijderd en alle items worden terug aan de voorraad toegevoegd.",
|
||||
"couldNotRenameTab": "Poef kon niet worden hernoemd.",
|
||||
"couldNotDeleteTab": "Poef kon niet worden verwijderd.",
|
||||
"tabItemSummary": "{count, plural, =1 {1 item - {total}} other {{count} items - {total}}}",
|
||||
"@tabItemSummary": {"placeholders": {"count": {"type": "int"}, "total": {"type": "String"}}},
|
||||
"tabItemCount": "{count, plural, =1 {1 item} other {{count} items}}",
|
||||
|
||||
@@ -4,12 +4,14 @@ enum AppLanguage { system, english, dutch }
|
||||
|
||||
class AppSettings {
|
||||
final bool pinRequired;
|
||||
final bool autoLockEnabled;
|
||||
final AppThemeMode themeMode;
|
||||
final AppLanguage language;
|
||||
final int barGridRows;
|
||||
|
||||
const AppSettings({
|
||||
required this.pinRequired,
|
||||
this.autoLockEnabled = true,
|
||||
required this.themeMode,
|
||||
this.language = AppLanguage.system,
|
||||
this.barGridRows = 3,
|
||||
@@ -17,12 +19,14 @@ class AppSettings {
|
||||
|
||||
AppSettings copyWith({
|
||||
bool? pinRequired,
|
||||
bool? autoLockEnabled,
|
||||
AppThemeMode? themeMode,
|
||||
AppLanguage? language,
|
||||
int? barGridRows,
|
||||
}) {
|
||||
return AppSettings(
|
||||
pinRequired: pinRequired ?? this.pinRequired,
|
||||
autoLockEnabled: autoLockEnabled ?? this.autoLockEnabled,
|
||||
themeMode: themeMode ?? this.themeMode,
|
||||
language: language ?? this.language,
|
||||
barGridRows: barGridRows ?? this.barGridRows,
|
||||
@@ -31,6 +35,7 @@ class AppSettings {
|
||||
|
||||
static const AppSettings defaults = AppSettings(
|
||||
pinRequired: false,
|
||||
autoLockEnabled: true,
|
||||
themeMode: AppThemeMode.system,
|
||||
);
|
||||
|
||||
@@ -39,11 +44,17 @@ class AppSettings {
|
||||
identical(this, other) ||
|
||||
other is AppSettings &&
|
||||
pinRequired == other.pinRequired &&
|
||||
autoLockEnabled == other.autoLockEnabled &&
|
||||
themeMode == other.themeMode &&
|
||||
language == other.language &&
|
||||
barGridRows == other.barGridRows;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
Object.hash(pinRequired, themeMode, language, barGridRows);
|
||||
int get hashCode => Object.hash(
|
||||
pinRequired,
|
||||
autoLockEnabled,
|
||||
themeMode,
|
||||
language,
|
||||
barGridRows,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,11 @@ abstract class BarTabService {
|
||||
|
||||
Future<BarTab> createTab({required String customerName});
|
||||
|
||||
Future<void> renameTab({required String tabId, required String customerName});
|
||||
|
||||
/// Deletes an open tab and returns its items to inventory.
|
||||
Future<List<TabItem>> deleteTab(String tabId);
|
||||
|
||||
Future<void> addProductToTab({
|
||||
required String tabId,
|
||||
required Product product,
|
||||
@@ -203,6 +208,61 @@ class DriftBarTabService implements BarTabService {
|
||||
return tab;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> renameTab({
|
||||
required String tabId,
|
||||
required String customerName,
|
||||
}) async {
|
||||
final name = customerName.trim();
|
||||
if (name.isEmpty) {
|
||||
throw ArgumentError.value(
|
||||
customerName,
|
||||
'customerName',
|
||||
'Must not be empty',
|
||||
);
|
||||
}
|
||||
|
||||
final updatedRows =
|
||||
await (database.update(database.barTabs)
|
||||
..where((tab) => tab.id.equals(tabId)))
|
||||
.write(BarTabsCompanion(customerName: Value(name)));
|
||||
|
||||
if (updatedRows != 1) {
|
||||
throw StateError('Tab not found.');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<TabItem>> deleteTab(String tabId) async {
|
||||
return database.transaction(() async {
|
||||
final tab = await (database.select(
|
||||
database.barTabs,
|
||||
)..where((row) => row.id.equals(tabId))).getSingleOrNull();
|
||||
|
||||
if (tab == null) return <TabItem>[];
|
||||
|
||||
final items = await _getItemsForTab(tabId);
|
||||
|
||||
for (final item in items) {
|
||||
await _increaseProductStock(item.productId, item.quantity);
|
||||
}
|
||||
|
||||
await (database.delete(
|
||||
database.tabItems,
|
||||
)..where((item) => item.tabId.equals(tabId))).go();
|
||||
|
||||
final deletedRows = await (database.delete(
|
||||
database.barTabs,
|
||||
)..where((row) => row.id.equals(tabId))).go();
|
||||
|
||||
if (deletedRows != 1) {
|
||||
throw StateError('Tab was changed before it could be deleted');
|
||||
}
|
||||
|
||||
return items;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> addProductToTab({
|
||||
required String tabId,
|
||||
|
||||
@@ -18,6 +18,7 @@ class DriftSettingsService implements SettingsService {
|
||||
AppSettings _mapRowToSettings(AppSettingsRow row) {
|
||||
return AppSettings(
|
||||
pinRequired: row.pinRequired,
|
||||
autoLockEnabled: row.autoLockEnabled,
|
||||
themeMode: AppThemeMode.values.firstWhere(
|
||||
(mode) => mode.name == row.themeMode,
|
||||
orElse: () => AppThemeMode.system,
|
||||
@@ -50,6 +51,7 @@ class DriftSettingsService implements SettingsService {
|
||||
AppSettingsTableCompanion.insert(
|
||||
id: _settingsId,
|
||||
pinRequired: Value(settings.pinRequired),
|
||||
autoLockEnabled: Value(settings.autoLockEnabled),
|
||||
themeMode: Value(settings.themeMode.name),
|
||||
language: Value(settings.language.name),
|
||||
barGridRows: Value(settings.barGridRows),
|
||||
|
||||
@@ -90,6 +90,37 @@ class BarScreenViewModel extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> renameTab(String tabId, String customerName) async {
|
||||
try {
|
||||
await barTabService.renameTab(tabId: tabId, customerName: customerName);
|
||||
await _reloadTabs();
|
||||
} catch (e, stack) {
|
||||
debugPrint('BarScreenViewModel: renameTab error: $e');
|
||||
_errorMessage = 'Could not rename tab.';
|
||||
notifyListeners();
|
||||
Sentry.captureException(e, stackTrace: stack);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> deleteTab(String tabId) async {
|
||||
try {
|
||||
final deletedItems = await barTabService.deleteTab(tabId);
|
||||
|
||||
for (final item in deletedItems) {
|
||||
inventory.applyStockDelta(item.productId, item.quantity);
|
||||
}
|
||||
|
||||
await _reloadTabs();
|
||||
} catch (e, stack) {
|
||||
debugPrint('BarScreenViewModel: deleteTab error: $e');
|
||||
_errorMessage = 'Could not delete tab.';
|
||||
notifyListeners();
|
||||
Sentry.captureException(e, stackTrace: stack);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> addProductToSelectedTab(Product product) async {
|
||||
final tab = selectedTab;
|
||||
|
||||
|
||||
@@ -108,6 +108,9 @@ class SettingsViewModel extends ChangeNotifier {
|
||||
Future<void> updatePinRequired(bool value) =>
|
||||
_save(_settings.copyWith(pinRequired: value));
|
||||
|
||||
Future<void> updateAutoLockEnabled(bool value) =>
|
||||
_save(_settings.copyWith(autoLockEnabled: value));
|
||||
|
||||
Future<void> updateThemeMode(AppThemeMode mode) =>
|
||||
_save(_settings.copyWith(themeMode: mode));
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:go_router/go_router.dart';
|
||||
import 'package:kooltab2/viewmodels/pin_lock_view_model.dart';
|
||||
import 'package:kooltab2/viewmodels/product_list_view_model.dart';
|
||||
import 'package:kooltab2/views/dialogs/close_tab_dialog.dart';
|
||||
import 'package:kooltab2/views/dialogs/edit_tab_dialog.dart';
|
||||
import 'package:kooltab2/views/dialogs/new_tab_dialog.dart';
|
||||
import 'package:kooltab2/views/widgets/product_tile.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
@@ -167,7 +168,8 @@ class _BarScreenViewState extends State<BarScreenView> {
|
||||
onTabSelected: viewModel.selectTab,
|
||||
onItemQuantityChanged: viewModel.changeItemQuantity,
|
||||
onCloseTabPressed: () => confirmCloseTab(context),
|
||||
onTabClosed: viewModel.closeTab,
|
||||
onTabEdited: (tab) => showEditTabDialog(context, tab),
|
||||
onTabDeleted: (tab) => confirmDeleteTab(context, tab),
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -345,7 +347,8 @@ class _TabPanel extends StatefulWidget {
|
||||
final ValueChanged<String> onTabSelected;
|
||||
final Future<void> Function(TabItem item, int delta) onItemQuantityChanged;
|
||||
final VoidCallback onCloseTabPressed;
|
||||
final Future<void> Function(String) onTabClosed;
|
||||
final Future<void> Function(BarTab) onTabEdited;
|
||||
final Future<void> Function(BarTab) onTabDeleted;
|
||||
|
||||
const _TabPanel({
|
||||
required this.tabs,
|
||||
@@ -356,7 +359,8 @@ class _TabPanel extends StatefulWidget {
|
||||
required this.onTabSelected,
|
||||
required this.onItemQuantityChanged,
|
||||
required this.onCloseTabPressed,
|
||||
required this.onTabClosed,
|
||||
required this.onTabEdited,
|
||||
required this.onTabDeleted,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -461,7 +465,8 @@ class _TabPanelState extends State<_TabPanel> {
|
||||
tabs: filteredTabs,
|
||||
selectedTabId: widget.selectedTabId,
|
||||
onTabSelected: widget.onTabSelected,
|
||||
onTabClosed: widget.onTabClosed,
|
||||
onTabEdited: widget.onTabEdited,
|
||||
onTabDeleted: widget.onTabDeleted,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -504,13 +509,15 @@ class _OpenTabsList extends StatelessWidget {
|
||||
final List<BarTab> tabs;
|
||||
final String? selectedTabId;
|
||||
final ValueChanged<String> onTabSelected;
|
||||
final Future<void> Function(String) onTabClosed;
|
||||
final Future<void> Function(BarTab) onTabEdited;
|
||||
final Future<void> Function(BarTab) onTabDeleted;
|
||||
|
||||
const _OpenTabsList({
|
||||
required this.tabs,
|
||||
required this.selectedTabId,
|
||||
required this.onTabSelected,
|
||||
required this.onTabClosed,
|
||||
required this.onTabEdited,
|
||||
required this.onTabDeleted,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -543,27 +550,16 @@ class _OpenTabsList extends StatelessWidget {
|
||||
extentRatio: 0.6,
|
||||
children: [
|
||||
SlidableAction(
|
||||
onPressed: (_) => onTabSelected(tab.id),
|
||||
onPressed: (_) => onTabEdited(tab),
|
||||
icon: Icons.edit_outlined,
|
||||
label: l10n.edit,
|
||||
backgroundColor: Theme.of(context).colorScheme.secondary,
|
||||
foregroundColor: Theme.of(context).colorScheme.onSecondary,
|
||||
),
|
||||
SlidableAction(
|
||||
onPressed: (_) async {
|
||||
try {
|
||||
await onTabClosed(tab.id);
|
||||
} catch (e, stack) {
|
||||
Sentry.captureException(e, stackTrace: stack);
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(l10n.couldNotCloseTab)),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
icon: Icons.close_rounded,
|
||||
label: l10n.close,
|
||||
onPressed: (_) => onTabDeleted(tab),
|
||||
icon: Icons.delete_outline_rounded,
|
||||
label: l10n.delete,
|
||||
backgroundColor: Theme.of(context).colorScheme.error,
|
||||
foregroundColor: Theme.of(context).colorScheme.onError,
|
||||
),
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
|
||||
import '../../l10n/app_localizations.dart';
|
||||
import '../../models/bar_tab.dart';
|
||||
import '../../viewmodels/bar_screen_view_model.dart';
|
||||
|
||||
Future<void> showEditTabDialog(BuildContext context, BarTab tab) async {
|
||||
final l10n = AppLocalizations.of(context);
|
||||
|
||||
final name = await showDialog<String>(
|
||||
context: context,
|
||||
builder: (_) => _EditTabDialog(initialName: tab.customerName),
|
||||
);
|
||||
|
||||
if (name == null || name.trim().isEmpty || !context.mounted) return;
|
||||
|
||||
try {
|
||||
await context.read<BarScreenViewModel>().renameTab(tab.id, name.trim());
|
||||
} catch (e, stack) {
|
||||
Sentry.captureException(e, stackTrace: stack);
|
||||
if (!context.mounted) return;
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(l10n.couldNotRenameTab)));
|
||||
}
|
||||
}
|
||||
|
||||
class _EditTabDialog extends StatefulWidget {
|
||||
final String initialName;
|
||||
|
||||
const _EditTabDialog({required this.initialName});
|
||||
|
||||
@override
|
||||
State<_EditTabDialog> createState() => _EditTabDialogState();
|
||||
}
|
||||
|
||||
class _EditTabDialogState extends State<_EditTabDialog> {
|
||||
late final TextEditingController _controller;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = TextEditingController(text: widget.initialName);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = AppLocalizations.of(context);
|
||||
|
||||
return AlertDialog(
|
||||
title: Text(l10n.editTab),
|
||||
content: TextField(
|
||||
controller: _controller,
|
||||
autofocus: true,
|
||||
textInputAction: TextInputAction.done,
|
||||
decoration: InputDecoration(labelText: l10n.customerGroupName),
|
||||
onSubmitted: (value) {
|
||||
Navigator.of(context).pop(value);
|
||||
},
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: Text(l10n.cancel),
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: () => Navigator.of(context).pop(_controller.text),
|
||||
child: Text(l10n.confirm),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> confirmDeleteTab(BuildContext context, BarTab tab) async {
|
||||
final l10n = AppLocalizations.of(context);
|
||||
|
||||
final confirmed = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (dialogContext) {
|
||||
return AlertDialog(
|
||||
title: Text(l10n.deleteTab),
|
||||
content: Text(l10n.deleteTabDescription),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(dialogContext).pop(false),
|
||||
child: Text(l10n.cancel),
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: () => Navigator.of(dialogContext).pop(true),
|
||||
child: Text(l10n.delete),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
if (confirmed != true || !context.mounted) return;
|
||||
|
||||
try {
|
||||
await context.read<BarScreenViewModel>().deleteTab(tab.id);
|
||||
} catch (e, stack) {
|
||||
Sentry.captureException(e, stackTrace: stack);
|
||||
if (!context.mounted) return;
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(l10n.couldNotDeleteTab)));
|
||||
}
|
||||
}
|
||||
@@ -241,6 +241,14 @@ class _SettingsScreenViewState extends State<SettingsScreenView> {
|
||||
}
|
||||
},
|
||||
),
|
||||
_SettingsSwitchTile(
|
||||
icon: Icons.lock_clock_rounded,
|
||||
title: l10n.autoLockOnExit,
|
||||
value: settings.autoLockEnabled,
|
||||
onChanged: (value) {
|
||||
settingsViewModel.updateAutoLockEnabled(value);
|
||||
},
|
||||
),
|
||||
if (settings.pinRequired)
|
||||
_SettingsTile(
|
||||
icon: Icons.pin_rounded,
|
||||
|
||||
Reference in New Issue
Block a user