Skip to content

Commit

Permalink
Better no internet connection states (#592)
Browse files Browse the repository at this point in the history
Should close #521
  • Loading branch information
josancamon19 authored Aug 12, 2024
2 parents 42031ad + ecd340e commit 05037df
Show file tree
Hide file tree
Showing 10 changed files with 546 additions and 350 deletions.
11 changes: 11 additions & 0 deletions app/lib/backend/preferences.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'dart:convert';
import 'package:collection/collection.dart';
import 'package:friend_private/backend/database/transcript_segment.dart';
import 'package:friend_private/backend/schema/memory.dart';
import 'package:friend_private/backend/schema/message.dart';
import 'package:friend_private/backend/schema/plugin.dart';
import 'package:shared_preferences/shared_preferences.dart';

Expand Down Expand Up @@ -162,6 +163,16 @@ class SharedPreferencesUtil {
saveStringList('cachedMemories', memories);
}

List<ServerMessage> get cachedMessages {
final List<String> messages = getStringList('cachedMessages') ?? [];
return messages.map((e) => ServerMessage.fromJson(jsonDecode(e))).toList();
}

set cachedMessages(List<ServerMessage> value) {
final List<String> messages = value.map((e) => jsonEncode(e.toJson())).toList();
saveStringList('cachedMessages', messages);
}

addFailedMemory(ServerMemory memory) {
if (memory.transcriptSegments.isEmpty && memory.photos.isEmpty) return;

Expand Down
28 changes: 28 additions & 0 deletions app/lib/backend/schema/message.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ class MessageMemoryStructured {
static MessageMemoryStructured fromJson(Map<String, dynamic> json) {
return MessageMemoryStructured(json['title'], json['emoji']);
}

Map<String, dynamic> toJson() {
return {
'title': title,
'emoji': emoji,
};
}
}

class MessageMemory {
Expand All @@ -27,6 +34,14 @@ class MessageMemory {
MessageMemoryStructured.fromJson(json['structured']),
);
}

Map<String, dynamic> toJson() {
return {
'id': id,
'created_at': createdAt.toIso8601String(),
'structured': structured.toJson(),
};
}
}

class ServerMessage {
Expand Down Expand Up @@ -64,4 +79,17 @@ class ServerMessage {
((json['memories'] ?? []) as List<dynamic>).map((m) => MessageMemory.fromJson(m)).toList(),
);
}

Map<String, dynamic> toJson() {
return {
'id': id,
'created_at': createdAt.toIso8601String(),
'text': text,
'sender': sender.toString().split('.').last,
'type': type.toString().split('.').last,
'plugin_id': pluginId,
'from_integration': fromIntegration,
'memories': memories.map((m) => m.toJson()).toList(),
};
}
}
1 change: 0 additions & 1 deletion app/lib/pages/capture/page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter_foreground_task/flutter_foreground_task.dart';
Expand Down
2 changes: 1 addition & 1 deletion app/lib/pages/capture/widgets/widgets.dart
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ getConnectionStateWidgets(
Text(
isWifiDisconnected
? 'No Internet'
: isWebsocketError
: (!isWifiDisconnected && isWebsocketError)
? 'Server Issue'
: 'Listening',
style: TextStyle(
Expand Down
77 changes: 51 additions & 26 deletions app/lib/pages/chat/page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'package:friend_private/backend/schema/message.dart';
import 'package:friend_private/backend/schema/plugin.dart';
import 'package:friend_private/pages/chat/widgets/ai_message.dart';
import 'package:friend_private/pages/chat/widgets/user_message.dart';
import 'package:friend_private/utils/connectivity_controller.dart';
import 'package:gradient_borders/gradient_borders.dart';
import 'package:uuid/uuid.dart';

Expand All @@ -16,13 +17,15 @@ class ChatPage extends StatefulWidget {
final List<ServerMessage> messages;
final Function(ServerMessage) addMessage;
final Function(ServerMemory) updateMemory;
final bool isLoadingMessages;

const ChatPage({
super.key,
required this.textFieldFocusNode,
required this.messages,
required this.addMessage,
required this.updateMemory,
required this.isLoadingMessages,
});

@override
Expand Down Expand Up @@ -70,31 +73,44 @@ class ChatPageState extends State<ChatPage> with AutomaticKeepAliveClientMixin {
super.build(context);
return Stack(
children: [
SingleChildScrollView(
controller: scrollController,
child: ListView.builder(
shrinkWrap: true,
reverse: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: widget.messages.length,
itemBuilder: (context, chatIndex) {
final message = widget.messages[chatIndex];
double topPadding = chatIndex == widget.messages.length - 1 ? 24 : 16;
double bottomPadding = chatIndex == 0 ? (widget.textFieldFocusNode.hasFocus ? 120 : 200) : 0;
return Padding(
key: ValueKey(message.id),
padding: EdgeInsets.only(bottom: bottomPadding, left: 18, right: 18, top: topPadding),
child: message.sender == MessageSender.ai
? AIMessage(
message: message,
sendMessage: _sendMessageUtil,
displayOptions: widget.messages.length <= 1,
pluginSender: plugins.firstWhereOrNull((e) => e.id == message.pluginId),
updateMemory: widget.updateMemory,
)
: HumanMessage(message: message),
);
},
Center(
child: SingleChildScrollView(
controller: scrollController,
child: widget.isLoadingMessages
? const CircularProgressIndicator(
color: Colors.white,
)
: (widget.messages.isEmpty)
? Text(
ConnectivityController().isConnected.value
? 'No messages yet!\nWhy don\'t you start a conversation?'
: 'Please check your internet connection and try again',
textAlign: TextAlign.center,
style: const TextStyle(color: Colors.white))
: ListView.builder(
shrinkWrap: true,
reverse: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: widget.messages.length,
itemBuilder: (context, chatIndex) {
final message = widget.messages[chatIndex];
double topPadding = chatIndex == widget.messages.length - 1 ? 24 : 16;
double bottomPadding = chatIndex == 0 ? (widget.textFieldFocusNode.hasFocus ? 120 : 200) : 0;
return Padding(
key: ValueKey(message.id),
padding: EdgeInsets.only(bottom: bottomPadding, left: 18, right: 18, top: topPadding),
child: message.sender == MessageSender.ai
? AIMessage(
message: message,
sendMessage: _sendMessageUtil,
displayOptions: widget.messages.length <= 1,
pluginSender: plugins.firstWhereOrNull((e) => e.id == message.pluginId),
updateMemory: widget.updateMemory,
)
: HumanMessage(message: message),
);
},
),
),
),
Align(
Expand Down Expand Up @@ -139,7 +155,16 @@ class ChatPageState extends State<ChatPage> with AutomaticKeepAliveClientMixin {
: () async {
String message = textController.text;
if (message.isEmpty) return;
_sendMessageUtil(message);
if (ConnectivityController().isConnected.value) {
_sendMessageUtil(message);
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Please check your internet connection and try again'),
duration: Duration(seconds: 2),
),
);
}
},
icon: loading
? const SizedBox(
Expand Down
56 changes: 33 additions & 23 deletions app/lib/pages/chat/widgets/ai_message.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import 'package:friend_private/backend/schema/message.dart';
import 'package:friend_private/backend/schema/plugin.dart';
import 'package:friend_private/pages/memory_detail/page.dart';
import 'package:friend_private/utils/analytics/mixpanel.dart';
import 'package:friend_private/utils/connectivity_controller.dart';
import 'package:friend_private/utils/other/temp.dart';
import 'package:friend_private/widgets/extensions/string.dart';

Expand Down Expand Up @@ -116,30 +117,39 @@ class _AIMessageState extends State<AIMessage> {
padding: const EdgeInsetsDirectional.fromSTEB(0.0, 0.0, 0.0, 4.0),
child: GestureDetector(
onTap: () async {
if (memoryDetailLoading[data.$1]) return;
setState(() => memoryDetailLoading[data.$1] = true);
if (ConnectivityController().isConnected.value) {
if (memoryDetailLoading[data.$1]) return;
setState(() => memoryDetailLoading[data.$1] = true);

ServerMemory? m = await getMemoryById(data.$2.id);
if (m == null) return;
MixpanelManager().chatMessageMemoryClicked(m);
setState(() => memoryDetailLoading[data.$1] = false);
await Navigator.of(context)
.push(MaterialPageRoute(builder: (c) => MemoryDetailPage(memory: m)));
if (SharedPreferencesUtil().modifiedMemoryDetails?.id == m.id) {
ServerMemory modifiedDetails = SharedPreferencesUtil().modifiedMemoryDetails!;
widget.updateMemory(SharedPreferencesUtil().modifiedMemoryDetails!);
var copy = List<MessageMemory>.from(widget.message.memories);
copy[data.$1] = MessageMemory(
modifiedDetails.id,
modifiedDetails.createdAt,
MessageMemoryStructured(
modifiedDetails.structured.title,
modifiedDetails.structured.emoji,
));
widget.message.memories.clear();
widget.message.memories.addAll(copy);
SharedPreferencesUtil().modifiedMemoryDetails = null;
setState(() {});
ServerMemory? m = await getMemoryById(data.$2.id);
if (m == null) return;
MixpanelManager().chatMessageMemoryClicked(m);
setState(() => memoryDetailLoading[data.$1] = false);
await Navigator.of(context)
.push(MaterialPageRoute(builder: (c) => MemoryDetailPage(memory: m)));
if (SharedPreferencesUtil().modifiedMemoryDetails?.id == m.id) {
ServerMemory modifiedDetails = SharedPreferencesUtil().modifiedMemoryDetails!;
widget.updateMemory(SharedPreferencesUtil().modifiedMemoryDetails!);
var copy = List<MessageMemory>.from(widget.message.memories);
copy[data.$1] = MessageMemory(
modifiedDetails.id,
modifiedDetails.createdAt,
MessageMemoryStructured(
modifiedDetails.structured.title,
modifiedDetails.structured.emoji,
));
widget.message.memories.clear();
widget.message.memories.addAll(copy);
SharedPreferencesUtil().modifiedMemoryDetails = null;
setState(() {});
}
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Please check your internet connection and try again'),
duration: Duration(seconds: 2),
),
);
}
},
child: Container(
Expand Down
11 changes: 8 additions & 3 deletions app/lib/pages/home/firmware_mixin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,19 @@ mixin FirmwareMixin<T extends StatefulWidget> on State<T> {
int device = deviceName == 'Friend' ? 1 : 2;
var res = await makeApiCall(
url: "${Env.apiBaseUrl}v1/firmware/latest?device=$device", headers: {}, body: '', method: 'GET');
var body = jsonDecode(res!.body);
latestFirmwareDetails = body;
if (res == null) {
latestFirmwareDetails = {};
return;
}
if (res.statusCode == 200) {
latestFirmwareDetails = jsonDecode(res.body);
}
}

Future<(String, bool)> shouldUpdateFirmware({required String currentFirmware, required String deviceName}) async {
Version currentVersion = Version.parse(currentFirmware);
if (latestFirmwareDetails.isEmpty) {
await getLatestVersion(deviceName: deviceName);
return ('Latest Version Not Available', false);
}
if (latestFirmwareDetails.isEmpty || latestFirmwareDetails['version'] == null) {
return ('Latest Version Not Available', false);
Expand Down
Loading

0 comments on commit 05037df

Please sign in to comment.