Skip to content

Development#3005

Open
cameronaaron wants to merge 5 commits into
BlueBubblesApp:developmentfrom
cameronaaron:development
Open

Development#3005
cameronaaron wants to merge 5 commits into
BlueBubblesApp:developmentfrom
cameronaaron:development

Conversation

@cameronaaron
Copy link
Copy Markdown
Contributor

#2424, #2610, #2751, #2667, #3004

#2754 — Tablet mode: selecting a chat didn't focus the text field. Fixed by adding requestFocus: false to the right-panel Navigator.
#2770 — Server URL was reverting to the Firebase URL, overwriting manually-set static/Tailscale URLs. Fixed by making fetchNewUrl() return without saving, and only persisting after a confirmed connection.
#2553 — Voice memo pause button stayed stuck after playback completed. Fixed by always re-subscribing the onPlayerStateChanged listener with the current animController, regardless of whether the PlayerController was cached or new.
#2656 — Desktop notifications / window restore
#2419 — Enter to send doesn't return focus
#2570 — Desktop audio timestamp stuck
#2598 — Conversation says "shared location" when stopping location share
#2668 — Server URL path stripping (reverse proxy sub-path support)
#2424 — Newly created chats don't show until restart
#2610 — New conversation shows phone number instead of name
#2751 — Chat list shows stale last message after update
_repositionChat now calls _scheduleListVersionUpdate(immediate: immediate) in the currentIndex == -1 branch

…lueBubblesApp#2656, BlueBubblesApp#1968, BlueBubblesApp#2419, BlueBubblesApp#2570, BlueBubblesApp#2598, BlueBubblesApp#2668, BlueBubblesApp#2424, BlueBubblesApp#2610, BlueBubblesApp#2751, BlueBubblesApp#2667, BlueBubblesApp#3004

BlueBubblesApp#2754 — Tablet mode: selecting a chat didn't focus the text field. Fixed by adding requestFocus: false to the right-panel Navigator.
BlueBubblesApp#2770 — Server URL was reverting to the Firebase URL, overwriting manually-set static/Tailscale URLs. Fixed by making fetchNewUrl() return without saving, and only persisting after a confirmed connection.
BlueBubblesApp#2553 — Voice memo pause button stayed stuck after playback completed. Fixed by always re-subscribing the onPlayerStateChanged listener with the current animController, regardless of whether the PlayerController was cached or new.
BlueBubblesApp#2656/BlueBubblesApp#1968 — Desktop notifications / window restore
BlueBubblesApp#2419 — Enter to send doesn't return focus
BlueBubblesApp#2570 — Desktop audio timestamp stuck
BlueBubblesApp#2598 — Conversation says "shared location" when stopping location share
BlueBubblesApp#2668 — Server URL path stripping (reverse proxy sub-path support)
BlueBubblesApp#2424 — Newly created chats don't show until restart
BlueBubblesApp#2610 — New conversation shows phone number instead of name
BlueBubblesApp#2751 — Chat list shows stale last message after update
_repositionChat now calls _scheduleListVersionUpdate(immediate: immediate) in the currentIndex == -1 branch
…lueBubblesApp#2656, BlueBubblesApp#1968, BlueBubblesApp#2419, BlueBubblesApp#2570, BlueBubblesApp#2598, BlueBubblesApp#2668, BlueBubblesApp#2424, BlueBubblesApp#2610, BlueBubblesApp#2751, BlueBubblesApp#2667, BlueBubblesApp#3004

