Hide tab bar when single tab

This commit is contained in:
Janez T
2026-03-05 11:51:23 +01:00
parent 0cb1d49804
commit 162d5333ce
5 changed files with 54 additions and 47 deletions

View File

@@ -489,7 +489,7 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 77; CURRENT_PROJECT_VERSION = 80;
DEVELOPMENT_TEAM = JND55328G8; DEVELOPMENT_TEAM = JND55328G8;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
@@ -511,7 +511,7 @@
buildSettings = { buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 77; CURRENT_PROJECT_VERSION = 80;
DEVELOPMENT_TEAM = JND55328G8; DEVELOPMENT_TEAM = JND55328G8;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
@@ -530,7 +530,7 @@
buildSettings = { buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 77; CURRENT_PROJECT_VERSION = 80;
DEVELOPMENT_TEAM = JND55328G8; DEVELOPMENT_TEAM = JND55328G8;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
@@ -547,7 +547,7 @@
buildSettings = { buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 77; CURRENT_PROJECT_VERSION = 80;
DEVELOPMENT_TEAM = JND55328G8; DEVELOPMENT_TEAM = JND55328G8;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
@@ -679,7 +679,7 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 77; CURRENT_PROJECT_VERSION = 80;
DEVELOPMENT_TEAM = JND55328G8; DEVELOPMENT_TEAM = JND55328G8;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
@@ -702,7 +702,7 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 77; CURRENT_PROJECT_VERSION = 80;
DEVELOPMENT_TEAM = JND55328G8; DEVELOPMENT_TEAM = JND55328G8;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;

View File

@@ -43,7 +43,7 @@
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>77</string> <string>80</string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>NSBluetoothAlwaysUsageDescription</key> <key>NSBluetoothAlwaysUsageDescription</key>

View File

@@ -5,22 +5,17 @@
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.001023"> <testcase classname="fastlane.lanes" name="0: default_platform" time="0.00024">
</testcase> </testcase>
<testcase classname="fastlane.lanes" name="1: increment_build_number" time="0.446008"> <testcase classname="fastlane.lanes" name="1: increment_build_number" time="0.420141">
</testcase> </testcase>
<testcase classname="fastlane.lanes" name="2: build_app" time="104.515166"> <testcase classname="fastlane.lanes" name="2: build_app" time="46.949616">
</testcase>
<testcase classname="fastlane.lanes" name="3: upload_to_app_store" time="268.810436">
</testcase> </testcase>

View File

@@ -212,9 +212,11 @@ class _ImageMessageBubbleState extends State<ImageMessageBubble> {
int pathLen = 0, int pathLen = 0,
}) async { }) async {
if (_isRequesting) return; if (_isRequesting) return;
final conn = context.read<ConnectionProvider>();
var sender = _resolveSender(envelope); var sender = _resolveSender(envelope);
if (sender == null) { if (sender == null ||
final conn = context.read<ConnectionProvider>(); sender.outPathLen < 0 ||
sender.outPathLen > _maxFetchHops) {
await conn.getContacts(); await conn.getContacts();
if (!mounted) return; if (!mounted) return;
sender = _resolveSender(envelope); sender = _resolveSender(envelope);
@@ -247,7 +249,6 @@ class _ImageMessageBubbleState extends State<ImageMessageBubble> {
} }
setState(() => _errorText = null); setState(() => _errorText = null);
final conn = context.read<ConnectionProvider>();
final imageProvider = context.read<ip.ImageProvider>(); final imageProvider = context.read<ip.ImageProvider>();
final deviceKey = conn.deviceInfo.publicKey; final deviceKey = conn.deviceInfo.publicKey;
if (deviceKey == null || deviceKey.length < 6) { if (deviceKey == null || deviceKey.length < 6) {
@@ -295,6 +296,7 @@ class _ImageMessageBubbleState extends State<ImageMessageBubble> {
); );
} catch (_) { } catch (_) {
if (mounted) { if (mounted) {
_showToast('Image fetch failed to send request');
setState(() { setState(() {
_isRequesting = false; _isRequesting = false;
_errorText = 'Image unavailable right now'; _errorText = 'Image unavailable right now';
@@ -323,7 +325,11 @@ class _ImageMessageBubbleState extends State<ImageMessageBubble> {
if (mounted && if (mounted &&
_isRequesting && _isRequesting &&
!imageProvider.isComplete(envelope.sessionId)) { !imageProvider.isComplete(envelope.sessionId)) {
setState(() => _isRequesting = false); _showToast('Image fetch timed out');
setState(() {
_isRequesting = false;
_errorText = 'Image fetch timed out';
});
} }
}, },
); );
@@ -331,19 +337,8 @@ class _ImageMessageBubbleState extends State<ImageMessageBubble> {
Contact? _resolveSender(ImageEnvelope envelope) { Contact? _resolveSender(ImageEnvelope envelope) {
final contactsProvider = context.read<ContactsProvider>(); final contactsProvider = context.read<ContactsProvider>();
final senderPrefix = widget.message.senderPublicKeyPrefix;
if (senderPrefix != null && senderPrefix.length >= 6) {
final c = contactsProvider.findContactByPrefix(
Uint8List.fromList(senderPrefix.sublist(0, 6)),
);
if (c != null) return c;
}
final contact = contactsProvider.findContactByPrefixHex(
envelope.senderKey6,
);
if (contact != null) return contact;
// For sent direct messages, fetch must target the recipient peer. // For sent direct messages, fetch must target the recipient peer first.
final recipientKey = widget.message.recipientPublicKey; final recipientKey = widget.message.recipientPublicKey;
if (widget.isSentByMe && recipientKey != null && recipientKey.isNotEmpty) { if (widget.isSentByMe && recipientKey != null && recipientKey.isNotEmpty) {
final byKey = contactsProvider.findContactByKey(recipientKey); final byKey = contactsProvider.findContactByKey(recipientKey);
@@ -356,6 +351,18 @@ class _ImageMessageBubbleState extends State<ImageMessageBubble> {
} }
} }
final senderPrefix = widget.message.senderPublicKeyPrefix;
if (senderPrefix != null && senderPrefix.length >= 6) {
final c = contactsProvider.findContactByPrefix(
Uint8List.fromList(senderPrefix.sublist(0, 6)),
);
if (c != null) return c;
}
final contact = contactsProvider.findContactByPrefixHex(
envelope.senderKey6,
);
if (contact != null) return contact;
final senderName = widget.message.senderName?.trim(); final senderName = widget.message.senderName?.trim();
if (senderName != null && senderName.isNotEmpty) { if (senderName != null && senderName.isNotEmpty) {
for (final c in contactsProvider.contacts) { for (final c in contactsProvider.contacts) {
@@ -377,6 +384,7 @@ class _ImageMessageBubbleState extends State<ImageMessageBubble> {
Future<void> _showBlockingAlert(String title, String message) async { Future<void> _showBlockingAlert(String title, String message) async {
if (!mounted) return; if (!mounted) return;
_showToast('$title: $message');
await showDialog<void>( await showDialog<void>(
context: context, context: context,
builder: (dialogContext) => AlertDialog( builder: (dialogContext) => AlertDialog(

View File

@@ -207,9 +207,11 @@ class _VoiceMessageBubbleState extends State<VoiceMessageBubble> {
int pathLen = 0, int pathLen = 0,
}) async { }) async {
if (_isRequesting) return; if (_isRequesting) return;
final connectionProvider = context.read<ConnectionProvider>();
var sender = _resolveSenderContact(); var sender = _resolveSenderContact();
if (sender == null) { if (sender == null ||
final connectionProvider = context.read<ConnectionProvider>(); sender.outPathLen < 0 ||
sender.outPathLen > _maxFetchHops) {
await connectionProvider.getContacts(); await connectionProvider.getContacts();
if (!mounted) return; if (!mounted) return;
sender = _resolveSenderContact(); sender = _resolveSenderContact();
@@ -246,7 +248,6 @@ class _VoiceMessageBubbleState extends State<VoiceMessageBubble> {
_errorText = null; _errorText = null;
}); });
final connectionProvider = context.read<ConnectionProvider>();
final deviceKey = connectionProvider.deviceInfo.publicKey; final deviceKey = connectionProvider.deviceInfo.publicKey;
if (deviceKey == null || deviceKey.length < 6) { if (deviceKey == null || deviceKey.length < 6) {
await _showBlockingAlert( await _showBlockingAlert(
@@ -310,6 +311,7 @@ class _VoiceMessageBubbleState extends State<VoiceMessageBubble> {
void _setUnavailable() { void _setUnavailable() {
if (!mounted) return; if (!mounted) return;
_showToast(AppLocalizations.of(context)!.voiceUnavailable);
setState(() { setState(() {
_isRequesting = false; _isRequesting = false;
_autoPlayWhenReady = false; _autoPlayWhenReady = false;
@@ -319,6 +321,20 @@ class _VoiceMessageBubbleState extends State<VoiceMessageBubble> {
Contact? _resolveSenderContact() { Contact? _resolveSenderContact() {
final contactsProvider = context.read<ContactsProvider>(); final contactsProvider = context.read<ContactsProvider>();
// For sent direct messages, fetch must target the recipient peer first.
final recipientKey = widget.message.recipientPublicKey;
if (widget.isSentByMe && recipientKey != null && recipientKey.isNotEmpty) {
final byKey = contactsProvider.findContactByKey(recipientKey);
if (byKey != null) return byKey;
if (recipientKey.length >= 6) {
final byPrefix = contactsProvider.findContactByPrefix(
Uint8List.fromList(recipientKey.sublist(0, 6)),
);
if (byPrefix != null) return byPrefix;
}
}
final senderPrefix = widget.message.senderPublicKeyPrefix; final senderPrefix = widget.message.senderPublicKeyPrefix;
if (senderPrefix != null && senderPrefix.length >= 6) { if (senderPrefix != null && senderPrefix.length >= 6) {
final contact = contactsProvider.findContactByPrefix( final contact = contactsProvider.findContactByPrefix(
@@ -335,19 +351,6 @@ class _VoiceMessageBubbleState extends State<VoiceMessageBubble> {
if (contact != null) return contact; if (contact != null) return contact;
} }
// For sent direct messages, fetch must target the recipient peer.
final recipientKey = widget.message.recipientPublicKey;
if (widget.isSentByMe && recipientKey != null && recipientKey.isNotEmpty) {
final byKey = contactsProvider.findContactByKey(recipientKey);
if (byKey != null) return byKey;
if (recipientKey.length >= 6) {
final byPrefix = contactsProvider.findContactByPrefix(
Uint8List.fromList(recipientKey.sublist(0, 6)),
);
if (byPrefix != null) return byPrefix;
}
}
final senderName = widget.message.senderName?.trim(); final senderName = widget.message.senderName?.trim();
if (senderName != null && senderName.isNotEmpty) { if (senderName != null && senderName.isNotEmpty) {
for (final contact in contactsProvider.contacts) { for (final contact in contactsProvider.contacts) {
@@ -369,6 +372,7 @@ class _VoiceMessageBubbleState extends State<VoiceMessageBubble> {
Future<void> _showBlockingAlert(String title, String message) async { Future<void> _showBlockingAlert(String title, String message) async {
if (!mounted) return; if (!mounted) return;
_showToast('$title: $message');
await showDialog<void>( await showDialog<void>(
context: context, context: context,
builder: (dialogContext) => AlertDialog( builder: (dialogContext) => AlertDialog(