🐛 Fix context menu mis placed on device which showing the side navigation

This commit is contained in:
LittleSheep 2024-12-24 23:07:47 +08:00
parent 06dd3e092a
commit 4d96a15c31
7 changed files with 178 additions and 126 deletions

View File

@ -173,7 +173,7 @@ PODS:
- in_app_review (2.0.0): - in_app_review (2.0.0):
- Flutter - Flutter
- Kingfisher (8.1.3) - Kingfisher (8.1.3)
- livekit_client (2.3.2): - livekit_client (2.3.3):
- Flutter - Flutter
- flutter_webrtc - flutter_webrtc
- WebRTC-SDK (= 125.6422.06) - WebRTC-SDK (= 125.6422.06)
@ -386,7 +386,7 @@ SPEC CHECKSUMS:
image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1 image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1
in_app_review: a31b5257259646ea78e0e35fc914979b0031d011 in_app_review: a31b5257259646ea78e0e35fc914979b0031d011
Kingfisher: f2af9028b16baf9dc6c07c570072bc41cbf009ef Kingfisher: f2af9028b16baf9dc6c07c570072bc41cbf009ef
livekit_client: 6108dad8b77db3142bafd4c630f471d0a54335cd livekit_client: 02cf2cc4357a655af12ccee70ff5596ae4e6feef
media_kit_libs_ios_video: a5fe24bc7875ccd6378a0978c13185e1344651c1 media_kit_libs_ios_video: a5fe24bc7875ccd6378a0978c13185e1344651c1
media_kit_native_event_loop: e6b2ab20cf0746eb1c33be961fcf79667304fa2a media_kit_native_event_loop: e6b2ab20cf0746eb1c33be961fcf79667304fa2a
media_kit_video: 5da63f157170e5bf303bf85453b7ef6971218a2e media_kit_video: 5da63f157170e5bf303bf85453b7ef6971218a2e

View File