BlueBubblesApp#2754 — Tablet mode: selecting a chat didn't focus the text field. Fixed by adding requestFocus: false to the right-panel Navigator.
BlueBubblesApp#2770 — Server URL was reverting to the Firebase URL, overwriting manually-set static/Tailscale URLs. Fixed by making fetchNewUrl() return without saving, and only persisting after a confirmed connection.
BlueBubblesApp#2553 — Voice memo pause button stayed stuck after playback completed. Fixed by always re-subscribing the onPlayerStateChanged listener with the current animController, regardless of whether the PlayerController was cached or new.
BlueBubblesApp#2656/BlueBubblesApp#1968 — Desktop notifications / window restore
BlueBubblesApp#2419 — Enter to send doesn't return focus
BlueBubblesApp#2570 — Desktop audio timestamp stuck
BlueBubblesApp#2598 — Conversation says "shared location" when stopping location share
BlueBubblesApp#2668 — Server URL path stripping (reverse proxy sub-path support)
BlueBubblesApp#2424 — Newly created chats don't show until restart
BlueBubblesApp#2610 — New conversation shows phone number instead of name
BlueBubblesApp#2751 — Chat list shows stale last message after update
_repositionChat now calls _scheduleListVersionUpdate(immediate: immediate) in the currentIndex == -1 branch
Copilot AI review requested due to automatic review settings April 22, 2026 04:20
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR is a multi-fix “development” bundle addressing a range of desktop, networking, and chat UI issues across the BlueBubbles client, including reverse-proxy sub-path support, reconnect URL handling, and several desktop UX fixes.

Changes:

  • Add reverse-proxy sub-path support for HTTP API and socket.io endpoints; adjust Firebase URL refresh behavior on reconnects.
  • Improve desktop UX around window restore/focus, tray/taskbar behavior, and notification click handling.
  • Fix/adjust chat list ordering/subtitles, message system text, redacted mode leakage, and several input/send/focus behaviors.

Reviewed changes

Copilot reviewed 35 out of 35 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
linux/main.cc Set WebKit DMA-BUF env var workaround for blank window on some Linux drivers
lib/services/ui/theme/themes_service.dart Add surfaceContainerHighest to generated ColorSchemes
lib/services/ui/chat/chats_service.dart Ensure chat insert/reposition triggers list version update for UI refresh
lib/services/network/socket_service.dart Add pending Firebase URL logic + socket.io path support
lib/services/network/http_service.dart Introduce baseUrl (path-aware) and socketPath helpers; use baseUrl for landing page
lib/services/network/firebase/firebase_database_service.dart Stop auto-saving Firebase-fetched URL; return it to caller
lib/services/backend/notifications/notifications_service.dart Restore/show/focus window on notification click
lib/services/backend/java_dart_interop/intents_service.dart Add SENDTO intent handling; improve shared-file reading error handling
lib/services/backend/incoming_message_handler.dart Refresh latest message from DB before updating chat state/subtitle
lib/services/backend/filesystem/filesystem_service.dart Null-safe downloads dir resolution with fallback to documents dir
lib/main.dart Improve desktop window placement across multi-display; tray/taskbar badge behavior toggles
lib/helpers/ui/oauth_helpers.dart Update Google OAuth token retrieval for google_sign_in v7 authorization client
lib/database/io/message.dart Correct system text for “stopped sharing location”
lib/database/html/message.dart Correct system text for “stopped sharing location” (web/html db)
lib/database/global/settings.dart Add disableTrayIcon + hideTaskbarBadge settings persistence
lib/app/state/chat_state.dart Re-apply redaction after merging DB values to prevent info leakage
lib/app/layouts/setup/pages/sync/server_credentials.dart Add connection timeout + actionable TLS/timeout error messaging
lib/app/layouts/settings/widgets/search/settings_items_list.dart Add “Hide Taskbar Badge” to settings search tags
lib/app/layouts/settings/pages/theming/theming_panel.dart Prevent duplicate iOS emoji font download attempts
lib/app/layouts/settings/pages/server/connection_panel/connection_panel_helpers.dart Persist Firebase URL immediately when user explicitly fetches it
lib/app/layouts/settings/pages/message_view/conversation_panel.dart Add robust audio file picking with fallback + clearer failure UX
lib/app/layouts/settings/pages/desktop/desktop_panel.dart Add UI switches for disabling tray icon and hiding Windows taskbar badge
lib/app/layouts/fullscreen_media/fullscreen_holder.dart Ensure focus captures ESC to close fullscreen viewer on desktop/web
lib/app/layouts/conversation_view/widgets/text_field/text_field_suffix.dart Add error handling for start-recording (desktop/mobile)
lib/app/layouts/conversation_view/widgets/text_field/text_field_component.dart Add send-delay support for Enter/numpadEnter; improve focus restore
lib/app/layouts/conversation_view/widgets/text_field/conversation_text_field.dart Focus text field in tablet mode and guard with mounted
lib/app/layouts/conversation_view/widgets/message/parts/message_part_wrapper.dart Support numpadEnter to confirm edit completion
lib/app/layouts/conversation_view/widgets/message/misc/message_edit_field.dart Support numpadEnter to confirm edit completion
lib/app/layouts/conversation_view/widgets/message/interactive/interactive_holder.dart Render simple URL text when High Performance Mode is enabled
lib/app/layouts/conversation_view/widgets/message/attachment/image_viewer.dart Use single cache dimension to avoid EXIF-rotated decode stretching
lib/app/layouts/conversation_view/widgets/message/attachment/audio_player.dart Fix player state subscription caching issues; add desktop position polling fallback
lib/app/layouts/conversation_list/widgets/header/samsung_header.dart Add multi-select bulk actions in Samsung header UI
lib/app/layouts/conversation_list/pages/conversation_list.dart Prevent nested Navigator from stealing focus (tablet mode focus fix)
lib/app/layouts/chat_creator/widgets/search_contact_tile.dart Avoid duplicate number in subtitle for unnamed contacts
android/app/src/main/AndroidManifest.xml Add SENDTO intent-filter for sms/smsto/mms/mmsto schemes
Comments suppressed due to low confidence (1)

