mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-01-15 07:48:40 +00:00
block users via settings
This commit is contained in:
parent
1b47c11aaa
commit
ac91e954f7
9 changed files with 366 additions and 161 deletions
14
README.md
14
README.md
|
|
@ -4,18 +4,22 @@ Don't be lonely, get twonly! Send pictures to a friend in real time and be sure
|
|||
|
||||
|
||||
## TODOS bevor first beta
|
||||
- Settings
|
||||
- Delete and Block active users
|
||||
- MessageKind -> Ausbauen?
|
||||
- Nachrichten nach 24h Stunden löschen
|
||||
- Real deployment aufsetzen, direkt auf Netcup?
|
||||
- Pro Invitation codes
|
||||
- Push Notification (Android)
|
||||
- FIX: Problem Bild falsch, wenn handy schräg...
|
||||
- Settings
|
||||
- Notification
|
||||
- Real deployment aufsetzen, direkt auf Netcup?
|
||||
- MediaView:
|
||||
- Bei weiteren geladenen Bildern -> Direkt anzeigen ohne zu popen
|
||||
- Bei weiteren geladenen Bildern -> Direkt anzeigen ohne pop
|
||||
|
||||
## Not my issues
|
||||
- FIX: Problem Bild falsch, wenn handy schräg... -> Issue already openend
|
||||
|
||||
## TODOS bevor first release
|
||||
- Settings
|
||||
- Subscription
|
||||
- Webpage
|
||||
- Instagam & Marketing vorbereiten
|
||||
- IT-Startup der TU Darmstadt anschreiben
|
||||
|
|
|
|||
137
lib/main.dart
137
lib/main.dart
|
|
@ -1,8 +1,4 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:twonly/src/providers/api/api.dart';
|
||||
import 'package:twonly/src/providers/api_provider.dart';
|
||||
|
|
@ -13,141 +9,13 @@ import 'package:twonly/src/providers/download_change_provider.dart';
|
|||
import 'package:twonly/src/providers/messages_change_provider.dart';
|
||||
import 'package:twonly/src/providers/contacts_change_provider.dart';
|
||||
import 'package:twonly/src/providers/settings_change_provider.dart';
|
||||
import 'package:twonly/src/services/notification_service.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
import 'src/app.dart';
|
||||
|
||||
late DbProvider dbProvider;
|
||||
late ApiProvider apiProvider;
|
||||
|
||||
/// Streams are created so that app can respond to notification-related events
|
||||
/// since the plugin is initialized in the `main` function
|
||||
final StreamController<NotificationResponse> selectNotificationStream =
|
||||
StreamController<NotificationResponse>.broadcast();
|
||||
|
||||
const MethodChannel platform =
|
||||
MethodChannel('dexterx.dev/flutter_local_notifications_example');
|
||||
|
||||
const String portName = 'notification_send_port';
|
||||
|
||||
class ReceivedNotification {
|
||||
ReceivedNotification({
|
||||
required this.id,
|
||||
required this.title,
|
||||
required this.body,
|
||||
required this.payload,
|
||||
this.data,
|
||||
});
|
||||
|
||||
final int id;
|
||||
final String? title;
|
||||
final String? body;
|
||||
final String? payload;
|
||||
final Map<String, dynamic>? data;
|
||||
}
|
||||
|
||||
String? selectedNotificationPayload;
|
||||
|
||||
/// A notification action which triggers a url launch event
|
||||
const String urlLaunchActionId = 'id_1';
|
||||
|
||||
/// A notification action which triggers a App navigation event
|
||||
const String navigationActionId = 'id_3';
|
||||
|
||||
/// Defines a iOS/MacOS notification category for text input actions.
|
||||
const String darwinNotificationCategoryText = 'textCategory';
|
||||
|
||||
/// Defines a iOS/MacOS notification category for plain actions.
|
||||
const String darwinNotificationCategoryPlain = 'plainCategory';
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
void notificationTapBackground(NotificationResponse notificationResponse) {
|
||||
// ignore: avoid_print
|
||||
print('notification(${notificationResponse.id}) action tapped: '
|
||||
'${notificationResponse.actionId} with'
|
||||
' payload: ${notificationResponse.payload}');
|
||||
if (notificationResponse.input?.isNotEmpty ?? false) {
|
||||
// ignore: avoid_print
|
||||
print(
|
||||
'notification action tapped with input: ${notificationResponse.input}');
|
||||
}
|
||||
}
|
||||
|
||||
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
|
||||
FlutterLocalNotificationsPlugin();
|
||||
|
||||
int id = 0;
|
||||
|
||||
Future<void> setupPushNotification() async {
|
||||
const AndroidInitializationSettings initializationSettingsAndroid =
|
||||
AndroidInitializationSettings("logo");
|
||||
|
||||
final List<DarwinNotificationCategory> darwinNotificationCategories =
|
||||
<DarwinNotificationCategory>[
|
||||
DarwinNotificationCategory(
|
||||
darwinNotificationCategoryText,
|
||||
actions: <DarwinNotificationAction>[
|
||||
DarwinNotificationAction.text(
|
||||
'text_1',
|
||||
'Action 1',
|
||||
buttonTitle: 'Send',
|
||||
placeholder: 'Placeholder',
|
||||
),
|
||||
],
|
||||
),
|
||||
DarwinNotificationCategory(
|
||||
darwinNotificationCategoryPlain,
|
||||
actions: <DarwinNotificationAction>[
|
||||
DarwinNotificationAction.plain('id_1', 'Action 1'),
|
||||
DarwinNotificationAction.plain(
|
||||
'id_2',
|
||||
'Action 2 (destructive)',
|
||||
options: <DarwinNotificationActionOption>{
|
||||
DarwinNotificationActionOption.destructive,
|
||||
},
|
||||
),
|
||||
DarwinNotificationAction.plain(
|
||||
navigationActionId,
|
||||
'Action 3 (foreground)',
|
||||
options: <DarwinNotificationActionOption>{
|
||||
DarwinNotificationActionOption.foreground,
|
||||
},
|
||||
),
|
||||
DarwinNotificationAction.plain(
|
||||
'id_4',
|
||||
'Action 4 (auth required)',
|
||||
options: <DarwinNotificationActionOption>{
|
||||
DarwinNotificationActionOption.authenticationRequired,
|
||||
},
|
||||
),
|
||||
],
|
||||
options: <DarwinNotificationCategoryOption>{
|
||||
DarwinNotificationCategoryOption.hiddenPreviewShowTitle,
|
||||
},
|
||||
)
|
||||
];
|
||||
|
||||
/// Note: permissions aren't requested here just to demonstrate that can be
|
||||
/// done later
|
||||
final DarwinInitializationSettings initializationSettingsDarwin =
|
||||
DarwinInitializationSettings(
|
||||
requestAlertPermission: false,
|
||||
requestBadgePermission: false,
|
||||
requestSoundPermission: false,
|
||||
notificationCategories: darwinNotificationCategories,
|
||||
);
|
||||
|
||||
final InitializationSettings initializationSettings = InitializationSettings(
|
||||
android: initializationSettingsAndroid,
|
||||
iOS: initializationSettingsDarwin,
|
||||
);
|
||||
|
||||
await flutterLocalNotificationsPlugin.initialize(
|
||||
initializationSettings,
|
||||
onDidReceiveNotificationResponse: selectNotificationStream.add,
|
||||
onDidReceiveBackgroundNotificationResponse: notificationTapBackground,
|
||||
);
|
||||
}
|
||||
|
||||
void main() async {
|
||||
final settingsController = SettingsChangeProvider();
|
||||
|
||||
|
|
@ -167,8 +35,7 @@ void main() async {
|
|||
}
|
||||
});
|
||||
|
||||
setupPushNotification();
|
||||
|
||||
await setupPushNotification();
|
||||
await initMediaStorage();
|
||||
|
||||
dbProvider = DbProvider();
|
||||
|
|
|
|||
|
|
@ -35,6 +35,9 @@
|
|||
"settingsSubscription": "Subscription",
|
||||
"settingsAppearance": "Appearance",
|
||||
"settingsPrivacy": "Privacy",
|
||||
"settingsPrivacyBlockUsers": "Block users",
|
||||
"settingsPrivacyBlockUsersDesc": "Blocked users will not be able to communicate with you. You can unblock a blocked user at any time.",
|
||||
"settingsPrivacyBlockUsersCount": "{len} contact(s)",
|
||||
"settingsNotification": "Notification",
|
||||
"settingsHelp": "Help",
|
||||
"settingsHelpSupport": "Support Center",
|
||||
|
|
|
|||
|
|
@ -10,12 +10,14 @@ class Contact {
|
|||
{required this.userId,
|
||||
required this.displayName,
|
||||
required this.accepted,
|
||||
required this.blocked,
|
||||
required this.totalMediaCounter,
|
||||
required this.requested});
|
||||
final Int64 userId;
|
||||
final String displayName;
|
||||
final bool accepted;
|
||||
final bool requested;
|
||||
final bool blocked;
|
||||
final int totalMediaCounter;
|
||||
}
|
||||
|
||||
|
|
@ -64,7 +66,21 @@ class DbContacts extends CvModelBase {
|
|||
[userId, displayName, accepted, requested, blocked, createdAt];
|
||||
|
||||
static Future<List<Contact>> getActiveUsers() async {
|
||||
return (await getUsers()).where((u) => u.accepted).toList();
|
||||
return (await _getAllUsers())
|
||||
.where((u) => u.accepted && !u.blocked)
|
||||
.toList();
|
||||
}
|
||||
|
||||
static Future<List<Contact>> getBlockedUsers() async {
|
||||
return (await _getAllUsers()).where((u) => u.blocked).toList();
|
||||
}
|
||||
|
||||
static Future<List<Contact>> getUsers() async {
|
||||
return (await _getAllUsers()).where((u) => !u.blocked).toList();
|
||||
}
|
||||
|
||||
static Future<List<Contact>> getAllUsers() async {
|
||||
return await _getAllUsers();
|
||||
}
|
||||
|
||||
static Future checkAndUpdateFlames(int userId, {DateTime? timestamp}) async {
|
||||
|
|
@ -96,18 +112,17 @@ class DbContacts extends CvModelBase {
|
|||
);
|
||||
}
|
||||
|
||||
static Future<List<Contact>> getUsers() async {
|
||||
static Future<List<Contact>> _getAllUsers() async {
|
||||
try {
|
||||
var users = await dbProvider.db!.query(tableName,
|
||||
columns: [
|
||||
columnUserId,
|
||||
columnDisplayName,
|
||||
columnAccepted,
|
||||
columnRequested,
|
||||
columnTotalMediaCounter,
|
||||
columnCreatedAt
|
||||
],
|
||||
where: "$columnBlocked = 0");
|
||||
var users = await dbProvider.db!.query(tableName, columns: [
|
||||
columnUserId,
|
||||
columnDisplayName,
|
||||
columnAccepted,
|
||||
columnRequested,
|
||||
columnBlocked,
|
||||
columnTotalMediaCounter,
|
||||
columnCreatedAt
|
||||
]);
|
||||
if (users.isEmpty) return [];
|
||||
|
||||
List<Contact> parsedUsers = [];
|
||||
|
|
@ -119,6 +134,7 @@ class DbContacts extends CvModelBase {
|
|||
totalMediaCounter: users.cast()[i][columnTotalMediaCounter],
|
||||
displayName: users.cast()[i][columnDisplayName],
|
||||
accepted: users[i][columnAccepted] == 1,
|
||||
blocked: users[i][columnBlocked] == 1,
|
||||
requested: users[i][columnRequested] == 1,
|
||||
),
|
||||
);
|
||||
|
|
@ -130,9 +146,9 @@ class DbContacts extends CvModelBase {
|
|||
}
|
||||
}
|
||||
|
||||
static Future blockUser(int userId) async {
|
||||
static Future blockUser(int userId, {bool unblock = false}) async {
|
||||
Map<String, dynamic> valuesToUpdate = {
|
||||
columnBlocked: 1,
|
||||
columnBlocked: unblock ? 0 : 1,
|
||||
};
|
||||
await dbProvider.db!.update(
|
||||
tableName,
|
||||
|
|
|
|||
132
lib/src/services/notification_service.dart
Normal file
132
lib/src/services/notification_service.dart
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
|
||||
/// Streams are created so that app can respond to notification-related events
|
||||
/// since the plugin is initialized in the `main` function
|
||||
final StreamController<NotificationResponse> selectNotificationStream =
|
||||
StreamController<NotificationResponse>.broadcast();
|
||||
|
||||
const MethodChannel platform = MethodChannel('twonly.eu/notifications');
|
||||
|
||||
const String portName = 'notification_send_port';
|
||||
|
||||
class ReceivedNotification {
|
||||
ReceivedNotification({
|
||||
required this.id,
|
||||
required this.title,
|
||||
required this.body,
|
||||
required this.payload,
|
||||
this.data,
|
||||
});
|
||||
|
||||
final int id;
|
||||
final String? title;
|
||||
final String? body;
|
||||
final String? payload;
|
||||
final Map<String, dynamic>? data;
|
||||
}
|
||||
|
||||
String? selectedNotificationPayload;
|
||||
|
||||
/// A notification action which triggers a url launch event
|
||||
const String urlLaunchActionId = 'id_1';
|
||||
|
||||
/// A notification action which triggers a App navigation event
|
||||
const String navigationActionId = 'id_3';
|
||||
|
||||
/// Defines a iOS/MacOS notification category for text input actions.
|
||||
const String darwinNotificationCategoryText = 'textCategory';
|
||||
|
||||
/// Defines a iOS/MacOS notification category for plain actions.
|
||||
const String darwinNotificationCategoryPlain = 'plainCategory';
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
void notificationTapBackground(NotificationResponse notificationResponse) {
|
||||
// ignore: avoid_print
|
||||
print('notification(${notificationResponse.id}) action tapped: '
|
||||
'${notificationResponse.actionId} with'
|
||||
' payload: ${notificationResponse.payload}');
|
||||
if (notificationResponse.input?.isNotEmpty ?? false) {
|
||||
// ignore: avoid_print
|
||||
print(
|
||||
'notification action tapped with input: ${notificationResponse.input}');
|
||||
}
|
||||
}
|
||||
|
||||
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
|
||||
FlutterLocalNotificationsPlugin();
|
||||
|
||||
int id = 0;
|
||||
|
||||
Future<void> setupPushNotification() async {
|
||||
const AndroidInitializationSettings initializationSettingsAndroid =
|
||||
AndroidInitializationSettings("logo");
|
||||
|
||||
final List<DarwinNotificationCategory> darwinNotificationCategories =
|
||||
<DarwinNotificationCategory>[
|
||||
DarwinNotificationCategory(
|
||||
darwinNotificationCategoryText,
|
||||
actions: <DarwinNotificationAction>[
|
||||
DarwinNotificationAction.text(
|
||||
'text_1',
|
||||
'Action 1',
|
||||
buttonTitle: 'Send',
|
||||
placeholder: 'Placeholder',
|
||||
),
|
||||
],
|
||||
),
|
||||
DarwinNotificationCategory(
|
||||
darwinNotificationCategoryPlain,
|
||||
actions: <DarwinNotificationAction>[
|
||||
DarwinNotificationAction.plain('id_1', 'Action 1'),
|
||||
DarwinNotificationAction.plain(
|
||||
'id_2',
|
||||
'Action 2 (destructive)',
|
||||
options: <DarwinNotificationActionOption>{
|
||||
DarwinNotificationActionOption.destructive,
|
||||
},
|
||||
),
|
||||
DarwinNotificationAction.plain(
|
||||
navigationActionId,
|
||||
'Action 3 (foreground)',
|
||||
options: <DarwinNotificationActionOption>{
|
||||
DarwinNotificationActionOption.foreground,
|
||||
},
|
||||
),
|
||||
DarwinNotificationAction.plain(
|
||||
'id_4',
|
||||
'Action 4 (auth required)',
|
||||
options: <DarwinNotificationActionOption>{
|
||||
DarwinNotificationActionOption.authenticationRequired,
|
||||
},
|
||||
),
|
||||
],
|
||||
options: <DarwinNotificationCategoryOption>{
|
||||
DarwinNotificationCategoryOption.hiddenPreviewShowTitle,
|
||||
},
|
||||
)
|
||||
];
|
||||
|
||||
/// Note: permissions aren't requested here just to demonstrate that can be
|
||||
/// done later
|
||||
final DarwinInitializationSettings initializationSettingsDarwin =
|
||||
DarwinInitializationSettings(
|
||||
requestAlertPermission: false,
|
||||
requestBadgePermission: false,
|
||||
requestSoundPermission: false,
|
||||
notificationCategories: darwinNotificationCategories,
|
||||
);
|
||||
|
||||
final InitializationSettings initializationSettings = InitializationSettings(
|
||||
android: initializationSettingsAndroid,
|
||||
iOS: initializationSettingsDarwin,
|
||||
);
|
||||
|
||||
await flutterLocalNotificationsPlugin.initialize(
|
||||
initializationSettings,
|
||||
onDidReceiveNotificationResponse: selectNotificationStream.add,
|
||||
onDidReceiveBackgroundNotificationResponse: notificationTapBackground,
|
||||
);
|
||||
}
|
||||
|
|
@ -44,12 +44,10 @@ class _ShareImageView extends State<ShareImageView> {
|
|||
}
|
||||
|
||||
Future<void> _loadAsync() async {
|
||||
final users = await DbContacts.getActiveUsers();
|
||||
setState(() {
|
||||
_users = users;
|
||||
_updateUsers(_users);
|
||||
});
|
||||
_users = await DbContacts.getActiveUsers();
|
||||
_updateUsers(_users);
|
||||
imageBytes = await widget.imageBytesFuture;
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
Future _updateUsers(List<Contact> users) async {
|
||||
|
|
|
|||
52
lib/src/views/settings/privacy_view.dart
Normal file
52
lib/src/views/settings/privacy_view.dart
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:twonly/src/model/contacts_model.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
import 'package:twonly/src/views/settings/privacy_view_block_users.dart';
|
||||
|
||||
class PrivacyView extends StatefulWidget {
|
||||
const PrivacyView({super.key});
|
||||
|
||||
@override
|
||||
State<PrivacyView> createState() => _PrivacyViewState();
|
||||
}
|
||||
|
||||
class _PrivacyViewState extends State<PrivacyView> {
|
||||
List<Contact> blockedUsers = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
updateBlockedUsers();
|
||||
}
|
||||
|
||||
Future updateBlockedUsers() async {
|
||||
blockedUsers = await DbContacts.getBlockedUsers();
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(context.lang.settingsPrivacy),
|
||||
),
|
||||
body: ListView(
|
||||
children: [
|
||||
ListTile(
|
||||
title: Text(context.lang.settingsPrivacyBlockUsers),
|
||||
subtitle: Text(
|
||||
context.lang.settingsPrivacyBlockUsersCount(blockedUsers.length),
|
||||
),
|
||||
onTap: () async {
|
||||
await Navigator.push(context,
|
||||
MaterialPageRoute(builder: (context) {
|
||||
return PrivacyViewBlockUsers();
|
||||
}));
|
||||
updateBlockedUsers();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
127
lib/src/views/settings/privacy_view_block_users.dart
Normal file
127
lib/src/views/settings/privacy_view_block_users.dart
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:twonly/src/components/initialsavatar.dart';
|
||||
import 'package:twonly/src/model/contacts_model.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
|
||||
class PrivacyViewBlockUsers extends StatefulWidget {
|
||||
const PrivacyViewBlockUsers({super.key});
|
||||
|
||||
@override
|
||||
State<PrivacyViewBlockUsers> createState() => _PrivacyViewBlockUsers();
|
||||
}
|
||||
|
||||
class _PrivacyViewBlockUsers extends State<PrivacyViewBlockUsers> {
|
||||
List<Contact> allUsers = [];
|
||||
List<Contact> filteredUsers = [];
|
||||
String lastQuery = "";
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
loadAsync();
|
||||
}
|
||||
|
||||
Future loadAsync() async {
|
||||
allUsers = await DbContacts.getAllUsers();
|
||||
_filterUsers(lastQuery);
|
||||
}
|
||||
|
||||
Future _filterUsers(String query) async {
|
||||
lastQuery = query;
|
||||
if (query.isEmpty) {
|
||||
filteredUsers = allUsers;
|
||||
return;
|
||||
}
|
||||
filteredUsers = allUsers
|
||||
.where((user) =>
|
||||
user.displayName.toLowerCase().contains(query.toLowerCase()))
|
||||
.toList();
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(context.lang.settingsPrivacyBlockUsers),
|
||||
),
|
||||
body: Padding(
|
||||
padding: EdgeInsets.only(bottom: 20, left: 10, top: 20, right: 10),
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
||||
child: TextField(
|
||||
onChanged: _filterUsers,
|
||||
decoration: getInputDecoration(
|
||||
context,
|
||||
context.lang.searchUsernameInput,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Text(
|
||||
context.lang.settingsPrivacyBlockUsersDesc,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
Expanded(
|
||||
child: UserList(
|
||||
List.from(filteredUsers),
|
||||
updateStatus: () {
|
||||
loadAsync();
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class UserList extends StatelessWidget {
|
||||
const UserList(this.users, {super.key, required this.updateStatus});
|
||||
final List<Contact> users;
|
||||
final Function updateStatus;
|
||||
|
||||
Future block(bool? value, int userId) async {
|
||||
if (value == null) return;
|
||||
await DbContacts.blockUser(userId, unblock: !value);
|
||||
updateStatus();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Step 1: Sort the users alphabetically
|
||||
users.sort((a, b) => a.displayName.compareTo(b.displayName));
|
||||
|
||||
return ListView.builder(
|
||||
restorationId: 'new_message_users_list',
|
||||
itemCount: users.length,
|
||||
itemBuilder: (BuildContext context, int i) {
|
||||
Contact user = users[i];
|
||||
print(user.blocked);
|
||||
return ListTile(
|
||||
title: Row(children: [
|
||||
Text(user.displayName),
|
||||
]),
|
||||
leading: InitialsAvatar(
|
||||
displayName: user.displayName,
|
||||
fontSize: 15,
|
||||
),
|
||||
trailing: Checkbox(
|
||||
value: user.blocked,
|
||||
onChanged: (bool? value) {
|
||||
print(value);
|
||||
block(value, user.userId.toInt());
|
||||
},
|
||||
),
|
||||
onTap: () {
|
||||
block(!user.blocked, user.userId.toInt());
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +1,15 @@
|
|||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:twonly/main.dart';
|
||||
import 'package:twonly/src/components/initialsavatar.dart';
|
||||
import 'package:twonly/src/model/json/user_data.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:twonly/src/services/notification_service.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
import 'package:twonly/src/utils/storage.dart';
|
||||
import 'package:twonly/src/views/settings/account_view.dart';
|
||||
import 'package:twonly/src/views/settings/appearance_view.dart';
|
||||
import 'package:twonly/src/views/settings/help_view.dart';
|
||||
import 'package:twonly/src/views/settings/privacy_view.dart';
|
||||
|
||||
class ProfileView extends StatefulWidget {
|
||||
const ProfileView({super.key});
|
||||
|
|
@ -107,7 +108,12 @@ class _ProfileViewState extends State<ProfileView> {
|
|||
SettingsListTile(
|
||||
icon: FontAwesomeIcons.lock,
|
||||
text: context.lang.settingsPrivacy,
|
||||
onTap: () {},
|
||||
onTap: () {
|
||||
Navigator.push(context,
|
||||
MaterialPageRoute(builder: (context) {
|
||||
return PrivacyView();
|
||||
}));
|
||||
},
|
||||
),
|
||||
SettingsListTile(
|
||||
icon: FontAwesomeIcons.bell,
|
||||
|
|
|
|||
Loading…
Reference in a new issue