Add support for importing raivo codes
This commit is contained in:
parent
471621880f
commit
94eb5d8c97
1 changed files with 152 additions and 0 deletions
152
lib/ui/settings/data/import/ravio_plain_text_import.dart
Normal file
152
lib/ui/settings/data/import/ravio_plain_text_import.dart
Normal file
|
@ -0,0 +1,152 @@
|
|||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:ente_auth/ente_theme_data.dart';
|
||||
import 'package:ente_auth/l10n/l10n.dart';
|
||||
import 'package:ente_auth/models/code.dart';
|
||||
import 'package:ente_auth/services/authenticator_service.dart';
|
||||
import 'package:ente_auth/store/code_store.dart';
|
||||
import 'package:ente_auth/ui/components/dialog_widget.dart';
|
||||
import 'package:ente_auth/ui/components/models/button_type.dart';
|
||||
import 'package:ente_auth/utils/dialog_util.dart';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
class PlainTextImport extends StatelessWidget {
|
||||
const PlainTextImport({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = context.l10n;
|
||||
return Column(
|
||||
children: [
|
||||
Text(
|
||||
l10n.importInstruction,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Container(
|
||||
color: Theme.of(context).colorScheme.gNavBackgroundColor,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Text(
|
||||
"otpauth://totp/provider.com:you@email.com?secret=YOUR_SECRET",
|
||||
style: TextStyle(
|
||||
fontFeatures: const [FontFeature.tabularFigures()],
|
||||
fontFamily: Platform.isIOS ? "Courier" : "monospace",
|
||||
fontSize: 13,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Text(l10n.importCodeDelimiterInfo),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Future<void> showImportInstructionDialog(BuildContext context) async {
|
||||
final l10n = context.l10n;
|
||||
final AlertDialog alert = AlertDialog(
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||
title: Text(
|
||||
l10n.importCodes,
|
||||
style: Theme.of(context).textTheme.headline6,
|
||||
),
|
||||
content: const SingleChildScrollView(
|
||||
child: PlainTextImport(),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
child: Text(
|
||||
l10n.cancel,
|
||||
style: const TextStyle(
|
||||
color: Colors.red,
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context, rootNavigator: true).pop('dialog');
|
||||
},
|
||||
),
|
||||
TextButton(
|
||||
child: Text(l10n.selectFile),
|
||||
onPressed: () {
|
||||
Navigator.of(context, rootNavigator: true).pop('dialog');
|
||||
_pickImportFile(context);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
return showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return alert;
|
||||
},
|
||||
barrierColor: Colors.black12,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Future<void> _pickImportFile(BuildContext context) async {
|
||||
final l10n = context.l10n;
|
||||
FilePickerResult? result = await FilePicker.platform.pickFiles();
|
||||
if (result == null) {
|
||||
return;
|
||||
}
|
||||
final progressDialog = createProgressDialog(context, l10n.pleaseWait);
|
||||
await progressDialog.show();
|
||||
try {
|
||||
File file = File(result.files.single.path!);
|
||||
final codes = await file.readAsString();
|
||||
List<String> splitCodes = codes.split(",");
|
||||
if (splitCodes.length == 1) {
|
||||
splitCodes = codes.split("\n");
|
||||
}
|
||||
final parsedCodes = [];
|
||||
for (final code in splitCodes) {
|
||||
try {
|
||||
parsedCodes.add(Code.fromRawData(code));
|
||||
} catch (e) {
|
||||
Logger('PlainText').severe("Could not parse code", e);
|
||||
}
|
||||
}
|
||||
for (final code in parsedCodes) {
|
||||
await CodeStore.instance.addCode(code, shouldSync: false);
|
||||
}
|
||||
unawaited(AuthenticatorService.instance.sync());
|
||||
await progressDialog.hide();
|
||||
final DialogWidget dialog = choiceDialog(
|
||||
title: context.l10n.importSuccessTitle,
|
||||
body: context.l10n.importSuccessDesc(parsedCodes.length),
|
||||
// body: "You have imported " + parsedCodes.length.toString() + " codes!",
|
||||
firstButtonLabel: l10n.ok,
|
||||
firstButtonOnTap: () async {
|
||||
Navigator.of(context, rootNavigator: true).pop('progressDialog');
|
||||
},
|
||||
firstButtonType: ButtonType.primary,
|
||||
);
|
||||
await showConfettiDialog(
|
||||
context: context,
|
||||
dialogBuilder: (BuildContext context) {
|
||||
return dialog;
|
||||
},
|
||||
);
|
||||
} catch (e) {
|
||||
await progressDialog.hide();
|
||||
await showErrorDialog(
|
||||
context,
|
||||
context.l10n.sorry,
|
||||
context.l10n.importFailureDesc,
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue