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,134 @@
// SPDX-FileCopyrightText: 2019-Present Christian Kußowski
// SPDX-FileCopyrightText: 2019-Present Contributors to FluffyChat
//
// SPDX-License-Identifier: AGPL-3.0-or-later
import 'dart:async';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:tatchat/l10n/l10n.dart';
import 'package:tatchat/pages/new_private_chat/new_private_chat_view.dart';
import 'package:tatchat/pages/new_private_chat/qr_scanner_modal.dart';
import 'package:tatchat/utils/adaptive_bottom_sheet.dart';
import 'package:tatchat/utils/tatu_share.dart';
import 'package:tatchat/utils/platform_infos.dart';
import 'package:tatchat/utils/url_launcher.dart';
import 'package:tatchat/widgets/matrix.dart';
import 'package:flutter/services.dart';
import 'package:material_ui/material_ui.dart';
import 'package:matrix/matrix.dart';
import '../../widgets/adaptive_dialogs/user_dialog.dart';
class NewPrivateChat extends StatefulWidget {
final String? deeplink;
const NewPrivateChat({super.key, required this.deeplink});
@override
NewPrivateChatController createState() => NewPrivateChatController();
}
class NewPrivateChatController extends State<NewPrivateChat> {
final TextEditingController controller = TextEditingController();
final FocusNode textFieldFocus = FocusNode();
Future<List<Profile>>? searchResponse;
Timer? _searchCoolDown;
static const Duration _coolDown = Duration(milliseconds: 500);
@override
void initState() {
super.initState();
final deeplink = widget.deeplink;
if (deeplink != null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
UrlLauncher(context, deeplink).openMatrixToUrl();
});
}
}
Future<void> searchUsers([String? input]) async {
final searchTerm = input ?? controller.text;
if (searchTerm.isEmpty) {
_searchCoolDown?.cancel();
setState(() {
searchResponse = _searchCoolDown = null;
});
return;
}
_searchCoolDown?.cancel();
_searchCoolDown = Timer(_coolDown, () {
setState(() {
searchResponse = _searchUser(searchTerm);
});
});
}
Future<List<Profile>> _searchUser(String searchTerm) async {
final result = await Matrix.of(
context,
).client.searchUserDirectory(searchTerm);
final profiles = result.results;
if (searchTerm.isValidMatrixIdStrict() &&
searchTerm.sigil == '@' &&
!profiles.any((profile) => profile.userId == searchTerm)) {
profiles.add(Profile(userId: searchTerm));
}
return profiles;
}
void inviteAction() => TatuShare.shareInviteLink(context);
Future<void> openScannerAction() async {
final l10n = L10n.of(context);
final scaffoldMessenger = ScaffoldMessenger.of(context);
if (PlatformInfos.isAndroid) {
final info = await DeviceInfoPlugin().androidInfo;
if (!mounted) return;
if (info.version.sdkInt < 21) {
scaffoldMessenger.showSnackBar(
SnackBar(content: Text(l10n.unsupportedAndroidVersionLong)),
);
return;
}
}
if (!mounted) return;
await showAdaptiveBottomSheet(
context: context,
builder: (_) => QrScannerModal(
onScan: (link) => UrlLauncher(context, link).openMatrixToUrl(),
),
);
}
Future<void> copyUserId() async {
final scaffoldMessenger = ScaffoldMessenger.of(context);
final l10n = L10n.of(context);
await Clipboard.setData(
ClipboardData(text: Matrix.of(context).client.userID!),
);
if (!mounted) return;
scaffoldMessenger.showSnackBar(
SnackBar(content: Text(l10n.copiedToClipboard)),
);
}
void openUserModal(Profile profile) =>
UserDialog.show(context: context, profile: profile);
@override
void dispose() {
controller.dispose();
textFieldFocus.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) => NewPrivateChatView(this);
}