Skip to content

Commit

Permalink
Fix issues caught by Instabug (#779)
Browse files Browse the repository at this point in the history
<!-- This is an auto-generated comment: release notes by OSS
Entelligence.AI -->
### Summary by Entelligence.AI

- Bug Fix: Improved error handling and robustness in HTTP API calls and
Bluetooth operations, reducing the likelihood of crashes due to
unexpected responses or connection issues.
- Refactor: Enhanced sign-in process with better error messaging and
conditional sign-in attempts, improving user experience during
authentication.
- Bug Fix: Ensured UI updates only occur when components are mounted,
preventing potential crashes during asynchronous operations.
- Refactor: Optimized event handling in CalendarUtil by adjusting time
zone usage, enhancing accuracy of event timings.
- Bug Fix: Added checks for file existence before deletion, preventing
errors related to non-existent files.
<!-- end of auto-generated comment: release notes by OSS Entelligence.AI
-->
  • Loading branch information
josancamon19 authored Sep 8, 2024
2 parents baac75d + d8ff6fd commit cdc73aa
Show file tree
Hide file tree
Showing 21 changed files with 191 additions and 98 deletions.
7 changes: 5 additions & 2 deletions app/lib/backend/http/api/messages.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@ Future<List<ServerMessage>> getMessagesServer() async {
if (response == null) return [];
debugPrint('getMessages: ${response.body}');
if (response.statusCode == 200) {
var messages =
(jsonDecode(response.body) as List<dynamic>).map((memory) => ServerMessage.fromJson(memory)).toList();
var decodedBody = jsonDecode(response.body) as List<dynamic>;
if (decodedBody.isEmpty) {
return [];
}
var messages = decodedBody.map((memory) => ServerMessage.fromJson(memory)).toList();
debugPrint('getMessages length: ${messages.length}');
return messages;
}
Expand Down
17 changes: 13 additions & 4 deletions app/lib/backend/http/api/plugins.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Future<List<Plugin>> retrievePlugins() async {
body: '',
method: 'GET',
);
if (response?.statusCode == 200) {
if (response != null && response.statusCode == 200 && response.body.isNotEmpty) {
try {
log('plugins: ${response?.body}');
var plugins = Plugin.fromJsonList(jsonDecode(response!.body));
Expand Down Expand Up @@ -95,7 +95,16 @@ Future<bool> isPluginSetupCompleted(String? url) async {
headers: {},
body: '',
);
var data = jsonDecode(response?.body ?? '{}');
print(data);
return data['is_setup_completed'] ?? false;
var data;
try {
data = jsonDecode(response?.body ?? '{}');
print(data);
return data['is_setup_completed'] ?? false;
} on FormatException catch (e) {
debugPrint('Response not a valid json: $e');
return false;
} catch (e) {
debugPrint('Error triggering memory request at endpoint: $e');
return false;
}
}
26 changes: 16 additions & 10 deletions app/lib/backend/http/cloud_storage.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,29 @@ import 'dart:io';

import 'package:flutter/material.dart';
import 'package:friend_private/backend/preferences.dart';
import 'package:friend_private/utils/logger.dart';
import 'package:googleapis_auth/auth_io.dart';
import 'package:http/http.dart' as http;

AuthClient? authClient;

Future<void> authenticateGCP({String? base64}) async {
var credentialsBase64 = base64 ?? SharedPreferencesUtil().gcpCredentials;
if (credentialsBase64.isEmpty) {
debugPrint('No GCP credentials found');
return;
try {
var credentialsBase64 = base64 ?? SharedPreferencesUtil().gcpCredentials;
if (credentialsBase64.isEmpty) {
debugPrint('No GCP credentials found');
return;
}
final credentialsBytes = base64Decode(credentialsBase64);
String decodedString = utf8.decode(credentialsBytes);
final credentials = ServiceAccountCredentials.fromJson(jsonDecode(decodedString));
var scopes = ['https://www.googleapis.com/auth/devstorage.full_control'];
authClient = await clientViaServiceAccount(credentials, scopes);
debugPrint('Authenticated');
} catch (e, s) {
Logger.handle(e, s,
message: 'Error authenticating with GCP credentials provided. Please check the credentials and try again.');
}
final credentialsBytes = base64Decode(credentialsBase64);
String decodedString = utf8.decode(credentialsBytes);
final credentials = ServiceAccountCredentials.fromJson(jsonDecode(decodedString));
var scopes = ['https://www.googleapis.com/auth/devstorage.full_control'];
authClient = await clientViaServiceAccount(credentials, scopes);
debugPrint('Authenticated');
}

Future<String?> uploadFile(File file, {bool prefixTimestamp = false}) async {
Expand Down
19 changes: 7 additions & 12 deletions app/lib/backend/http/openai.dart
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ Future<String> getPhotoDescription(Uint8List data) async {
],
},
];
return await gptApiCall(model: 'gpt-4o', messages: messages, maxTokens: 100);
var res = await gptApiCall(model: 'gpt-4o', messages: messages, maxTokens: 100);
if (res == null) return '';
return res;
}

Future<dynamic> gptApiCall({
Expand All @@ -75,11 +77,7 @@ Future<dynamic> gptApiCall({
if (urlSuffix == 'embeddings') {
body = jsonEncode({'model': model, 'input': contentToEmbed});
} else {
var bodyData = {
'model': model,
'messages': messages,
'temperature': temperature
};
var bodyData = {'model': model, 'messages': messages, 'temperature': temperature};
if (jsonResponseFormat) {
bodyData['response_format'] = {'type': 'json_object'};
} else if (tools.isNotEmpty) {
Expand All @@ -92,24 +90,21 @@ Future<dynamic> gptApiCall({
body = jsonEncode(bodyData);
}

var response =
await makeApiCall(url: url, headers: headers, body: body, method: 'POST');
var response = await makeApiCall(url: url, headers: headers, body: body, method: 'POST');
return extractContentFromResponse(
response,
isEmbedding: urlSuffix == 'embeddings',
isFunctionCalling: tools.isNotEmpty,
);
}

Future<String> executeGptPrompt(String? prompt,
{bool ignoreCache = false}) async {
Future<String> executeGptPrompt(String? prompt, {bool ignoreCache = false}) async {
if (prompt == null) return '';

var prefs = SharedPreferencesUtil();
var promptBase64 = base64Encode(utf8.encode(prompt));
var cachedResponse = prefs.gptCompletionCache(promptBase64);
if (!ignoreCache && prefs.gptCompletionCache(promptBase64).isNotEmpty)
return cachedResponse;
if (!ignoreCache && prefs.gptCompletionCache(promptBase64).isNotEmpty) return cachedResponse;

String response = await gptApiCall(model: 'gpt-4o', messages: [
{'role': 'system', 'content': prompt}
Expand Down
15 changes: 13 additions & 2 deletions app/lib/backend/http/webhooks.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:friend_private/backend/schema/transcript_segment.dart';
import 'package:friend_private/backend/http/shared.dart';
import 'package:friend_private/backend/preferences.dart';
import 'package:friend_private/backend/schema/memory.dart';
import 'package:http/http.dart';
import 'package:instabug_flutter/instabug_flutter.dart';
import 'package:mime/mime.dart';

Expand All @@ -31,10 +32,13 @@ Future<String> webhookOnMemoryCreatedCall(ServerMemory? memory, {bool returnRawB
);
debugPrint('response: ${response?.statusCode}');
if (returnRawBody) return jsonEncode({'statusCode': response?.statusCode, 'body': response?.body});

var body = jsonDecode(response?.body ?? '{}');
print(body);
//TODO: Some endpoints return an array. I guess it makes sense to have standardised response strcuture. What do you think? @josancamon19
return body['message'] ?? '';
} on FormatException catch (e) {
debugPrint('Response not a valid json: $e');
return '';
} catch (e) {
debugPrint('Error triggering memory request at endpoint: $e');
// TODO: is it bad for reporting? I imagine most of the time is backend error, so nah.
Expand All @@ -60,8 +64,9 @@ Future<String> triggerTranscriptSegmentsRequest(String url, String sessionId, Li
} else {
url += '?uid=${SharedPreferencesUtil().uid}';
}
Response? response;
try {
var response = await makeApiCall(
response = await makeApiCall(
url: url,
headers: {'Content-Type': 'application/json'},
body: jsonEncode({
Expand All @@ -74,10 +79,16 @@ Future<String> triggerTranscriptSegmentsRequest(String url, String sessionId, Li
if (response?.statusCode == 200) {
var body = jsonDecode(response?.body ?? '{}');
print(body);
// TODO: Some endpoints return an array. I guess it makes sense to have standardised response structure.
return body['message'] ?? '';
} else {
return '';
}
} on FormatException catch (e) {
debugPrint('Response not a valid json: $e');
// TODO: It is either a string or html string. So I guess we can return it as is.
// I guess we should probably have a predefined response structure for webhooks which the webhook creators have to follow? @josancamon19
return response?.body ?? '';
} catch (e) {
debugPrint('Error triggering transcript request at endpoint: $e');
// TODO: is it bad for reporting? I imagine most of the time is backend error, so nah.
Expand Down
6 changes: 5 additions & 1 deletion app/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,11 @@ class _MyAppState extends State<MyApp> {
void initState() {
NotificationUtil.initializeNotificationsEventListeners();
NotificationUtil.initializeIsolateReceivePort();
NotificationService.instance.saveNotificationToken();
WidgetsBinding.instance.addPostFrameCallback((_) {
//TODO: Internet connection check required
NotificationService.instance.saveNotificationToken();
});

super.initState();
}

Expand Down
12 changes: 7 additions & 5 deletions app/lib/pages/home/firmware_update.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,13 @@ class _FirmwareUpdateState extends State<FirmwareUpdate> with FirmwareMixin {
await getLatestVersion(deviceName: widget.device!.name);
var (a, b) = await shouldUpdateFirmware(
currentFirmware: widget.deviceInfo.firmwareRevision, deviceName: widget.device!.name);
setState(() {
shouldUpdate = b;
updateMessage = a;
isLoading = false;
});
if (mounted) {
setState(() {
shouldUpdate = b;
updateMessage = a;
isLoading = false;
});
}
});
super.initState();
}
Expand Down
68 changes: 36 additions & 32 deletions app/lib/pages/home/page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -167,41 +167,45 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver, Ticker
previousConnection = isConnected;
if (!isConnected) {
Future.delayed(Duration.zero, () {
ScaffoldMessenger.of(ctx).showMaterialBanner(
MaterialBanner(
content: const Text('No internet connection. Please check your connection.'),
backgroundColor: Colors.red,
actions: [
TextButton(
onPressed: () {
ScaffoldMessenger.of(ctx).hideCurrentMaterialBanner();
},
child: const Text('Dismiss'),
),
],
),
);
if (mounted) {
ScaffoldMessenger.of(ctx).showMaterialBanner(
MaterialBanner(
content: const Text('No internet connection. Please check your connection.'),
backgroundColor: Colors.red,
actions: [
TextButton(
onPressed: () {
ScaffoldMessenger.of(ctx).hideCurrentMaterialBanner();
},
child: const Text('Dismiss'),
),
],
),
);
}
});
} else {
Future.delayed(Duration.zero, () {
ScaffoldMessenger.of(ctx).hideCurrentMaterialBanner();
ScaffoldMessenger.of(ctx).showMaterialBanner(
MaterialBanner(
content: const Text('Internet connection is restored.'),
backgroundColor: Colors.green,
actions: [
TextButton(
onPressed: () {
ScaffoldMessenger.of(ctx).hideCurrentMaterialBanner();
},
child: const Text('Dismiss'),
),
],
onVisible: () => Future.delayed(const Duration(seconds: 3), () {
ScaffoldMessenger.of(ctx).hideCurrentMaterialBanner();
}),
),
);
if (mounted) {
ScaffoldMessenger.of(ctx).hideCurrentMaterialBanner();
ScaffoldMessenger.of(ctx).showMaterialBanner(
MaterialBanner(
content: const Text('Internet connection is restored.'),
backgroundColor: Colors.green,
actions: [
TextButton(
onPressed: () {
ScaffoldMessenger.of(ctx).hideCurrentMaterialBanner();
},
child: const Text('Dismiss'),
),
],
onVisible: () => Future.delayed(const Duration(seconds: 3), () {
ScaffoldMessenger.of(ctx).hideCurrentMaterialBanner();
}),
),
);
}

WidgetsBinding.instance.addPostFrameCallback((_) async {
if (mounted) {
Expand Down
4 changes: 3 additions & 1 deletion app/lib/pages/memory_detail/page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,9 @@ class _MemoryDetailPageState extends State<MemoryDetailPage> with TickerProvider
if (widget.memory.source == MemorySource.openglass) {
getMemoryPhotos(widget.memory.id).then((value) {
photos = value;
setState(() {}); // TODO: if left before this closes, fails
if (mounted) {
setState(() {}); // TODO: if left before this closes, fails
}
});
} else if (widget.memory.source == MemorySource.friend) {
hasMemoryRecording(widget.memory.id).then((value) {
Expand Down
6 changes: 5 additions & 1 deletion app/lib/pages/memory_detail/widgets.dart
Original file line number Diff line number Diff line change
Expand Up @@ -779,8 +779,12 @@ showOptionsBottomSheet(
builder: (context) => StatefulBuilder(builder: (context, setModalState) {
changeDisplayDevOptions(bool value) => setModalState(() => displayDevTools = value);
changeDisplayShareOptions(bool value) => setModalState(() => displayShareOptions = value);
changeLoadingReprocessMemory(bool value) {
if (context.mounted) {
setModalState(() => loadingReprocessMemory = value);
}
}

changeLoadingReprocessMemory(bool value) => setModalState(() => loadingReprocessMemory = value);
changeLoadingPluginIntegrationTest(bool value) => setModalState(() => loadingPluginIntegrationTest = value);

changeLoadingShareMemoryTranscript(bool value) => setModalState(() => loadingShareMemoryTranscript = value);
Expand Down
1 change: 1 addition & 0 deletions app/lib/pages/onboarding/wrapper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class _OnboardingWrapperState extends State<OnboardingWrapper> with TickerProvid

@override
void initState() {
//TODO: Change from tab controller to default controller and use provider (part of instabug cleanup) @mdmohsin7
_controller = TabController(length: 7, vsync: this);
_controller!.addListener(() => setState(() {}));
WidgetsBinding.instance.addPostFrameCallback((_) async {
Expand Down
6 changes: 4 additions & 2 deletions app/lib/pages/plugins/plugin_detail.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ class _PluginDetailPageState extends State<PluginDetailPage> {
checkSetupCompleted() {
// TODO: move check to backend
isPluginSetupCompleted(widget.plugin.externalIntegration!.setupCompletedUrl).then((value) {
setState(() => setupCompleted = value);
if (mounted) {
setState(() => setupCompleted = value);
}
});
}

Expand Down Expand Up @@ -186,7 +188,7 @@ class _PluginDetailPageState extends State<PluginDetailPage> {
? Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Text(
widget.plugin.chatPrompt!,
widget.plugin.chatPrompt ?? '',
style: const TextStyle(color: Colors.grey, fontSize: 15, height: 1.4),
),
)
Expand Down
14 changes: 8 additions & 6 deletions app/lib/pages/settings/recordings_storage_permission.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@ class _RecordingsStoragePermissionState extends State<RecordingsStoragePermissio

Future<void> _checkPermission() async {
final permission = await getStoreRecordingPermission();
setState(() {
_hasPermission = permission;
if (permission != null) {
SharedPreferencesUtil().permissionStoreRecordingsEnabled = permission;
}
});
if (mounted) {
setState(() {
_hasPermission = permission;
if (permission != null) {
SharedPreferencesUtil().permissionStoreRecordingsEnabled = permission;
}
});
}
}

@override
Expand Down
8 changes: 5 additions & 3 deletions app/lib/pages/settings/webview.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@ class _PageWebViewState extends State<PageWebView> {
..setNavigationDelegate(
NavigationDelegate(
onProgress: (int p) {
setState(() {
progress = p;
});
if (mounted) {
setState(() {
progress = p;
});
}
},
onPageStarted: (String url) {},
onPageFinished: (String url) {},
Expand Down
Loading

0 comments on commit cdc73aa

Please sign in to comment.