Initial commit
Some checks failed
Main Deploy Workflow / deploy_web (push) Has been cancelled
Main Deploy Workflow / deploy_playstore_internal (push) Has been cancelled
Close stale issues and PRs / stale (push) Has been cancelled

This commit is contained in:
2026-09-11 15:59:56 -03:00
commit 0037b0b9cf
651 changed files with 199705 additions and 0 deletions

View File

@@ -0,0 +1,104 @@
// SPDX-FileCopyrightText: 2019-Present Christian Kußowski
// SPDX-FileCopyrightText: 2019-Present Contributors to FluffyChat
//
// SPDX-License-Identifier: AGPL-3.0-or-later
import 'package:tatchat/l10n/l10n.dart';
import 'package:tatchat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart';
import 'package:tatchat/widgets/adaptive_dialogs/show_text_input_dialog.dart';
import 'package:tatchat/widgets/future_loading_dialog.dart';
import 'package:tatchat/widgets/matrix.dart';
import 'package:material_ui/material_ui.dart';
import 'package:matrix/matrix.dart';
import 'settings_3pid_view.dart';
class Settings3Pid extends StatefulWidget {
static int sendAttempt = 0;
const Settings3Pid({super.key});
@override
Settings3PidController createState() => Settings3PidController();
}
class Settings3PidController extends State<Settings3Pid> {
Future<void> add3PidAction() async {
final l10n = L10n.of(context);
final matrix = Matrix.of(context);
final input = await showTextInputDialog(
useRootNavigator: false,
context: context,
title: l10n.enterAnEmailAddress,
okLabel: l10n.ok,
cancelLabel: l10n.cancel,
hintText: l10n.enterAnEmailAddress,
keyboardType: TextInputType.emailAddress,
);
if (input == null) return;
if (!mounted) return;
final clientSecret = DateTime.now().millisecondsSinceEpoch.toString();
final response = await showFutureLoadingDialog(
context: context,
future: () => matrix.client.requestTokenToRegisterEmail(
clientSecret,
input,
Settings3Pid.sendAttempt++,
),
);
if (response.error != null) return;
if (!mounted) return;
final ok = await showOkAlertDialog(
useRootNavigator: false,
context: context,
title: l10n.weSentYouAnEmail,
message: l10n.pleaseClickOnLink,
okLabel: l10n.iHaveClickedOnLink,
);
if (ok != OkCancelResult.ok) return;
if (!mounted) return;
final success = await showFutureLoadingDialog(
context: context,
delay: false,
future: () => matrix.client.uiaRequestBackground(
(auth) => matrix.client.add3PID(
clientSecret,
response.result!.sid,
auth: auth,
),
),
);
if (success.error != null) return;
setState(() => request = null);
}
Future<List<ThirdPartyIdentifier>?>? request;
Future<void> delete3Pid(ThirdPartyIdentifier identifier) async {
final l10n = L10n.of(context);
final matrix = Matrix.of(context);
if (await showOkCancelAlertDialog(
useRootNavigator: false,
context: context,
title: l10n.areYouSure,
okLabel: l10n.yes,
cancelLabel: l10n.cancel,
) !=
OkCancelResult.ok) {
return;
}
if (!mounted) return;
final success = await showFutureLoadingDialog(
context: context,
future: () => matrix.client.delete3pidFromAccount(
identifier.address,
identifier.medium,
),
);
if (success.error != null) return;
setState(() => request = null);
}
@override
Widget build(BuildContext context) => Settings3PidView(this);
}

View File

@@ -0,0 +1,119 @@
// SPDX-FileCopyrightText: 2019-Present Christian Kußowski
// SPDX-FileCopyrightText: 2019-Present Contributors to FluffyChat
//
// SPDX-License-Identifier: AGPL-3.0-or-later
import 'package:tatchat/l10n/l10n.dart';
import 'package:tatchat/pages/settings_3pid/settings_3pid.dart';
import 'package:tatchat/widgets/layouts/max_width_body.dart';
import 'package:tatchat/widgets/matrix.dart';
import 'package:material_ui/material_ui.dart';
import 'package:matrix/matrix.dart';
class Settings3PidView extends StatelessWidget {
final Settings3PidController controller;
const Settings3PidView(this.controller, {super.key});
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
controller.request ??= Matrix.of(context).client.getAccount3PIDs();
return Scaffold(
appBar: AppBar(
leading: const Center(child: BackButton()),
title: Text(L10n.of(context).passwordRecovery),
actions: [
IconButton(
icon: const Icon(Icons.add_outlined),
onPressed: controller.add3PidAction,
tooltip: L10n.of(context).addEmail,
),
],
),
body: MaxWidthBody(
withScrolling: false,
child: FutureBuilder<List<ThirdPartyIdentifier>?>(
future: controller.request,
builder:
(
BuildContext context,
AsyncSnapshot<List<ThirdPartyIdentifier>?> snapshot,
) {
if (snapshot.hasError) {
return Center(
child: Text(
snapshot.error.toString(),
textAlign: TextAlign.center,
),
);
}
if (!snapshot.hasData) {
return const Center(
child: CircularProgressIndicator.adaptive(strokeWidth: 2),
);
}
final identifier = snapshot.data!;
return Column(
children: [
ListTile(
leading: CircleAvatar(
backgroundColor: theme.scaffoldBackgroundColor,
foregroundColor: identifier.isEmpty
? Colors.orange
: Colors.grey,
child: Icon(
identifier.isEmpty
? Icons.warning_outlined
: Icons.info_outlined,
),
),
title: Text(
identifier.isEmpty
? L10n.of(context).noPasswordRecoveryDescription
: L10n.of(
context,
).withTheseAddressesRecoveryDescription,
),
),
const Divider(),
Expanded(
child: ListView.builder(
itemCount: identifier.length,
itemBuilder: (BuildContext context, int i) => ListTile(
leading: CircleAvatar(
backgroundColor: theme.scaffoldBackgroundColor,
foregroundColor: Colors.grey,
child: Icon(identifier[i].iconData),
),
title: Text(identifier[i].address),
trailing: IconButton(
tooltip: L10n.of(context).delete,
icon: const Icon(Icons.delete_forever_outlined),
color: Colors.red,
onPressed: () =>
controller.delete3Pid(identifier[i]),
),
),
),
),
],
);
},
),
),
);
}
}
extension on ThirdPartyIdentifier {
IconData get iconData {
switch (medium) {
case ThirdPartyIdentifierMedium.email:
return Icons.mail_outline_rounded;
case ThirdPartyIdentifierMedium.msisdn:
return Icons.phone_android_outlined;
}
}
}