This commit is contained in:
otsmr 2025-04-08 17:26:42 +02:00
parent 42330594bb
commit d25f4cb36d
8 changed files with 269 additions and 45 deletions

View file

@ -27,17 +27,30 @@ class _UserContextMenuState extends State<UserContextMenu> {
return PieMenu(
onPressed: () => (),
actions: [
PieAction(
tooltip: Text(context.lang.contextMenuArchiveUser),
onSelect: () async {
final update = ContactsCompanion(archived: Value(true));
if (context.mounted) {
await twonlyDatabase.contactsDao
.updateContact(widget.contact.userId, update);
}
},
child: FaIcon(FontAwesomeIcons.boxArchive),
),
if (!widget.contact.archived)
PieAction(
tooltip: Text(context.lang.contextMenuArchiveUser),
onSelect: () async {
final update = ContactsCompanion(archived: Value(true));
if (context.mounted) {
await twonlyDatabase.contactsDao
.updateContact(widget.contact.userId, update);
}
},
child: FaIcon(FontAwesomeIcons.boxArchive),
),
if (widget.contact.archived)
PieAction(
tooltip: Text(context.lang.contextMenuUndoArchiveUser),
onSelect: () async {
final update = ContactsCompanion(archived: Value(false));
if (context.mounted) {
await twonlyDatabase.contactsDao
.updateContact(widget.contact.userId, update);
}
},
child: FaIcon(FontAwesomeIcons.boxOpen),
),
PieAction(
tooltip: Text(context.lang.contextMenuVerifyUser),
onSelect: () {

View file

@ -54,6 +54,14 @@
"@contextMenuVerifyUser": {},
"contextMenuArchiveUser": "Archivieren",
"@contextMenuArchiveUser": {},
"contextMenuUndoArchiveUser": "Archivierung aufheben",
"@contextMenuUndoArchiveUser": {},
"startNewChatTitle": "Kontakt wählen",
"@startNewChatTitle": {},
"startNewChatNewContact": "Neuer Kontakt",
"@startNewChatNewContact": {},
"startNewChatYourContacts": "Deine Kontakte",
"@startNewChatYourContacts": {},
"contextMenuOpenChat": "Chat",
"contextMenuSendImage": "Bild senden",
"mediaViewerAuthReason": "Bitte authentifiziere dich, um diesen twonly zu sehen!",

View file

@ -60,6 +60,12 @@
"@shareImagedEditorSavedImage": {},
"shareImageSearchAllContacts": "Search all contacts",
"@shareImageSearchAllContacts": {},
"startNewChatTitle": "Select Contact",
"@startNewChatTitle": {},
"startNewChatNewContact": "New Contact",
"@startNewChatNewContact": {},
"startNewChatYourContacts": "Your Contacts",
"@startNewChatYourContacts": {},
"shareImageAllUsers": "All contacts",
"@shareImageAllUsers": {},
"shareImageAllTwonlyWarning": "twonlies can only be send to verified contacts!",
@ -98,6 +104,8 @@
"@contextMenuVerifyUser": {},
"contextMenuArchiveUser": "Archive",
"@contextMenuArchiveUser": {},
"contextMenuUndoArchiveUser": "Undo archiving",
"@contextMenuUndoArchiveUser": {},
"contextMenuOpenChat": "Open chat",
"@contextMenuOpenChat": {},
"contextMenuSendImage": "Send image",

View file

@ -9,6 +9,7 @@ import 'package:local_auth/local_auth.dart';
import 'package:logging/logging.dart';
import 'package:path_provider/path_provider.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:pie_menu/pie_menu.dart';
import 'package:provider/provider.dart';
import 'package:twonly/src/database/twonly_database.dart';
import 'package:twonly/src/proto/api/error.pb.dart';
@ -177,3 +178,27 @@ Future<bool> isAllowedToDownload() async {
}
return true;
}
PieTheme getPieCanvasTheme(BuildContext context) {
return PieTheme(
brightness: Theme.of(context).brightness,
rightClickShowsMenu: true,
radius: 70,
buttonTheme: PieButtonTheme(
backgroundColor: Theme.of(context).colorScheme.tertiary,
iconColor: Theme.of(context).colorScheme.surfaceBright,
),
buttonThemeHovered: PieButtonTheme(
backgroundColor: Theme.of(context).colorScheme.primary,
iconColor: Theme.of(context).colorScheme.surfaceBright,
),
tooltipPadding: EdgeInsets.all(20),
overlayColor: const Color.fromARGB(41, 0, 0, 0),
// spacing: 0,
tooltipTextStyle: TextStyle(
fontSize: 32,
fontWeight: FontWeight.w600,
),
);
}

View file

@ -16,6 +16,7 @@ import 'package:twonly/src/utils/misc.dart';
import 'package:twonly/src/views/camera_to_share/share_image_view.dart';
import 'package:twonly/src/views/chats/chat_item_details_view.dart';
import 'package:twonly/src/views/chats/media_viewer_view.dart';
import 'package:twonly/src/views/chats/start_new_chat.dart';
import 'package:twonly/src/views/home_view.dart';
import 'package:twonly/src/views/settings/settings_main_view.dart';
import 'package:twonly/src/views/chats/search_username_view.dart';
@ -32,18 +33,7 @@ class _ChatListViewState extends State<ChatListView> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SettingsMainView(),
),
);
},
child: Text("twonly"),
),
// title:
title: Text("twonly"),
actions: [
StreamBuilder(
stream: twonlyDatabase.contactsDao.watchContactsRequested(),
@ -133,6 +123,21 @@ class _ChatListViewState extends State<ChatListView> {
);
},
),
floatingActionButton: Padding(
padding: const EdgeInsets.only(bottom: 30.0),
child: FloatingActionButton(
foregroundColor: Colors.white,
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return StartNewChat();
}),
);
},
child: FaIcon(FontAwesomeIcons.penToSquare),
),
),
);
}
}

