diff --git a/assets/demo/WormholeDemo.pdf b/assets/demo/WormholeDemo.pdf new file mode 100644 index 0000000..2440d71 Binary files /dev/null and b/assets/demo/WormholeDemo.pdf differ diff --git a/lib/transfer/demo_transfer.dart b/lib/transfer/demo_transfer.dart new file mode 100644 index 0000000..aaa124a --- /dev/null +++ b/lib/transfer/demo_transfer.dart @@ -0,0 +1,74 @@ +import 'dart:async'; +import 'dart:io'; +import 'package:flutter/services.dart'; +import '../src/rust/wormhole/types/t_update.dart'; +import '../src/rust/wormhole/types/events.dart'; +import '../src/rust/wormhole/types/value.dart'; + +/// Generates a demo receive stream that simulates downloading a file +/// without actually connecting to a wormhole server. +/// This is used for App Store review demonstrations. +Stream generateDemoReceiveStream(String downloadPath) async* { + // Step 1: Connecting + yield const TUpdate( + event: Events.connecting, + value: Value.int(0), + ); + await Future.delayed(const Duration(milliseconds: 800)); + + // Step 2: Start transfer + yield const TUpdate( + event: Events.startTransfer, + value: Value.int(0), + ); + await Future.delayed(const Duration(milliseconds: 500)); + + // Step 3: Send total size (1MB demo file) + const int totalBytes = 1048576; // 1 MB + yield const TUpdate( + event: Events.total, + value: Value.int(totalBytes), + ); + await Future.delayed(const Duration(milliseconds: 200)); + + // Step 4: Simulate progress in chunks + const int chunkSize = 131072; // 128 KB chunks + for (int sent = chunkSize; sent <= totalBytes; sent += chunkSize) { + final int bytesToSend = sent > totalBytes ? totalBytes : sent; + yield TUpdate( + event: Events.sent, + value: Value.int(bytesToSend), + ); + await Future.delayed(const Duration(milliseconds: 100)); + } + + // Step 5: Copy demo file from assets to download path + // Load the demo file from assets + final ByteData data = await rootBundle.load('assets/demo/WormholeDemo.pdf'); + final List bytes = data.buffer.asUint8List(); + + // Create the destination file path + final String fileName = 'WormholeDemo.pdf'; + String destinationPath = '$downloadPath${Platform.pathSeparator}$fileName'; + + // Handle file name conflicts (add number suffix if file exists) + int counter = 1; + while (File(destinationPath).existsSync()) { + destinationPath = + '$downloadPath${Platform.pathSeparator}WormholeDemo ($counter).pdf'; + counter++; + } + + // Write the file + final File file = File(destinationPath); + await file.writeAsBytes(bytes); + final finalPath = destinationPath; + + await Future.delayed(const Duration(milliseconds: 300)); + + // Step 6: Finished + yield TUpdate( + event: Events.finished, + value: Value.string(finalPath), + ); +} diff --git a/lib/transfer/transfer_receiver.dart b/lib/transfer/transfer_receiver.dart index 5d0a390..516fbba 100644 --- a/lib/transfer/transfer_receiver.dart +++ b/lib/transfer/transfer_receiver.dart @@ -18,7 +18,9 @@ import '../pages/transfer_widgets/transfer_finished.dart'; import '../settings/settings.dart'; import '../utils/paths.dart'; import '../utils/logger.dart'; +import '../utils/code.dart'; import 'transfer_provider.dart'; +import 'demo_transfer.dart'; class TransferReceiver extends StatefulWidget { const TransferReceiver({super.key, required this.child}); @@ -111,6 +113,21 @@ class _TransferReceiverState extends State { return; } + // Check if this is a demo code for App Store review + if (isDemoCode(passphrase)) { + // Use demo transfer stream instead of real wormhole connection + final s = generateDemoReceiveStream(dpath); + if (!mounted) return; + Provider.of(context, listen: false).push( + ConnectingPage( + key: UniqueKey(), + stream: s, + finish: (file) => ReceiveFinished(file: file), + ), + ); + return; + } + // we need storage permission to store files if (!(Platform.isAndroid || Platform.isIOS) || (Platform.isAndroid && diff --git a/lib/utils/code.dart b/lib/utils/code.dart index b40f998..9507535 100644 --- a/lib/utils/code.dart +++ b/lib/utils/code.dart @@ -1,6 +1,15 @@ // regex to match code validity final RegExp _regex = RegExp(r'^\d+-[^\s]*$'); +/// Demo code for App Store review (simulates file transfer without server) +const String demoCode = + '999763-demoooooo-mode-transfer-that-should-never-collide'; + +/// Check if the provided code is the demo code +bool isDemoCode(String code) { + return code == demoCode; +} + /// validate syntax of correction code bool isCodeValid(String code) { return _regex.hasMatch(code); diff --git a/pubspec.yaml b/pubspec.yaml index f79937a..26be38d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -70,6 +70,10 @@ flutter: # include material icons uses-material-design: true + # include demo assets for App Store review + assets: + - assets/demo/ + # app icon generation flutter_launcher_icons: android: "ic_launcher"