Skip to content

Commit

Permalink
Merge branch 'main' into chore/migrate-makefile
Browse files Browse the repository at this point in the history
  • Loading branch information
raphaelrobert authored Dec 10, 2024
2 parents 65724b4 + 8eef05c commit 61ac1e9
Show file tree
Hide file tree
Showing 26 changed files with 295 additions and 188 deletions.
7 changes: 7 additions & 0 deletions prototype/devtools_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# SPDX-FileCopyrightText: 2024 Phoenix R&D GmbH <[email protected]>
#
# SPDX-License-Identifier: AGPL-3.0-or-later

description: This file stores settings for Dart & Flutter DevTools.
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
extensions:
4 changes: 2 additions & 2 deletions prototype/integration_test/simple_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
// SPDX-License-Identifier: AGPL-3.0-or-later

import 'package:flutter_test/flutter_test.dart';
import 'package:prototype/main.dart';
import 'package:prototype/app.dart';
import 'package:prototype/core/frb_generated.dart';
import 'package:integration_test/integration_test.dart';

void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
setUpAll(() async => await RustLib.init());
testWidgets('Can call rust function', (WidgetTester tester) async {
await tester.pumpWidget(const MyApp());
await tester.pumpWidget(const App());
expect(find.textContaining('Result: `Hello, Tom!`'), findsOneWidget);
});
}
101 changes: 101 additions & 0 deletions prototype/lib/app.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// SPDX-FileCopyrightText: 2024 Phoenix R&D GmbH <[email protected]>
//
// SPDX-License-Identifier: AGPL-3.0-or-later

import 'dart:io';

import 'package:flutter/material.dart';
import 'package:logging/logging.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:prototype/core_client.dart';
import 'package:prototype/homescreen.dart';
import 'package:prototype/platform.dart';
import 'package:provider/provider.dart';

import 'theme/theme.dart';

final GlobalKey<NavigatorState> appNavigator = GlobalKey<NavigatorState>();

final _log = Logger('App');

class App extends StatefulWidget {
const App({super.key});

@override
State<App> createState() => _AppState();
}

class _AppState extends State<App> with WidgetsBindingObserver {
final CoreClient _coreClient = CoreClient();

@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
_requestMobileNotifications();
}

@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}

@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
_onStateChanged(state);
}

Future<void> _onStateChanged(AppLifecycleState state) async {
if (state == AppLifecycleState.paused) {
_log.fine('App is in the background');

// iOS only
if (Platform.isIOS) {
// only set the badge count if the user is logged in
if (_coreClient.maybeUser case final user?) {
final count = await user.globalUnreadMessagesCount();
await setBadgeCount(count);
}
}
}
}

@override
Widget build(BuildContext context) {
// TODO: This provider should be moved below the `MaterialApp`. This can be
// done when the app router is introduced. We can't just wrap the
// `HomeScreen` because it is replaced in other places by another screens.
return Provider.value(
value: _coreClient,
child: MaterialApp(
title: 'Prototype',
debugShowCheckedModeBanner: false,
theme: themeData(context),
navigatorKey: appNavigator,
home: const HomeScreen(),
),
);
}
}

void _requestMobileNotifications() async {
// Mobile initialization
if (Platform.isAndroid || Platform.isIOS) {
// Initialize the method channel
initMethodChannel();

// Ask for notification permission
var status = await Permission.notification.status;
switch (status) {
case PermissionStatus.denied:
_log.info("Notification permission denied, will ask the user");
var requestStatus = await Permission.notification.request();
_log.fine("The status is $requestStatus");
break;
default:
_log.info("Notification permission status: $status");
}
}
}
32 changes: 21 additions & 11 deletions prototype/lib/conversation_list_pane/conversation_list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import 'package:prototype/core_client.dart';
import 'package:prototype/conversation_pane/conversation_pane.dart';
import 'package:prototype/elements.dart';
import 'package:prototype/messenger_view.dart';
import 'package:prototype/styles.dart';
import 'package:prototype/theme/theme.dart';
import '../styles.dart';
import 'package:convert/convert.dart';
import 'package:collection/collection.dart';