View file

@ -184,12 +184,13 @@ class _SearchUsernameView extends State<SearchUsernameView> {
floatingActionButton: Padding(
padding: const EdgeInsets.only(bottom: 30.0),
child: FloatingActionButton(
foregroundColor: Colors.white,
onPressed: () {
if (!_isLoading) _addNewUser(context);
},
child: (_isLoading)
? const Center(child: CircularProgressIndicator())
: Icon(Icons.arrow_right_rounded),
: FaIcon(FontAwesomeIcons.magnifyingGlassPlus),
),
),
);

View file

@ -0,0 +1,183 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:pie_menu/pie_menu.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/components/flame.dart';
import 'package:twonly/src/components/headline.dart';
import 'package:twonly/src/components/initialsavatar.dart';
import 'package:twonly/src/components/user_context_menu.dart';
import 'package:twonly/src/database/daos/contacts_dao.dart';
import 'package:twonly/src/database/twonly_database.dart';
import 'package:twonly/src/utils/misc.dart';
import 'package:twonly/src/views/chats/chat_item_details_view.dart';
import 'package:twonly/src/views/chats/search_username_view.dart';
class StartNewChat extends StatefulWidget {
const StartNewChat({super.key});
@override
State<StartNewChat> createState() => _StartNewChat();
}
class _StartNewChat extends State<StartNewChat> {
List<Contact> contacts = [];
List<Contact> allContacts = [];
final TextEditingController searchUserName = TextEditingController();
late StreamSubscription<List<Contact>> contactSub;
int maxTotalMediaCounter = 1000;
@override
void initState() {
super.initState();
Stream<List<Contact>> stream =
twonlyDatabase.contactsDao.watchContactsForShareView();
contactSub = stream.listen((update) {
setState(() {
allContacts = update;
});
filterUsers();
});
}
@override
void dispose() {
super.dispose();
contactSub.cancel();
}
Future filterUsers() async {
if (searchUserName.value.text.isEmpty) {
setState(() {
contacts = allContacts;
});
return;
}
List<Contact> usersFiltered = allContacts
.where((user) => getContactDisplayName(user)
.toLowerCase()
.contains(searchUserName.value.text.toLowerCase()))
.toList();
setState(() {
contacts = usersFiltered;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(context.lang.startNewChatTitle),
),
body: SafeArea(
child: PieCanvas(
theme: getPieCanvasTheme(context),
child: Padding(
padding: EdgeInsets.only(bottom: 40, left: 10, top: 20, right: 10),
child: Column(
children: [
Padding(
padding: EdgeInsets.symmetric(horizontal: 10),
child: TextField(
onChanged: (_) {
filterUsers();
},
decoration: getInputDecoration(
context,
context.lang.shareImageSearchAllContacts,
),
),
),
const SizedBox(height: 10),
Expanded(
child: UserList(
contacts,
maxTotalMediaCounter,
),
)
],
),
),
),
),
);
}
}
class UserList extends StatelessWidget {
const UserList(
this.users,
this.maxTotalMediaCounter, {
super.key,
});
final List<Contact> users;
final int maxTotalMediaCounter;
@override
Widget build(BuildContext context) {
// Step 1: Sort the users alphabetically
users
.sort((a, b) => b.lastMessageExchange.compareTo(a.lastMessageExchange));
return ListView.builder(
restorationId: 'new_message_users_list',
itemCount: users.length + 2,
itemBuilder: (BuildContext context, int i) {
if (i == 0) {
return ListTile(
title: Text(context.lang.startNewChatNewContact),
leading: CircleAvatar(
child: FaIcon(
FontAwesomeIcons.userPlus,
size: 15,
),
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SearchUsernameView(),
),
);
},
);
}
if (i == 1) {
return HeadLineComponent(context.lang.startNewChatYourContacts);
}
Contact user = users[i - 2];
int flameCounter = getFlameCounterFromContact(user);
return UserContextMenu(
contact: user,
child: ListTile(
title: Row(
mainAxisAlignment: MainAxisAlignment.start, // Center horizontally
crossAxisAlignment:
CrossAxisAlignment.center, // Center vertically
children: [
Text(getContactDisplayName(user)),
if (flameCounter >= 1)
FlameCounterWidget(
user,
flameCounter,
maxTotalMediaCounter,
prefix: true,
),
],
),
leading: ContactAvatar(contact: user),
onTap: () {
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) {
return ChatItemDetailsView(user);
}),
);
},
),
);
},
);
}
}

