diff --git a/lib/screens/explore.dart b/lib/screens/explore.dart
index 21670f8..22a642d 100644
--- a/lib/screens/explore.dart
+++ b/lib/screens/explore.dart
@@ -48,7 +48,12 @@ class ExploreScreen extends ConsumerWidget {
                 onFetchData: controller.fetchMore,
                 itemBuilder: (context, index) {
                   final post = controller.posts[index];
-                  return PostItem(item: post);
+                  return PostItem(
+                    item: post,
+                    onRefresh: (_) {
+                      ref.invalidate(postListProvider);
+                    },
+                  );
                 },
                 separatorBuilder: (_, __) => const Divider(height: 1),
               ),
diff --git a/lib/screens/posts/compose.dart b/lib/screens/posts/compose.dart
index 7ac71b4..5912a07 100644
--- a/lib/screens/posts/compose.dart
+++ b/lib/screens/posts/compose.dart
@@ -131,7 +131,7 @@ class PostComposeScreen extends HookConsumerWidget {
         attachmentProgress.value = {...attachmentProgress.value, index: 0};
         final cloudFile =
             await putMediaToCloud(
-              fileData: attachment,
+              fileData: attachment.data,
               atk: atk,
               baseUrl: baseUrl,
               filename: attachment.data.name ?? 'Post media',
diff --git a/lib/screens/posts/detail.dart b/lib/screens/posts/detail.dart
index 603f4f2..2d4e724 100644
--- a/lib/screens/posts/detail.dart
+++ b/lib/screens/posts/detail.dart
@@ -38,7 +38,7 @@ class PostDetailScreen extends HookConsumerWidget {
               children: [
                 Column(
                   children: [
-                    PostItem(item: post!),
+                    PostItem(item: post!, isOpenable: false),
                     const Divider(height: 1),
                     Expanded(child: PostRepliesList(postId: id)),
                     Gap(MediaQuery.of(context).padding.bottom),
diff --git a/lib/widgets/content/video.native.dart b/lib/widgets/content/video.native.dart
index 9236df7..59cf436 100644
--- a/lib/widgets/content/video.native.dart
+++ b/lib/widgets/content/video.native.dart
@@ -3,11 +3,14 @@ import 'dart:io';
 import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_cache_manager/flutter_cache_manager.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:island/pods/config.dart';
+import 'package:island/pods/network.dart';
 import 'package:island/widgets/alert.dart';
 import 'package:media_kit/media_kit.dart';
 import 'package:media_kit_video/media_kit_video.dart';
 
-class UniversalVideo extends StatefulWidget {
+class UniversalVideo extends ConsumerStatefulWidget {
   final String uri;
   final double aspectRatio;
   const UniversalVideo({
@@ -17,10 +20,10 @@ class UniversalVideo extends StatefulWidget {
   });
 
   @override
-  State<UniversalVideo> createState() => _UniversalVideoState();
+  ConsumerState<UniversalVideo> createState() => _UniversalVideoState();
 }
 
-class _UniversalVideoState extends State<UniversalVideo> {
+class _UniversalVideoState extends ConsumerState<UniversalVideo> {
   Player? _player;
   VideoController? _videoController;
 
@@ -35,9 +38,18 @@ class _UniversalVideoState extends State<UniversalVideo> {
     final inCacheInfo = await DefaultCacheManager().getFileFromCache(url);
     if (inCacheInfo == null) {
       log('[MediaPlayer] Miss cache: $url');
+      final baseUrl = ref.watch(serverUrlProvider);
+      final atk = await getFreshAtk(
+        ref.watch(tokenPairProvider),
+        baseUrl,
+        onRefreshed: (atk, rtk) {
+          setTokenPair(ref.watch(sharedPreferencesProvider), atk, rtk);
+          ref.invalidate(tokenPairProvider);
+        },
+      );
       final fileStream = DefaultCacheManager().getFileStream(
         url,
-        // headers: {'Authorization': 'Bearer ${await ua.atk}'},
+        headers: {'Authorization': 'Bearer $atk'},
         withProgress: true,
       );
       await for (var fileInfo in fileStream) {
@@ -55,7 +67,7 @@ class _UniversalVideoState extends State<UniversalVideo> {
       return;
     }
 
-    _player!.open(Media(uri));
+    _player!.open(Media(uri), play: false);
   }
 
   @override
diff --git a/lib/widgets/context_menu.dart b/lib/widgets/context_menu.dart
new file mode 100644
index 0000000..416860f
--- /dev/null
+++ b/lib/widgets/context_menu.dart
@@ -0,0 +1,84 @@
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+
+typedef ContextMenuBuilder =
+    Widget Function(BuildContext context, Offset offset);
+
+class ContextMenuRegion extends HookWidget {
+  final Offset? mobileAnchor;
+  final Widget child;
+  final ContextMenuBuilder contextMenuBuilder;
+  const ContextMenuRegion({
+    super.key,
+    required this.child,
+    required this.contextMenuBuilder,
+    this.mobileAnchor,
+  });
+
+  @override
+  Widget build(BuildContext context) {
+    final contextMenuController = useMemoized(() => ContextMenuController());
+    final mobileOffset = useState<Offset?>(null);
+
+    bool canBeTouchScreen = switch (defaultTargetPlatform) {
+      TargetPlatform.android || TargetPlatform.iOS => true,
+      _ => false,
+    };
+
+    void showMenu(Offset position) {
+      contextMenuController.show(
+        context: context,
+        contextMenuBuilder: (BuildContext context) {
+          return contextMenuBuilder(context, position);
+        },
+      );
+    }
+
+    void hideMenu() {
+      contextMenuController.remove();
+    }
+
+    void onSecondaryTapUp(TapUpDetails details) {
+      showMenu(details.globalPosition);
+    }
+
+    void onTap() {
+      if (!contextMenuController.isShown) {
+        return;
+      }
+      hideMenu();
+    }
+
+    void onLongPressStart(LongPressStartDetails details) {
+      mobileOffset.value = details.globalPosition;
+    }
+
+    void onLongPress() {
+      assert(mobileOffset.value != null);
+      showMenu(mobileAnchor ?? mobileOffset.value!);
+      mobileOffset.value = null;
+    }
+
+    useEffect(() {
+      return () {
+        hideMenu();
+      };
+    }, []);
+
+    return TapRegion(
+      behavior: HitTestBehavior.opaque,
+      child: GestureDetector(
+        behavior: HitTestBehavior.opaque,
+        onSecondaryTapUp: onSecondaryTapUp,
+        onTap: onTap,
+        onLongPress: canBeTouchScreen ? onLongPress : null,
+        onLongPressStart: canBeTouchScreen ? onLongPressStart : null,
+        child: child,
+      ),
+      onTapOutside: (_) {
+        hideMenu();
+      },
+    );
+  }
+}
diff --git a/lib/widgets/post/post_item.dart b/lib/widgets/post/post_item.dart
index e14d1fd..cdbc1f5 100644
--- a/lib/widgets/post/post_item.dart
+++ b/lib/widgets/post/post_item.dart
@@ -1,4 +1,5 @@
 import 'package:auto_route/auto_route.dart';
+import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/cupertino.dart';
 import 'package:flutter/material.dart';
 import 'package:island/models/post.dart';
@@ -6,18 +7,20 @@ import 'package:island/route.gr.dart';
 import 'package:island/widgets/content/cloud_file_collection.dart';
 import 'package:island/widgets/content/cloud_files.dart';
 import 'package:island/widgets/content/markdown.dart';
-import 'package:lucide_icons/lucide_icons.dart';
+import 'package:island/widgets/context_menu.dart';
 import 'package:styled_widget/styled_widget.dart';
 
 class PostItem extends StatelessWidget {
   final SnPost item;
   final EdgeInsets? padding;
   final bool isOpenable;
+  final Function? onRefresh;
   const PostItem({
     super.key,
     required this.item,
     this.padding,
     this.isOpenable = true,
+    this.onRefresh,
   });
 
   @override
@@ -25,59 +28,60 @@ class PostItem extends StatelessWidget {
     final renderingPadding =
         padding ?? EdgeInsets.symmetric(horizontal: 12, vertical: 16);
 
-    return CupertinoContextMenu.builder(
-      actions: [
-        CupertinoContextMenuAction(
-          trailingIcon: LucideIcons.edit,
-          onPressed: () {
-            context.router.push(PostEditRoute(id: item.id));
-          },
-          child: Text('Edit'),
-        ),
-      ],
-      builder: (context, animation) {
-        return Material(
-          color: Theme.of(context).colorScheme.surface,
-          child: SingleChildScrollView(
-            physics: const NeverScrollableScrollPhysics(),
-            child: Padding(
-              padding: renderingPadding,
-              child: Column(
-                spacing: 8,
-                children: [
-                  Row(
-                    mainAxisSize: MainAxisSize.min,
-                    crossAxisAlignment: CrossAxisAlignment.start,
-                    spacing: 12,
-                    children: [
-                      ProfilePictureWidget(item: item.publisher.picture),
-                      Expanded(
-                        child: GestureDetector(
-                          child: Column(
-                            crossAxisAlignment: CrossAxisAlignment.start,
-                            children: [
-                              Text(item.publisher.nick).bold(),
-                              if (item.content.isNotEmpty)
-                                MarkdownTextContent(content: item.content),
-                            ],
-                          ),
-                          onTap: () {
-                            if (isOpenable) {
-                              context.router.push(PostDetailRoute(id: item.id));
-                            }
-                          },
-                        ),
-                      ),
-                    ],
-                  ),
-                  if (item.attachments.isNotEmpty)
-                    CloudFileList(files: item.attachments),
-                ],
-              ),
+    return ContextMenuRegion(
+      contextMenuBuilder: (_, offset) {
+        return AdaptiveTextSelectionToolbar.buttonItems(
+          anchors: TextSelectionToolbarAnchors(primaryAnchor: offset),
+          buttonItems: <ContextMenuButtonItem>[
+            ContextMenuButtonItem(
+              onPressed: () {
+                ContextMenuController.removeAny();
+                context.router.push(PostEditRoute(id: item.id)).then((value) {
+                  if (value != null) {
+                    onRefresh?.call();
+                  }
+                });
+              },
+              label: 'edit'.tr(),
             ),
-          ),
+          ],
         );
       },
+      child: Padding(
+        padding: renderingPadding,
+        child: Column(
+          spacing: 8,
+          children: [
+            Row(
+              mainAxisSize: MainAxisSize.min,
+              crossAxisAlignment: CrossAxisAlignment.start,
+              spacing: 12,
+              children: [
+                ProfilePictureWidget(item: item.publisher.picture),
+                Expanded(
+                  child: GestureDetector(
+                    child: Column(
+                      crossAxisAlignment: CrossAxisAlignment.start,
+                      children: [
+                        Text(item.publisher.nick).bold(),
+                        if (item.content.isNotEmpty)
+                          MarkdownTextContent(content: item.content),
+                      ],
+                    ),
+                    onTap: () {
+                      if (isOpenable) {
+                        context.router.push(PostDetailRoute(id: item.id));
+                      }
+                    },
+                  ),
+                ),
+              ],
+            ),
+            if (item.attachments.isNotEmpty)
+              CloudFileList(files: item.attachments),
+          ],
+        ),
+      ),
     );
   }
 }