feat: Add compass and region support

This commit is contained in:
Janez T
2026-03-22 19:04:43 +01:00
parent 7ea6334f02
commit 2b42e7dd15
31 changed files with 1530 additions and 137 deletions

View File

@@ -27,6 +27,8 @@ class MessagesComposer extends StatelessWidget {
final Future<void> Function() onStartVoiceRecording;
final Future<void> Function() onStopAndSendVoice;
final Future<void> Function() onSendMessage;
final String? regionScopeName;
final VoidCallback? onRegionScopeTap;
const MessagesComposer({
super.key,
@@ -49,6 +51,8 @@ class MessagesComposer extends StatelessWidget {
required this.onStartVoiceRecording,
required this.onStopAndSendVoice,
required this.onSendMessage,
this.regionScopeName,
this.onRegionScopeTap,
});
@override
@@ -102,6 +106,14 @@ class MessagesComposer extends StatelessWidget {
? onStopAndSendVoice
: onShowComposerActions,
),
if (regionScopeName != null &&
onRegionScopeTap != null) ...[
const SizedBox(width: 6),
_RegionScopeChip(
name: regionScopeName!,
onTap: onRegionScopeTap!,
),
],
const SizedBox(width: 8),
Expanded(
child: _DestinationSelector(
@@ -534,3 +546,52 @@ class _SendButton extends StatelessWidget {
);
}
}
class _RegionScopeChip extends StatelessWidget {
final String name;
final VoidCallback onTap;
const _RegionScopeChip({required this.name, required this.onTap});
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
return GestureDetector(
onTap: onTap,
child: Container(
height: 40,
padding: const EdgeInsets.symmetric(horizontal: 10),
decoration: BoxDecoration(
color: colorScheme.tertiaryContainer,
borderRadius: BorderRadius.circular(20),
border: Border.all(
color: colorScheme.tertiary.withValues(alpha: 0.3),
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.language_rounded,
size: 16,
color: colorScheme.onTertiaryContainer,
),
const SizedBox(width: 4),
ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 80),
child: Text(
name,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: colorScheme.onTertiaryContainer,
),
overflow: TextOverflow.ellipsis,
),
),
],
),
),
);
}
}

View File

@@ -18,6 +18,9 @@ class RecipientSelectorSheet extends StatefulWidget {
final bool showAllOption;
final Function(String type, Contact? recipient) onSelect;
/// Region scope names per channel index (e.g. {0: "#auckland"}).
final Map<int, String> channelRegionScopes;
const RecipientSelectorSheet({
super.key,
required this.contacts,
@@ -29,6 +32,7 @@ class RecipientSelectorSheet extends StatefulWidget {
this.currentRecipientPublicKey,
this.showAllOption = true,
required this.onSelect,
this.channelRegionScopes = const {},
});
@override
@@ -165,13 +169,18 @@ class _RecipientSelectorSheetState extends State<RecipientSelectorSheet> {
String _channelSubtitle(BuildContext context, Contact channel) {
final l10n = AppLocalizations.of(context)!;
final channelIdx = channel.publicKey.length > 1 ? channel.publicKey[1] : 0;
final scopeName = widget.channelRegionScopes[channelIdx];
if (channel.isPublicChannel) {
return l10n.broadcastToAllNearby;
return scopeName != null
? '${l10n.broadcastToAllNearby}$scopeName'
: l10n.broadcastToAllNearby;
}
final channelIdx = channel.publicKey.length > 1 ? channel.publicKey[1] : 0;
final shortKey = channel.publicKeyShort.toUpperCase();
return '${l10n.channel} $channelIdx$shortKey';
final base = '${l10n.channel} $channelIdx$shortKey';
return scopeName != null ? '$base$scopeName' : base;
}
bool _isDenseSection(String type) => type == 'channel' || type == 'contact';