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 {
await dbProvider.db!.delete(
tableName,

View file

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

View file

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

View file

@ -3,7 +3,6 @@ import 'dart:convert';
import 'dart:math';
import 'package:fixnum/fixnum.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
import 'package:logging/logging.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/error.pb.dart';
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/storage.dart';
// ignore: library_prefixes
@ -176,6 +174,10 @@ class ApiProvider {
DbContacts.deleteUser(fromUserId.toInt());
updateNotifier();
break;
case MessageKind.acceptRequest:
DbContacts.acceptUser(fromUserId.toInt());
updateNotifier();
break;
default:
log.shout("Got unknown MessageKind $message");
}
@ -253,39 +255,6 @@ class ApiProvider {
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) {
if (msg.v0.response.hasOk()) {
return Result.success(msg.v0.response.ok);

View file

@ -64,6 +64,12 @@ Future<Result> rejectUserRequest(Int64 userId) async {
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 {
final storage = getSecureStorage();

View file

@ -1,10 +1,13 @@
import 'dart:io';
import 'dart:math';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:gal/gal.dart';
import 'package:logging/logging.dart';
import 'package:path_provider/path_provider.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 {
final directory = await getApplicationDocumentsDirectory();
@ -49,3 +52,36 @@ Uint8List getRandomUint8List(int length) {
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 {
final users = await DbContacts.getUsers();
final users =
(await DbContacts.getUsers()).where((c) => c.accepted).toList();
setState(() {
_knownUsers = users;
_filteredUsers = List.from(_knownUsers);

View file

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

View file

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