View file

@ -2,6 +2,7 @@ import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:pie_menu/pie_menu.dart';
import 'package:twonly/src/services/notification_service.dart';
import 'package:twonly/src/utils/misc.dart';
import 'package:twonly/src/views/camera_to_share/share_image_view.dart';
import 'camera_to_share/camera_preview_view.dart';
import 'chats/chat_list_view.dart';
@ -65,27 +66,7 @@ class HomeViewState extends State<HomeView> {
@override
Widget build(BuildContext context) {
return PieCanvas(
theme: PieTheme(
brightness: Theme.of(context).brightness,
rightClickShowsMenu: true,
radius: 70,
buttonTheme: PieButtonTheme(
backgroundColor: Theme.of(context).colorScheme.tertiary,
iconColor: Theme.of(context).colorScheme.surfaceBright,
),
buttonThemeHovered: PieButtonTheme(
backgroundColor: Theme.of(context).colorScheme.primary,
iconColor: Theme.of(context).colorScheme.surfaceBright,
),
tooltipPadding: EdgeInsets.all(20),
overlayColor: const Color.fromARGB(41, 0, 0, 0),
// spacing: 0,
tooltipTextStyle: TextStyle(
fontSize: 32,
fontWeight: FontWeight.w600,
),
),
theme: getPieCanvasTheme(context),
child: Scaffold(
body: PageView(
controller: homeViewPageController,