accept user request

This commit is contained in:
otsmr 2025-01-25 13:30:18 +01:00
parent 0da87f1712
commit cd27294fde
9 changed files with 88 additions and 45 deletions

View file

@ -96,6 +96,19 @@ class DbContacts extends CvModelBase {
); );
} }
static Future acceptUser(int userId) async {
Map<String, dynamic> valuesToUpdate = {
columnAccepted: 1,
columnRequested: 0,
};
await dbProvider.db!.update(
tableName,
valuesToUpdate,
where: "$columnUserId = ?",
whereArgs: [userId],
);
}
static Future deleteUser(int userId) async { static Future deleteUser(int userId) async {
await dbProvider.db!.delete( await dbProvider.db!.delete(
tableName, tableName,

View file

@ -3,7 +3,14 @@ import 'package:json_annotation/json_annotation.dart';
import 'package:twonly/src/utils/json.dart'; import 'package:twonly/src/utils/json.dart';
part 'message.g.dart'; part 'message.g.dart';
enum MessageKind { textMessage, image, video, contactRequest, rejectRequest } enum MessageKind {
textMessage,
image,
video,
contactRequest,
rejectRequest,
acceptRequest
}
// so _$MessageKindEnumMap gets generated // so _$MessageKindEnumMap gets generated
@JsonSerializable() @JsonSerializable()

View file

@ -20,6 +20,7 @@ const _$MessageKindEnumMap = {
MessageKind.video: 'video', MessageKind.video: 'video',
MessageKind.contactRequest: 'contactRequest', MessageKind.contactRequest: 'contactRequest',
MessageKind.rejectRequest: 'rejectRequest', MessageKind.rejectRequest: 'rejectRequest',
MessageKind.acceptRequest: 'acceptRequest',
}; };
Message _$MessageFromJson(Map<String, dynamic> json) => Message( Message _$MessageFromJson(Map<String, dynamic> json) => Message(

View file

@ -3,7 +3,6 @@ import 'dart:convert';
import 'dart:math'; import 'dart:math';
import 'package:fixnum/fixnum.dart'; import 'package:fixnum/fixnum.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart'; import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:twonly/src/model/contacts_model.dart'; import 'package:twonly/src/model/contacts_model.dart';
@ -12,7 +11,6 @@ import 'package:twonly/src/proto/api/client_to_server.pb.dart' as client;
import 'package:twonly/src/proto/api/client_to_server.pbserver.dart'; import 'package:twonly/src/proto/api/client_to_server.pbserver.dart';
import 'package:twonly/src/proto/api/error.pb.dart'; import 'package:twonly/src/proto/api/error.pb.dart';
import 'package:twonly/src/proto/api/server_to_client.pb.dart' as server; import 'package:twonly/src/proto/api/server_to_client.pb.dart' as server;
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/misc.dart';
import 'package:twonly/src/utils/storage.dart'; import 'package:twonly/src/utils/storage.dart';
// ignore: library_prefixes // ignore: library_prefixes
@ -176,6 +174,10 @@ class ApiProvider {
DbContacts.deleteUser(fromUserId.toInt()); DbContacts.deleteUser(fromUserId.toInt());
updateNotifier(); updateNotifier();
break; break;
case MessageKind.acceptRequest:
DbContacts.acceptUser(fromUserId.toInt());
updateNotifier();
break;
default: default:
log.shout("Got unknown MessageKind $message"); log.shout("Got unknown MessageKind $message");
} }
@ -253,39 +255,6 @@ class ApiProvider {
return ClientToServer()..v0 = v0; return ClientToServer()..v0 = v0;
} }
static String getLocalizedString(BuildContext context, ErrorCode code) {
switch (code.toString()) {
case "Unknown":
return AppLocalizations.of(context)!.errorUnknown;
case "BadRequest":
return AppLocalizations.of(context)!.errorBadRequest;
case "TooManyRequests":
return AppLocalizations.of(context)!.errorTooManyRequests;
case "InternalError":
return AppLocalizations.of(context)!.errorInternalError;
case "InvalidInvitationCode":
return AppLocalizations.of(context)!.errorInvalidInvitationCode;
case "UsernameAlreadyTaken":
return AppLocalizations.of(context)!.errorUsernameAlreadyTaken;
case "SignatureNotValid":
return AppLocalizations.of(context)!.errorSignatureNotValid;
case "UsernameNotFound":
return AppLocalizations.of(context)!.errorUsernameNotFound;
case "UsernameNotValid":
return AppLocalizations.of(context)!.errorUsernameNotValid;
case "InvalidPublicKey":
return AppLocalizations.of(context)!.errorInvalidPublicKey;
case "SessionAlreadyAuthenticated":
return AppLocalizations.of(context)!.errorSessionAlreadyAuthenticated;
case "SessionNotAuthenticated":
return AppLocalizations.of(context)!.errorSessionNotAuthenticated;
case "OnlyOneSessionAllowed":
return AppLocalizations.of(context)!.errorOnlyOneSessionAllowed;
default:
return code.toString(); // Fallback for unrecognized keys
}
}
Result _asResult(server.ServerToClient msg) { Result _asResult(server.ServerToClient msg) {
if (msg.v0.response.hasOk()) { if (msg.v0.response.hasOk()) {
return Result.success(msg.v0.response.ok); return Result.success(msg.v0.response.ok);

View file

@ -64,6 +64,12 @@ Future<Result> rejectUserRequest(Int64 userId) async {
return encryptAndSendMessage(userId, msg); return encryptAndSendMessage(userId, msg);
} }
Future<Result> acceptUserRequest(Int64 userId) async {
Message msg =
Message(kind: MessageKind.acceptRequest, timestamp: DateTime.now());
return encryptAndSendMessage(userId, msg);
}
Future<Result> createNewUser(String username, String inviteCode) async { Future<Result> createNewUser(String username, String inviteCode) async {
final storage = getSecureStorage(); final storage = getSecureStorage();

View file

@ -1,10 +1,13 @@
import 'dart:io'; import 'dart:io';
import 'dart:math'; import 'dart:math';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:gal/gal.dart'; import 'package:gal/gal.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:twonly/src/proto/api/error.pb.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
Future<void> writeLogToFile(LogRecord record) async { Future<void> writeLogToFile(LogRecord record) async {
final directory = await getApplicationDocumentsDirectory(); final directory = await getApplicationDocumentsDirectory();
@ -49,3 +52,36 @@ Uint8List getRandomUint8List(int length) {
return randomBytes; return randomBytes;
} }
String errorCodeToText(BuildContext context, ErrorCode code) {
switch (code.toString()) {
case "Unknown":
return AppLocalizations.of(context)!.errorUnknown;
case "BadRequest":
return AppLocalizations.of(context)!.errorBadRequest;
case "TooManyRequests":
return AppLocalizations.of(context)!.errorTooManyRequests;
case "InternalError":
return AppLocalizations.of(context)!.errorInternalError;
case "InvalidInvitationCode":
return AppLocalizations.of(context)!.errorInvalidInvitationCode;
case "UsernameAlreadyTaken":
return AppLocalizations.of(context)!.errorUsernameAlreadyTaken;
case "SignatureNotValid":
return AppLocalizations.of(context)!.errorSignatureNotValid;
case "UsernameNotFound":
return AppLocalizations.of(context)!.errorUsernameNotFound;
case "UsernameNotValid":
return AppLocalizations.of(context)!.errorUsernameNotValid;
case "InvalidPublicKey":
return AppLocalizations.of(context)!.errorInvalidPublicKey;
case "SessionAlreadyAuthenticated":
return AppLocalizations.of(context)!.errorSessionAlreadyAuthenticated;
case "SessionNotAuthenticated":
return AppLocalizations.of(context)!.errorSessionNotAuthenticated;
case "OnlyOneSessionAllowed":
return AppLocalizations.of(context)!.errorOnlyOneSessionAllowed;
default:
return code.toString(); // Fallback for unrecognized keys
}
}

View file

@ -24,7 +24,8 @@ class _NewMessageView extends State<NewMessageView> {
} }
Future<void> _loadUsers() async { Future<void> _loadUsers() async {
final users = await DbContacts.getUsers(); final users =
(await DbContacts.getUsers()).where((c) => c.accepted).toList();
setState(() { setState(() {
_knownUsers = users; _knownUsers = users;
_filteredUsers = List.from(_knownUsers); _filteredUsers = List.from(_knownUsers);

View file

@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:twonly/src/utils/api.dart'; import 'package:twonly/src/utils/api.dart';
import 'package:twonly/src/utils/misc.dart';
class RegisterView extends StatefulWidget { class RegisterView extends StatefulWidget {
const RegisterView({super.key, required this.callbackOnSuccess}); const RegisterView({super.key, required this.callbackOnSuccess});
@ -131,8 +132,7 @@ class _RegisterViewState extends State<RegisterView> {
return; return;
} }
if (context.mounted) { if (context.mounted) {
final errMsg = final errMsg = errorCodeToText(context, res.error);
ApiProvider.getLocalizedString(context, res.error);
showAlertDialog(context, "Oh no!", errMsg); showAlertDialog(context, "Oh no!", errMsg);
} }
}, },

View file

@ -1,7 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:logging/logging.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:twonly/src/components/initialsavatar.dart'; import 'package:twonly/src/components/initialsavatar.dart';
import 'package:twonly/src/model/contacts_model.dart'; import 'package:twonly/src/model/contacts_model.dart';
@ -93,7 +92,11 @@ class _SearchUsernameView extends State<SearchUsernameView> {
Text(AppLocalizations.of(context)!.searchUsernameQrCodeBtn), Text(AppLocalizations.of(context)!.searchUsernameQrCodeBtn),
), ),
SizedBox(height: 30), SizedBox(height: 30),
if (context.read<NotifyProvider>().allContacts.isNotEmpty) if (context
.read<NotifyProvider>()
.allContacts
.where((contact) => !contact.accepted)
.isNotEmpty)
Container( Container(
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
padding: EdgeInsets.symmetric(horizontal: 4.0, vertical: 10), padding: EdgeInsets.symmetric(horizontal: 4.0, vertical: 10),
@ -131,7 +134,11 @@ class ContactsListView extends StatefulWidget {
class _ContactsListViewState extends State<ContactsListView> { class _ContactsListViewState extends State<ContactsListView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
List<Contact> contacts = context.read<NotifyProvider>().allContacts; List<Contact> contacts = context
.read<NotifyProvider>()
.allContacts
.where((contact) => !contact.accepted)
.toList();
return ListView.builder( return ListView.builder(
itemCount: contacts.length, itemCount: contacts.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
@ -172,9 +179,12 @@ class _ContactsListViewState extends State<ContactsListView> {
), ),
IconButton( IconButton(
icon: Icon(Icons.check, color: Colors.green), icon: Icon(Icons.check, color: Colors.green),
onPressed: () { onPressed: () async {
// Handle accept action await DbContacts.acceptUser(contact.userId.toInt());
print('Accepted ${contact.displayName}'); if (context.mounted) {
context.read<NotifyProvider>().update();
}
acceptUserRequest(contact.userId);
}, },
), ),
], ],