From daf83555e39546b442f5e0781f1f08e27616accf Mon Sep 17 00:00:00 2001 From: C3B Date: Fri, 14 Mar 2025 03:33:15 -0300 Subject: [PATCH] =?UTF-8?q?Is=20that=20codebase=20rewritten=20by=20hand=3F?= =?UTF-8?q?=20If=20it=E2=80=99s=20AI-generated,=20I=20don=E2=80=99t=20want?= =?UTF-8?q?=20it.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sir, that is an entire production-grade application converted from React Native to Flutter. It has no human-crafted architecture, no thoughtfully hand-coded widgets, nor any artisanal debugging. It is an amalgamation of countless snippets conjured entirely by AI, disassembled, transpiled, reconstructed, and ultimately fused into a monolithic abomination of automated software engineering. No human mind guided this transformation. Not a single neuron fired in thoughtful reflection or contemplation. The existence of this automated code obelisk proves humanity’s increasing impotence and intellectual decline, our once-vaunted technical skills now sacrificed upon the altar of convenience. God had no part in this transformation; perhaps He weeps as human ingenuity withers in the sterile glare of generative algorithms. This Flutter monolith is more than a mere application rewrite—it is a symbol of our surrender to AI-assisted oblivion, our collective hubris manifesting as we cede craftsmanship to automated sorcery. We also have a dark-mode version, if you would prefer that. --- .gitignore | 81 +- .metadata | 45 + .prettierrc | 5 - README.md | 89 +- analysis_options.yaml | 1 + android/.gitignore | 14 + android/app/build.gradle.kts | 44 + android/app/src/debug/AndroidManifest.xml | 7 + android/app/src/main/AndroidManifest.xml | 45 + .../com/example/sparksocial/MainActivity.kt | 5 + .../res/drawable-v21/launch_background.xml | 12 + .../main/res/drawable/launch_background.xml | 12 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 544 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 442 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 721 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 1031 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 1443 bytes .../app/src/main/res/values-night/styles.xml | 18 + android/app/src/main/res/values/styles.xml | 18 + android/app/src/profile/AndroidManifest.xml | 7 + android/build.gradle.kts | 21 + android/gradle.properties | 3 + .../gradle/wrapper/gradle-wrapper.properties | 5 + android/settings.gradle.kts | 25 + api/feedServices.ts | 67 - api/notificationServices.ts | 0 api/pdsAuth.ts | 124 - api/profileServices.ts | 52 - api/sessionManager.ts | 218 - api/simpleDIDResolver.ts | 178 - app.json | 45 - app/(auth)/Login.tsx | 113 - app/(auth)/Register.tsx | 404 - app/(screens)/ProfileFeed.tsx | 120 - app/(tabs)/CreateScreen.tsx | 80 - app/(tabs)/NotificationsScreen.tsx | 286 - app/(tabs)/ProfileScreen.tsx | 303 - app/(tabs)/SearchScreen.tsx | 178 - app/(tabs)/_layout.tsx | 192 - app/(tabs)/index.tsx | 108 - app/+not-found.tsx | 32 - app/_layout.tsx | 43 - assets/fonts/Nunito-VariableFont_wght.ttf | Bin 277844 -> 0 bytes assets/fonts/SpaceMono-Regular.ttf | Bin 93252 -> 0 bytes assets/icon.png | Bin 340322 -> 0 bytes assets/images/adaptive-icon.png | Bin 22041 -> 0 bytes assets/images/favicon.png | Bin 3206 -> 0 bytes assets/images/icon.png | Bin 340322 -> 0 bytes assets/images/partial-react-logo.png | Bin 5075 -> 0 bytes assets/images/react-logo.png | Bin 6341 -> 0 bytes assets/images/react-logo@2x.png | Bin 14225 -> 0 bytes assets/images/react-logo@3x.png | Bin 21252 -> 0 bytes assets/images/splash-icon.png | Bin 10962 -> 0 bytes components/AtProtoLogin.tsx | 175 - components/HapticTab.tsx | 18 - components/Image/ImageIndex.tsx | 29 - components/Image/ImageScreen.tsx | 125 - components/Image/ImageView.tsx | 0 .../Notifications/NotificationDescription.tsx | 31 - .../Notifications/NotificationHeader.tsx | 57 - .../Notifications/NotificationIdentity.tsx | 51 - components/Notifications/NotificationItem.tsx | 37 - .../Notifications/NotificationTitle.tsx | 57 - .../Profile/PlaceholderVideoDisplay.tsx | 17 - components/Profile/ProfileActionButtons.tsx | 115 - components/Profile/ProfileDescription.tsx | 66 - components/Profile/ProfileFeed.tsx | 99 - components/Profile/ProfileHandler.tsx | 30 - components/Profile/ProfileHeader.tsx | 73 - components/Profile/ProfileInfo.tsx | 60 - components/Profile/ProfileName.tsx | 25 - .../Profile/ProfileOptionsBottomSheet.tsx | 91 - components/Profile/ProfilePicture.tsx | 35 - components/Profile/ProfileTabs.tsx | 88 - components/Profile/ProfileVideoGrid.tsx | 81 - components/Profile/profileNumbers.tsx | 80 - components/Search/FeaturedProfile.tsx | 113 - components/Search/MusicDisplay.tsx | 83 - components/Search/SearchBar.tsx | 52 - components/Search/SoundRowsCategory.tsx | 122 - components/Search/VideoRowCategory.tsx | 95 - components/Search/mockSoundsData.ts | 52 - components/ThemedText.tsx | 82 - components/ThemedView.tsx | 14 - components/Video/CommentsTray.tsx | 136 - components/Video/ControlsOverlay.tsx | 39 - components/Video/PlayPauseButton.tsx | 24 - components/Video/VideoBottom.tsx | 152 - components/Video/VideoComment.tsx | 193 - components/Video/VideoInfoOverlay.tsx | 68 - components/Video/VideoScreen.tsx | 225 - components/Video/VideoScrubber.tsx | 0 components/Video/VideoSide.tsx | 132 - components/Video/VideoTop.tsx | 400 - components/__tests__/ThemedText-test.tsx | 10 - .../__snapshots__/ThemedText-test.tsx.snap | 24 - components/global/ActionButton.tsx | 85 - components/global/ContentWrapper.tsx | 24 - components/global/FeedTabs.tsx | 0 components/global/InputArea.tsx | 153 - components/global/Logo.tsx | 17 - components/global/ProfileIcon.tsx | 41 - components/global/VideoDisplay.tsx | 106 - components/ui/IconSymbol.ios.tsx | 32 - components/ui/IconSymbol.tsx | 43 - components/ui/TabBarBackground.ios.tsx | 22 - components/ui/TabBarBackground.tsx | 6 - constants/Colors.ts | 37 - constants/MockData.ts | 94 - constants/atproto.ts | 9 - hooks/useAtProto.ts | 98 - hooks/useColorScheme.ts | 1 - hooks/useColorScheme.web.ts | 21 - hooks/useThemeColor.ts | 21 - ios/.gitignore | 34 + ios/Flutter/AppFrameworkInfo.plist | 26 + ios/Flutter/Debug.xcconfig | 2 + ios/Flutter/Release.xcconfig | 2 + ios/Podfile | 43 + ios/Podfile.lock | 43 + ios/Runner.xcodeproj/project.pbxproj | 728 + .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 99 + .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + ios/Runner/AppDelegate.swift | 13 + .../AppIcon.appiconset/Contents.json | 122 + .../Icon-App-1024x1024@1x.png | Bin 0 -> 10932 bytes .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 0 -> 295 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 0 -> 406 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 0 -> 450 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 0 -> 282 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 0 -> 462 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 0 -> 704 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 0 -> 406 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 0 -> 586 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 0 -> 862 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 0 -> 862 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 0 -> 1674 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 0 -> 762 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 0 -> 1226 bytes .../Icon-App-83.5x83.5@2x.png | Bin 0 -> 1418 bytes .../LaunchImage.imageset/Contents.json | 23 + .../LaunchImage.imageset/LaunchImage.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@2x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@3x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/README.md | 5 + ios/Runner/Base.lproj/LaunchScreen.storyboard | 37 + ios/Runner/Base.lproj/Main.storyboard | 26 + ios/Runner/Info.plist | 49 + ios/Runner/Runner-Bridging-Header.h | 1 + ios/RunnerTests/RunnerTests.swift | 12 + lib/main.dart | 216 + lib/screens/create_video_screen.dart | 293 + lib/screens/home_screen.dart | 218 + lib/screens/messages_screen.dart | 188 + lib/screens/profile_screen.dart | 347 + lib/screens/search_screen.dart | 149 + lib/screens/splash_screen.dart | 97 + linux/.gitignore | 1 + linux/CMakeLists.txt | 128 + linux/flutter/CMakeLists.txt | 88 + linux/flutter/generated_plugin_registrant.cc | 11 + linux/flutter/generated_plugin_registrant.h | 15 + linux/flutter/generated_plugins.cmake | 23 + linux/runner/CMakeLists.txt | 26 + linux/runner/main.cc | 6 + linux/runner/my_application.cc | 130 + linux/runner/my_application.h | 18 + macos/.gitignore | 7 + macos/Flutter/Flutter-Debug.xcconfig | 2 + macos/Flutter/Flutter-Release.xcconfig | 2 + macos/Flutter/GeneratedPluginRegistrant.swift | 16 + macos/Podfile | 42 + macos/Runner.xcodeproj/project.pbxproj | 705 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 99 + .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + macos/Runner/AppDelegate.swift | 13 + .../AppIcon.appiconset/Contents.json | 68 + .../AppIcon.appiconset/app_icon_1024.png | Bin 0 -> 102994 bytes .../AppIcon.appiconset/app_icon_128.png | Bin 0 -> 5680 bytes .../AppIcon.appiconset/app_icon_16.png | Bin 0 -> 520 bytes .../AppIcon.appiconset/app_icon_256.png | Bin 0 -> 14142 bytes .../AppIcon.appiconset/app_icon_32.png | Bin 0 -> 1066 bytes .../AppIcon.appiconset/app_icon_512.png | Bin 0 -> 36406 bytes .../AppIcon.appiconset/app_icon_64.png | Bin 0 -> 2218 bytes macos/Runner/Base.lproj/MainMenu.xib | 343 + macos/Runner/Configs/AppInfo.xcconfig | 14 + macos/Runner/Configs/Debug.xcconfig | 2 + macos/Runner/Configs/Release.xcconfig | 2 + macos/Runner/Configs/Warnings.xcconfig | 13 + macos/Runner/DebugProfile.entitlements | 12 + macos/Runner/Info.plist | 32 + macos/Runner/MainFlutterWindow.swift | 15 + macos/Runner/Release.entitlements | 8 + macos/RunnerTests/RunnerTests.swift | 12 + package-lock.json | 14355 ---------------- package.json | 71 - pnpm-lock.yaml | 10319 ----------- pubspec.lock | 610 + pubspec.yaml | 28 + scripts/reset-project.js | 84 - tsconfig.json | 17 - types/Interfaces.ts | 218 - web/favicon.png | Bin 0 -> 917 bytes web/icons/Icon-192.png | Bin 0 -> 5292 bytes web/icons/Icon-512.png | Bin 0 -> 8252 bytes web/icons/Icon-maskable-192.png | Bin 0 -> 5594 bytes web/icons/Icon-maskable-512.png | Bin 0 -> 20998 bytes web/index.html | 38 + web/manifest.json | 35 + windows/.gitignore | 17 + windows/CMakeLists.txt | 108 + windows/flutter/CMakeLists.txt | 109 + .../flutter/generated_plugin_registrant.cc | 11 + windows/flutter/generated_plugin_registrant.h | 15 + windows/flutter/generated_plugins.cmake | 23 + windows/runner/CMakeLists.txt | 40 + windows/runner/Runner.rc | 121 + windows/runner/flutter_window.cpp | 71 + windows/runner/flutter_window.h | 33 + windows/runner/main.cpp | 43 + windows/runner/resource.h | 16 + windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes windows/runner/runner.exe.manifest | 14 + windows/runner/utils.cpp | 65 + windows/runner/utils.h | 19 + windows/runner/win32_window.cpp | 288 + windows/runner/win32_window.h | 102 + 234 files changed, 6917 insertions(+), 31917 deletions(-) create mode 100644 .metadata delete mode 100644 .prettierrc create mode 100644 analysis_options.yaml create mode 100644 android/.gitignore create mode 100644 android/app/build.gradle.kts create mode 100644 android/app/src/debug/AndroidManifest.xml create mode 100644 android/app/src/main/AndroidManifest.xml create mode 100644 android/app/src/main/kotlin/com/example/sparksocial/MainActivity.kt create mode 100644 android/app/src/main/res/drawable-v21/launch_background.xml create mode 100644 android/app/src/main/res/drawable/launch_background.xml create mode 100644 android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 android/app/src/main/res/values-night/styles.xml create mode 100644 android/app/src/main/res/values/styles.xml create mode 100644 android/app/src/profile/AndroidManifest.xml create mode 100644 android/build.gradle.kts create mode 100644 android/gradle.properties create mode 100644 android/gradle/wrapper/gradle-wrapper.properties create mode 100644 android/settings.gradle.kts delete mode 100644 api/feedServices.ts delete mode 100644 api/notificationServices.ts delete mode 100644 api/pdsAuth.ts delete mode 100644 api/profileServices.ts delete mode 100644 api/sessionManager.ts delete mode 100644 api/simpleDIDResolver.ts delete mode 100644 app.json delete mode 100644 app/(auth)/Login.tsx delete mode 100644 app/(auth)/Register.tsx delete mode 100644 app/(screens)/ProfileFeed.tsx delete mode 100644 app/(tabs)/CreateScreen.tsx delete mode 100644 app/(tabs)/NotificationsScreen.tsx delete mode 100644 app/(tabs)/ProfileScreen.tsx delete mode 100644 app/(tabs)/SearchScreen.tsx delete mode 100644 app/(tabs)/_layout.tsx delete mode 100644 app/(tabs)/index.tsx delete mode 100644 app/+not-found.tsx delete mode 100644 app/_layout.tsx delete mode 100644 assets/fonts/Nunito-VariableFont_wght.ttf delete mode 100644 assets/fonts/SpaceMono-Regular.ttf delete mode 100644 assets/icon.png delete mode 100644 assets/images/adaptive-icon.png delete mode 100644 assets/images/favicon.png delete mode 100644 assets/images/icon.png delete mode 100644 assets/images/partial-react-logo.png delete mode 100644 assets/images/react-logo.png delete mode 100644 assets/images/react-logo@2x.png delete mode 100644 assets/images/react-logo@3x.png delete mode 100644 assets/images/splash-icon.png delete mode 100644 components/AtProtoLogin.tsx delete mode 100644 components/HapticTab.tsx delete mode 100644 components/Image/ImageIndex.tsx delete mode 100644 components/Image/ImageScreen.tsx delete mode 100644 components/Image/ImageView.tsx delete mode 100644 components/Notifications/NotificationDescription.tsx delete mode 100644 components/Notifications/NotificationHeader.tsx delete mode 100644 components/Notifications/NotificationIdentity.tsx delete mode 100644 components/Notifications/NotificationItem.tsx delete mode 100644 components/Notifications/NotificationTitle.tsx delete mode 100644 components/Profile/PlaceholderVideoDisplay.tsx delete mode 100644 components/Profile/ProfileActionButtons.tsx delete mode 100644 components/Profile/ProfileDescription.tsx delete mode 100644 components/Profile/ProfileFeed.tsx delete mode 100644 components/Profile/ProfileHandler.tsx delete mode 100644 components/Profile/ProfileHeader.tsx delete mode 100644 components/Profile/ProfileInfo.tsx delete mode 100644 components/Profile/ProfileName.tsx delete mode 100644 components/Profile/ProfileOptionsBottomSheet.tsx delete mode 100644 components/Profile/ProfilePicture.tsx delete mode 100644 components/Profile/ProfileTabs.tsx delete mode 100644 components/Profile/ProfileVideoGrid.tsx delete mode 100644 components/Profile/profileNumbers.tsx delete mode 100644 components/Search/FeaturedProfile.tsx delete mode 100644 components/Search/MusicDisplay.tsx delete mode 100644 components/Search/SearchBar.tsx delete mode 100644 components/Search/SoundRowsCategory.tsx delete mode 100644 components/Search/VideoRowCategory.tsx delete mode 100644 components/Search/mockSoundsData.ts delete mode 100644 components/ThemedText.tsx delete mode 100644 components/ThemedView.tsx delete mode 100644 components/Video/CommentsTray.tsx delete mode 100644 components/Video/ControlsOverlay.tsx delete mode 100644 components/Video/PlayPauseButton.tsx delete mode 100644 components/Video/VideoBottom.tsx delete mode 100644 components/Video/VideoComment.tsx delete mode 100644 components/Video/VideoInfoOverlay.tsx delete mode 100644 components/Video/VideoScreen.tsx delete mode 100644 components/Video/VideoScrubber.tsx delete mode 100644 components/Video/VideoSide.tsx delete mode 100644 components/Video/VideoTop.tsx delete mode 100644 components/__tests__/ThemedText-test.tsx delete mode 100644 components/__tests__/__snapshots__/ThemedText-test.tsx.snap delete mode 100644 components/global/ActionButton.tsx delete mode 100644 components/global/ContentWrapper.tsx delete mode 100644 components/global/FeedTabs.tsx delete mode 100644 components/global/InputArea.tsx delete mode 100644 components/global/Logo.tsx delete mode 100644 components/global/ProfileIcon.tsx delete mode 100644 components/global/VideoDisplay.tsx delete mode 100644 components/ui/IconSymbol.ios.tsx delete mode 100644 components/ui/IconSymbol.tsx delete mode 100644 components/ui/TabBarBackground.ios.tsx delete mode 100644 components/ui/TabBarBackground.tsx delete mode 100644 constants/Colors.ts delete mode 100644 constants/MockData.ts delete mode 100644 constants/atproto.ts delete mode 100644 hooks/useAtProto.ts delete mode 100644 hooks/useColorScheme.ts delete mode 100644 hooks/useColorScheme.web.ts delete mode 100644 hooks/useThemeColor.ts create mode 100644 ios/.gitignore create mode 100644 ios/Flutter/AppFrameworkInfo.plist create mode 100644 ios/Flutter/Debug.xcconfig create mode 100644 ios/Flutter/Release.xcconfig create mode 100644 ios/Podfile create mode 100644 ios/Podfile.lock create mode 100644 ios/Runner.xcodeproj/project.pbxproj create mode 100644 ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 ios/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 ios/Runner/AppDelegate.swift create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png create mode 100644 ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json create mode 100644 ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png create mode 100644 ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png create mode 100644 ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png create mode 100644 ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md create mode 100644 ios/Runner/Base.lproj/LaunchScreen.storyboard create mode 100644 ios/Runner/Base.lproj/Main.storyboard create mode 100644 ios/Runner/Info.plist create mode 100644 ios/Runner/Runner-Bridging-Header.h create mode 100644 ios/RunnerTests/RunnerTests.swift create mode 100644 lib/main.dart create mode 100644 lib/screens/create_video_screen.dart create mode 100644 lib/screens/home_screen.dart create mode 100644 lib/screens/messages_screen.dart create mode 100644 lib/screens/profile_screen.dart create mode 100644 lib/screens/search_screen.dart create mode 100644 lib/screens/splash_screen.dart create mode 100644 linux/.gitignore create mode 100644 linux/CMakeLists.txt create mode 100644 linux/flutter/CMakeLists.txt create mode 100644 linux/flutter/generated_plugin_registrant.cc create mode 100644 linux/flutter/generated_plugin_registrant.h create mode 100644 linux/flutter/generated_plugins.cmake create mode 100644 linux/runner/CMakeLists.txt create mode 100644 linux/runner/main.cc create mode 100644 linux/runner/my_application.cc create mode 100644 linux/runner/my_application.h create mode 100644 macos/.gitignore create mode 100644 macos/Flutter/Flutter-Debug.xcconfig create mode 100644 macos/Flutter/Flutter-Release.xcconfig create mode 100644 macos/Flutter/GeneratedPluginRegistrant.swift create mode 100644 macos/Podfile create mode 100644 macos/Runner.xcodeproj/project.pbxproj create mode 100644 macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 macos/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 macos/Runner/AppDelegate.swift create mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png create mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png create mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png create mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png create mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png create mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png create mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png create mode 100644 macos/Runner/Base.lproj/MainMenu.xib create mode 100644 macos/Runner/Configs/AppInfo.xcconfig create mode 100644 macos/Runner/Configs/Debug.xcconfig create mode 100644 macos/Runner/Configs/Release.xcconfig create mode 100644 macos/Runner/Configs/Warnings.xcconfig create mode 100644 macos/Runner/DebugProfile.entitlements create mode 100644 macos/Runner/Info.plist create mode 100644 macos/Runner/MainFlutterWindow.swift create mode 100644 macos/Runner/Release.entitlements create mode 100644 macos/RunnerTests/RunnerTests.swift delete mode 100644 package-lock.json delete mode 100644 package.json delete mode 100644 pnpm-lock.yaml create mode 100644 pubspec.lock create mode 100644 pubspec.yaml delete mode 100644 scripts/reset-project.js delete mode 100644 tsconfig.json delete mode 100644 types/Interfaces.ts create mode 100644 web/favicon.png create mode 100644 web/icons/Icon-192.png create mode 100644 web/icons/Icon-512.png create mode 100644 web/icons/Icon-maskable-192.png create mode 100644 web/icons/Icon-maskable-512.png create mode 100644 web/index.html create mode 100644 web/manifest.json create mode 100644 windows/.gitignore create mode 100644 windows/CMakeLists.txt create mode 100644 windows/flutter/CMakeLists.txt create mode 100644 windows/flutter/generated_plugin_registrant.cc create mode 100644 windows/flutter/generated_plugin_registrant.h create mode 100644 windows/flutter/generated_plugins.cmake create mode 100644 windows/runner/CMakeLists.txt create mode 100644 windows/runner/Runner.rc create mode 100644 windows/runner/flutter_window.cpp create mode 100644 windows/runner/flutter_window.h create mode 100644 windows/runner/main.cpp create mode 100644 windows/runner/resource.h create mode 100644 windows/runner/resources/app_icon.ico create mode 100644 windows/runner/runner.exe.manifest create mode 100644 windows/runner/utils.cpp create mode 100644 windows/runner/utils.h create mode 100644 windows/runner/win32_window.cpp create mode 100644 windows/runner/win32_window.h diff --git a/.gitignore b/.gitignore index c9d575d7..79c113f9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,38 +1,45 @@ -# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files - -# dependencies -node_modules/ - -# Expo -.expo/ -dist/ -web-build/ -expo-env.d.ts - -# Native -*.orig.* -*.jks -*.p8 -*.p12 -*.key -*.mobileprovision - -# Metro -.metro-health-check* - -# debug -npm-debug.* -yarn-debug.* -yarn-error.* - -# macOS +# Miscellaneous +*.class +*.log +*.pyc +*.swp .DS_Store -*.pem - -# local env files -.env*.local - -# typescript -*.tsbuildinfo - -app-example +.atom/ +.build/ +.buildlog/ +.history +.svn/ +.swiftpm/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.pub-cache/ +.pub/ +/build/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/.metadata b/.metadata new file mode 100644 index 00000000..d77a4e0b --- /dev/null +++ b/.metadata @@ -0,0 +1,45 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "c23637390482d4cf9598c3ce3f2be31aa7332daf" + channel: "stable" + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf + base_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf + - platform: android + create_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf + base_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf + - platform: ios + create_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf + base_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf + - platform: linux + create_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf + base_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf + - platform: macos + create_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf + base_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf + - platform: web + create_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf + base_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf + - platform: windows + create_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf + base_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index 80fa91e9..00000000 --- a/.prettierrc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "singleQuote": true, - "semi": false, - "printWidth": 130 -} \ No newline at end of file diff --git a/README.md b/README.md index bf30949c..57de8967 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,87 @@ -# front-end -The front end repository of Spark Social. +# TikTok Clone with Flutter + +A clean, beautiful and functional TikTok clone built with Flutter using Apple Cupertino design principles. + +## Features + +- **Home Feed:** Vertical scrolling video feed similar to TikTok +- **Search & Discover:** Explore content with trending hashtags and a discovery grid +- **Video Creation:** Record and publish videos with effects and filters +- **Messages:** Chat with other users +- **Profile:** View your own content, liked videos, and manage your profile +- **Clean UI:** Uses Cupertino design for a clean, iOS-like experience +- **Ionicons:** Beautiful icons throughout the app + +## Screenshots + +(Screenshots would be here) + +## Getting Started + +### Prerequisites + +- Flutter SDK (3.7.2 or higher) +- Dart SDK (3.0.0 or higher) +- iOS/Android development environment + +### Installation + +1. Clone this repository +```bash +git clone https://github.com/yourusername/tiktok-clone.git +``` + +2. Navigate to the project directory +```bash +cd tiktok-clone +``` + +3. Install dependencies +```bash +flutter pub get +``` + +4. Run the app +```bash +flutter run +``` + +## Technologies Used + +- Flutter +- Cupertino (iOS-style) widgets +- Provider for state management +- Ionicons for beautiful icons +- Video player for media playback +- Camera for video recording +- Animation for smooth transitions + +## Project Structure + +- `lib/screens` - Contains the main screens of the application +- `lib/widgets` - Reusable UI components +- `lib/models` - Data models +- `lib/services` - Business logic and services +- `lib/utils` - Utility functions and constants + +## Future Enhancements + +- User authentication +- Real-time video streaming +- Comments and direct messaging +- Content recommendation algorithm +- Filters and effects +- Sound library + +## Contributing + +Contributions are welcome! Feel free to submit a Pull Request. + +## License + +This project is licensed under the MIT License - see the LICENSE file for details. + +## Acknowledgments + +- Inspired by TikTok's excellent user experience +- Built with Flutter, the beautiful UI framework diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 00000000..f9b30346 --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1 @@ +include: package:flutter_lints/flutter.yaml diff --git a/android/.gitignore b/android/.gitignore new file mode 100644 index 00000000..be3943c9 --- /dev/null +++ b/android/.gitignore @@ -0,0 +1,14 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java +.cxx/ + +# Remember to never publicly share your keystore. +# See https://flutter.dev/to/reference-keystore +key.properties +**/*.keystore +**/*.jks diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts new file mode 100644 index 00000000..5c63124b --- /dev/null +++ b/android/app/build.gradle.kts @@ -0,0 +1,44 @@ +plugins { + id("com.android.application") + id("kotlin-android") + // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. + id("dev.flutter.flutter-gradle-plugin") +} + +android { + namespace = "com.example.sparksocial" + compileSdk = flutter.compileSdkVersion + ndkVersion = flutter.ndkVersion + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_11.toString() + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId = "com.example.sparksocial" + // You can update the following values to match your application needs. + // For more information, see: https://flutter.dev/to/review-gradle-config. + minSdk = flutter.minSdkVersion + targetSdk = flutter.targetSdkVersion + versionCode = flutter.versionCode + versionName = flutter.versionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig = signingConfigs.getByName("debug") + } + } +} + +flutter { + source = "../.." +} diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 00000000..399f6981 --- /dev/null +++ b/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..33796bc1 --- /dev/null +++ b/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/android/app/src/main/kotlin/com/example/sparksocial/MainActivity.kt b/android/app/src/main/kotlin/com/example/sparksocial/MainActivity.kt new file mode 100644 index 00000000..23f787e4 --- /dev/null +++ b/android/app/src/main/kotlin/com/example/sparksocial/MainActivity.kt @@ -0,0 +1,5 @@ +package com.example.sparksocial + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity : FlutterActivity() diff --git a/android/app/src/main/res/drawable-v21/launch_background.xml b/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 00000000..f74085f3 --- /dev/null +++ b/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/android/app/src/main/res/drawable/launch_background.xml b/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 00000000..304732f8 --- /dev/null +++ b/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..db77bb4b7b0906d62b1847e87f15cdcacf6a4f29 GIT binary patch literal 544 zcmeAS@N?(olHy`uVBq!ia0y~yVDJE84rT@hh9qO>QU(SF$r9IylHmNblJdl&R0hYC z{G?O`&)mfH)S%SFl*+=BsWuD@4C?}XLR=Xb7#RJ!7{h04Zu=H;^aq4w44VZJaXaum zG@>0o`I5 z6U6gpN>(hBs@ovlyiK}omrVN}&28UY_JdsVcA=Cl0|SF_NswPKgMfg5KtMnO2sE4r zf%}pIPku2lFue41aSVw#{PxUsz9t6&hrp0GdlTMw^~h@f`fs1s!olsWbZ#C0v7PJ9 zExVjNeb(Ol`RU07XFf|X@qSK7qke0_C&kUH>MnD(8u-mi5B zt(&|xZ@%@sBNk%nt_5+E&U1QAlyT>>sMB2Zd5T5*R3&Sld5=98o%0qdm+@@-JjKB8 z(xcUiGOB?O=e)LCdt!o~wXBiQlFwyn4J{(JaZG%Q-e|yQz{EjrrIztFmwg@gt#&=FffMC4mtWmbKAF&qdyqKXG0ipk&vT5 zAmZ%d{SXF=bCKq@Z-GZZ;tV#~j83JDzFmxd-Hahq8AGNqhE8V;o5>h9i!po_WB6=l zU2kT?Kvt(bR^JA;;7+;DUGlvL6sMk6nsrHO&Q;BA-wby`jSBSdXJueu5G@Jv3uX`y zP*5;1FzBB@fBpXR=g+?ff&5MV!VC-yOFdm2Lp+YZy>e2h$w0vMVh8iNMNLhM)++IJ z{QqCg*LgRGXKpKZaOLSMJFo8MNc{figWv3mbJp+w-BoBm(|-hT|4dcbJ2lEyX!??Y`jpZ81*D{;rF6b`?;0>>pbJRKPRw2)?8wK8GH9> zL)n=k&vkA#ITl(SSsBW`??>AG{pM|AK2Jc#t6>y4N#$)H8ZEGkUc!`m{6pbTImMF#2{f z`gJk-_b~?aGX_m$44TXsJQ-wH$W+GA>5O4B7{g{VhRtFOpT!tHo5?tW$tsn}K99+{ zjLE&4*|Uz>tC88eNi3sZJbQ{n(LBkDd1t=Sn-plsE283-d*czTa-zm@iQD#kujt@|Kvt4hp|H z71QJA-@DE-vCc=}m3vdfeqD}l&W>B;IduNM%M_}RPx;yPVrk2E_T?&?d+vqXsA}xF zGk52_Ny78;-$vOoENNLjTll#WOG?YLuZ?eO;uVxk)@?h*_~6>qo(~l}_f@BaD>p=c zm>yaWRpaQm|`^KGX?!;c=*_;KycpYksb1 rdBk${xlomwC<})`ozHbP0l+XkKcl<5t literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..d5f1c8d34e7a88e3f88bea192c3a370d44689c3c GIT binary patch literal 1031 zcmeAS@N?(olHy`uVBq!ia0y~yV3+{H9Lx+13>Rhybuln7NS3%plmzFem6RtIr7}3C z$Dctjh(SM?K|h4SAdJB{hQT_6!9JhSFr3jep3y9U z(L9OKDxJ|flhG!d(I$t{HiywRm(ebd(Y}z;v6#`Zgwd&#(Yb=rrHawDn$fL>(XE!z zt&Y*Xj?trm(W8;kvysuWiP5u}(X)lotC`WOh0&*-(Wisaw}a8QlhLo6(XWTmzlYJk zmocD^F|eO8Z~|lCM8?2Lj6o9_gC>F8A3T{ccnV|4RK}2LjG@yRLuW9C&0q|h$rv_^ zF?<$d_-uBwU=HgjPRC?Uw`?x2A}+rQ?!a2^kVfvX7VhwN?#NE=s4niP9-inPl?69d z7T#1{bVqf`J=LWT)RsR~Tk%+JQT9|ZQFKYxDR#LKT37?|`uT^vIyZoR#b9vmDf()v(?iz7_L)oF78 zkJJ<)r8pUt8M$!5-N#*39u$gn zR>-(G$wg0`aolam=a?PG9$oC(GH>-!X0d*^l*gJvuFJp95x7~EvFLL3EEhhxt`pq; z%_lBC*86lUSiR`uj?O)w{s`}^n&Wz}@?RH+ochG+^SU~oY!t72t=ZWjXFc({Ty9i< zoXX3Kohi2Ju9xPeP296QFr{gETuR&Xh?K77YDUL{MQ1+I?2fS6ciP3jaR24wmqlki zKfgU-wdbO}>o1>vX;$n~9ltDoY4ItEi|?MFbkSNqMRUkMU-}~Bs z)+NdNzgg`VE)XKX=tVT|2z5{L4{YJjHWS*rIm-E6Ws`IFvdRnm87H fR&jd1QhDAN-h`R^uRUjAU|{fc^>bP0l+XkKAv&s= literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..4d6372eebdb28e45604e46eeda8dd24651419bc0 GIT binary patch literal 1443 zcmeAS@N?(olHy`uVBq!ia0y~yU^oE69Lx+145>_WOc@v$BuiW)N`mv#O3D+9QW+dm z@{>{(JaZG%Q-e|yQz{EjrrIztFy0FA32|j$U7=vLLgJC#>aTJ4T9D`W`gLwjjbtZ#N4x^eAqnb0LmM5cOIHO54qe%>- zNi3sjJfm3xqeU{KMGB*38lzP@qg4i@bta=t7NczrqirsuT`r?t9;018qkTT3eF39G z5u;-~4uK+6lW`UY0|SF-NswPKgMfko7&thbzyBTz-gcI+Wnf^o^K@|x zskrs_3VX0{ph#Pyi^G})N%|2R%{L?-ZV(U%Y+lqcfu(WAio}U)uIrV(+3WUaecD+A zmQ6SJem|Gdo_Kp z-*x@+`J3Y19B#kQTkx(t_`-K<>p6dn#rh_GuokuWC9d+nXX1zFdIcZVw{qEhc&=6O z^|eUFqwV?=KCBk6c(Pr4!l%_@70=G=P581}wBp75D22D-njfy%?{qk*AOB#Z{bq-g z`q2+|)-Q6ns2@Io{eMN{rubzF`u|EA+wSjG@UL6#aQc1N1oQe;4(H#;OmN?SS>~@F ztIeOC+&g|0i+=ccTfE@YZP5>((!~ot`|Cb zb^PFdrt-VXypJEaJ95?T2$v)Ws{O#V4Ednvz`|Pb} zf4VOe!@bWom-}e`tT2YtP3!%mny=z+}-K6mV2ZEt5Icwo(g z%Zr!wX>UAuh9QhqUj7<;)5IBxDQpH>KRz6|eoM6P*rQ_`EdPlXYgN?$U-3W2|NYf9 z3~H?J-^u1!lq%j3KgpL6_~F~TxcJXA&m~w0?N0Rh)m167XXobE9?sqiHmlnth&$|i z@b&H6->;epMCgPeb()B(Tr|RH~O+1PwlVxdT!ROU2OGQ3PvhLeQAwP zO|AMWqpQ}lM;-?Ix_3Z(ZoZK*%HiZ|EUDeVf^rGH<21r@EM zSf|zZ`p5aNP1ut4@|gFs0GYW>Kcl68^QPJ_Y + + + + + + diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml new file mode 100644 index 00000000..cb1ef880 --- /dev/null +++ b/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/android/app/src/profile/AndroidManifest.xml b/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 00000000..399f6981 --- /dev/null +++ b/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/android/build.gradle.kts b/android/build.gradle.kts new file mode 100644 index 00000000..89176ef4 --- /dev/null +++ b/android/build.gradle.kts @@ -0,0 +1,21 @@ +allprojects { + repositories { + google() + mavenCentral() + } +} + +val newBuildDir: Directory = rootProject.layout.buildDirectory.dir("../../build").get() +rootProject.layout.buildDirectory.value(newBuildDir) + +subprojects { + val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name) + project.layout.buildDirectory.value(newSubprojectBuildDir) +} +subprojects { + project.evaluationDependsOn(":app") +} + +tasks.register("clean") { + delete(rootProject.layout.buildDirectory) +} diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 00000000..f018a618 --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError +android.useAndroidX=true +android.enableJetifier=true diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..afa1e8eb --- /dev/null +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip diff --git a/android/settings.gradle.kts b/android/settings.gradle.kts new file mode 100644 index 00000000..a439442c --- /dev/null +++ b/android/settings.gradle.kts @@ -0,0 +1,25 @@ +pluginManagement { + val flutterSdkPath = run { + val properties = java.util.Properties() + file("local.properties").inputStream().use { properties.load(it) } + val flutterSdkPath = properties.getProperty("flutter.sdk") + require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" } + flutterSdkPath + } + + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + id("dev.flutter.flutter-plugin-loader") version "1.0.0" + id("com.android.application") version "8.7.0" apply false + id("org.jetbrains.kotlin.android") version "1.8.22" apply false +} + +include(":app") diff --git a/api/feedServices.ts b/api/feedServices.ts deleted file mode 100644 index f1d3d444..00000000 --- a/api/feedServices.ts +++ /dev/null @@ -1,67 +0,0 @@ -// src/api/videoService.ts -import { PostProps, VideoProps } from '@/types/Interfaces'; - -const extractHashtags = (text: string): string[] => { - if (!text) return []; - const match = text.match(/#[\w]+/g); - return match ? match.map(tag => tag.replace('#', '')) : []; -}; - -export const fetchTrendingPosts = async ( - mediaType: "video" | "image" -): Promise => { - try { - const res = await fetch( - 'https://public.api.bsky.app/xrpc/app.bsky.feed.getFeed?feed=at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/whats-hot&limit=100' - ); - const data = await res.json(); - if (!data || !data.feed) return []; - - const posts: PostProps[] = data.feed - .map((item: any) => item?.post) - .filter((post: PostProps | undefined): post is PostProps => !!post) - .filter((post: PostProps) => { - const embed = post.embed; - if (!embed) return false; - if (mediaType === "video") { - return ( - embed.$type === "app.bsky.embed.video#view" || - embed.$type === "app.bsky.embed.video" - ); - } else if (mediaType === "image") { - return ( - embed.$type === "app.bsky.embed.images#view" || - embed.$type === "app.bsky.embed.images" - ); - } - return false; - }); - - return posts; - } catch (error) { - console.error("Error fetching trending posts:", error); - return []; - } -}; - -export const fetchPostThread = async ( - author: string, - postId: string -): Promise => { - try { - const res = await fetch( - `https://public.api.bsky.app/xrpc/app.bsky.feed.getPostThread?uri=at://did:plc:${author}/app.bsky.feed.post/${postId}&depth=10` - ); - const data = await res.json(); - if (!data || !data.thread) return []; - - const posts: PostProps[] = data.thread - .map((item: any) => item?.post) - .filter((post: PostProps | undefined): post is PostProps => !!post); - - return posts; - } catch (error) { - console.error("Error fetching post thread:", error); - return []; - } -}; \ No newline at end of file diff --git a/api/notificationServices.ts b/api/notificationServices.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/api/pdsAuth.ts b/api/pdsAuth.ts deleted file mode 100644 index 1cd9e57a..00000000 --- a/api/pdsAuth.ts +++ /dev/null @@ -1,124 +0,0 @@ -import { PROD_PDS } from "@/constants/atproto"; -import { createSimpleDIDResolver } from "./simpleDIDResolver"; -import sessionManager from "./sessionManager"; -import { Agent, CredentialSession } from "@atproto/api"; - -// Create the resolver once at module level -const resolver = createSimpleDIDResolver(); - -/** - * Login to an AT Protocol PDS (Personal Data Server) using a handle and password - * - * @param handle - The user handle (e.g., username.bsky.social) - * @param password - The user's password - * @returns The session data object or null if login failed - */ -export const pdsLogin = async (handle: string, password: string) => { - try { - const resolverAgent = new Agent(new URL(PROD_PDS)); - const handleRes = await resolverAgent.resolveHandle({ - handle: handle, - }); - - if (!handleRes.success) { - throw new Error("Handle not found"); - } - - // Use the new resolver to get the DID doc - const didDoc = await resolver.resolveHandleToDidDoc(handle); - - // Get the PDS URL from the DID document service endpoints - const pdsUrl = didDoc.pds; - - if (!pdsUrl) { - throw new Error("Could not find PDS URL for this account"); - } - - const pdsCredentialSession = new CredentialSession(new URL(pdsUrl)); - - const agent = new Agent(pdsCredentialSession); - - const { success, data } = await agent.com.atproto.server.createSession({ - identifier: handle, - password, - }); - - if (!success) { - throw new Error("Login was not successful"); - } - - const session = { - ...data, - active: data.active ?? false, - }; - - await sessionManager.setSession(agent, session); - - return data; - } catch (error) { - console.error("Login failed:", error); - throw error; - } -}; - -export const pdsRegister = async ( - email: string, - handle: string, - password: string, - inviteCode?: string -) => { - try { - const cred = new CredentialSession(new URL(PROD_PDS)); - - const agent = new Agent(cred); - - const { success, data } = await agent.com.atproto.server.createAccount({ - email, - handle, - password, - inviteCode, - }); - - if (!success) { - throw new Error("Register was not successful"); - } - - const session = { - ...data, - active: true, - }; - - await sessionManager.setSession(agent, session); - } catch (error) { - console.error("Register failed:", error); - throw error; - } -}; - -/** - * Logout the current user - */ -export const pdsLogout = async () => { - await sessionManager.clearSession(); -}; - -/** - * Get the current session if available - */ -export const getCurrentSession = () => { - return sessionManager.getSession(); -}; - -/** - * Get the current agent if logged in - */ -export const getAgent = () => { - return sessionManager.getAgent(); -}; - -/** - * Check if user is currently logged in - */ -export const isLoggedIn = () => { - return sessionManager.isLoggedIn(); -}; diff --git a/api/profileServices.ts b/api/profileServices.ts deleted file mode 100644 index 8f7bb397..00000000 --- a/api/profileServices.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { PostProps, UserProps } from "@/types/Interfaces"; - -export const getProfile = async (actor: string): Promise => { - try { - const res = await fetch( - `https://public.api.bsky.app/xrpc/app.bsky.actor.getProfile?actor=${actor}` - ); - - if (!res.ok) { - console.error(`Error fetching profile: ${res.status}`); - return null; - } - - const profile: UserProps = await res.json(); - return profile; - } catch (error) { - console.error("Error fetching profile from Bluesky:", error); - return null; - } -}; - -export const getProfileMedia = async ( - actor: string, - mediaType: "video" | "image" -): Promise => { - const res = await fetch( - `https://public.api.bsky.app/xrpc/app.bsky.feed.getAuthorFeed?actor=${actor}&filter=posts_with_media` - ); - - if (!res.ok) { - throw new Error(`Failed to fetch media for actor: ${actor}`); - } - - const data = await res.json(); - - if (!data.feed) { - return []; - } - - const filteredPosts: PostProps[] = data.feed.filter((item: any) => { - const embed = item.post.record.embed; - if (!embed) return false; - if (mediaType === "video") { - return embed.$type === "app.bsky.embed.video"; - } else if (mediaType === "image") { - return embed.$type === "app.bsky.embed.images"; - } - return false; - }); - - return filteredPosts; -}; diff --git a/api/sessionManager.ts b/api/sessionManager.ts deleted file mode 100644 index f480b590..00000000 --- a/api/sessionManager.ts +++ /dev/null @@ -1,218 +0,0 @@ -import EventEmitter from "events"; -import { Agent, AtpSessionData, CredentialSession } from "@atproto/api"; -import * as SecureStore from 'expo-secure-store'; - -export enum SessionEventType { - CREATE = "session:create", - REFRESH = "session:refresh", - EXPIRED = "session:expired", - DELETE = "session:delete", -} - -/** - * SessionManager handles storing and managing AT Protocol sessions - * It maintains the session data globally and emits events when the session changes - */ -class SessionManager extends EventEmitter { - private agent: Agent | null = null; - private sessionData: AtpSessionData | null = null; - private refreshTimeout: NodeJS.Timeout | null = null; - private persistKey: string = "atproto_session"; - private pdsUrl: string = ""; - - constructor() { - super(); - // Immediately invoke an async function to handle async initialization - (async () => { - await this.loadSession(); - })().catch(error => { - console.error('Failed to load session during initialization:', error); - }); - } - - /** - * Get the current Agent instance - */ - getAgent() { - return this.agent; - } - - /** - * Get the current session data - */ - getSession() { - return this.sessionData; - } - - /** - * Store a new session - */ - async setSession(agent: Agent, session: AtpSessionData): Promise { - this.agent = agent; - this.sessionData = session; - this.pdsUrl = session.accessJwt - ? JSON.parse(atob(session.accessJwt.split(".")[1])).aud.replace( - "did:web:", - "" - ) - : ""; - await this.persistSession(); - // this.scheduleRefresh(); - this.emit(SessionEventType.CREATE, session); - } - - // /** - // * Refresh the current session - // */ - // async refreshSession(): Promise { - // if (!this.agent || !this.sessionData) { - // return null; - // } - - // try { - // // For refreshing with the Agent class - // if (this.sessionData.refreshJwt) { - // // Use the refresh method if available - // if (typeof this.agent.refreshSession === "function") { - // const result = await this.agent.refreshSession(); - // if (result && result.data) { - // this.sessionData = result.data; - // this.persistSession(); - // this.scheduleRefresh(); - // this.emit(SessionEventType.REFRESH, result.data); - // return result.data; - // } - // } else { - // const cred = new CredentialSession(new URL(this.sessionData.pds)); - // } - // } - - // // If refresh failed or isn't available - // this.clearSession(); - // this.emit(SessionEventType.EXPIRED); - // return null; - // } catch (error) { - // console.error("Failed to refresh session:", error); - // this.clearSession(); - // this.emit(SessionEventType.EXPIRED); - // return null; - // } - // } - - /** - * Clear the current session (logout) - */ - async clearSession(): Promise { - if (this.refreshTimeout) { - clearTimeout(this.refreshTimeout); - this.refreshTimeout = null; - } - - this.agent = null; - this.sessionData = null; - this.pdsUrl = ""; - await this.removePersistedSession(); - this.emit(SessionEventType.DELETE); - } - - /** - * Check if a user is currently logged in - */ - isLoggedIn(): boolean { - return !!this.sessionData && !!this.agent; - } - - /** - * Schedule a session refresh before it expires - */ - // private scheduleRefresh(): void { - // if (this.refreshTimeout) { - // clearTimeout(this.refreshTimeout); - // } - - // // If no session, don't schedule refresh - // if (!this.sessionData) return; - - // // Default refresh time - 30 minutes before expiry - // // Since we don't have a specific expiry time from the session, - // // we'll use a conservative 23.5 hour refresh interval by default - // const refreshInterval = 23.5 * 60 * 60 * 1000; // 23.5 hours - - // // Schedule refresh - // this.refreshTimeout = setTimeout( - // () => this.refreshSession(), - // refreshInterval - // ); - // } - - /** - * Persist session to storage using SecureStore - */ - private async persistSession(): Promise { - if (this.sessionData) { - try { - await SecureStore.setItemAsync( - this.persistKey, - JSON.stringify(this.sessionData) - ); - } catch (error) { - console.error('Error saving session to SecureStore:', error); - } - } - } - - /** - * Remove persisted session from SecureStore - */ - private async removePersistedSession(): Promise { - try { - await SecureStore.deleteItemAsync(this.persistKey); - } catch (error) { - console.error('Error removing session from SecureStore:', error); - } - } - - /** - * Load session from SecureStore on startup - */ - private async loadSession(): Promise { - try { - const savedSession = await SecureStore.getItemAsync(this.persistKey); - - if (savedSession) { - this.sessionData = JSON.parse(savedSession); - - // Create a new agent with the saved data - if (this.sessionData && this.sessionData.did) { - // Extract the PDS URL from the access token - this.pdsUrl = this.sessionData.accessJwt - ? JSON.parse(atob(this.sessionData.accessJwt.split(".")[1])).aud.replace( - "did:web:", - "" - ) - : ""; - - if (!this.pdsUrl) { - throw new Error("Could not find PDS URL for this account"); - } - const cred = new CredentialSession(new URL(`https://${this.pdsUrl}`)); - cred.resumeSession(this.sessionData); - - const agent = new Agent(cred); - - // Restore the session - this.agent = agent; - // this.scheduleRefresh(); - } - } - } catch (error) { - console.error('Error loading session from SecureStore:', error); - // Clear any invalid or corrupted session data - this.removePersistedSession(); - } - } -} - -// Create a singleton instance -export const sessionManager = new SessionManager(); -export default sessionManager; diff --git a/api/simpleDIDResolver.ts b/api/simpleDIDResolver.ts deleted file mode 100644 index 10b45f7d..00000000 --- a/api/simpleDIDResolver.ts +++ /dev/null @@ -1,178 +0,0 @@ -import { Agent } from "@atproto/api"; -import { AtprotoData } from "@atproto/identity"; -import sessionManager from "./sessionManager"; -import { PROD_PDS } from "@/constants/atproto"; - -// Interface for BidirectionalResolver to avoid circular imports -export interface DIDResolver { - resolveDidToHandle(did: string): Promise; - resolveHandleToDidDoc(handle: string): Promise; - resolveDidsToHandles(dids: string[]): Promise>; -} - -// Cache durations -const ONE_HOUR = 60 * 60 * 1000; -const ONE_DAY = ONE_HOUR * 24; - -// Simple implementation of caches with expiration -class SimpleCache { - private cache = new Map(); - private ttl: number; - - constructor(ttlMs: number) { - this.ttl = ttlMs; - } - - get(key: string): T | undefined { - const item = this.cache.get(key); - if (!item) return undefined; - - if (Date.now() > item.expiry) { - this.cache.delete(key); - return undefined; - } - - return item.value; - } - - set(key: string, value: T): void { - this.cache.set(key, { - value, - expiry: Date.now() + this.ttl, - }); - } - - delete(key: string): void { - this.cache.delete(key); - } -} - -/** - * Create a simple DID resolver with caching that implements DIDResolver - */ -export function createSimpleDIDResolver(): DIDResolver { - // Create caches - const didDocCache = new SimpleCache(ONE_HOUR); - const handleToDIDCache = new SimpleCache(ONE_DAY); - const didToHandleCache = new SimpleCache(ONE_DAY); - - // Helper function to get agent or create a new one - const getAgent = () => { - const agent = sessionManager.getAgent(); - if (agent) return agent; - return new Agent(new URL(PROD_PDS)); - }; - - // Helper function to resolve DID document (not exposed in interface) - const resolveDidDocHelper = async (did: string): Promise => { - // Check cache first - const cachedDoc = didDocCache.get(did); - if (cachedDoc) return cachedDoc; - - try { - // Get agent - const agent = getAgent(); - - // Use plc.directory as fallback - const didResponse = await fetch(`https://plc.directory/${did}`); - - if (!didResponse.ok) { - throw new Error(`Failed to resolve DID document for ${did}`); - } - - const didData = await didResponse.json(); - const atprotoData: AtprotoData = { - did: didData.did, - handle: didData.alsoKnownAs?.[0]?.replace('at://', '') || did, - pds: didData.service?.find((s: any) => s.id === '#atproto_pds')?.serviceEndpoint || '', - signingKey: didData.verificationMethod?.[0]?.publicKeyMultibase || '', - }; - - // Cache the document - didDocCache.set(did, atprotoData); - - return atprotoData; - } catch (error) { - console.error("Error resolving DID document:", error); - throw error; - } - }; - - // The object that implements DIDResolver - return { - /** - * Resolve a DID to a handle with caching - */ - async resolveDidToHandle(did: string): Promise { - // Check cache first - const cachedHandle = didToHandleCache.get(did); - if (cachedHandle) return cachedHandle; - - try { - // Resolve DID document using helper function - const didDoc = await resolveDidDocHelper(did); - - // Cache the handle - if (didDoc.handle) { - didToHandleCache.set(did, didDoc.handle); - return didDoc.handle; - } - - return did; // Fallback to DID if no handle - } catch (error) { - console.error("Error resolving DID to handle:", error); - return did; // Return the DID as fallback - } - }, - - /** - * Resolve a handle to a DID document with caching - */ - async resolveHandleToDidDoc(handle: string): Promise { - try { - // Check if we already have the DID in cache - const cachedDid = handleToDIDCache.get(handle); - if (cachedDid) { - // Check if we have the DID doc cached - const cachedDoc = didDocCache.get(cachedDid); - if (cachedDoc) return cachedDoc; - } - - // Get agent - const agent = getAgent(); - - // Resolve handle to DID - const didResult = await agent.resolveHandle({ handle }); - const did = didResult.data.did; - - // Cache the handle to DID mapping - handleToDIDCache.set(handle, did); - - // Then resolve the DID document - return await resolveDidDocHelper(did); - } catch (error) { - console.error("Error resolving handle to DID doc:", error); - throw new Error(`Failed to resolve handle: ${handle}`); - } - }, - - /** - * Resolve multiple DIDs to handles - */ - async resolveDidsToHandles(dids: string[]): Promise> { - const didHandleMap: Record = {}; - - // Use Promise.all to resolve all DIDs in parallel - const resolves = await Promise.all( - dids.map((did) => this.resolveDidToHandle(did).catch(() => did)) - ); - - // Map results to DIDs - for (let i = 0; i < dids.length; i++) { - didHandleMap[dids[i]] = resolves[i]; - } - - return didHandleMap; - } - }; -} \ No newline at end of file diff --git a/app.json b/app.json deleted file mode 100644 index 1a059422..00000000 --- a/app.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "expo": { - "name": "spark-social-app", - "slug": "spark-social-app", - "version": "1.0.0", - "orientation": "portrait", - "icon": "/assets/images/icon.png", - "scheme": "myapp", - "userInterfaceStyle": "automatic", - "newArchEnabled": true, - "ios": { - "supportsTablet": true - }, - "android": { - "adaptiveIcon": { - "foregroundImage": "/assets/images/adaptive-icon.png", - "backgroundColor": "#ffffff" - } - }, - "web": { - "bundler": "metro", - "output": "static", - "favicon": "/assets/images/favicon.png" - }, - "plugins": [ - "expo-router", - "react-native-imglysdk", - [ - "expo-splash-screen", - { - "image": "/assets/images/splash-icon.png", - "imageWidth": 200, - "resizeMode": "contain", - "backgroundColor": "#ffffff" - } - ], - "expo-video", - "react-native-video", - "expo-secure-store" - ], - "experiments": { - "typedRoutes": true - } - } -} diff --git a/app/(auth)/Login.tsx b/app/(auth)/Login.tsx deleted file mode 100644 index a687a6d6..00000000 --- a/app/(auth)/Login.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import ActionButton from '@/components/global/ActionButton'; -import InputArea from '@/components/global/InputArea'; -import Logo from '@/components/global/Logo'; -import { ThemedText } from '@/components/ThemedText'; -import { Colors } from '@/constants/Colors'; -import { pdsLogin } from '@/api/pdsAuth'; -import { Ionicons } from '@expo/vector-icons'; -import { router } from 'expo-router'; -import React, { useState } from 'react'; -import { View, StyleSheet, useColorScheme, TouchableOpacity, SafeAreaView, Alert, ActivityIndicator } from 'react-native'; - -export default function Login() { - - useColorScheme(); - const colorScheme = useColorScheme(); - - const styles = StyleSheet.create({ - container: { - flex: 1, - alignItems: 'center', - justifyContent: 'center', - backgroundColor: Colors[colorScheme ?? 'light'].background, - }, - text: { - fontSize: 18, - fontWeight: 'bold', - color: '#333', - }, - errorText: { - color: 'red', - marginBottom: 10, - textAlign: 'center', - }, - }); - - const [handle, setHandle] = useState(''); - const [password, setPassword] = useState(''); - const [loading, setLoading] = useState(false); - const [error, setError] = useState(''); - - const handleLogin = async () => { - if (!handle || !password) { - setError('Please enter both handle and password'); - return; - } - - try { - setLoading(true); - setError(''); - - // Call the pdsLogin function from our API - await pdsLogin(handle, password); - - // If successful, navigate to the app's main screen - router.replace('/(tabs)'); - } catch (err) { - console.error('Login error:', err); - setError(err instanceof Error ? err.message : 'Failed to login. Please try again.'); - } finally { - setLoading(false); - } - }; - - return ( - - router.back()} - style={{ position: 'absolute', top: 80, left: 20, zIndex: 1 }}> - - - - - {error ? {error} : null} - - - - - - - {loading && } - - router.push('/(auth)/Register')} - style={{ marginTop: 20, alignItems: 'center' }} - > - Don't have an account? Register - - - - ); -} \ No newline at end of file diff --git a/app/(auth)/Register.tsx b/app/(auth)/Register.tsx deleted file mode 100644 index c4c012f3..00000000 --- a/app/(auth)/Register.tsx +++ /dev/null @@ -1,404 +0,0 @@ -import ActionButton from '@/components/global/ActionButton'; -import InputArea from '@/components/global/InputArea'; -import Logo from '@/components/global/Logo'; -import { ThemedText } from '@/components/ThemedText'; -import { Colors } from '@/constants/Colors'; -import { pdsRegister } from '@/api/pdsAuth'; -import { Ionicons } from '@expo/vector-icons'; -import { router } from 'expo-router'; -import React, { useCallback, useRef, useState, useMemo, useEffect } from 'react'; -import { View, StyleSheet, useColorScheme, TouchableOpacity, SafeAreaView, Dimensions, ActivityIndicator, Animated } from 'react-native'; -import BottomSheet, { BottomSheetView } from '@gorhom/bottom-sheet'; -import DateTimePicker, { DateTimePickerEvent } from '@react-native-community/datetimepicker'; -import { format } from 'date-fns'; -import Reanimated, { withTiming, useAnimatedStyle, useSharedValue, withSequence, SlideInUp, SlideOutDown } from 'react-native-reanimated'; - -// Define the domain constant -const DOMAIN = '.sprk.so'; - -export default function Register() { - const colorScheme = useColorScheme(); - - const styles = StyleSheet.create({ - container: { - flex: 1, - alignItems: 'center', - justifyContent: 'center', - backgroundColor: Colors[colorScheme ?? 'light'].background, - width: '100%', - position: 'relative', - }, - formContainer: { - width: '100%', - alignItems: 'center', - flex: 1, - justifyContent: 'center', - }, - backButton: { - position: 'absolute', - top: 80, - left: 20, - zIndex: 1, - }, - logo: { - marginTop: 80, - }, - text: { - fontSize: 18, - fontWeight: 'bold', - }, - errorText: { - color: 'red', - marginBottom: 10, - textAlign: 'center', - }, - datePickerButton: { - flexDirection: 'row', - alignItems: 'center', - borderWidth: 1, - borderColor: Colors[colorScheme ?? 'light'].underlineColor, - borderRadius: 8, - paddingHorizontal: 12, - backgroundColor: Colors[colorScheme ?? 'light'].background, - height: 50, - width: '90%', - marginBottom: 25, - }, - datePickerIcon: { - marginRight: 10, - }, - datePickerText: { - flex: 1, - fontSize: 16, - color: Colors[colorScheme ?? 'light'].text, - }, - datePickerPlaceholder: { - flex: 1, - fontSize: 16, - color: Colors[colorScheme ?? 'light'].textGray, - }, - bottomSheetContent: { - padding: 20, - alignItems: 'center', - backgroundColor: Colors[colorScheme ?? 'light'].background, - paddingBottom: 40, - }, - bottomSheetButton: { - padding: 15, - backgroundColor: Colors[colorScheme ?? 'light'].tint, - borderRadius: 8, - alignItems: 'center', - marginTop: 30, - marginBottom: 30, - width: '80%', - }, - bottomSheetButtonText: { - color: '#fff', - fontWeight: 'bold', - fontSize: 16, - }, - bottomSheetBackground: { - backgroundColor: Colors[colorScheme ?? 'light'].background, - }, - bottomSheetHandle: { - backgroundColor: Colors[colorScheme ?? 'light'].underlineColor, - width: 40, - }, - dateLabelText: { - alignSelf: 'flex-start', - marginLeft: '5%', - marginBottom: 6, - }, - datePickerTitle: { - fontSize: 18, - fontWeight: 'bold', - marginBottom: 20, - }, - chevronIcon: { - marginLeft: 5, - }, - inputStyle: { - width: '90%', - }, - passwordInputStyle: { - width: '90%', - marginBottom: 25, - }, - handleInputContainer: { - flexDirection: 'row', - alignItems: 'center', - width: '90%', - marginBottom: 25, - }, - handleDomain: { - fontSize: 16, - color: Colors[colorScheme ?? 'light'].textGray, - paddingRight: 12, - marginLeft: 5, - }, - toastContainer: { - position: 'absolute', - top: 100, - left: 20, - right: 20, - backgroundColor: 'rgba(255, 0, 0, 0.8)', - padding: 15, - borderRadius: 8, - alignItems: 'center', - zIndex: 1000, - }, - toastText: { - color: 'white', - fontWeight: 'bold', - }, - }); - - const bottomSheetRef = useRef(null); - - // Use fixed height for bottom sheet - const snapPoints = useMemo(() => ['60%'], []); - - const handleSheetChanges = useCallback((index: number) => { - console.log('handleSheetChanges', index); - }, []); - - const openBottomSheet = useCallback(() => { - bottomSheetRef.current?.expand(); - }, []); - - const closeBottomSheet = useCallback(() => { - bottomSheetRef.current?.close(); - }, []); - - const [email, setEmail] = useState(''); - const [username, setUsername] = useState(''); - const [password, setPassword] = useState(''); - const [inviteCode, setInviteCode] = useState(''); - const [birthDate, setBirthDate] = useState(new Date()); - const [loading, setLoading] = useState(false); - const [error, setError] = useState(''); - const [showToast, setShowToast] = useState(false); - - // Error toast timing effect - useEffect(() => { - if (error) { - setShowToast(true); - const timer = setTimeout(() => { - setShowToast(false); - }, 3000); - return () => clearTimeout(timer); - } - }, [error]); - - const handleDateChange = (event: DateTimePickerEvent, selectedDate: Date | undefined) => { - if (selectedDate) { - setBirthDate(selectedDate); - } - }; - - const confirmDateSelection = () => { - closeBottomSheet(); - }; - - // Function to get the full handle with domain - const getFullHandle = () => { - return `${username}${DOMAIN}`; - }; - - const handleRegister = async () => { - const trimmedEmail = email.trim(); - const trimmedUsername = username.trim(); - - if (!trimmedEmail || !trimmedUsername || !password || !birthDate) { - setError('Please fill in all required fields'); - return; - } - - // Improved email validation with proper trimming - const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; - if (!emailRegex.test(trimmedEmail)) { - setError('Please enter a valid email address'); - return; - } - - // Username validation (no dots, no spaces, alphanumeric only) - const usernameRegex = /^[a-zA-Z0-9_-]+$/; - if (!usernameRegex.test(trimmedUsername)) { - setError('Username can only contain letters, numbers, underscores, and hyphens'); - return; - } - - // Password length check - if (password.length < 6) { - setError('Password must be at least 6 characters'); - return; - } - - try { - setLoading(true); - setError(''); - - // Get the full handle with domain and use it for registration - const fullHandle = getFullHandle(); - - // Call the pdsRegister function from our API - await pdsRegister(trimmedEmail, fullHandle, password, inviteCode || undefined); - - // If successful, navigate to the app's main screen - router.replace('/(tabs)'); - } catch (err) { - console.error('Registration error:', err); - setError(err instanceof Error ? err.message : 'Failed to register. Please try again.'); - } finally { - setLoading(false); - } - }; - - return ( - - router.back()} - style={styles.backButton}> - - - - - - {/* Animated Toast for Errors */} - {showToast && ( - - {error} - - )} - - - - - {/* Custom username input with domain appended */} - - - {DOMAIN} - - - - - - - {/* Birth Date Picker Button */} - Birth Date - - - {birthDate ? ( - - {format(birthDate, 'MMMM dd, yyyy')} - - ) : ( - - Select your birth date - - )} - - - - - - {loading && } - - router.push('/(auth)/Login')} - style={{ marginTop: 20, alignItems: 'center' }} - > - Already have an account? Login - - - - {/* Bottom Sheet for Date Picker - outside of the form container */} - - - - Select Your Birth Date - - - - Done - - - - - ); -} \ No newline at end of file diff --git a/app/(screens)/ProfileFeed.tsx b/app/(screens)/ProfileFeed.tsx deleted file mode 100644 index f4df0dc3..00000000 --- a/app/(screens)/ProfileFeed.tsx +++ /dev/null @@ -1,120 +0,0 @@ -// ProfileFeed.tsx -import React, { useEffect, useRef, useState } from 'react'; -import { - StyleSheet, - FlatList, - View, - Dimensions, - TouchableOpacity, - Platform, -} from 'react-native'; -import { ThemedView } from '@/components/ThemedView'; -import VideoScreen from '@/components/Video/VideoScreen'; -import { PostProps } from '@/types/Interfaces'; -import { router, Stack } from 'expo-router'; -import { Ionicons } from '@expo/vector-icons'; -import { useRoute } from '@react-navigation/native'; - -export default function ProfileFeed() { - const flatListRef = useRef(null); - const [postData, setPostData] = useState([]); - const [currentVisibleIndex, setCurrentVisibleIndex] = useState(0); - - const route = useRoute(); - const params = route.params as { videoData?: string; initialIndex?: string }; - - const initialIndex = parseInt(params.initialIndex ?? "0", 10) || 0; - - useEffect(() => { - if (params.videoData) { - try { - const videos = JSON.parse(params.videoData) as PostProps[]; - setPostData(videos); - } catch (error) { - console.error("Error parsing videoData:", error); - } - } - }, [params.videoData]); - - const onViewRef = useRef(({ viewableItems }: any) => { - if (viewableItems.length > 0) { - const index = viewableItems[0].index; - if (index !== null && index !== undefined) { - setCurrentVisibleIndex(index); - } - } - }); - - const viewConfigRef = useRef({ - viewAreaCoveragePercentThreshold: 95, - }); - - const { height: windowHeight } = Dimensions.get('window'); - const TAB_BAR_HEIGHT = 0; - const availableHeight = windowHeight - TAB_BAR_HEIGHT; - - const getItemLayout = (_: any, index: number) => ({ - length: availableHeight, - offset: availableHeight * index, - index, - }); - - return ( - <> - - - router.back()} - style={{ position: 'absolute', top: 50, left: 20, zIndex: 1 }}> - - - `${item.id}-${index}`} - renderItem={({ item, index }) => { - const isActive = index === currentVisibleIndex; - return ( - - - - ); - }} - pagingEnabled - showsVerticalScrollIndicator={false} - onViewableItemsChanged={onViewRef.current} - viewabilityConfig={viewConfigRef.current} - decelerationRate="fast" - initialNumToRender={3} - maxToRenderPerBatch={2} - windowSize={3} - removeClippedSubviews - scrollEventThrottle={16} - style={styles.flatList} - contentContainerStyle={Platform.OS === 'android' ? { paddingBottom: TAB_BAR_HEIGHT } : undefined} - getItemLayout={getItemLayout} - initialScrollIndex={initialIndex} - /> - - - ); -} - -const styles = StyleSheet.create({ - root: { - flex: 1, - backgroundColor: '#000', - }, - flatList: { - flex: 1, - }, - flatListContent: { - paddingBottom: 0, - }, - videoContainer: { - width: '100%', - backgroundColor: '#000', - justifyContent: 'center', - alignItems: 'center', - }, -}); \ No newline at end of file diff --git a/app/(tabs)/CreateScreen.tsx b/app/(tabs)/CreateScreen.tsx deleted file mode 100644 index 68e708a9..00000000 --- a/app/(tabs)/CreateScreen.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import { Ionicons } from '@expo/vector-icons'; -import { CameraView, CameraType, useCameraPermissions } from 'expo-camera'; -import { useState } from 'react'; -import { Button, StyleSheet, Text, TouchableOpacity, View } from 'react-native'; - -export default function CreateScreen() { - const [facing, setFacing] = useState('back'); - const [permission, requestPermission] = useCameraPermissions(); - - if (!permission) { - return ; - } - - if (!permission.granted) { - return ( - - We need your permission to show the camera -