9dfa06ffee
Docker image / Build (linux/amd64) (push) Has been cancelled
Docker image / Build (linux/arm64) (push) Has been cancelled
Docker image / Merge release multi-arch manifest (push) Has been cancelled
Docker image / Merge debug multi-arch manifest (push) Has been cancelled
Docker image / Build public push gateway (linux/amd64) (push) Has been cancelled
Docker image / Build public push gateway (linux/arm64) (push) Has been cancelled
Docker image / Publish public push gateway image (push) Has been cancelled
Sprig image / Build (linux/amd64) (push) Has been cancelled
Sprig image / Build (linux/arm64) (push) Has been cancelled
Sprig image / Merge multi-arch manifest (push) Has been cancelled
Harbor Buzz Orchestra / Python tests and lint (push) Has been cancelled
CI / Detect Changed Paths (push) Has been cancelled
CI / Rust Lint (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Desktop Core (push) Has been cancelled
CI / Desktop Smoke E2E (1) (push) Has been cancelled
CI / Desktop Smoke E2E (2) (push) Has been cancelled
CI / Desktop Smoke E2E (3) (push) Has been cancelled
CI / Desktop Smoke E2E (4) (push) Has been cancelled
CI / Desktop (push) Has been cancelled
CI / Desktop E2E Relay (push) Has been cancelled
CI / Desktop E2E Integration (1/2) (push) Has been cancelled
CI / Desktop E2E Integration (2/2) (push) Has been cancelled
CI / Desktop E2E Integration (push) Has been cancelled
CI / Backend Integration (relay e2e) (push) Has been cancelled
CI / Relay E2E (push) Has been cancelled
CI / Web (push) Has been cancelled
CI / Mobile (push) Has been cancelled
CI / Security (push) Has been cancelled
CI / Dead Token Reference Guard (push) Has been cancelled
CI / Server Cross-Compile (aarch64-unknown-linux-musl) (push) Has been cancelled
CI / Server Cross-Compile (x86_64-unknown-linux-musl) (push) Has been cancelled
CI / Windows Rust (x86_64-pc-windows-msvc) (push) Has been cancelled
CI / Desktop Build (macOS) (push) Has been cancelled
helm chart / lint + unittest + render matrix (push) Has been cancelled
helm chart / install on kind (gated) (push) Has been cancelled
helm chart / publish chart to GHCR (push) Has been cancelled
Mesh Lifecycle / Relay-Driven Mesh Lifecycle Smoke (push) Has been cancelled
Sprig / Build (aarch64-unknown-linux-musl) (push) Has been cancelled
Sprig / Build (x86_64-unknown-linux-musl) (push) Has been cancelled
Sprig / Publish rolling release (push) Has been cancelled
Sprig / Publish tagged release (push) Has been cancelled
Signed-off-by: cls_宁波本机 <908705107@qq.com>
185 lines
6.6 KiB
Dart
185 lines
6.6 KiB
Dart
part of '../channels_page.dart';
|
|
|
|
const _kQuickActionsTabMotionDuration = Duration(milliseconds: 220);
|
|
const _kQuickActionsTabMotionCurve = Cubic(0.77, 0, 0.175, 1);
|
|
const _kQuickActionsHiddenOverlap = Grid.half;
|
|
const _kQuickActionsHiddenScale = 0.8;
|
|
|
|
/// Places the channel quick-actions button beside mobile navigation.
|
|
///
|
|
/// The button remains available only on Home and moves behind the navigation
|
|
/// bar when another destination is selected.
|
|
class ChannelQuickActionsLauncher extends HookConsumerWidget {
|
|
/// Whether the launcher should be visible beside the navigation bar.
|
|
final bool visible;
|
|
|
|
/// Height of the navigation bar used to vertically center the closed button.
|
|
final double navigationBarHeight;
|
|
|
|
/// Space between the navigation bar and the bottom safe area.
|
|
final double navigationBarBottomGap;
|
|
|
|
/// Full width of the navigation bar, including its inner edge padding.
|
|
final double navigationBarWidth;
|
|
|
|
/// Bottom system inset used by the navigation bar's [SafeArea].
|
|
final double systemBottomInset;
|
|
|
|
/// Distance between the launcher and the right edge of the screen.
|
|
final double rightInset;
|
|
|
|
/// Creates a launcher aligned with the supplied navigation geometry.
|
|
const ChannelQuickActionsLauncher({
|
|
super.key,
|
|
required this.visible,
|
|
required this.navigationBarHeight,
|
|
required this.navigationBarBottomGap,
|
|
required this.navigationBarWidth,
|
|
required this.systemBottomInset,
|
|
required this.rightInset,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
final currentPubkey = ref.watch(currentPubkeyProvider);
|
|
final quickActionsOpen = useState(false);
|
|
final reducedMotion = MediaQuery.of(context).disableAnimations;
|
|
final navigationBottomInset = systemBottomInset > navigationBarBottomGap
|
|
? systemBottomInset
|
|
: navigationBarBottomGap;
|
|
final closedBottomInset =
|
|
navigationBottomInset + ((navigationBarHeight - _kMorphClosedSize) / 2);
|
|
final openLift =
|
|
navigationBarHeight +
|
|
Grid.xxs -
|
|
((navigationBarHeight - _kMorphClosedSize) / 2);
|
|
final effectiveOpen = visible && quickActionsOpen.value;
|
|
final screenWidth = MediaQuery.sizeOf(context).width;
|
|
final navigationBarRight = (screenWidth + navigationBarWidth) / 2;
|
|
final launcherRight = screenWidth - rightInset;
|
|
final hiddenHorizontalOffset =
|
|
navigationBarRight - launcherRight - _kQuickActionsHiddenOverlap;
|
|
|
|
useEffect(() {
|
|
if (!visible && quickActionsOpen.value) {
|
|
quickActionsOpen.value = false;
|
|
}
|
|
return null;
|
|
}, [visible]);
|
|
|
|
Future<void> openChannel(Channel channel) async {
|
|
if (!context.mounted) return;
|
|
await Navigator.of(context).push(
|
|
MaterialPageRoute<void>(
|
|
builder: (_) => ChannelDetailPage(channel: channel),
|
|
),
|
|
);
|
|
}
|
|
|
|
Future<void> selectQuickAction(_QuickAction action) async {
|
|
quickActionsOpen.value = false;
|
|
if (!reducedMotion) {
|
|
await Future<void>.delayed(_kMorphCloseDuration);
|
|
}
|
|
if (!context.mounted) return;
|
|
|
|
switch (action) {
|
|
case _QuickAction.createChannel:
|
|
final created = await showBuzzModalBottomSheet<Channel>(
|
|
context: context,
|
|
constraints: _quickActionSheetConstraints(context),
|
|
isScrollControlled: true,
|
|
showDragHandle: true,
|
|
builder: (_) => const _CreateChannelSheet(channelType: 'stream'),
|
|
);
|
|
if (created != null && context.mounted) {
|
|
await openChannel(created);
|
|
}
|
|
case _QuickAction.newDm:
|
|
final opened = await showBuzzModalBottomSheet<Channel>(
|
|
context: context,
|
|
constraints: _quickActionSheetConstraints(context),
|
|
isScrollControlled: true,
|
|
showDragHandle: true,
|
|
builder: (_) =>
|
|
_NewDirectMessageSheet(currentPubkey: currentPubkey),
|
|
);
|
|
if (opened != null && context.mounted) {
|
|
await openChannel(opened);
|
|
}
|
|
}
|
|
}
|
|
|
|
return Stack(
|
|
fit: StackFit.expand,
|
|
clipBehavior: Clip.none,
|
|
children: [
|
|
if (quickActionsOpen.value)
|
|
Positioned.fill(
|
|
child: Semantics(
|
|
button: true,
|
|
label: context.l10n.closeQuickActions,
|
|
child: GestureDetector(
|
|
behavior: HitTestBehavior.opaque,
|
|
onTap: () => quickActionsOpen.value = false,
|
|
),
|
|
),
|
|
),
|
|
AnimatedPositioned(
|
|
duration: reducedMotion
|
|
? Duration.zero
|
|
: _kQuickActionsTabMotionDuration,
|
|
curve: _kQuickActionsTabMotionCurve,
|
|
right: rightInset,
|
|
bottom: closedBottomInset + (effectiveOpen ? openLift : 0),
|
|
child: IgnorePointer(
|
|
ignoring: !visible,
|
|
child: ExcludeSemantics(
|
|
excluding: !visible,
|
|
child: TweenAnimationBuilder<double>(
|
|
key: const Key('channel-quick-actions-motion'),
|
|
tween: Tween(end: visible ? 0 : 1),
|
|
duration: reducedMotion
|
|
? Duration.zero
|
|
: _kQuickActionsTabMotionDuration,
|
|
curve: _kQuickActionsTabMotionCurve,
|
|
builder: (context, hiddenProgress, child) => Opacity(
|
|
key: const Key('channel-quick-actions-opacity'),
|
|
opacity: 1 - hiddenProgress,
|
|
child: Transform.translate(
|
|
key: const Key('channel-quick-actions-transform'),
|
|
offset: Offset(hiddenHorizontalOffset * hiddenProgress, 0),
|
|
child: Transform.scale(
|
|
key: const Key('channel-quick-actions-scale'),
|
|
scale:
|
|
1 -
|
|
((1 - _kQuickActionsHiddenScale) * hiddenProgress),
|
|
child: child,
|
|
),
|
|
),
|
|
),
|
|
child: _MorphingQuickActionsButton(
|
|
open: effectiveOpen,
|
|
openEdgeOffset: rightInset - Grid.gutter,
|
|
onToggle: () {
|
|
unawaited(HapticFeedback.lightImpact());
|
|
quickActionsOpen.value = !quickActionsOpen.value;
|
|
},
|
|
onSelected: (action) => unawaited(selectQuickAction(action)),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|
|
|
|
BoxConstraints _quickActionSheetConstraints(BuildContext context) {
|
|
final mediaQuery = MediaQuery.of(context);
|
|
return BoxConstraints(
|
|
maxHeight: mediaQuery.size.height - mediaQuery.viewPadding.top - Grid.sm,
|
|
);
|
|
}
|