mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-01-15 09:28:41 +00:00
follow request reject and block works
This commit is contained in:
parent
20c20eb1e1
commit
0da87f1712
18 changed files with 332 additions and 178 deletions
|
|
@ -1,8 +1,10 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:twonly/src/providers/api_provider.dart';
|
||||
import 'package:twonly/src/providers/db_provider.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:twonly/src/providers/notify_provider.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
import 'src/app.dart';
|
||||
import 'src/settings/settings_controller.dart';
|
||||
|
|
@ -49,5 +51,12 @@ void main() async {
|
|||
// return true;
|
||||
// });
|
||||
|
||||
runApp(MyApp(settingsController: settingsController));
|
||||
runApp(
|
||||
MultiProvider(
|
||||
providers: [
|
||||
ChangeNotifierProvider(create: (_) => NotifyProvider()),
|
||||
],
|
||||
child: MyApp(settingsController: settingsController),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import 'package:provider/provider.dart';
|
||||
import 'package:twonly/main.dart';
|
||||
import 'package:twonly/src/providers/notify_provider.dart';
|
||||
import 'package:twonly/src/utils/storage.dart';
|
||||
import 'package:twonly/src/views/onboarding_view.dart';
|
||||
import 'package:twonly/src/views/home_view.dart';
|
||||
|
|
@ -30,13 +32,18 @@ class _MyAppState extends State<MyApp> {
|
|||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// Start the color animation
|
||||
_startColorAnimation();
|
||||
|
||||
apiProvider.setConnectionStateCallback((isConnected) {
|
||||
setState(() {
|
||||
_isConnected = isConnected;
|
||||
});
|
||||
});
|
||||
apiProvider.setUpdatedContacts(() {
|
||||
context.read<NotifyProvider>().update();
|
||||
});
|
||||
|
||||
context.read<NotifyProvider>().update();
|
||||
apiProvider.connect();
|
||||
}
|
||||
|
||||
|
|
|
|||
70
lib/src/components/message_send_state_icon.dart
Normal file
70
lib/src/components/message_send_state_icon.dart
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
enum MessageSendState {
|
||||
opened,
|
||||
received,
|
||||
send,
|
||||
sending,
|
||||
}
|
||||
|
||||
class MessageSendStateIcon extends StatelessWidget {
|
||||
final MessageSendState state;
|
||||
|
||||
const MessageSendStateIcon({super.key, required this.state});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget icon = Placeholder();
|
||||
String text = "";
|
||||
|
||||
switch (state) {
|
||||
case MessageSendState.opened:
|
||||
icon = Icon(
|
||||
Icons.crop_square,
|
||||
size: 14,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
);
|
||||
text = "Opened";
|
||||
break;
|
||||
case MessageSendState.received:
|
||||
icon = Icon(
|
||||
Icons.square_rounded,
|
||||
size: 14,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
);
|
||||
text = "Received";
|
||||
break;
|
||||
case MessageSendState.send:
|
||||
icon = Icon(
|
||||
Icons.send,
|
||||
size: 14,
|
||||
);
|
||||
text = "Send";
|
||||
break;
|
||||
case MessageSendState.sending:
|
||||
icon = Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 10,
|
||||
height: 10,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 1,
|
||||
),
|
||||
),
|
||||
SizedBox(width: 2),
|
||||
],
|
||||
);
|
||||
text = "Sending";
|
||||
break;
|
||||
}
|
||||
|
||||
return Row(
|
||||
children: [
|
||||
icon,
|
||||
const SizedBox(width: 3),
|
||||
Text(text, style: TextStyle(fontSize: 12)),
|
||||
const SizedBox(width: 5),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
39
lib/src/components/notification_badge.dart
Normal file
39
lib/src/components/notification_badge.dart
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
class NotificationBadge extends StatelessWidget {
|
||||
final int count;
|
||||
final Widget child;
|
||||
|
||||
const NotificationBadge(
|
||||
{super.key, required this.count, required this.child});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (count == 0) return child;
|
||||
return Stack(
|
||||
children: [
|
||||
child,
|
||||
Positioned(
|
||||
right: 5,
|
||||
top: 0,
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(5.0), // Add some padding
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.red, // Background color
|
||||
shape: BoxShape.circle, // Make it circular
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
count.toString(),
|
||||
style: TextStyle(
|
||||
color: Colors.white, // Text color
|
||||
fontSize: 10,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -16,6 +16,8 @@
|
|||
"searchUsernameInput": "Username",
|
||||
"searchUsernameTitle": "Search username",
|
||||
"searchUsernameNotFound": "Username not found",
|
||||
"searchUsernameNewFollowerTitle": "Follow requests",
|
||||
"searchUsernameQrCodeBtn": "Scan QR code",
|
||||
"searchUsernameNotFoundLong": "\"{username}\" is not a twonly user. Please check the username and try again.",
|
||||
"errorUnknown": "An unexpected error has occurred. Please try again later.",
|
||||
"errorBadRequest": "The request could not be understood by the server due to malformed syntax. Please check your input and try again.",
|
||||
|
|
|
|||
|
|
@ -30,6 +30,9 @@ class DbContacts extends CvModelBase {
|
|||
static const columnRequested = "requested";
|
||||
final requested = CvField<int>(columnRequested);
|
||||
|
||||
static const columnBlocked = "blocked";
|
||||
final blocked = CvField<int>(columnBlocked);
|
||||
|
||||
static const columnCreatedAt = "created_at";
|
||||
final createdAt = CvField<DateTime>(columnCreatedAt);
|
||||
|
||||
|
|
@ -40,6 +43,7 @@ class DbContacts extends CvModelBase {
|
|||
$columnDisplayName TEXT,
|
||||
$columnAccepted INT NOT NULL DEFAULT 0,
|
||||
$columnRequested INT NOT NULL DEFAULT 0,
|
||||
$columnBlocked INT NOT NULL DEFAULT 0,
|
||||
$columnCreatedAt DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
""";
|
||||
|
|
@ -47,22 +51,23 @@ class DbContacts extends CvModelBase {
|
|||
|
||||
@override
|
||||
List<CvField> get fields =>
|
||||
[userId, displayName, accepted, requested, createdAt];
|
||||
[userId, displayName, accepted, requested, blocked, createdAt];
|
||||
|
||||
static Future<List<Contact>> getUsers() async {
|
||||
try {
|
||||
var users = await dbProvider.db!.query(tableName, columns: [
|
||||
columnUserId,
|
||||
columnDisplayName,
|
||||
columnAccepted,
|
||||
columnRequested,
|
||||
columnCreatedAt
|
||||
]);
|
||||
var users = await dbProvider.db!.query(tableName,
|
||||
columns: [
|
||||
columnUserId,
|
||||
columnDisplayName,
|
||||
columnAccepted,
|
||||
columnRequested,
|
||||
columnCreatedAt
|
||||
],
|
||||
where: "$columnBlocked = 0");
|
||||
if (users.isEmpty) return [];
|
||||
|
||||
List<Contact> parsedUsers = [];
|
||||
for (int i = 0; i < users.length; i++) {
|
||||
print(users[i]);
|
||||
parsedUsers.add(
|
||||
Contact(
|
||||
userId: Int64(users.cast()[i][columnUserId]),
|
||||
|
|
@ -79,6 +84,26 @@ class DbContacts extends CvModelBase {
|
|||
}
|
||||
}
|
||||
|
||||
static Future blockUser(int userId) async {
|
||||
Map<String, dynamic> valuesToUpdate = {
|
||||
columnBlocked: 1,
|
||||
};
|
||||
await dbProvider.db!.update(
|
||||
tableName,
|
||||
valuesToUpdate,
|
||||
where: "$columnUserId = ?",
|
||||
whereArgs: [userId],
|
||||
);
|
||||
}
|
||||
|
||||
static Future deleteUser(int userId) async {
|
||||
await dbProvider.db!.delete(
|
||||
tableName,
|
||||
where: "$columnUserId = ?",
|
||||
whereArgs: [userId],
|
||||
);
|
||||
}
|
||||
|
||||
static Future<bool> insertNewContact(
|
||||
String username, int userId, bool requested) async {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import 'package:json_annotation/json_annotation.dart';
|
|||
import 'package:twonly/src/utils/json.dart';
|
||||
part 'message.g.dart';
|
||||
|
||||
enum MessageKind { textMessage, image, video, contactRequest }
|
||||
enum MessageKind { textMessage, image, video, contactRequest, rejectRequest }
|
||||
|
||||
// so _$MessageKindEnumMap gets generated
|
||||
@JsonSerializable()
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ const _$MessageKindEnumMap = {
|
|||
MessageKind.image: 'image',
|
||||
MessageKind.video: 'video',
|
||||
MessageKind.contactRequest: 'contactRequest',
|
||||
MessageKind.rejectRequest: 'rejectRequest',
|
||||
};
|
||||
|
||||
Message _$MessageFromJson(Map<String, dynamic> json) => Message(
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ class ApiProvider {
|
|||
bool _tryingToConnect = false;
|
||||
final log = Logger("api_provider");
|
||||
Function(bool)? _connectionStateCallback;
|
||||
Function? _updatedContacts;
|
||||
|
||||
final HashMap<Int64, server.ServerToClient?> messagesV0 = HashMap();
|
||||
|
||||
|
|
@ -109,6 +110,10 @@ class ApiProvider {
|
|||
_connectionStateCallback = callBack;
|
||||
}
|
||||
|
||||
void setUpdatedContacts(Function callBack) {
|
||||
_updatedContacts = callBack;
|
||||
}
|
||||
|
||||
void tryToReconnect() {
|
||||
if (_tryingToConnect) return;
|
||||
_tryingToConnect = true;
|
||||
|
|
@ -157,13 +162,22 @@ class ApiProvider {
|
|||
Int64 fromUserId = msg.v0.newMessage.fromUserId;
|
||||
Message? message = await SignalHelper.getDecryptedText(fromUserId, body);
|
||||
if (message != null) {
|
||||
Result username = await getUsername(fromUserId);
|
||||
if (username.isSuccess) {
|
||||
print(username.value);
|
||||
Uint8List name = username.value.userdata.username;
|
||||
DbContacts.insertNewContact(
|
||||
utf8.decode(name), fromUserId.toInt(), true);
|
||||
print(message);
|
||||
switch (message.kind) {
|
||||
case MessageKind.contactRequest:
|
||||
Result username = await getUsername(fromUserId);
|
||||
if (username.isSuccess) {
|
||||
Uint8List name = username.value.userdata.username;
|
||||
DbContacts.insertNewContact(
|
||||
utf8.decode(name), fromUserId.toInt(), true);
|
||||
updateNotifier();
|
||||
}
|
||||
break;
|
||||
case MessageKind.rejectRequest:
|
||||
DbContacts.deleteUser(fromUserId.toInt());
|
||||
updateNotifier();
|
||||
break;
|
||||
default:
|
||||
log.shout("Got unknown MessageKind $message");
|
||||
}
|
||||
}
|
||||
var ok = client.Response_Ok()..none = true;
|
||||
|
|
@ -182,6 +196,12 @@ class ApiProvider {
|
|||
_channel!.sink.add(resBytes);
|
||||
}
|
||||
|
||||
Future updateNotifier() async {
|
||||
if (_updatedContacts != null) {
|
||||
_updatedContacts!();
|
||||
}
|
||||
}
|
||||
|
||||
Future<server.ServerToClient?> _waitForResponse(Int64 seq) async {
|
||||
final startTime = DateTime.now();
|
||||
|
||||
|
|
|
|||
29
lib/src/providers/notify_provider.dart
Normal file
29
lib/src/providers/notify_provider.dart
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:twonly/src/model/contacts_model.dart';
|
||||
|
||||
/// Mix-in [DiagnosticableTreeMixin] to have access to [debugFillProperties] for the devtool
|
||||
// ignore: prefer_mixin
|
||||
class NotifyProvider with ChangeNotifier, DiagnosticableTreeMixin {
|
||||
int _newContactRequests = 0;
|
||||
List<Contact> _allContacts = [];
|
||||
|
||||
int get newContactRequests => _newContactRequests;
|
||||
List<Contact> get allContacts => _allContacts;
|
||||
|
||||
void update() async {
|
||||
_allContacts = await DbContacts.getUsers();
|
||||
|
||||
_newContactRequests = _allContacts
|
||||
.where((contact) => !contact.accepted && contact.requested)
|
||||
.length;
|
||||
print(_newContactRequests);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// Makes `Counter` readable inside the devtools by listing all of its properties
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
super.debugFillProperties(properties);
|
||||
properties.add(IntProperty('count', newContactRequests));
|
||||
}
|
||||
}
|
||||
|
|
@ -38,7 +38,6 @@ class ConnectPreKeyStore extends PreKeyStore {
|
|||
@override
|
||||
Future<void> storePreKey(int preKeyId, PreKeyRecord record) async {
|
||||
if (!await containsPreKey(preKeyId)) {
|
||||
print(preKeyId);
|
||||
await dbProvider.db!.insert(DB.tableName,
|
||||
{DB.columnPreKeyId: preKeyId, DB.columnPreKey: record.serialize()});
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import 'dart:convert';
|
||||
import 'package:fixnum/fixnum.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:twonly/main.dart';
|
||||
|
|
@ -44,6 +45,25 @@ Future<bool> addNewContact(String username) async {
|
|||
return res.isSuccess;
|
||||
}
|
||||
|
||||
Future<Result> encryptAndSendMessage(Int64 userId, Message msg) async {
|
||||
Uint8List? bytes = await SignalHelper.encryptMessage(msg, userId);
|
||||
|
||||
if (bytes == null) {
|
||||
Logger("utils/api").shout("Error encryption message!");
|
||||
return Result.error(ErrorCode.InternalError);
|
||||
}
|
||||
|
||||
Result resp = await apiProvider.sendTextMessage(userId, bytes);
|
||||
|
||||
return resp;
|
||||
}
|
||||
|
||||
Future<Result> rejectUserRequest(Int64 userId) async {
|
||||
Message msg =
|
||||
Message(kind: MessageKind.rejectRequest, timestamp: DateTime.now());
|
||||
return encryptAndSendMessage(userId, msg);
|
||||
}
|
||||
|
||||
Future<Result> createNewUser(String username, String inviteCode) async {
|
||||
final storage = getSecureStorage();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:ffi';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
import 'package:fixnum/fixnum.dart';
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
import 'package:twonly/src/components/initialsavatar_component.dart';
|
||||
import 'package:twonly/src/model/contacts_model.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:twonly/src/components/initialsavatar.dart';
|
||||
import 'package:twonly/src/components/message_send_state_icon.dart';
|
||||
import 'package:twonly/src/components/notification_badge.dart';
|
||||
import 'package:twonly/src/providers/notify_provider.dart';
|
||||
import 'package:twonly/src/views/search_username_view.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import 'new_message_view.dart';
|
||||
|
|
@ -7,8 +10,6 @@ import 'package:flutter/material.dart';
|
|||
import 'chat_item_details_view.dart';
|
||||
import 'dart:async';
|
||||
|
||||
enum MessageSendState { sending, send, opened, received }
|
||||
|
||||
class ChatItem {
|
||||
const ChatItem(
|
||||
{required this.username,
|
||||
|
|
@ -57,19 +58,12 @@ class ChatListView extends StatefulWidget {
|
|||
|
||||
class _ChatListViewState extends State<ChatListView> {
|
||||
int _secondsSinceOpen = 0;
|
||||
int _newContactRequests = 0;
|
||||
late Timer _timer;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_startTimer();
|
||||
_checkNewContactRequests();
|
||||
}
|
||||
|
||||
Future _checkNewContactRequests() async {
|
||||
_newContactRequests = (await DbContacts.getUsers()).length;
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
void _startTimer() {
|
||||
|
|
@ -101,57 +95,12 @@ class _ChatListViewState extends State<ChatListView> {
|
|||
}
|
||||
}
|
||||
|
||||
Widget getMessageSateIcon(MessageSendState state) {
|
||||
List<Widget> children = [];
|
||||
Widget icon = Placeholder();
|
||||
String text = "";
|
||||
|
||||
switch (state) {
|
||||
case MessageSendState.opened:
|
||||
icon = Icon(
|
||||
Icons.crop_square,
|
||||
size: 14,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
);
|
||||
text = "Opened";
|
||||
break;
|
||||
case MessageSendState.received:
|
||||
icon = Icon(Icons.square_rounded,
|
||||
size: 14, color: Theme.of(context).colorScheme.primary);
|
||||
text = "Received";
|
||||
break;
|
||||
case MessageSendState.send:
|
||||
icon = Icon(Icons.send, size: 14);
|
||||
text = "Send";
|
||||
break;
|
||||
case MessageSendState.sending:
|
||||
icon = Row(children: [
|
||||
SizedBox(
|
||||
width: 10,
|
||||
height: 10,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 1,
|
||||
)),
|
||||
SizedBox(width: 2)
|
||||
]);
|
||||
text = "Sending";
|
||||
break;
|
||||
}
|
||||
children.add(const SizedBox(width: 5));
|
||||
return Row(
|
||||
children: [
|
||||
icon,
|
||||
const SizedBox(width: 3),
|
||||
Text(text, style: TextStyle(fontSize: 12)),
|
||||
const SizedBox(width: 5)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget getSubtitle(ChatItem item) {
|
||||
return Row(
|
||||
children: [
|
||||
getMessageSateIcon(item.state),
|
||||
MessageSendStateIcon(
|
||||
state: item.state,
|
||||
),
|
||||
Text("•"),
|
||||
const SizedBox(width: 5),
|
||||
Text(formatDuration(item.lastMessageInSeconds + _secondsSinceOpen),
|
||||
|
|
@ -180,62 +129,42 @@ class _ChatListViewState extends State<ChatListView> {
|
|||
appBar: AppBar(
|
||||
title: Text(AppLocalizations.of(context)!.chatsTitle),
|
||||
actions: [
|
||||
Stack(
|
||||
children: [
|
||||
IconButton(
|
||||
icon: Icon(Icons.person_add), // User with add icon
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => SearchUsernameView(),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
if (_newContactRequests > 0)
|
||||
Positioned(
|
||||
right: 5,
|
||||
top: 0,
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(5.0), // Add some padding
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.red, // Background color
|
||||
shape: BoxShape.circle, // Make it circular
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
_newContactRequests.toString(),
|
||||
style: TextStyle(
|
||||
color: Colors.white, // Text color
|
||||
fontSize: 10),
|
||||
),
|
||||
),
|
||||
NotificationBadge(
|
||||
count: context.watch<NotifyProvider>().newContactRequests,
|
||||
child: IconButton(
|
||||
icon: Icon(Icons.person_add),
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => SearchUsernameView(),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
body: ListView.builder(
|
||||
restorationId: 'sampleItemListView',
|
||||
restorationId: 'chat_list_view',
|
||||
itemCount: widget.items.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
final item = widget.items[index];
|
||||
return ListTile(
|
||||
title: Text(item.username),
|
||||
subtitle: getSubtitle(item),
|
||||
leading: InitialsAvatar(displayName: item.username),
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => SampleItemDetailsView(
|
||||
userId: item.userId,
|
||||
),
|
||||
title: Text(item.username),
|
||||
subtitle: getSubtitle(item),
|
||||
leading: InitialsAvatar(displayName: item.username),
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => SampleItemDetailsView(
|
||||
userId: item.userId,
|
||||
),
|
||||
);
|
||||
});
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import 'package:twonly/src/components/initialsavatar_component.dart';
|
||||
import 'package:twonly/src/components/initialsavatar.dart';
|
||||
import 'package:twonly/src/model/contacts_model.dart';
|
||||
import 'package:twonly/src/views/search_username_view.dart';
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,10 @@ 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';
|
||||
import 'package:twonly/src/providers/notify_provider.dart';
|
||||
import 'package:twonly/src/utils/api.dart';
|
||||
import 'package:twonly/src/views/register_view.dart';
|
||||
|
||||
|
|
@ -28,11 +31,9 @@ class _SearchUsernameView extends State<SearchUsernameView> {
|
|||
_isLoading = false;
|
||||
});
|
||||
|
||||
Logger("search_user_name").warning("Replace instead of pop");
|
||||
|
||||
if (context.mounted) {
|
||||
if (status) {
|
||||
// Navigator.pop(context);
|
||||
context.read<NotifyProvider>().update();
|
||||
} else if (context.mounted) {
|
||||
showAlertDialog(
|
||||
context,
|
||||
|
|
@ -88,17 +89,19 @@ class _SearchUsernameView extends State<SearchUsernameView> {
|
|||
showAlertDialog(context, "Coming soon",
|
||||
"This feature is not yet implemented!");
|
||||
},
|
||||
label: Text("QR-Code scannen"),
|
||||
label:
|
||||
Text(AppLocalizations.of(context)!.searchUsernameQrCodeBtn),
|
||||
),
|
||||
SizedBox(height: 30),
|
||||
Container(
|
||||
alignment: Alignment.centerLeft,
|
||||
padding: EdgeInsets.symmetric(horizontal: 4.0, vertical: 10),
|
||||
child: Text(
|
||||
"Neue Followanfragen",
|
||||
style: TextStyle(fontSize: 20),
|
||||
if (context.read<NotifyProvider>().allContacts.isNotEmpty)
|
||||
Container(
|
||||
alignment: Alignment.centerLeft,
|
||||
padding: EdgeInsets.symmetric(horizontal: 4.0, vertical: 10),
|
||||
child: Text(
|
||||
AppLocalizations.of(context)!.searchUsernameNewFollowerTitle,
|
||||
style: TextStyle(fontSize: 20),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: ContactsListView(),
|
||||
)
|
||||
|
|
@ -126,53 +129,55 @@ class ContactsListView extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _ContactsListViewState extends State<ContactsListView> {
|
||||
List<Contact> _allContacts = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadContacts();
|
||||
}
|
||||
|
||||
Future _loadContacts() async {
|
||||
List<Contact> allContacts = await DbContacts.getUsers();
|
||||
_allContacts = allContacts.where((contact) => !contact.accepted).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
List<Contact> contacts = context.read<NotifyProvider>().allContacts;
|
||||
return ListView.builder(
|
||||
itemCount: _allContacts.length,
|
||||
itemCount: contacts.length,
|
||||
itemBuilder: (context, index) {
|
||||
final contact = _allContacts[index];
|
||||
|
||||
if (!contact.requested) {
|
||||
return ListTile(
|
||||
title: Text(contact.displayName),
|
||||
subtitle: Text('Pending'),
|
||||
);
|
||||
}
|
||||
|
||||
final contact = contacts[index];
|
||||
return ListTile(
|
||||
title: Text(contact.displayName),
|
||||
leading: InitialsAvatar(displayName: contact.displayName),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
IconButton(
|
||||
icon: Icon(Icons.close, color: Colors.red),
|
||||
onPressed: () {
|
||||
// Handle reject action
|
||||
print('Rejected ${contact.displayName}');
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.check, color: Colors.green),
|
||||
onPressed: () {
|
||||
// Handle accept action
|
||||
print('Accepted ${contact.displayName}');
|
||||
},
|
||||
),
|
||||
],
|
||||
children: (!contact.requested)
|
||||
? [Text('Pending')]
|
||||
: [
|
||||
Tooltip(
|
||||
message: "Block the user without informing.",
|
||||
child: IconButton(
|
||||
icon: Icon(Icons.person_off_rounded,
|
||||
color: const Color.fromARGB(164, 244, 67, 54)),
|
||||
onPressed: () async {
|
||||
await DbContacts.blockUser(contact.userId.toInt());
|
||||
if (context.mounted) {
|
||||
context.read<NotifyProvider>().update();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
Tooltip(
|
||||
message: "Reject the request and let the requester know.",
|
||||
child: IconButton(
|
||||
icon: Icon(Icons.close, color: Colors.red),
|
||||
onPressed: () async {
|
||||
await DbContacts.deleteUser(contact.userId.toInt());
|
||||
if (context.mounted) {
|
||||
context.read<NotifyProvider>().update();
|
||||
}
|
||||
rejectUserRequest(contact.userId);
|
||||
},
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.check, color: Colors.green),
|
||||
onPressed: () {
|
||||
// Handle accept action
|
||||
print('Accepted ${contact.displayName}');
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import 'dart:collection';
|
|||
import 'package:fixnum/fixnum.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import 'package:twonly/src/components/initialsavatar_component.dart';
|
||||
import 'package:twonly/src/components/initialsavatar.dart';
|
||||
import 'package:twonly/src/model/contacts_model.dart';
|
||||
|
||||
class ShareImageView extends StatefulWidget {
|
||||
|
|
|
|||
Loading…
Reference in a new issue