feat: import Chinese-localized Buzz source snapshot
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
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>
This commit is contained in:
@@ -0,0 +1,260 @@
|
||||
part of '../message_actions.dart';
|
||||
|
||||
const _reactionTrayMaxWidth = (52.0 * 6) + (Grid.twelve * 5) + (Grid.xxs * 2);
|
||||
const _reactionTrayMaxHeight = 52.0 + (Grid.xxs * 2);
|
||||
const _reactionTraySpringAllowance = 16.0;
|
||||
const _reactionPopoverDuration = Duration(milliseconds: 320);
|
||||
final _reactionSpringCurve = _SpringEaseOutCurve(
|
||||
duration: const Duration(milliseconds: 260),
|
||||
bounce: 0.22,
|
||||
);
|
||||
|
||||
/// Presents the frequently-used reaction tray over a long-pressed message.
|
||||
///
|
||||
/// The conversation is frosted around [anchorRect] so the selected message
|
||||
/// remains visually connected to the tray.
|
||||
void _showMessageReactionPopover({
|
||||
required BuildContext context,
|
||||
required WidgetRef ref,
|
||||
required TimelineMessage message,
|
||||
required Rect anchorRect,
|
||||
required EdgeInsets spotlightPadding,
|
||||
}) {
|
||||
unawaited(HapticFeedback.mediumImpact());
|
||||
final reduceMotion = MediaQuery.disableAnimationsOf(context);
|
||||
showGeneralDialog<void>(
|
||||
context: context,
|
||||
barrierDismissible: true,
|
||||
barrierLabel: 'Dismiss reaction picker',
|
||||
barrierColor: Colors.transparent,
|
||||
transitionDuration: reduceMotion ? Duration.zero : _reactionPopoverDuration,
|
||||
transitionBuilder: (context, animation, secondaryAnimation, child) => child,
|
||||
pageBuilder: (dialogContext, animation, secondaryAnimation) =>
|
||||
_MessageReactionPopover(
|
||||
anchorRect: anchorRect,
|
||||
spotlightPadding: spotlightPadding,
|
||||
animation: animation,
|
||||
message: message,
|
||||
pageContext: context,
|
||||
pageRef: ref,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
class _MessageReactionPopover extends StatelessWidget {
|
||||
final Rect anchorRect;
|
||||
final EdgeInsets spotlightPadding;
|
||||
final Animation<double> animation;
|
||||
final TimelineMessage message;
|
||||
final BuildContext pageContext;
|
||||
final WidgetRef pageRef;
|
||||
|
||||
const _MessageReactionPopover({
|
||||
required this.anchorRect,
|
||||
required this.spotlightPadding,
|
||||
required this.animation,
|
||||
required this.message,
|
||||
required this.pageContext,
|
||||
required this.pageRef,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final mediaQuery = MediaQuery.of(context);
|
||||
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final safeTop = mediaQuery.padding.top + mediaQuery.viewInsets.top;
|
||||
final safeBottom =
|
||||
constraints.maxHeight -
|
||||
mediaQuery.padding.bottom -
|
||||
mediaQuery.viewInsets.bottom;
|
||||
final availableAbove = anchorRect.top - Grid.xxs - safeTop;
|
||||
final availableBelow = safeBottom - anchorRect.bottom - Grid.xxs;
|
||||
final showAbove =
|
||||
availableAbove >= _reactionTrayMaxHeight ||
|
||||
(availableBelow < _reactionTrayMaxHeight &&
|
||||
availableAbove >= availableBelow);
|
||||
final proposedTop = showAbove
|
||||
? anchorRect.top - _reactionTrayMaxHeight - Grid.xxs
|
||||
: anchorRect.bottom + Grid.xxs;
|
||||
final maxTop = math.max(safeTop, safeBottom - _reactionTrayMaxHeight);
|
||||
final top = proposedTop.clamp(safeTop, maxTop).toDouble();
|
||||
final trayScaleAlignment = showAbove
|
||||
? Alignment.bottomLeft
|
||||
: Alignment.topLeft;
|
||||
final trayWidth = math.min(
|
||||
_reactionTrayMaxWidth,
|
||||
constraints.maxWidth - (Grid.xxs * 2),
|
||||
);
|
||||
final maxLeft = constraints.maxWidth - trayWidth - Grid.xxs;
|
||||
final left = (anchorRect.center.dx - (trayWidth / 2))
|
||||
.clamp(Grid.xxs, maxLeft)
|
||||
.toDouble();
|
||||
|
||||
return Stack(
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: AnimatedBuilder(
|
||||
animation: animation,
|
||||
builder: (context, child) {
|
||||
final blurProgress = const Interval(
|
||||
0,
|
||||
0.30,
|
||||
curve: Curves.easeOutCubic,
|
||||
).transform(animation.value);
|
||||
final sigma = 20 * blurProgress;
|
||||
return ClipPath(
|
||||
key: const ValueKey('reaction-popover-background'),
|
||||
clipper: _OutsideAnchorClipper(
|
||||
anchorRect,
|
||||
spotlightPadding,
|
||||
),
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(sigmaX: sigma, sigmaY: sigma),
|
||||
child: ColoredBox(
|
||||
color: context.colors.inverseSurface.withValues(
|
||||
alpha: 0.10 * blurProgress,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
Positioned.fill(
|
||||
child: GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () => Navigator.of(context).pop(),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: top,
|
||||
left: left,
|
||||
width: trayWidth + _reactionTraySpringAllowance,
|
||||
height: _reactionTrayMaxHeight,
|
||||
child: AnimatedBuilder(
|
||||
animation: animation,
|
||||
child: SizedBox(
|
||||
width: trayWidth,
|
||||
height: _reactionTrayMaxHeight,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(Grid.xxs),
|
||||
child: _QuickReactionRow(
|
||||
message: message,
|
||||
sheetContext: context,
|
||||
pageContext: pageContext,
|
||||
pageRef: pageRef,
|
||||
presentationAnimation: animation,
|
||||
),
|
||||
),
|
||||
),
|
||||
builder: (context, child) {
|
||||
final appearance = const Interval(
|
||||
0.04,
|
||||
0.23,
|
||||
curve: Curves.easeOutCubic,
|
||||
).transform(animation.value);
|
||||
final expansion = const Interval(
|
||||
0.16,
|
||||
0.92,
|
||||
).transform(animation.value);
|
||||
final springExpansion = _reactionSpringCurve.transform(
|
||||
expansion,
|
||||
);
|
||||
final width = lerpDouble(
|
||||
_reactionTrayMaxHeight,
|
||||
trayWidth,
|
||||
springExpansion,
|
||||
)!;
|
||||
|
||||
return Opacity(
|
||||
opacity: appearance,
|
||||
child: Transform.scale(
|
||||
alignment: trayScaleAlignment,
|
||||
scale: lerpDouble(0.95, 1, appearance)!,
|
||||
child: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: SizedBox(
|
||||
key: const ValueKey('reaction-popover-tray'),
|
||||
width: width,
|
||||
height: _reactionTrayMaxHeight,
|
||||
child: Material(
|
||||
color: context.colors.surface,
|
||||
surfaceTintColor: Colors.transparent,
|
||||
elevation: 8,
|
||||
shadowColor: Colors.black.withValues(alpha: 0.2),
|
||||
shape: const StadiumBorder(),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: OverflowBox(
|
||||
alignment: Alignment.centerLeft,
|
||||
minWidth: trayWidth,
|
||||
maxWidth: trayWidth,
|
||||
minHeight: _reactionTrayMaxHeight,
|
||||
maxHeight: _reactionTrayMaxHeight,
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _OutsideAnchorClipper extends CustomClipper<Path> {
|
||||
final Rect anchorRect;
|
||||
final EdgeInsets spotlightPadding;
|
||||
|
||||
const _OutsideAnchorClipper(this.anchorRect, this.spotlightPadding);
|
||||
|
||||
@override
|
||||
Path getClip(Size size) {
|
||||
final spotlightRect = Rect.fromLTRB(
|
||||
anchorRect.left - spotlightPadding.left,
|
||||
anchorRect.top - spotlightPadding.top,
|
||||
anchorRect.right + spotlightPadding.right,
|
||||
anchorRect.bottom + spotlightPadding.bottom,
|
||||
);
|
||||
return Path()
|
||||
..fillType = PathFillType.evenOdd
|
||||
..addRect(Offset.zero & size)
|
||||
..addRRect(
|
||||
RRect.fromRectAndRadius(spotlightRect, const Radius.circular(Radii.lg)),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldReclip(_OutsideAnchorClipper oldClipper) =>
|
||||
oldClipper.anchorRect != anchorRect ||
|
||||
oldClipper.spotlightPadding != spotlightPadding;
|
||||
}
|
||||
|
||||
class _SpringEaseOutCurve extends Curve {
|
||||
final double _durationSeconds;
|
||||
final SpringSimulation _simulation;
|
||||
|
||||
_SpringEaseOutCurve({required Duration duration, required double bounce})
|
||||
: _durationSeconds =
|
||||
duration.inMicroseconds / Duration.microsecondsPerSecond,
|
||||
_simulation = SpringSimulation(
|
||||
SpringDescription.withDurationAndBounce(
|
||||
duration: duration,
|
||||
bounce: bounce,
|
||||
),
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
snapToEnd: true,
|
||||
);
|
||||
|
||||
@override
|
||||
double transformInternal(double t) => _simulation.x(t * _durationSeconds);
|
||||
}
|
||||
Reference in New Issue
Block a user