Fix Crash Bug in Android (ShareFile and Reconnect) + Force prettyDateString to use .toLocal() time + Formatting #794
|
@ -1 +1 @@
|
|||
2023-09-26-13-15-v0.0.10
|
||||
2024-01-03-20-52-v0.0.10-4-g6c0b2e2
|
|
@ -331,8 +331,9 @@ class MainActivity: FlutterActivity() {
|
|||
val conversation: Int = call.argument("conversation") ?: 0
|
||||
val indexI: Int = call.argument("index") ?: 0
|
||||
val count: Int = call.argument("count") ?: 1
|
||||
val ucount : Int = maxOf(1, count) // don't allow negative counts
|
||||
|
||||
result.success(Cwtch.getMessages(profile, conversation.toLong(), indexI.toLong(), count.toLong()))
|
||||
result.success(Cwtch.getMessages(profile, conversation.toLong(), indexI.toLong(), ucount.toLong()))
|
||||
return
|
||||
}
|
||||
"SendMessage" -> {
|
||||
|
|
|
@ -156,4 +156,6 @@ abstract class Cwtch {
|
|||
void DeleteServerInfo(String profile, String handle);
|
||||
void PublishServerUpdate(String onion);
|
||||
Future<void> ConfigureConnections(String onion, bool listen, bool peers, bool servers);
|
||||
|
||||
bool IsLoaded();
|
||||
}
|
||||
|
|
|
@ -268,6 +268,8 @@ class CwtchFfi implements Cwtch {
|
|||
// Called on object being disposed to (presumably on app close) to close the isolate that's listening to libcwtch-go events
|
||||
@override
|
||||
void dispose() {
|
||||
EnvironmentConfig.debugLog("tearing down cwtch FFI isolate");
|
||||
library.close();
|
||||
cwtchIsolate.kill(priority: Isolate.immediate);
|
||||
}
|
||||
|
||||
|
@ -1121,4 +1123,11 @@ class CwtchFfi implements Cwtch {
|
|||
PublishServerUpdate(utf8profile, utf8profile.length);
|
||||
malloc.free(utf8profile);
|
||||
}
|
||||
|
||||
@override
|
||||
bool IsLoaded() {
|
||||
bool check = library.providesSymbol("c_UpdateSettings");
|
||||
EnvironmentConfig.debugLog("Checking that the FFI Interface is Correctly Loaded... $check");
|
||||
return check;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -469,4 +469,9 @@ class CwtchGomobile implements Cwtch {
|
|||
void PublishServerUpdate(String profile) {
|
||||
cwtchPlatform.invokeMethod("PublishServerUpdate", {"ProfileOnion": profile});
|
||||
}
|
||||
|
||||
@override
|
||||
bool IsLoaded() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -173,6 +173,10 @@ class FlwtchState extends State<Flwtch> with WindowListener {
|
|||
getServerListStateProvider(),
|
||||
],
|
||||
builder: (context, widget) {
|
||||
// in test mode...rebuild everything every second...if cwtch isn't loaded...
|
||||
if (EnvironmentConfig.TEST_MODE && cwtch.IsLoaded() == false) {
|
||||
Timer t = new Timer.periodic(Duration(seconds: 1), (Timer t) => setState(() {}));
|
||||
}
|
||||
return Consumer2<Settings, AppState>(
|
||||
builder: (context, settings, appState, child) => MaterialApp(
|
||||
key: Key('app'),
|
||||
|
@ -190,7 +194,7 @@ class FlwtchState extends State<Flwtch> with WindowListener {
|
|||
title: 'Cwtch',
|
||||
showSemanticsDebugger: settings.useSemanticDebugger,
|
||||
theme: mkThemeData(settings),
|
||||
home: (!appState.cwtchInit || appState.modalState != ModalState.none) ? SplashView() : ProfileMgrView(),
|
||||
home: (!appState.cwtchInit || appState.modalState != ModalState.none) || !cwtch.IsLoaded() ? SplashView() : ProfileMgrView(),
|
||||
),
|
||||
);
|
||||
},
|
||||
|
|
|
@ -75,6 +75,8 @@ class ContactInfoState extends ChangeNotifier {
|
|||
DateTime _lastRetryTime = DateTime.now();
|
||||
DateTime loaded = DateTime.now();
|
||||
|
||||
List<ContactEvent> contactEvents = List.empty(growable: true);
|
||||
|
||||
ContactInfoState(
|
||||
this.profileOnion,
|
||||
this.identifier,
|
||||
|
@ -198,6 +200,7 @@ class ContactInfoState extends ChangeNotifier {
|
|||
|
||||
set status(String newVal) {
|
||||
this._status = newVal;
|
||||
this.contactEvents.add(ContactEvent("Update Peer Status Received: $newVal"));
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
|
@ -486,3 +489,11 @@ class ContactInfoState extends ChangeNotifier {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ContactEvent {
|
||||
String summary;
|
||||
late DateTime timestamp;
|
||||
ContactEvent(this.summary) {
|
||||
this.timestamp = DateTime.now();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -98,7 +98,7 @@ class ContactListState extends ChangeNotifier {
|
|||
notifyListeners();
|
||||
//} </todo>
|
||||
}
|
||||
|
||||
|
||||
void updateLastMessageReceivedTime(int forIdentifier, DateTime newMessageTime) {
|
||||
var contact = getContact(forIdentifier);
|
||||
if (contact == null) return;
|
||||
|
|
|
@ -30,22 +30,27 @@ class LocalIndexMessage {
|
|||
this.messageId = messageId;
|
||||
this.cacheOnly = cacheOnly;
|
||||
this.isLoading = isLoading;
|
||||
if (isLoading) {
|
||||
loader = Completer<void>();
|
||||
loaded = loader.future;
|
||||
loader = Completer<void>();
|
||||
loaded = loader.future;
|
||||
if (!isLoading) {
|
||||
loader.complete(); // complete this
|
||||
}
|
||||
}
|
||||
|
||||
void finishLoad(int messageId) {
|
||||
this.messageId = messageId;
|
||||
isLoading = false;
|
||||
loader.complete(true);
|
||||
if (!loader.isCompleted) {
|
||||
isLoading = false;
|
||||
loader.complete(true);
|
||||
}
|
||||
}
|
||||
|
||||
void failLoad() {
|
||||
this.messageId = null;
|
||||
isLoading = false;
|
||||
loader.complete(true);
|
||||
if (!loader.isCompleted) {
|
||||
isLoading = false;
|
||||
loader.complete(true);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> waitForLoad() {
|
||||
|
@ -95,7 +100,7 @@ class MessageCache extends ChangeNotifier {
|
|||
this._storageMessageCount = newval;
|
||||
}
|
||||
|
||||
// On android reconnect, if backend supplied message count > UI message count, add the differnce to the front of the index
|
||||
// On android reconnect, if backend supplied message count > UI message count, add the difference to the front of the index
|
||||
void addFrontIndexGap(int count) {
|
||||
this._indexUnsynced = count;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:cwtch/config.dart';
|
||||
import 'package:cwtch/models/remoteserver.dart';
|
||||
|
@ -248,8 +249,22 @@ class ProfileInfoState extends ChangeNotifier {
|
|||
if (profileContact != null) {
|
||||
profileContact.status = contact["status"];
|
||||
|
||||
var newCount = contact["numMessages"];
|
||||
var newCount = contact["numMessages"] as int;
|
||||
if (newCount != profileContact.totalMessages) {
|
||||
if (newCount < profileContact.totalMessages) {
|
||||
// on Android, when sharing a file the UI may be briefly unloaded for the
|
||||
// OS to display the file management/selection screen. Afterwards a
|
||||
// call to ReconnectCwtchForeground will be made which will refresh all values (including count of numMessages)
|
||||
// **at the same time** the foreground will increment .totalMessages and send a new message to the backend.
|
||||
// This will result in a negative number of messages being calculated here, and an incorrect totalMessage count.
|
||||
// This bug is exacerbated in debug mode, and when multiple files are sent in succession. Both cases result in multiple ReconnectCwtchForeground
|
||||
// events that have the potential to conflict with currentMessageCounts.
|
||||
// Note that *if* a new message came in at the same time, we would be unable to distinguish this case - as such this is specific instance of a more general problem
|
||||
// TODO: A true-fix to this bug is to implement a syncing step in the foreground where totalMessages and inFlightMessages can be distinguished
|
||||
// This requires a change to the backend to confirm submission of an inFlightMessage, which will be implemented in #664
|
||||
EnvironmentConfig.debugLog("Conflicting message counts: $newCount ${profileContact.totalMessages}");
|
||||
newCount = max(newCount, profileContact.totalMessages);
|
||||
}
|
||||
profileContact.messageCache.addFrontIndexGap(newCount - profileContact.totalMessages);
|
||||
}
|
||||
profileContact.totalMessages = newCount;
|
||||
|
|
|
@ -39,5 +39,5 @@ String prettyDateString(BuildContext context, DateTime date) {
|
|||
// }
|
||||
return AppLocalizations.of(context)!.now;
|
||||
}
|
||||
return DateFormat.yMd(Platform.localeName).add_jm().format(date);
|
||||
return DateFormat.yMd(Platform.localeName).add_jm().format(date.toLocal());
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ LoadAssetThemes() async {
|
|||
themes = await loadYamlThemes();
|
||||
}
|
||||
|
||||
OpaqueThemeType getTheme(String themeId, String mode) {
|
||||
OpaqueThemeType getTheme(String themeId, String mode) {
|
||||
if (themeId == "") {
|
||||
themeId = cwtch_theme;
|
||||
}
|
||||
|
|
|
@ -8,8 +8,6 @@ import 'package:flutter/services.dart';
|
|||
import 'package:yaml/yaml.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
|
||||
|
||||
Future<Map<String, Map<String, OpaqueThemeType>>> loadYamlThemes() async {
|
||||
final manifestJson = await rootBundle.loadString('AssetManifest.json');
|
||||
final themesList = json.decode(manifestJson).keys.where((String key) => key.startsWith('assets/themes'));
|
||||
|
@ -17,7 +15,7 @@ Future<Map<String, Map<String, OpaqueThemeType>>> loadYamlThemes() async {
|
|||
Map<String, Map<String, OpaqueThemeType>> themes = Map();
|
||||
|
||||
for (String themefile in themesList) {
|
||||
if (themefile.substring(themefile.length-4) != ".yml") {
|
||||
if (themefile.substring(themefile.length - 4) != ".yml") {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -77,16 +75,16 @@ class YmlTheme extends OpaqueThemeType {
|
|||
|
||||
Color? getColor(String name) {
|
||||
var val = yml["themes"][mode]["theme"][name];
|
||||
if (! (val is int)) {
|
||||
if (!(val is int)) {
|
||||
val = yml["themes"][mode]["theme"][val] ?? val;
|
||||
}
|
||||
if (! (val is int)) {
|
||||
if (!(val is int)) {
|
||||
val = yml["themes"][mode]?["colors"][val] ?? val;
|
||||
}
|
||||
if (! (val is int)) {
|
||||
if (!(val is int)) {
|
||||
val = yml["colors"]?[val];
|
||||
}
|
||||
if (! (val is int)) {
|
||||
if (!(val is int)) {
|
||||
return null;
|
||||
}
|
||||
return Color(0xFF000000 + val as int);
|
||||
|
@ -95,7 +93,7 @@ class YmlTheme extends OpaqueThemeType {
|
|||
String? getImage(String name) {
|
||||
var val = yml["themes"][mode]["theme"]?[name];
|
||||
if (val != null) {
|
||||
return path.join("assets", "themes", yml["themes"]["name"], val);
|
||||
return path.join("assets", "themes", yml["themes"]["name"], val);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -137,4 +135,4 @@ class YmlTheme extends OpaqueThemeType {
|
|||
// Images
|
||||
|
||||
get chatImage => getImage("chatImage") ?? fallbackTheme.chatImage;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,7 +83,7 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
|
|||
}
|
||||
|
||||
Widget _buildSettingsList() {
|
||||
return Consumer<Settings>(builder: (context, settings, child) {
|
||||
return Consumer<Settings>(builder: (ccontext, settings, child) {
|
||||
return LayoutBuilder(builder: (BuildContext context, BoxConstraints viewportConstraints) {
|
||||
var appIcon = Icon(Icons.info, color: settings.current().mainTextColor);
|
||||
return Scrollbar(
|
||||
|
|
|
@ -125,6 +125,7 @@ class _MessageViewState extends State<MessageView> {
|
|||
}
|
||||
// reset the disconnect button to allow for immediate connection...
|
||||
Provider.of<ContactInfoState>(context, listen: false).lastRetryTime = DateTime.now().subtract(Duration(minutes: 2));
|
||||
Provider.of<ContactInfoState>(context, listen: false).contactEvents.add(ContactEvent("Disconnect from Peer"));
|
||||
}));
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:ui';
|
||||
import 'package:cwtch/config.dart';
|
||||
import 'package:cwtch/cwtch_icons_icons.dart';
|
||||
import 'package:cwtch/models/appstate.dart';
|
||||
import 'package:cwtch/models/contact.dart';
|
||||
|
@ -84,6 +85,13 @@ class _PeerSettingsViewState extends State<PeerSettingsView> {
|
|||
}
|
||||
}
|
||||
|
||||
List<Widget> evWidgets = Provider.of<ContactInfoState>(context, listen: false).contactEvents.map((ev) {
|
||||
return ListTile(
|
||||
title: Text(ev.summary, style: Provider.of<Settings>(context).scaleFonts(defaultTextStyle)),
|
||||
subtitle: Text(ev.timestamp.toLocal().toIso8601String(), style: Provider.of<Settings>(context).scaleFonts(defaultTextStyle)),
|
||||
);
|
||||
}).toList();
|
||||
|
||||
return Scrollbar(
|
||||
trackVisibility: true,
|
||||
controller: peerSettingsScrollController,
|
||||
|
@ -319,8 +327,14 @@ class _PeerSettingsViewState extends State<PeerSettingsView> {
|
|||
style: settings.scaleFonts(defaultTextButtonStyle.copyWith(decoration: TextDecoration.underline)),
|
||||
),
|
||||
))
|
||||
])
|
||||
])
|
||||
]),
|
||||
]),
|
||||
Visibility(
|
||||
visible: EnvironmentConfig.BUILD_VER == dev_version,
|
||||
maintainSize: false,
|
||||
child: Column(
|
||||
children: evWidgets,
|
||||
))
|
||||
])))));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -75,6 +75,7 @@ class _MessageListState extends State<MessageList> {
|
|||
.AttemptReconnection(Provider.of<ProfileInfoState>(context, listen: false).onion, Provider.of<ContactInfoState>(context, listen: false).onion);
|
||||
}
|
||||
Provider.of<ContactInfoState>(context, listen: false).lastRetryTime = DateTime.now();
|
||||
Provider.of<ContactInfoState>(context, listen: false).contactEvents.add(ContactEvent("Actively Retried Connection"));
|
||||
setState(() {
|
||||
// force update of this view...otherwise the button won't be removed fast enough...
|
||||
});
|
||||
|
@ -116,8 +117,8 @@ class _MessageListState extends State<MessageList> {
|
|||
// Only show broken heart is the contact is offline...
|
||||
decoration: BoxDecoration(
|
||||
image: Provider.of<ContactInfoState>(outerContext).isOnline()
|
||||
? (Provider.of<Settings>(context).theme.chatImage != null) ?
|
||||
DecorationImage(
|
||||
? (Provider.of<Settings>(context).theme.chatImage != null)
|
||||
? DecorationImage(
|
||||
repeat: ImageRepeat.repeat,
|
||||
image: AssetImage(Provider.of<Settings>(context).theme.chatImage),
|
||||
colorFilter: ColorFilter.mode(Provider.of<Settings>(context).theme.hilightElementColor.withOpacity(0.15), BlendMode.srcIn))
|
||||
|
|
12
run-tests.sh
12
run-tests.sh
|
@ -5,11 +5,11 @@ sed "s|featurePaths: REPLACED_BY_SCRIPT|featurePaths: <String>[$paths]|" integra
|
|||
flutter pub run build_runner clean
|
||||
flutter pub run build_runner build --delete-conflicting-outputs
|
||||
|
||||
PATH=$PATH:$PWD/linux/Tor
|
||||
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:"$PWD/linux/":"$PWD/linux/Tor/"
|
||||
PATH=$PATH LD_LIBRARY_PATH=$LD_LIBRARY_PATH LOG_FILE=test.log CWTCH_HOME=$PWD/integration_test/env/temp/ flutter test -d linux --dart-define TEST_MODE=true integration_test/gherkin_suite_test.dart
|
||||
#node index2.js
|
||||
#if [ "$HEADLESS" = "false" ]; then
|
||||
PATH=$PATH:"$PWD/linux/Tor"
|
||||
LD_LIBRARY_PATH="$PWD/linux/":"$PWD/linux/Tor/":$LD_LIBRARY_PATH
|
||||
env PATH=$PATH LD_LIBRARY_PATH=$LD_LIBRARY_PATH LOG_FILE=test.log CWTCH_HOME=$PWD/integration_test/env/temp/ flutter test -d linux --dart-define TEST_MODE=true integration_test/gherkin_suite_test.dart
|
||||
# node index2.js
|
||||
# if [ "$HEADLESS" = "false" ]; then
|
||||
# xdg-open integration_test/gherkin/reports/cucumber_report.html
|
||||
#fi
|
||||
# fi
|
||||
|
||||
|
|
Loading…
Reference in New Issue