[READ ONLY MIRROR] Open Source TikTok alternative built on AT Protocol github.com/sprksocial/client
flutter atproto video dart
10
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: long-press sharing on side action bar

+32 -27
+6
lib/src/core/design_system/components/organisms/side_action_bar.dart
··· 19 19 this.onRepost, 20 20 this.onCurate, 21 21 this.onShare, 22 + this.onShareLongPress, 22 23 this.onSoundTap, 23 24 this.onOptions, 24 25 this.likeCount, ··· 43 44 final VoidCallback? 44 45 onCurate; // called after a feed selection (or when opening?) 45 46 final VoidCallback? onShare; 47 + final VoidCallback? onShareLongPress; 46 48 final VoidCallback? onSoundTap; 47 49 final VoidCallback? onOptions; 48 50 ··· 195 197 _ActionItem( 196 198 icon: AppIcons.share(size: 32), 197 199 onTap: widget.onShare, 200 + onLongPress: widget.onShareLongPress, 198 201 ), 199 202 ]); 200 203 ··· 235 238 required this.icon, 236 239 this.label, 237 240 this.onTap, 241 + this.onLongPress, 238 242 this.isActive = false, 239 243 super.key, 240 244 }); ··· 242 246 final Widget icon; 243 247 final String? label; 244 248 final VoidCallback? onTap; 249 + final VoidCallback? onLongPress; 245 250 final bool isActive; 246 251 247 252 @override ··· 289 294 return GestureDetector( 290 295 behavior: HitTestBehavior.opaque, 291 296 onTap: widget.onTap, 297 + onLongPress: widget.onLongPress, 292 298 child: Column( 293 299 children: [ 294 300 AnimatedBuilder(
+26 -27
lib/src/features/feed/ui/widgets/action_buttons/side_action_bar.dart
··· 4 4 import 'package:flutter/services.dart'; 5 5 import 'package:flutter_riverpod/flutter_riverpod.dart'; 6 6 import 'package:get_it/get_it.dart'; 7 + import 'package:share_plus/share_plus.dart'; 7 8 import 'package:spark/src/core/auth/data/repositories/auth_repository.dart'; 8 9 import 'package:spark/src/core/design_system/components/organisms/side_action_bar.dart'; 9 10 import 'package:spark/src/core/network/atproto/atproto.dart'; ··· 231 232 } 232 233 } 233 234 234 - void _handleShare() { 235 - final currentPost = _currentPost ?? widget.post; 236 - final originalAtUri = currentPost.uri.toString(); 237 - var postUri = originalAtUri; 238 - String shareUrl; 235 + String _buildShareUrl(PostView post) { 236 + var postUri = post.uri.toString(); 239 237 240 - // Special case for Bluesky posts 238 + // Special case for Bluesky posts. 241 239 if (postUri.contains('/app.bsky.feed.post/')) { 242 - // Extract the DID and post ID for Bluesky format 243 - // Format: at://did:plc:xxx/app.bsky.feed.post/yyy -> https://bsky.app/profile/did:plc:xxx/post/yyy 244 - 245 - // Remove 'at://' prefix if present 246 240 if (postUri.startsWith('at://')) { 247 241 postUri = postUri.substring(5); 248 242 } 249 243 250 - // Split to get DID and post ID 251 244 final parts = postUri.split('/app.bsky.feed.post/'); 252 245 if (parts.length == 2) { 253 246 final did = parts[0]; 254 247 final postId = parts[1]; 255 - 256 - // Format as Bluesky URL 257 - shareUrl = 'https://bsky.app/profile/$did/post/$postId'; 258 - } else { 259 - // Fallback if parsing fails 260 - shareUrl = 'https://bsky.app'; 261 - } 262 - } else { 263 - // Standard Spark format 264 - // Remove 'at://' prefix if present 265 - if (postUri.startsWith('at://')) { 266 - postUri = postUri.substring(5); 248 + return 'https://bsky.app/profile/$did/post/$postId'; 267 249 } 268 250 269 - // Remove 'so.sprk.feed.post/' from the path if present 270 - postUri = postUri.replaceAll('so.sprk.feed.post/', ''); 251 + return 'https://bsky.app'; 252 + } 271 253 272 - shareUrl = 'https://watch.sprk.so/?uri=$postUri'; 254 + if (postUri.startsWith('at://')) { 255 + postUri = postUri.substring(5); 273 256 } 257 + postUri = postUri.replaceAll('so.sprk.feed.post/', ''); 258 + 259 + return 'https://watch.sprk.so/?uri=$postUri'; 260 + } 261 + 262 + Future<void> _handleShareLongPress() async { 263 + final currentPost = _currentPost ?? widget.post; 264 + final shareUrl = _buildShareUrl(currentPost); 265 + await SharePlus.instance.share(ShareParams(uri: Uri.parse(shareUrl))); 266 + } 267 + 268 + void _handleShare() { 269 + final currentPost = _currentPost ?? widget.post; 270 + final originalAtUri = currentPost.uri.toString(); 271 + final shareUrl = _buildShareUrl(currentPost); 274 272 275 273 showModalBottomSheet( 276 274 context: context, 277 275 isScrollControlled: true, 278 276 backgroundColor: Colors.transparent, 279 - builder: (BuildContext context) { 277 + builder: (context) { 280 278 return SharePanel( 281 279 shareUrl: shareUrl, 282 280 atUri: originalAtUri, ··· 443 441 onRepost: _handleRepost, 444 442 // onCurate: _handleCurate, // Curation disabled 445 443 onShare: _handleShare, 444 + onShareLongPress: _handleShareLongPress, 446 445 onSoundTap: currentPost.sound != null ? _handleSoundTap : null, 447 446 onOptions: () => OptionsPanel.show( 448 447 context: context,