@ -10,6 +10,7 @@ import 'package:surface/providers/userinfo.dart';
import 'package:surface/types/chat.dart'; import 'package:surface/types/chat.dart';
import 'package:surface/widgets/account/account_image.dart'; import 'package:surface/widgets/account/account_image.dart';
import 'package:surface/widgets/attachment/attachment_list.dart'; import 'package:surface/widgets/attachment/attachment_list.dart';
import 'package:surface/widgets/context_menu.dart';
import 'package:surface/widgets/link_preview.dart'; import 'package:surface/widgets/link_preview.dart';
import 'package:surface/widgets/markdown_content.dart'; import 'package:surface/widgets/markdown_content.dart';
import 'package:swipe_to/swipe_to.dart'; import 'package:swipe_to/swipe_to.dart';
@ -53,7 +54,7 @@ class ChatMessage extends StatelessWidget {
swipeSensitivity: 20, swipeSensitivity: 20,
onLeftSwipe: onReply != null ? (_) => onReply!(data) : null, onLeftSwipe: onReply != null ? (_) => onReply!(data) : null,
onRightSwipe: onEdit != null ? (_) => onEdit!(data) : null, onRightSwipe: onEdit != null ? (_) => onEdit!(data) : null,
child: ContextMenuRegion( child: ContextMenuArea(
contextMenu: ContextMenu( contextMenu: ContextMenu(
entries: [ entries: [
MenuHeader(text: "eventResourceTag".tr(args: ['#${data.id}'])), MenuHeader(text: "eventResourceTag".tr(args: ['#${data.id}'])),

View File

@ -0,0 +1,47 @@
import 'package:flutter/material.dart';
import 'package:flutter_animate/flutter_animate.dart';
import 'package:flutter_context_menu/flutter_context_menu.dart';
import 'package:responsive_framework/responsive_framework.dart';
class ContextMenuArea extends StatelessWidget {
final ContextMenu contextMenu;
final Widget child;
final ValueChanged<dynamic>? onItemSelected;
const ContextMenuArea({
super.key,
required this.contextMenu,
required this.child,
this.onItemSelected,
});
@override
Widget build(BuildContext context) {
Offset mousePosition = Offset.zero;
return Listener(
onPointerDown: (event) {
mousePosition = event.position;
final isCollapseDrawer = ResponsiveBreakpoints.of(context).smallerOrEqualTo(MOBILE);
if (!isCollapseDrawer) {
final isExpandDrawer = ResponsiveBreakpoints.of(context).largerThan(TABLET);
// Leave padding for side navigation
mousePosition = isExpandDrawer
? mousePosition.copyWith(dx: mousePosition.dx - 304 * 2)
: mousePosition.copyWith(dx: mousePosition.dx - 72 * 2);
}
},
child: GestureDetector(
onLongPress: () => _showMenu(context, mousePosition),
onSecondaryTap: () => _showMenu(context, mousePosition),
child: child,
),
);
}
void _showMenu(BuildContext context, Offset mousePosition) async {
final menu = contextMenu.copyWith(position: contextMenu.position ?? mousePosition);
final value = await showContextMenu(context, contextMenu: menu);
onItemSelected?.call(value);
}
}

View File

@ -14,6 +14,7 @@ import 'package:styled_widget/styled_widget.dart';
import 'package:surface/controllers/post_write_controller.dart'; import 'package:surface/controllers/post_write_controller.dart';
import 'package:surface/providers/sn_network.dart'; import 'package:surface/providers/sn_network.dart';
import 'package:surface/widgets/attachment/attachment_zoom.dart'; import 'package:surface/widgets/attachment/attachment_zoom.dart';
import 'package:surface/widgets/context_menu.dart';
import 'package:surface/widgets/dialog.dart'; import 'package:surface/widgets/dialog.dart';
class PostMediaPendingList extends StatelessWidget { class PostMediaPendingList extends StatelessWidget {
@ -87,7 +88,7 @@ class PostMediaPendingList extends StatelessWidget {
} }
} }
ContextMenu _buildContextMenu(BuildContext context, int idx, PostWriteMedia media) { ContextMenu _createContextMenu(BuildContext context, int idx, PostWriteMedia media) {
return ContextMenu( return ContextMenu(
entries: [ entries: [
if (media.attachment == null && onUpload != null) if (media.attachment == null && onUpload != null)
@ -174,8 +175,8 @@ class PostMediaPendingList extends StatelessWidget {
children: [ children: [
const Gap(8), const Gap(8),
if (thumbnail != null) if (thumbnail != null)
ContextMenuRegion( ContextMenuArea(
contextMenu: _buildContextMenu(context, -1, thumbnail!), contextMenu: _createContextMenu(context, -1, thumbnail!),
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border.all( border: Border.all(
@ -224,8 +225,8 @@ class PostMediaPendingList extends StatelessWidget {
itemCount: attachments.length, itemCount: attachments.length,
itemBuilder: (context, idx) { itemBuilder: (context, idx) {
final media = attachments[idx]; final media = attachments[idx];
return ContextMenuRegion( return ContextMenuArea(
contextMenu: _buildContextMenu(context, idx, media), contextMenu: _createContextMenu(context, idx, media),
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border.all( border: Border.all(

View File

@ -753,10 +753,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: flutter_udid name: flutter_udid
sha256: be464dc5b1fb7ee894f6a32d65c086ca5e177fdcf9375ac08d77495b98150f84 sha256: "166bee5989a58c66b8b62000ea65edccc7c8167bbafdbb08022638db330dd030"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.1" version: "4.0.0"
flutter_web_plugins: flutter_web_plugins:
dependency: "direct main" dependency: "direct main"
description: flutter description: flutter
@ -766,10 +766,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: flutter_webrtc name: flutter_webrtc
sha256: "430859fb5b763d7556d06ef287cfca582e17d9a2dc36da26017f25a5c0b2523e" sha256: "0e138a0a3bf6830c29c8439b17be0e222d0de27fa72f24e6aee4d34de72f22ef"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.12.4" version: "0.12.5"
freezed: freezed:
dependency: "direct dev" dependency: "direct dev"
description: description:
@ -934,10 +934,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: image_picker_android name: image_picker_android
sha256: fa8141602fde3f7e2f81dbf043613eb44dfa325fa0bcf93c0f142c9f7a2c193e sha256: aa6f1280b670861ac45220cc95adc59bb6ae130259d36f980ccb62220dc5e59f
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.8.12+18" version: "0.8.12+19"
image_picker_for_web: image_picker_for_web:
dependency: transitive dependency: transitive
description: description:
@ -1086,10 +1086,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: livekit_client name: livekit_client
sha256: "7802b5de1cae2ee3439db730d24d31c6dcbce173c5e6db2fc5774039a290bc2d" sha256: a3ff529fe6745ee40cdedcd021d81c4a6ad946dd495e782596f2856eeeabc739
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.3.2" version: "2.3.3"
logging: logging:
dependency: transitive dependency: transitive
description: description:

View File

@ -80,7 +80,7 @@ dependencies:
firebase_core: ^3.8.0 firebase_core: ^3.8.0
firebase_messaging: ^15.1.5 firebase_messaging: ^15.1.5
firebase_analytics: ^11.3.5 firebase_analytics: ^11.3.5
flutter_udid: ^3.0.0 flutter_udid: ^4.0.0
media_kit: ^1.1.11 media_kit: ^1.1.11
media_kit_video: ^1.2.5 media_kit_video: ^1.2.5
media_kit_libs_video: ^1.0.5 media_kit_libs_video: ^1.0.5

View File

@ -1,130 +1,133 @@
<!DOCTYPE html><html><head> <!DOCTYPE html>
<!-- <html lang="en" oncontextmenu="event.preventDefault();">
If you are serving your web app in a path other than the root, change the <head>
href value below to reflect the base path you are serving from. <!--
If you are serving your web app in a path other than the root, change the
href value below to reflect the base path you are serving from.
The path provided below has to start and end with a slash "/" in order for The path provided below has to start and end with a slash "/" in order for
it to work correctly. it to work correctly.
For more details: For more details:
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
This is a placeholder for base href that will be replaced by the value of This is a placeholder for base href that will be replaced by the value of
the `--base-href` argument provided to `flutter build`. the `--base-href` argument provided to `flutter build`.
--> -->
<base href="$FLUTTER_BASE_HREF"> <base href="$FLUTTER_BASE_HREF">
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible"> <meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="A new Flutter project."> <meta name="description" content="A new Flutter project.">
<!-- iOS meta tags & icons --> <!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black"> <meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="surface"> <meta name="apple-mobile-web-app-title" content="surface">
<link rel="apple-touch-icon" href="icons/Icon-192.png"> <link rel="apple-touch-icon" href="icons/Icon-192.png">
<!-- Favicon --> <!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png"> <link rel="icon" type="image/png" href="favicon.png">
<title>Solian</title> <title>Solian</title>
<link rel="manifest" href="manifest.json"> <link rel="manifest" href="manifest.json">
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"> <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
name="viewport">
<style id="splash-screen-style">
html {
height: 100%
}
body {
margin: 0;
min-height: 100%;
background-color: #ffffff;
background-size: 100% 100%;
}
<style id="splash-screen-style"> .center {
html { margin: 0;
height: 100% position: absolute;
} top: 50%;
left: 50%;
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
body { .contain {
margin: 0; display:block;
min-height: 100%; width:100%; height:100%;
background-color: #ffffff; object-fit: contain;
background-size: 100% 100%; }
}
.center { .stretch {
margin: 0; display:block;
position: absolute; width:100%; height:100%;
top: 50%; }
left: 50%;
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
.contain { .cover {
display:block; display:block;
width:100%; height:100%; width:100%; height:100%;
object-fit: contain; object-fit: cover;
} }
.stretch { .bottom {
display:block; position: absolute;
width:100%; height:100%; bottom: 0;
} left: 50%;
-ms-transform: translate(-50%, 0);
transform: translate(-50%, 0);
}
.cover { .bottomLeft {
display:block; position: absolute;
width:100%; height:100%; bottom: 0;
object-fit: cover; left: 0;
} }
.bottom { .bottomRight {
position: absolute; position: absolute;
bottom: 0; bottom: 0;
left: 50%; right: 0;
-ms-transform: translate(-50%, 0); }
transform: translate(-50%, 0);
}
.bottomLeft { @media (prefers-color-scheme: dark) {
position: absolute; body {
bottom: 0; background-color: #000000;
left: 0; }
} }
</style>
.bottomRight { <script id="splash-screen-script">
position: absolute; function removeSplashFromWeb() {
bottom: 0; document.getElementById("splash")?.remove();
right: 0; document.getElementById("splash-branding")?.remove();
} document.body.style.background = "transparent";
}
@media (prefers-color-scheme: dark) { </script>
body {
background-color: #000000;
}
}
</style>
<script id="splash-screen-script">
function removeSplashFromWeb() {
document.getElementById("splash")?.remove();
document.getElementById("splash-branding")?.remove();
document.body.style.background = "transparent";
}
</script>
</head> </head>
<body> <body>
<picture id="splash-branding"> <picture id="splash-branding">
<source srcset="splash/img/branding-1x.png 1x, splash/img/branding-2x.png 2x, splash/img/branding-3x.png 3x, splash/img/branding-4x.png 4x" media="(prefers-color-scheme: light)"> <source srcset="splash/img/branding-1x.png 1x, splash/img/branding-2x.png 2x, splash/img/branding-3x.png 3x, splash/img/branding-4x.png 4x"
<source srcset="splash/img/branding-dark-1x.png 1x, splash/img/branding-dark-2x.png 2x, splash/img/branding-dark-3x.png 3x, splash/img/branding-dark-4x.png 4x" media="(prefers-color-scheme: dark)"> media="(prefers-color-scheme: light)">
<source srcset="splash/img/branding-dark-1x.png 1x, splash/img/branding-dark-2x.png 2x, splash/img/branding-dark-3x.png 3x, splash/img/branding-dark-4x.png 4x"
media="(prefers-color-scheme: dark)">
<img class="bottom" aria-hidden="true" src="splash/img/branding-1x.png" alt=""> <img class="bottom" aria-hidden="true" src="splash/img/branding-1x.png" alt="">
</picture> </picture>
<picture id="splash"> <picture id="splash">
<source srcset="splash/img/light-1x.png 1x, splash/img/light-2x.png 2x, splash/img/light-3x.png 3x, splash/img/light-4x.png 4x" media="(prefers-color-scheme: light)"> <source srcset="splash/img/light-1x.png 1x, splash/img/light-2x.png 2x, splash/img/light-3x.png 3x, splash/img/light-4x.png 4x"
<source srcset="splash/img/dark-1x.png 1x, splash/img/dark-2x.png 2x, splash/img/dark-3x.png 3x, splash/img/dark-4x.png 4x" media="(prefers-color-scheme: dark)"> media="(prefers-color-scheme: light)">
<img class="center" aria-hidden="true" src="splash/img/light-1x.png" alt=""> <source srcset="splash/img/dark-1x.png 1x, splash/img/dark-2x.png 2x, splash/img/dark-3x.png 3x, splash/img/dark-4x.png 4x"
</picture> media="(prefers-color-scheme: dark)">
<img class="center" aria-hidden="true" src="splash/img/light-1x.png" alt="">
</picture>
<script src="flutter_bootstrap.js" async=""></script>
</body>
<script src="flutter_bootstrap.js" async=""></script> </html>
</body></html>