Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 35 additions & 23 deletions lib/accounts/account_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -113,51 +113,63 @@ class AccountFeatureWidget extends HookConsumerWidget {
final scrollController = useScrollController();
final statusTop = MediaQuery.paddingOf(context).top;
final artwork = account.profile.background;
// With artwork the page opens on a clean header image that runs all the way
// to the top edge, so the pill is kept out of sight until the page has
// scrolled 30% of the viewport. Accounts without artwork keep the pill
// surfaced from the start: there is nothing to uncover.
final pillRevealThreshold = MediaQuery.sizeOf(context).height * 0.3;
// The artwork sliding under the pill drives its glass. Without artwork the
// page opens on the identity block and never scrolls anything behind the
// bar, so the pill stays surfaced instead of hiding over an empty gap.
final fade = useValueNotifier(0.0);

final pillChild = Row(
children: [
const SolDrawerButton(),
const Gap(SolSpace.sm),
Expanded(
child: Align(
alignment: Alignment.centerLeft,
child: Text(
'account'.tr(),
style: Theme.of(context).appBarTheme.titleTextStyle,
),
),
),
],
);

return AppScaffold(
isNoBackground: isWide,
appBarOverContent: true,
appBar: PillAppBar(
fade: artwork == null ? null : fade,
statusTop: statusTop,
child: Row(
children: [
const SolDrawerButton(),
const Gap(SolSpace.sm),
Expanded(
child: Align(
alignment: Alignment.centerLeft,
child: Text(
'account'.tr(),
style: Theme.of(context).appBarTheme.titleTextStyle,
child: artwork == null
? pillChild
: ValueListenableBuilder<double>(
valueListenable: fade,
builder: (context, t, child) => IgnorePointer(
ignoring: t == 0,
child: Opacity(opacity: t, child: child),
),
child: pillChild,
),
),
],
),
),
body: NotificationListener<ScrollNotification>(
onNotification: (notification) => updatePillFade(notification, fade),
onNotification: (notification) =>
updatePillFade(notification, fade, threshold: pillRevealThreshold),
child: CustomScrollView(
controller: scrollController,
slivers: [
// The band the pill floats in. Its content is painted by the
// header below, which overhangs into it: the artwork's own top
// edge, mirrored upward and blurred, so the picture reads as
// continuing off the top of the screen instead of starting at a
// seam.
if (artwork != null) SliverGap(statusTop + kPillAppBarHeight),
// The artwork owns the very top of the page while the pill is
// hidden; once revealed, the pill floats over the content that has
// scrolled beneath it.
SliverToBoxAdapter(
child: _AccountHeader(
account: account,
statusTop: statusTop,
reflectionBand: artwork == null
? 0
: statusTop + kPillAppBarHeight,
reflectionBand: 0,
onAvatarTap: () {
context.router.push(AccountProfileRoute(name: account.name));
},
Expand Down
8 changes: 5 additions & 3 deletions lib/chat/pods/chat_subscribe.dart
Original file line number Diff line number Diff line change
Expand Up @@ -315,9 +315,11 @@ class ChatSubscribeNotifier extends _$ChatSubscribeNotifier {
_chatRoom = chatRoomAsync.value!;
_chatIdentity = chatIdentityAsync.value!;

// Subscribe to messages
final wsState = ref.read(websocketStateProvider.notifier);
_sendMessage = wsState.sendMessage;
// Subscribe to messages. Capture the service's own send function, not the
// notifier method: `onCancel`/`onResume` run inside Riverpod life-cycles
// where any `ref.read` is forbidden, and `WebSocketStateNotifier.sendMessage`
// reads `websocketProvider` from its ref on every call.
_sendMessage = ref.read(websocketProvider).sendMessage;
_sendSubscribe(reason: 'initial');

// Send initial read receipt
Expand Down
Loading
Loading