mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-01-15 21:18:40 +00:00
fix #81
This commit is contained in:
parent
42330594bb
commit
d25f4cb36d
8 changed files with 269 additions and 45 deletions
|
|
@ -27,6 +27,7 @@ class _UserContextMenuState extends State<UserContextMenu> {
|
||||||
return PieMenu(
|
return PieMenu(
|
||||||
onPressed: () => (),
|
onPressed: () => (),
|
||||||
actions: [
|
actions: [
|
||||||
|
if (!widget.contact.archived)
|
||||||
PieAction(
|
PieAction(
|
||||||
tooltip: Text(context.lang.contextMenuArchiveUser),
|
tooltip: Text(context.lang.contextMenuArchiveUser),
|
||||||
onSelect: () async {
|
onSelect: () async {
|
||||||
|
|
@ -38,6 +39,18 @@ class _UserContextMenuState extends State<UserContextMenu> {
|
||||||
},
|
},
|
||||||
child: FaIcon(FontAwesomeIcons.boxArchive),
|
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(
|
PieAction(
|
||||||
tooltip: Text(context.lang.contextMenuVerifyUser),
|
tooltip: Text(context.lang.contextMenuVerifyUser),
|
||||||
onSelect: () {
|
onSelect: () {
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,14 @@
|
||||||
"@contextMenuVerifyUser": {},
|
"@contextMenuVerifyUser": {},
|
||||||
"contextMenuArchiveUser": "Archivieren",
|
"contextMenuArchiveUser": "Archivieren",
|
||||||
"@contextMenuArchiveUser": {},
|
"@contextMenuArchiveUser": {},
|
||||||
|
"contextMenuUndoArchiveUser": "Archivierung aufheben",
|
||||||
|
"@contextMenuUndoArchiveUser": {},
|
||||||
|
"startNewChatTitle": "Kontakt wählen",
|
||||||
|
"@startNewChatTitle": {},
|
||||||
|
"startNewChatNewContact": "Neuer Kontakt",
|
||||||
|
"@startNewChatNewContact": {},
|
||||||
|
"startNewChatYourContacts": "Deine Kontakte",
|
||||||
|
"@startNewChatYourContacts": {},
|
||||||
"contextMenuOpenChat": "Chat",
|
"contextMenuOpenChat": "Chat",
|
||||||
"contextMenuSendImage": "Bild senden",
|
"contextMenuSendImage": "Bild senden",
|
||||||
"mediaViewerAuthReason": "Bitte authentifiziere dich, um diesen twonly zu sehen!",
|
"mediaViewerAuthReason": "Bitte authentifiziere dich, um diesen twonly zu sehen!",
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,12 @@
|
||||||
"@shareImagedEditorSavedImage": {},
|
"@shareImagedEditorSavedImage": {},
|
||||||
"shareImageSearchAllContacts": "Search all contacts",
|
"shareImageSearchAllContacts": "Search all contacts",
|
||||||
"@shareImageSearchAllContacts": {},
|
"@shareImageSearchAllContacts": {},
|
||||||
|
"startNewChatTitle": "Select Contact",
|
||||||
|
"@startNewChatTitle": {},
|
||||||
|
"startNewChatNewContact": "New Contact",
|
||||||
|
"@startNewChatNewContact": {},
|
||||||
|
"startNewChatYourContacts": "Your Contacts",
|
||||||
|
"@startNewChatYourContacts": {},
|
||||||
"shareImageAllUsers": "All contacts",
|
"shareImageAllUsers": "All contacts",
|
||||||
"@shareImageAllUsers": {},
|
"@shareImageAllUsers": {},
|
||||||
"shareImageAllTwonlyWarning": "twonlies can only be send to verified contacts!",
|
"shareImageAllTwonlyWarning": "twonlies can only be send to verified contacts!",
|
||||||
|
|
@ -98,6 +104,8 @@
|
||||||
"@contextMenuVerifyUser": {},
|
"@contextMenuVerifyUser": {},
|
||||||
"contextMenuArchiveUser": "Archive",
|
"contextMenuArchiveUser": "Archive",
|
||||||
"@contextMenuArchiveUser": {},
|
"@contextMenuArchiveUser": {},
|
||||||
|
"contextMenuUndoArchiveUser": "Undo archiving",
|
||||||
|
"@contextMenuUndoArchiveUser": {},
|
||||||
"contextMenuOpenChat": "Open chat",
|
"contextMenuOpenChat": "Open chat",
|
||||||
"@contextMenuOpenChat": {},
|
"@contextMenuOpenChat": {},
|
||||||
"contextMenuSendImage": "Send image",
|
"contextMenuSendImage": "Send image",
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import 'package:local_auth/local_auth.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:pie_menu/pie_menu.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:twonly/src/database/twonly_database.dart';
|
import 'package:twonly/src/database/twonly_database.dart';
|
||||||
import 'package:twonly/src/proto/api/error.pb.dart';
|
import 'package:twonly/src/proto/api/error.pb.dart';
|
||||||
|
|
@ -177,3 +178,27 @@ Future<bool> isAllowedToDownload() async {
|
||||||
}
|
}
|
||||||
return true;
|
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,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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/camera_to_share/share_image_view.dart';
|
||||||
import 'package:twonly/src/views/chats/chat_item_details_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/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/home_view.dart';
|
||||||
import 'package:twonly/src/views/settings/settings_main_view.dart';
|
import 'package:twonly/src/views/settings/settings_main_view.dart';
|
||||||
import 'package:twonly/src/views/chats/search_username_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) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: GestureDetector(
|
title: Text("twonly"),
|
||||||
onTap: () {
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => SettingsMainView(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: Text("twonly"),
|
|
||||||
),
|
|
||||||
// title:
|
|
||||||
actions: [
|
actions: [
|
||||||
StreamBuilder(
|
StreamBuilder(
|
||||||
stream: twonlyDatabase.contactsDao.watchContactsRequested(),
|
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),
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -184,12 +184,13 @@ class _SearchUsernameView extends State<SearchUsernameView> {
|
||||||
floatingActionButton: Padding(
|
floatingActionButton: Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 30.0),
|
padding: const EdgeInsets.only(bottom: 30.0),
|
||||||
child: FloatingActionButton(
|
child: FloatingActionButton(
|
||||||
|
foregroundColor: Colors.white,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (!_isLoading) _addNewUser(context);
|
if (!_isLoading) _addNewUser(context);
|
||||||
},
|
},
|
||||||
child: (_isLoading)
|
child: (_isLoading)
|
||||||
? const Center(child: CircularProgressIndicator())
|
? const Center(child: CircularProgressIndicator())
|
||||||
: Icon(Icons.arrow_right_rounded),
|
: FaIcon(FontAwesomeIcons.magnifyingGlassPlus),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
183
lib/src/views/chats/start_new_chat.dart
Normal file
183
lib/src/views/chats/start_new_chat.dart
Normal 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);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@ import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:pie_menu/pie_menu.dart';
|
import 'package:pie_menu/pie_menu.dart';
|
||||||
import 'package:twonly/src/services/notification_service.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 'package:twonly/src/views/camera_to_share/share_image_view.dart';
|
||||||
import 'camera_to_share/camera_preview_view.dart';
|
import 'camera_to_share/camera_preview_view.dart';
|
||||||
import 'chats/chat_list_view.dart';
|
import 'chats/chat_list_view.dart';
|
||||||
|
|
@ -65,27 +66,7 @@ class HomeViewState extends State<HomeView> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return PieCanvas(
|
return PieCanvas(
|
||||||
theme: PieTheme(
|
theme: getPieCanvasTheme(context),
|
||||||
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,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
body: PageView(
|
body: PageView(
|
||||||
controller: homeViewPageController,
|
controller: homeViewPageController,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue