Add confirm=? query-param to delete-user route and confirm dialog in flutter [skip-tests]
This commit is contained in:
@@ -190,6 +190,9 @@ class APIClient {
|
||||
relURL: 'users/$uid',
|
||||
fn: User.fromJson,
|
||||
authToken: auth.getToken(),
|
||||
query: {
|
||||
'confirm': ['true']
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -1,5 +1,7 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:action_slider/action_slider.dart';
|
||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
@@ -527,17 +529,30 @@ class _AccountRootPageState extends State<AccountRootPage> {
|
||||
final acc = AppAuth();
|
||||
if (!acc.isAuth()) return;
|
||||
|
||||
try {
|
||||
TODO ASK BEFORE DELETING TEH FUCKING USER !!!!!!!
|
||||
final r1 = await UIDialogs.showConfirmDialog(context, "Delete Account?", text: "Are you sure you want to delete your account?", okText: "Delete", cancelText: "Cancel");
|
||||
if (!r1) return;
|
||||
|
||||
final r2 = await UIDialogs.showConfirmDialog(context, "Really sure?", text: "Are you really sure you want to delete your account?.\n\nThis includes all your messages and channels etc.\nThis action cannot be undone!", okText: "Delete", cancelText: "Cancel");
|
||||
if (!r2) return;
|
||||
|
||||
final r3 = await UIDialogs.showTextInput(context, "Please input 'Delete' to delete your Account.", "");
|
||||
if (r3 == null) return;
|
||||
if (r3.trim().toLowerCase() != 'delete') return;
|
||||
|
||||
final r4 = await this._showDeleteDialog(context);
|
||||
if (!r4) return;
|
||||
|
||||
final r5 = await this._showDeleteAccountWaitDialog(context);
|
||||
if (!r5) return;
|
||||
|
||||
try {
|
||||
await APIClient.deleteUser(acc, acc.userID!);
|
||||
|
||||
Toaster.info('Logout', 'Successfully logged out');
|
||||
|
||||
//TODO clear messages/channels/etc in open views
|
||||
acc.clear();
|
||||
await acc.save();
|
||||
|
||||
//TODO clear messages/channels/etc in open views
|
||||
} catch (exc, trace) {
|
||||
Toaster.error("Error", 'Failed to delete user');
|
||||
ApplicationLog.error('Failed to delete user: ' + exc.toString(), trace: trace);
|
||||
@@ -570,4 +585,136 @@ class _AccountRootPageState extends State<AccountRootPage> {
|
||||
ApplicationLog.error('Failed to update username: ' + exc.toString(), trace: trace);
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> _showDeleteDialog(BuildContext context) {
|
||||
return showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Text("Delete Account?"),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
SizedBox(
|
||||
child: ActionSlider.standard(
|
||||
sliderBehavior: SliderBehavior.stretch,
|
||||
width: 300,
|
||||
backgroundColor: Colors.white,
|
||||
toggleColor: Colors.red,
|
||||
action: (controller) => Navigator.of(context).pop(true),
|
||||
child: const Text('Slide to delete'),
|
||||
),
|
||||
width: 300,
|
||||
height: 65,
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(false),
|
||||
child: Text('Cancel'),
|
||||
),
|
||||
],
|
||||
),
|
||||
).then((value) => value ?? false);
|
||||
}
|
||||
|
||||
Future<bool> _showDeleteAccountWaitDialog(BuildContext context) {
|
||||
final completer = Completer<bool>();
|
||||
|
||||
bool isTimerActive = true;
|
||||
|
||||
const int totalSeconds = 20;
|
||||
int secondsRemaining = totalSeconds;
|
||||
double percentageRemaining = 1.0;
|
||||
|
||||
late Timer timer;
|
||||
|
||||
showDialog<void>(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (BuildContext dialogContext) {
|
||||
void Function(void Function())? setStateInner = null;
|
||||
|
||||
final t0 = DateTime.now();
|
||||
|
||||
timer = Timer.periodic(const Duration(milliseconds: 50), (t) {
|
||||
setStateInner?.call(() {
|
||||
percentageRemaining = 1 - (DateTime.now().millisecondsSinceEpoch - t0.millisecondsSinceEpoch) / (totalSeconds * 1000.0);
|
||||
secondsRemaining = (totalSeconds * percentageRemaining).ceil();
|
||||
|
||||
if (secondsRemaining <= 0) {
|
||||
t.cancel();
|
||||
isTimerActive = false;
|
||||
|
||||
// Close the dialog and return true for successful deletion
|
||||
Navigator.of(dialogContext).pop();
|
||||
if (!completer.isCompleted) {
|
||||
completer.complete(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return StatefulBuilder(
|
||||
builder: (context, setState) {
|
||||
setStateInner = setState;
|
||||
return AlertDialog(
|
||||
title: const Text('Delete Account'),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
'Your account will be deleted in $secondsRemaining seconds.',
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
SizedBox(
|
||||
height: 100,
|
||||
width: 100,
|
||||
child: CircularProgressIndicator(
|
||||
value: 1 - percentageRemaining,
|
||||
strokeWidth: 8.0,
|
||||
valueColor: const AlwaysStoppedAnimation<Color>(Colors.red),
|
||||
backgroundColor: Colors.grey[300],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
// Cancel the timer
|
||||
if (timer.isActive) {
|
||||
timer.cancel();
|
||||
}
|
||||
isTimerActive = false;
|
||||
|
||||
// Close the dialog and return false for cancellation
|
||||
Navigator.of(dialogContext).pop();
|
||||
if (!completer.isCompleted) {
|
||||
completer.complete(false);
|
||||
}
|
||||
},
|
||||
child: const Text('CANCEL'),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
).then((_) {
|
||||
// Ensure timer is cancelled if dialog is dismissed
|
||||
if (timer.isActive) {
|
||||
timer.cancel();
|
||||
}
|
||||
|
||||
// If the completer hasn't been completed yet (e.g., if the dialog is dismissed),
|
||||
// complete it with false
|
||||
if (!completer.isCompleted && isTimerActive) {
|
||||
completer.complete(false);
|
||||
}
|
||||
});
|
||||
|
||||
return completer.future;
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:simplecloudnotifier/api/api_client.dart';
|
||||
import 'package:simplecloudnotifier/models/keytoken.dart';
|
||||
|
@@ -1,6 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:simplecloudnotifier/api/api_client.dart';
|
||||
import 'package:simplecloudnotifier/components/error_display/error_display.dart';
|
||||
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
||||
|
@@ -5,7 +5,6 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:simplecloudnotifier/models/channel.dart';
|
||||
import 'package:simplecloudnotifier/models/scn_message.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:simplecloudnotifier/state/app_settings.dart';
|
||||
import 'package:simplecloudnotifier/utils/ui.dart';
|
||||
|
||||
|
@@ -1,7 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
import 'package:simplecloudnotifier/api/api_client.dart';
|
||||
|
@@ -186,8 +186,8 @@ class _SettingsRootPageState extends State<SettingsRootPage> {
|
||||
SettingsTile.navigation(
|
||||
leading: Icon(FontAwesomeIcons.solidBell),
|
||||
title: Text('FCM Token'),
|
||||
value: Text(AppAuth().getToken()),
|
||||
onPressed: (context) => _clipboardCopy(AppAuth().getToken()),
|
||||
value: Text(AppAuth().getTokenOrNull() ?? 'N/A'),
|
||||
onPressed: (context) => _clipboardCopy(AppAuth().getTokenOrNull() ?? ''),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@@ -1,6 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:simplecloudnotifier/api/api_client.dart';
|
||||
import 'package:simplecloudnotifier/components/error_display/error_display.dart';
|
||||
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
||||
|
@@ -225,6 +225,10 @@ class AppAuth extends ChangeNotifier implements TokenSource {
|
||||
return _tokenAdmin!;
|
||||
}
|
||||
|
||||
String? getTokenOrNull() {
|
||||
return _tokenAdmin;
|
||||
}
|
||||
|
||||
@override
|
||||
String getUserID() {
|
||||
return _userID!;
|
||||
|
@@ -1,4 +1,3 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:simplecloudnotifier/state/globals.dart';
|
||||
|
||||
|
@@ -22,6 +22,14 @@ packages:
|
||||
description: dart
|
||||
source: sdk
|
||||
version: "0.3.3"
|
||||
action_slider:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: action_slider
|
||||
sha256: fad0720cde9bf06c12594c15da17dba087556a3285875a91aee3d3a64a3072e2
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.0"
|
||||
analyzer:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@@ -2,7 +2,7 @@ name: simplecloudnotifier
|
||||
description: "Receive push messages"
|
||||
publish_to: 'none'
|
||||
|
||||
version: 2.0.0+474
|
||||
version: 2.0.0+479
|
||||
|
||||
environment:
|
||||
sdk: '>=3.2.6 <4.0.0'
|
||||
@@ -40,6 +40,7 @@ dependencies:
|
||||
mobile_scanner: ^6.0.1
|
||||
settings_ui: ^2.0.2
|
||||
git_stamp: ^5.10.0
|
||||
action_slider: ^0.7.0
|
||||
dependency_overrides:
|
||||
font_awesome_flutter:
|
||||
path: deps/font_awesome_flutter
|
||||
|
Reference in New Issue
Block a user