Expand All @@ -24,6 +24,8 @@ class ConversationList extends StatefulWidget {
}

class _ConversationListState extends State<ConversationList> {
_ConversationListState();

late List<UiConversationDetails> _conversations;
UiConversationDetails? _currentConversation;
StreamSubscription<ConversationIdBytes>? _conversationListUpdateListener;
Expand All @@ -32,19 +34,19 @@ class _ConversationListState extends State<ConversationList> {

static const double _topBaseline = 12;

_ConversationListState() {
@override
void initState() {
super.initState();

final coreClient = context.coreClient;
_conversations = coreClient.conversationsList;
_currentConversation = coreClient.currentConversation;
_conversationListUpdateListener = coreClient.onConversationListUpdate
.listen(conversationListUpdateListener);
_conversationSwitchListener =
coreClient.onConversationSwitch.listen(conversationSwitchListener);
}

@override
void initState() {
super.initState();
updateConversationList();
updateConversationList(coreClient);
}

@override
Expand All @@ -66,7 +68,10 @@ class _ConversationListState extends State<ConversationList> {
}
}

void selectConversation(ConversationIdBytes conversationId) {
void selectConversation(
CoreClient coreClient,
ConversationIdBytes conversationId,
) {
print("Tapped on conversation ${hex.encode(conversationId.bytes)}");
coreClient.selectConversation(conversationId);
if (isSmallScreen(context)) {
Expand All @@ -75,10 +80,10 @@ class _ConversationListState extends State<ConversationList> {
}

void conversationListUpdateListener(ConversationIdBytes uuid) async {
updateConversationList();
updateConversationList(context.coreClient);
}

void updateConversationList() async {
void updateConversationList(CoreClient coreClient) async {
await coreClient.conversations().then((conversations) {
setState(() {
if (_currentConversation == null && conversations.isNotEmpty) {
Expand Down Expand Up @@ -147,6 +152,8 @@ class _ConversationListState extends State<ConversationList> {

final senderStyle = style.copyWith(fontVariations: variationSemiBold);

final coreClient = context.coreClient;

if (lastMessage != null) {
lastMessage.message.when(
contentFlight: (c) {
Expand Down Expand Up @@ -290,7 +297,10 @@ class _ConversationListState extends State<ConversationList> {
selected: isConversationSelected(
_currentConversation, _conversations[index], context),
focusColor: convListItemSelectedColor,
onTap: () => selectConversation(_conversations[index].id),
onTap: () => selectConversation(
context.coreClient,
_conversations[index].id,
),
);
}

Expand Down
7 changes: 5 additions & 2 deletions prototype/lib/conversation_list_pane/footer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class ConversationListFooter extends StatelessWidget {

@override
Widget build(BuildContext context) {
final coreClient = context.coreClient;
return Container(
alignment: AlignmentDirectional.topStart,
padding: const EdgeInsets.fromLTRB(15, 15, 15, 30),
Expand Down Expand Up @@ -43,8 +44,10 @@ class ConversationListFooter extends StatelessWidget {
try {
await coreClient.createConnection(connectionUsername);
} catch (e) {
showErrorBanner(context,
'The user $connectionUsername could not be found');
if (context.mounted) {
showErrorBanner(context,
'The user $connectionUsername could not be found');
}
}
}
},
Expand Down
11 changes: 9 additions & 2 deletions prototype/lib/conversation_list_pane/pane.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,19 @@ class ConversationView extends StatefulWidget {
}

class _ConversationViewState extends State<ConversationView> {
String? displayName = coreClient.ownProfile.displayName;
Uint8List? profilePicture = coreClient.ownProfile.profilePictureOption;
String? displayName;
Uint8List? profilePicture;

@override
void initState() {
super.initState();

final coreClient = context.coreClient;
setState(() {
displayName = coreClient.ownProfile.displayName;
profilePicture = coreClient.ownProfile.profilePictureOption;
});

// Listen for changes to the user's profile picture
coreClient.onOwnProfileUpdate.listen((profile) {
if (mounted) {
Expand Down
8 changes: 4 additions & 4 deletions prototype/lib/conversation_list_pane/top.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class ConversationListTop extends StatelessWidget {
children: [
UserAvatar(
size: 32,
username: coreClient.username,
username: context.coreClient.username,
image: profilePicture,
onPressed: () {
Navigator.push(
Expand All @@ -54,7 +54,7 @@ class ConversationListTop extends StatelessWidget {
);
}

Column _usernameSpace() {
Column _usernameSpace(String username) {
return Column(
children: [
Text(
Expand All @@ -68,7 +68,7 @@ class ConversationListTop extends StatelessWidget {
),
const SizedBox(height: 5),
Text(
coreClient.username,
username,
style: const TextStyle(
color: colorDMB,
fontSize: 10,
Expand Down Expand Up @@ -118,7 +118,7 @@ class ConversationListTop extends StatelessWidget {
children: [
_avatar(context),
Expanded(
child: _usernameSpace(),
child: _usernameSpace(context.coreClient.username),
),
_settingsButton(context),
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class _ConversationContentState extends State<ConversationContent> {
super.initState();
_scrollController.addListener(_onScroll);

final coreClient = context.coreClient;
_conversationListener =
coreClient.onConversationSwitch.listen(conversationListener);
_messageListener = coreClient.onMessageUpdate.listen(messageListener);
Expand Down Expand Up @@ -100,7 +101,7 @@ class _ConversationContentState extends State<ConversationContent> {

void _onMessageVisible(String timestamp) {
if (_currentConversation != null) {
coreClient.user.markMessagesAsReadDebounced(
context.coreClient.user.markMessagesAsReadDebounced(
conversationId: _currentConversation!.id, timestamp: timestamp);
}
}
Expand All @@ -113,7 +114,7 @@ class _ConversationContentState extends State<ConversationContent> {

Future<void> updateMessages() async {
if (_currentConversation != null) {
final messages = await coreClient.user
final messages = await context.coreClient.user
.getMessages(conversationId: _currentConversation!.id, lastN: 50);
setState(() {
print("Number of messages: ${messages.length}");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class _TextMessageTileState extends State<TextMessageTile> {
@override
void initState() {
super.initState();
coreClient.user
context.coreClient.user
.userProfile(userName: widget.contentFlight.last.sender)
.then((p) {
if (mounted) {
Expand All @@ -36,7 +36,7 @@ class _TextMessageTileState extends State<TextMessageTile> {
}

bool isSender() {
return widget.contentFlight.last.sender == coreClient.username;
return widget.contentFlight.last.sender == context.coreClient.username;
}

@override
Expand Down Expand Up @@ -95,7 +95,7 @@ class _TextMessageTileState extends State<TextMessageTile> {

Widget _avatar() {
return FutureUserAvatar(
profile: coreClient.user
profile: context.coreClient.user
.userProfile(userName: widget.contentFlight.last.sender),
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class _AddMembersState extends State<AddMembers> {
void initState() {
super.initState();
_conversationListener =
coreClient.onConversationSwitch.listen(conversationListener);
context.coreClient.onConversationSwitch.listen(conversationListener);
getContacts();
}

Expand All @@ -42,13 +42,14 @@ class _AddMembersState extends State<AddMembers> {
}

getContacts() async {
contacts = await coreClient.getContacts();
contacts = await context.coreClient.getContacts();
setState(() {});
}

addContacts() async {
for (var contact in selectedContacts) {
await coreClient.addUserToConversation(widget.conversation.id, contact);
await context.coreClient
.addUserToConversation(widget.conversation.id, contact);
}
}

Expand Down Expand Up @@ -92,7 +93,7 @@ class _AddMembersState extends State<AddMembers> {
final contact = contacts[index];
return ListTile(
leading: FutureUserAvatar(
profile: coreClient.user
profile: context.coreClient.user
.userProfile(userName: contact.userName),
),
title: Text(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class ConnectionDetails extends StatelessWidget {

@override
Widget build(BuildContext context) {
final coreClient = context.coreClient;
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
Expand Down
Loading

0 comments on commit 61ac1e9

Please sign in to comment.