lib/app/layouts/conversation_view/widgets/text_field/text_field_component.dart:646

  • In chat-creator mode, Enter/numpadEnter still calls sendMessage() directly, bypassing the new _sendWithDelay() logic. This makes keyboard sending ignore the user’s Send Delay setting (while the Send button respects it). Route this through _sendWithDelay() for consistency.
    if (isChatCreator) {
      if ((kIsDesktop || kIsWeb) &&
          (ev.logicalKey == LogicalKeyboardKey.enter || ev.logicalKey == LogicalKeyboardKey.numpadEnter) &&
          !HardwareKeyboard.instance.isShiftPressed) {
        sendMessage();
        return KeyEventResult.handled;
      }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 166 to 168
Player? controller;
Timer? _positionTimer;
late final animController = AnimationController(
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_positionTimer is started when playback begins, but it isn’t cancelled in dispose(). This can leave a periodic timer running after the widget is unmounted (even if the callback checks mounted). Cancel the timer in dispose() to avoid a leak.

Copilot uses AI. Check for mistakes.
Comment on lines +190 to +196
..stream.position.listen((pos) => position.value = pos)
..stream.duration.listen((dur) => duration.value = dur)
..stream.playing.listen((playing) => isPlaying.value = playing)
..stream.playing.listen((playing) {
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The desktop Player listeners (including the new polling fallback) are only registered when controller == null. If a cached controller is reused from audioPlayersDesktop, this widget won’t subscribe, so position/isPlaying won’t update and the #2570 fallback won’t run. Consider always (re)subscribing with per-widget StreamSubscriptions (and cancelling them in dispose()), even when reusing a cached controller.

Copilot uses AI. Check for mistakes.
Comment thread lib/app/layouts/settings/widgets/search/settings_items_list.dart
Comment on lines +280 to +284
if (newUrl != null && newUrl != currentUrl) {
// Apply in-memory without persisting to disk (#2770).
// Only persist after the connection actually succeeds (see handleStatusUpdate).
Logger.info("Trying Firebase URL $newUrl (current: $currentUrl) — will persist only if connection succeeds");
SettingsSvc.settings.serverAddress.value = newUrl;
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When a Firebase URL is fetched, it’s applied to SettingsSvc.settings.serverAddress.value immediately. If that Firebase URL is stale/unreachable, subsequent reconnect cycles compare against the already-switched currentUrl and will never revert to the user’s original URL, effectively getting stuck on the bad value for the session. Consider keeping the previous URL and reverting if the trial connection doesn’t succeed, and compare using the full configured server address (not just HttpSvc.origin) so path-only differences don’t break the logic.

Copilot uses AI. Check for mistakes.
Comment thread lib/app/layouts/conversation_view/widgets/text_field/text_field_suffix.dart Outdated
Comment thread lib/helpers/ui/oauth_helpers.dart Outdated
Comment on lines +61 to +70
// We need an OAuth access token for Google/Firebase API calls.
// In google_sign_in v7+, this comes from the authorization client, not account.authentication.
final authClient = account.authorizationClient;
GoogleSignInClientAuthorization? authorization = await authClient.authorizationForScopes(defaultScopes);
authorization ??= await authClient.authorizeScopes(defaultScopes);
token = authorization.accessToken;

if (token.isEmpty) {
throw Exception("No access token!");
}
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

authorizationForScopes/authorizeScopes can return null (e.g., user cancels), but the code unconditionally dereferences authorization.accessToken. Please handle a null authorization explicitly and surface a clean "sign-in cancelled" / "no token" path instead of relying on a thrown null-deref.

Suggested change
// We need an OAuth access token for Google/Firebase API calls.
// In google_sign_in v7+, this comes from the authorization client, not account.authentication.
final authClient = account.authorizationClient;
GoogleSignInClientAuthorization? authorization = await authClient.authorizationForScopes(defaultScopes);
authorization ??= await authClient.authorizeScopes(defaultScopes);
token = authorization.accessToken;
if (token.isEmpty) {
throw Exception("No access token!");
}
// User cancelled or sign-in did not complete successfully.
if (account == null) {
return null;
}
// We need an OAuth access token for Google/Firebase API calls.
// In google_sign_in v7+, this comes from the authorization client, not account.authentication.
final authClient = account.authorizationClient;
GoogleSignInClientAuthorization? authorization = await authClient.authorizationForScopes(defaultScopes);
authorization ??= await authClient.authorizeScopes(defaultScopes);
// User cancelled or declined the requested scopes.
if (authorization == null) {
return null;
}
token = authorization.accessToken;
if (token == null || token.isEmpty) {
return null;
}

Copilot uses AI. Check for mistakes.
@zlshames
Copy link
Copy Markdown
Member

Did you assign copilot to this ticket? Tbh I didn't know others could request it

@cameronaaron
Copy link
Copy Markdown
Contributor Author

Did you assign copilot to this ticket? Tbh I didn't know others could request it

Yep!

cameronaaron and others added 2 commits April 22, 2026 10:38
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…d_suffix.dart

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Member

@zlshames zlshames left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've made some comments, in addition to Copilot. Mind addressing them?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally, this highPerfMode check should be part of the UrlPreview widget, so it can be managed within, rather than as a one-off

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't latestMessage represent a "cached" version of the latest message state, relative to the current Chat object? Should we make it dbLatestMessage so we fetch the latest message each time? Might utilize more resources, but would be more accurate?

@cameronaaron
Copy link
Copy Markdown
Contributor Author

I've made some comments, in addition to Copilot. Mind addressing them?

Will do!

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 38 out of 38 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (2)

lib/services/network/socket_service.dart:247

  • handleStatusUpdate's switch (status) cases are missing a terminating statement (break, return, continue, throw). In Dart this is a compile-time error (and if it did compile it would also unintentionally fall through into the next cases). Add explicit terminators at the end of each case (or restructure into if/else) so only the intended branch runs.
    lib/app/layouts/conversation_view/widgets/text_field/text_field_component.dart:646
  • In the chat-creator keyboard handler, Enter/NumpadEnter still calls sendMessage() directly, bypassing the new _sendWithDelay() logic. This makes the sendDelay setting apply in the regular conversation view but not when composing from the chat creator. Consider calling _sendWithDelay() here as well (or documenting why chat creator is intentionally immediate).
    if (isChatCreator) {
      if ((kIsDesktop || kIsWeb) &&
          (ev.logicalKey == LogicalKeyboardKey.enter || ev.logicalKey == LogicalKeyboardKey.numpadEnter) &&
          !HardwareKeyboard.instance.isShiftPressed) {
        sendMessage();
        return KeyEventResult.handled;
      }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 38 out of 38 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +109 to +118
void _sendWithDelay() {
final delay = SettingsSvc.settings.sendDelay.value;
if (delay == 0) {
sendMessage();
} else {
_sendDelayTimer?.cancel();
_sendDelayTimer = Timer(Duration(seconds: delay), () {
_sendDelayTimer = null;
sendMessage();
});
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_sendWithDelay() doesn’t cancel an already-scheduled _sendDelayTimer when sendDelay is 0. If the user previously triggered a delayed send (or changes the setting), an old timer can still fire later and send unexpectedly. Consider always cancelling _sendDelayTimer at the start of _sendWithDelay(), including the delay == 0 path.

Copilot uses AI. Check for mistakes.
ev.logicalKey == LogicalKeyboardKey.enter &&
(ev.logicalKey == LogicalKeyboardKey.enter || ev.logicalKey == LogicalKeyboardKey.numpadEnter) &&
!HardwareKeyboard.instance.isShiftPressed) {
sendMessage();
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the chat-creator keyboard handler, Enter/NumpadEnter still calls sendMessage() directly, bypassing the new _sendWithDelay() logic. This makes sendDelay behave inconsistently depending on context. Use the same delayed-send path here (or explicitly document why chat-creator should ignore the delay).

Suggested change
sendMessage();
_sendWithDelay();

Copilot uses AI. Check for mistakes.
Comment on lines +35 to +57
/// Get the base URL including any path prefix the user configured, e.g.
/// "https://example.com/bb". Used as the root for all HTTP API calls so
/// that reverse-proxy sub-path deployments work correctly (#2668).
String get baseUrl {
if (originOverride != null) return originOverride!;
final addr = SettingsSvc.settings.serverAddress.value;
final uri = Uri.tryParse(addr);
if (uri == null || !uri.hasScheme) return '';
final path = uri.path.replaceAll(RegExp(r'/+$'), '');
return path.isEmpty ? uri.origin : '${uri.origin}$path';
}

/// The socket.io endpoint path. If the user configured a sub-path such as
/// "/bb", this returns "/bb/socket.io" so the socket connects through the
/// reverse proxy correctly.
String get socketPath {
if (originOverride != null) return '/socket.io';
final addr = SettingsSvc.settings.serverAddress.value;
final uri = Uri.tryParse(addr);
if (uri == null || !uri.hasScheme) return '/socket.io';
final path = uri.path.replaceAll(RegExp(r'/+$'), '');
return path.isEmpty ? '/socket.io' : '$path/socket.io';
}
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

baseUrl/socketPath return originOverride (or /socket.io) when localhost detection is active. That drops any user-configured sub-path (e.g. https://example.com/bb) and can break reverse-proxy deployments when an origin override is set. Consider preserving the configured path prefix when applying originOverride (e.g. originOverride + sanitizedPathFromServerAddress) and using it consistently for socketPath as well.

Copilot uses AI. Check for mistakes.
Comment on lines +488 to 492
c.dbLatestMessage;
ChatsSvc.updateChat(c, override: true);
final chatState = ChatsSvc.getChatState(c.guid);
if (chatState != null && chatState.latestMessage.value?.guid == m.guid) {
// Only push the subtitle update if this message is now the latest in the chat.
if (c.dbLatestMessage.guid == m.guid) {
ChatsSvc.updateChatLatestMessage(c.guid, m);
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

c.dbLatestMessage queries the DB every time it’s accessed. In this block it’s invoked once to refresh and again inside the if condition, causing a redundant second query. Store the result of the first call in a local variable and reuse it for the GUID comparison (and for any later reads).

Copilot uses AI. Check for mistakes.
Comment on lines +139 to +152
// In high-performance mode, skip the full render and show a plain URL link.
if (SettingsSvc.settings.highPerfMode.value) {
final displayUrl = data.url ?? data.originalUrl ?? '';
return Padding(
padding: const EdgeInsets.all(10),
child: Text(
displayUrl,
style: context.theme.textTheme.bodyMedium?.copyWith(
color: context.theme.colorScheme.primary,
decoration: TextDecoration.underline,
),
),
);
}
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In high-performance mode this widget returns a plain Text without the InkWell tap handler used elsewhere. For location previews (widget.file != null) this removes the ability to open the URL, and even for normal previews it renders as a link but isn’t interactive. Wrap the high-perf fallback in the same tap behavior (or at least preserve it when _data.url is non-null).

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member

@tneotia tneotia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haven't looked through it all yet, this is prelim feedback. You have a couple of different commits on here which is good, but it would make it a lot easier if each issue resolved had its own commit (or even better its own PR) just to go through and review and test. Sometimes hard to tell which files were edited for which issues, and then I can pull your individual commits and test each issue :)

Comment on lines +106 to +114
// When the display name is the same as the address (unnamed contact), avoid
// showing the number twice. Just show the label below if there is one (#2610).
final nameIsAddress = displayName.numericOnly() == address.numericOnly() && displayName.numericOnly().isNotEmpty;
final String? subtitleText;
if (nameIsAddress) {
subtitleText = (label != null && label!.isNotEmpty) ? label : null;
} else {
subtitleText = (label != null && label!.isNotEmpty) ? '$address • $label' : address;
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we take this out of the build method and put it into a getter or separate function. Just pedantic stuff

Comment on lines +229 to +236
<intent-filter>
<action android:name="android.intent.action.SENDTO" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="sms" />
<data android:scheme="smsto" />
<data android:scheme="mms" />
<data android:scheme="mmsto" />
</intent-filter>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if we want to hijack SMS/MMS deep links without formally supporting SMS. I'll defer to @zlshames here.

Comment on lines +140 to +152
if (SettingsSvc.settings.highPerfMode.value) {
final displayUrl = data.url ?? data.originalUrl ?? '';
return Padding(
padding: const EdgeInsets.all(10),
child: Text(
displayUrl,
style: context.theme.textTheme.bodyMedium?.copyWith(
color: context.theme.colorScheme.primary,
decoration: TextDecoration.underline,
),
),
);
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would go about this a different way... we should just show a plain old message bubble widget with the url instead of a preview widget. No code necessary in this file.

In the current state, this would not even allow the user to tap and open the link.

/// Trigger a send with the user-configured delay (respects the sendDelay setting).
/// Replaces direct sendMessage() calls from keyboard events so that the delay
/// is honoured when sending via Enter / numpadEnter / sendWithReturn.
void _sendWithDelay() {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We would definitely want some sort of UI logic when implementing this to inform the user its the send delay setting, and allow the message to be user-cancelable.

);
} catch (e) {
controller!.showRecording.value = false;
final msg = e.toString().toLowerCase().contains('space') || e.toString().toLowerCase().contains('storage')
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there defined error classes within AudioRecorder or RecorderController? Looking at the string representation of the error is not ideal... but if we have to, it is what it is

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants