├── .DS_Store ├── .github └── workflows │ ├── bundle.yml │ └── changelog.yml ├── .gitignore ├── .idea ├── filesync.iml ├── inspectionProfiles │ └── Project_Default.xml ├── material_theme_project_new.xml ├── modules.xml ├── vcs.xml └── workspace.xml ├── .vscode ├── extensions.json └── settings.json ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── android ├── .gitignore ├── .idea │ ├── .gitignore │ ├── .name │ ├── AndroidProjectSystem.xml │ ├── appInsightsSettings.xml │ ├── compiler.xml │ ├── deploymentTargetDropDown.xml │ ├── deploymentTargetSelector.xml │ ├── gradle.xml │ ├── inspectionProfiles │ │ └── Project_Default.xml │ ├── kotlinc.xml │ ├── material_theme_project_new.xml │ ├── migrations.xml │ ├── misc.xml │ ├── runConfigurations.xml │ └── vcs.xml ├── app │ ├── .gitignore │ ├── build.gradle.kts │ ├── proguard-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── com │ │ │ └── filesync │ │ │ └── app │ │ │ └── ExampleInstrumentedTest.kt │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── filesync │ │ │ │ └── app │ │ │ │ ├── FIleManager.kt │ │ │ │ ├── MainActivity.kt │ │ │ │ ├── components │ │ │ │ ├── Buttons.kt │ │ │ │ ├── FileItem.kt │ │ │ │ ├── FileListAdapter.kt │ │ │ │ ├── PulsingCircle.kt │ │ │ │ └── WifiConfigModal.kt │ │ │ │ ├── hooks │ │ │ │ ├── APManager.java │ │ │ │ └── DefaultFailureListener.java │ │ │ │ ├── screens │ │ │ │ └── MainScreen.kt │ │ │ │ └── ui │ │ │ │ └── theme │ │ │ │ ├── Color.kt │ │ │ │ ├── Theme.kt │ │ │ │ └── Type.kt │ │ └── res │ │ │ ├── assets │ │ │ └── svg │ │ │ │ ├── close.svg │ │ │ │ ├── lock.svg │ │ │ │ ├── qr-scanner.svg │ │ │ │ ├── qr_code.svg │ │ │ │ ├── signal.svg │ │ │ │ └── wifi.svg │ │ │ ├── drawable │ │ │ ├── blue_semicircle.xml │ │ │ ├── cancel.xml │ │ │ ├── copy.xml │ │ │ ├── custom_card_background.xml │ │ │ ├── delete.xml │ │ │ ├── details.xml │ │ │ ├── folder.xml │ │ │ ├── gray_semisircle.xml │ │ │ ├── green_semicircle.xml │ │ │ ├── ic_launcher_background.xml │ │ │ ├── ic_launcher_foreground.xml │ │ │ ├── image.xml │ │ │ ├── loading.gif │ │ │ ├── lock.xml │ │ │ ├── move.xml │ │ │ ├── mp3.xml │ │ │ ├── mp4.xml │ │ │ ├── paste.xml │ │ │ ├── pdf.xml │ │ │ ├── progress_bar_custom.xml │ │ │ ├── purple_semicirlce.xml │ │ │ ├── qr_code.xml │ │ │ ├── qr_scanner.xml │ │ │ ├── rename.xml │ │ │ ├── selectall.xml │ │ │ ├── selected_item_background.xml │ │ │ ├── semi_circle_background.xml │ │ │ ├── signal.xml │ │ │ ├── storage.xml │ │ │ ├── text.xml │ │ │ ├── wifi.xml │ │ │ └── yellow_semisircle.xml │ │ │ ├── layout │ │ │ ├── activity_main.xml │ │ │ └── item_file.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ │ ├── values │ │ │ ├── colors.xml │ │ │ ├── strings.xml │ │ │ └── themes.xml │ │ │ └── xml │ │ │ ├── backup_rules.xml │ │ │ └── data_extraction_rules.xml │ │ └── test │ │ └── java │ │ └── com │ │ └── filesync │ │ └── app │ │ └── ExampleUnitTest.kt ├── build.gradle.kts ├── gradle.properties ├── gradle │ ├── libs.versions.toml │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle.kts └── signal.svg ├── app-icon.png ├── desktop ├── .eslintrc.json ├── .gitignore ├── .idea │ ├── .gitignore │ ├── desktop.iml │ ├── material_theme_project_new.xml │ ├── modules.xml │ └── vcs.xml ├── .taurignore ├── Cargo.lock ├── Cargo.toml ├── README.md ├── Trunk.toml ├── app-icon.png ├── assets │ ├── folder-icon.png │ ├── js │ │ └── qr-scanner.min.js │ └── style │ │ ├── app.css │ │ └── main.css ├── index.html ├── justfile ├── package-lock.json ├── package.json ├── rust-toolchain.toml ├── server │ ├── .DS_Store │ ├── .gitignore │ ├── Cargo.toml │ ├── Justfile │ └── src │ │ ├── errors.rs │ │ ├── lib.rs │ │ ├── no-tls.rs │ │ ├── router.rs │ │ ├── routes.rs │ │ └── server.rs ├── shared │ ├── Cargo.toml │ ├── Justfile │ └── src │ │ ├── cmd.rs │ │ ├── config.rs │ │ ├── lib.rs │ │ └── state.rs ├── src-tauri │ ├── .gitignore │ ├── Cargo.toml │ ├── build.rs │ ├── capabilities │ │ ├── default.json │ │ └── mobile.json │ ├── icons │ │ ├── 128x128.png │ │ ├── 128x128@2x.png │ │ ├── 32x32.png │ │ ├── Square107x107Logo.png │ │ ├── Square142x142Logo.png │ │ ├── Square150x150Logo.png │ │ ├── Square284x284Logo.png │ │ ├── Square30x30Logo.png │ │ ├── Square310x310Logo.png │ │ ├── Square44x44Logo.png │ │ ├── Square71x71Logo.png │ │ ├── Square89x89Logo.png │ │ ├── StoreLogo.png │ │ ├── icon.icns │ │ ├── icon.ico │ │ └── icon.png │ ├── src │ │ ├── commands │ │ │ ├── app.rs │ │ │ ├── keygen.rs │ │ │ ├── mod.rs │ │ │ └── server.rs │ │ ├── database │ │ │ ├── db.rs │ │ │ ├── migrations.rs │ │ │ ├── mod.rs │ │ │ └── models │ │ │ │ └── mod.rs │ │ ├── error.rs │ │ ├── lib.rs │ │ ├── main.rs │ │ └── utils │ │ │ ├── generator.rs │ │ │ └── mod.rs │ └── tauri.conf.json ├── src │ ├── app.rs │ ├── components │ │ ├── mod.rs │ │ └── toolbar.rs │ ├── icons │ │ ├── arrow_left_right_icon.rs │ │ ├── calendar_icon.rs │ │ ├── chevron.rs │ │ ├── cloud.rs │ │ ├── cog.rs │ │ ├── dots.rs │ │ ├── download_icon.rs │ │ ├── history_icon.rs │ │ ├── home_icon.rs │ │ ├── info_icon.rs │ │ ├── menu_icon.rs │ │ ├── mod.rs │ │ ├── moon_icon.rs │ │ ├── platform.rs │ │ ├── plus_icon.rs │ │ ├── scan_qr_icon.rs │ │ ├── share_icon.rs │ │ ├── sun_icon.rs │ │ ├── upload_icon.rs │ │ └── user_icon.rs │ ├── js_bindgen │ │ ├── Cargo.toml │ │ ├── api_request.js │ │ ├── api_request.rs │ │ ├── go_to_prev_location.js │ │ ├── go_to_prev_location.rs │ │ ├── is_active_route.js │ │ ├── is_active_route.rs │ │ ├── mod.rs │ │ ├── navigate.js │ │ ├── navigate.rs │ │ ├── window_location.js │ │ └── window_location.rs │ ├── layouts │ │ ├── default_layout.rs │ │ ├── desktop_layout.rs │ │ ├── mod.rs │ │ └── welcome_screen_layout.rs │ ├── main.rs │ ├── routes.rs │ └── screens │ │ ├── about.rs │ │ ├── ddwelcome │ │ ├── choose_platform.rs │ │ ├── default.rs │ │ ├── mod.rs │ │ ├── receive.rs │ │ ├── select_action.rs │ │ └── send.rs │ │ ├── history.rs │ │ ├── home.rs │ │ ├── mod.rs │ │ ├── settings.rs │ │ ├── share.rs │ │ └── welcome.rs └── tailwind.config.cjs ├── screenshots ├── .DS_Store ├── filesync-0.7.16.png ├── filesync-new.png ├── filesync.png ├── home-dark.png ├── home-light.png ├── mobile.png ├── music-dark.png ├── music-light.png ├── share-files-light.png └── share-files.png └── tmp ├── .zcrash └── design.zcrash /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opeolluwa/filesync/bfc91a48d5432ba8131c5a44cf1104d0fae2b182/.DS_Store -------------------------------------------------------------------------------- /.github/workflows/bundle.yml: -------------------------------------------------------------------------------- 1 | name: 'publish' 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - release 8 | 9 | jobs: 10 | publish-tauri: 11 | permissions: 12 | contents: write 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | include: 17 | - platform: 'macos-latest' # for Arm based macs (M1 and above). 18 | args: '--target aarch64-apple-darwin' 19 | - platform: 'macos-latest' # for Intel based macs. 20 | args: '--target x86_64-apple-darwin' 21 | - platform: 'ubuntu-22.04' 22 | args: '' 23 | - platform: 'windows-latest' 24 | args: '' 25 | 26 | runs-on: ${{ matrix.platform }} 27 | steps: 28 | - uses: actions/checkout@v4 29 | 30 | - name: install dependencies (ubuntu only) 31 | if: matrix.platform == 'ubuntu-22.04' # This must match the platform value defined above. 32 | run: | 33 | sudo apt-get update 34 | sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf 35 | 36 | - name: install Rust stable 37 | uses: dtolnay/rust-toolchain@stable # Set this to dtolnay/rust-toolchain@nightly 38 | with: 39 | # Those targets are only used on macos runners so it's in an `if` to slightly speed up windows and linux builds. 40 | targets: ${{ matrix.platform == 'macos-latest' && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }} 41 | 42 | - name: Rust cache 43 | uses: swatinem/rust-cache@v2 44 | with: 45 | workspaces: './tauri -> target' 46 | 47 | - name: install trunk 48 | run: cargo install --locked trunk 49 | 50 | - uses: tauri-apps/tauri-action@v0 51 | env: 52 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 53 | with: 54 | tagName: app-v__VERSION__ # the action automatically replaces \_\_VERSION\_\_ with the app version. 55 | releaseName: 'App v__VERSION__' 56 | releaseBody: 'See the assets to download this version and install.' 57 | releaseDraft: true 58 | prerelease: false 59 | args: ${{ matrix.args }} -------------------------------------------------------------------------------- /.github/workflows/changelog.yml: -------------------------------------------------------------------------------- 1 | name: Deploy 2 | 3 | on: 4 | push: 5 | tags: 6 | - v[0-9]+.[0-9]+.[0-9]+ 7 | 8 | jobs: 9 | deploy: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Checkout Code 14 | uses: actions/checkout@v3 15 | 16 | - name: Update CHANGELOG 17 | id: changelog 18 | uses: requarks/changelog-action@v1 19 | with: 20 | token: ${{ github.token }} 21 | tag: ${{ github.ref_name }} 22 | 23 | - name: Create Release 24 | uses: ncipollo/release-action@v1.12.0 25 | with: 26 | allowUpdates: true 27 | draft: false 28 | makeLatest: true 29 | name: ${{ github.ref_name }} 30 | body: ${{ steps.changelog.outputs.changes }} 31 | token: ${{ github.token }} 32 | 33 | - name: Commit CHANGELOG.md 34 | uses: stefanzweifel/git-auto-commit-action@v4 35 | with: 36 | branch: main 37 | commit_message: 'docs: update CHANGELOG.md for ${{ github.ref_name }} [skip ci]' 38 | file_pattern: CHANGELOG.md -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .bin -------------------------------------------------------------------------------- /.idea/filesync.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/material_theme_project_new.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 15 | 16 | 18 | 19 | 21 | 22 | 24 | { 25 | "lastFilter": { 26 | "state": "OPEN", 27 | "assignee": "opeolluwa" 28 | } 29 | } 30 | { 31 | "selectedUrlAndAccountId": { 32 | "url": "https://github.com/opeolluwa/filesync", 33 | "accountId": "1bdd2b45-e724-4bc4-8430-c312b88ee51d" 34 | } 35 | } 36 | 37 | 38 | 39 | 40 | 42 | 43 | 45 | 46 | 48 | { 49 | "associatedIndex": 6 50 | } 51 | 52 | 53 | 56 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 92 | 93 | 94 | 95 | 96 | 1715329945427 97 | 106 | 107 | 108 | 109 | 111 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opeolluwa/filesync/bfc91a48d5432ba8131c5a44cf1104d0fae2b182/.vscode/extensions.json -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": [ 3 | "Adefemi", 4 | "Adeoye", 5 | "bindgen", 6 | "filesync", 7 | "fullscreen", 8 | "leptos", 9 | "qrcode", 10 | "tauri" 11 | ], 12 | "java.configuration.updateBuildConfiguration": "interactive" 13 | } 14 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct - SendFile 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to make participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to a positive environment for our 15 | community include: 16 | 17 | * Demonstrating empathy and kindness toward other people 18 | * Being respectful of differing opinions, viewpoints, and experiences 19 | * Giving and gracefully accepting constructive feedback 20 | * Accepting responsibility and apologizing to those affected by our mistakes, 21 | and learning from the experience 22 | * Focusing on what is best not just for us as individuals, but for the 23 | overall community 24 | 25 | Examples of unacceptable behavior include: 26 | 27 | * The use of sexualized language or imagery, and sexual attention or 28 | advances 29 | * Trolling, insulting or derogatory comments, and personal or political attacks 30 | * Public or private harassment 31 | * Publishing others' private information, such as a physical or email 32 | address, without their explicit permission 33 | * Other conduct which could reasonably be considered inappropriate in a 34 | professional setting 35 | 36 | ## Our Responsibilities 37 | 38 | Project maintainers are responsible for clarifying and enforcing our standards of 39 | acceptable behavior and will take appropriate and fair corrective action in 40 | response to any instances of unacceptable behavior. 41 | 42 | Project maintainers have the right and responsibility to remove, edit, or reject 43 | comments, commits, code, wiki edits, issues, and other contributions that are 44 | not aligned to this Code of Conduct, or to ban 45 | temporarily or permanently any contributor for other behaviors that they deem 46 | inappropriate, threatening, offensive, or harmful. 47 | 48 | ## Scope 49 | 50 | This Code of Conduct applies within all community spaces, and also applies when 51 | an individual is officially representing the community in public spaces. 52 | Examples of representing our community include using an official e-mail address, 53 | posting via an official social media account, or acting as an appointed 54 | representative at an online or offline event. 55 | 56 | ## Enforcement 57 | 58 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 59 | reported to the community leaders responsible for enforcement at . 60 | All complaints will be reviewed and investigated promptly and fairly. 61 | 62 | All community leaders are obligated to respect the privacy and security of the 63 | reporter of any incident. 64 | 65 | ## Attribution 66 | 67 | This Code of Conduct is adapted from the [Contributor Covenant](https://contributor-covenant.org/), version 68 | [1.4](https://www.contributor-covenant.org/version/1/4/code-of-conduct/code_of_conduct.md) and 69 | [2.0](https://www.contributor-covenant.org/version/2/0/code_of_conduct/code_of_conduct.md), 70 | and was generated by [contributing-gen](https://github.com/bttger/contributing-gen). -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Adeoye Adefemi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FIleSync: WiFi File Sharing Application 2 | 3 | FileSync is a file-sharing application, targeting Linux, Mac, Windows and 4 | Android platforms 5 | ![screenshot](./screenshots/filesync-0.7.16.png) 6 | 7 | 8 | 9 | ## Disclaimer 10 | 11 | _⚠️ The application is still a work in progress_ 12 | To proceed with the preview anyway, see the 13 | [release page](https://github.com/opeolluwa/filesync/releases)_ 14 | 15 | ## Getting Started 16 | 17 | ### General Requirement 18 | 19 | To compile the application or modify it locally, you need the following 20 | dependencies: 21 | 22 | - [Rust v1.83 or greater](https://www.rust-lang.org/)' 23 | - [just](https://just.systems/) 24 | - [Android Studio](https://developer.android.com/studio?gad_source=1&gclid=CjwKCAjwnqK1BhBvEiwAi7o0XxODlu3Mk1cb2BoP1HV7g32vB5N37BVL5ab6OJ-3UAfpfkWmBM2nlRoCBfoQAvD_BwE&gclsrc=aw.ds) 25 | To build and debug the Android application to build reliable and efficient 26 | software. 27 | 28 | Once you have the dependencies installed, clone the project and install the 29 | required packages: 30 | 31 | ### Workspace Requirements 32 | 33 | The project may require some system requirements based on your platform see the 34 | necessary [prerequisite](https://v2.tauri.app/start/prerequisites/) for your 35 | operating system. 36 | 37 | ```sh 38 | git clone https://github.com/opeolluwa/filesync.git 39 | 40 | cd filesync 41 | 42 | just install-depenencies 43 | ``` 44 | 45 | ### Running the project 46 | 47 | To see all available commands 48 | 49 | ## Roadmap 50 | 51 | - [x] Adaptive UI 52 | - [x] Build an executable for the Mac operating system 53 | - [x] Build an executable for the Linux Operating system 54 | - [x] Build an executable for Window 55 | - [ ] Support mobile devices 56 | - [ ] Support dark mode 57 | - [ ] Add walkthrough after installation 58 | 59 | ## Acknowledgments 60 | 61 | - [Linux Wifi Hotspot](https://awesomeopensource.com/project/elangosundar/awesome-README-templateshttps://github.com/lakinduakash/linux-wifi-hotspot) 62 | - [File Streaming](https://github.com/tokio-rs/axum/tree/main/examples/stream-to-file) 63 | 64 | ## Contributing 65 | 66 | Contributions are always welcome! 67 | 68 | See [contributing.md](./CONTRIBUTING.md) for ways to get started. 69 | 70 | Please adhere to this project's [code of conduct](CODE_OF_CONDUCT.md). 71 | 72 | ## License 73 | 74 | This project is distributed under the [MIT License](./LICENSE) 75 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | -------------------------------------------------------------------------------- /android/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /android/.idea/.name: -------------------------------------------------------------------------------- 1 | filesync android -------------------------------------------------------------------------------- /android/.idea/AndroidProjectSystem.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /android/.idea/appInsightsSettings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 25 | 26 | -------------------------------------------------------------------------------- /android/.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /android/.idea/deploymentTargetDropDown.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /android/.idea/deploymentTargetSelector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /android/.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 18 | 19 | -------------------------------------------------------------------------------- /android/.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 77 | -------------------------------------------------------------------------------- /android/.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /android/.idea/material_theme_project_new.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /android/.idea/migrations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /android/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 10 | -------------------------------------------------------------------------------- /android/.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 17 | -------------------------------------------------------------------------------- /android/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /android/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /android/app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | alias(libs.plugins.androidApplication) 3 | alias(libs.plugins.jetbrainsKotlinAndroid) 4 | } 5 | 6 | android { 7 | namespace = "com.filesync.app" 8 | compileSdk = 35 9 | 10 | defaultConfig { 11 | applicationId = "com.filesync.app" 12 | minSdk = 24 13 | targetSdk = 34 14 | versionCode = 1 15 | versionName = "1.0" 16 | 17 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 18 | vectorDrawables { 19 | useSupportLibrary = true 20 | } 21 | } 22 | 23 | buildTypes { 24 | release { 25 | isMinifyEnabled = false 26 | proguardFiles( 27 | getDefaultProguardFile("proguard-android-optimize.txt"), 28 | "proguard-rules.pro" 29 | ) 30 | } 31 | } 32 | compileOptions { 33 | sourceCompatibility = JavaVersion.VERSION_1_8 34 | targetCompatibility = JavaVersion.VERSION_1_8 35 | } 36 | kotlinOptions { 37 | jvmTarget = "1.8" 38 | } 39 | buildFeatures { 40 | compose = true 41 | } 42 | composeOptions { 43 | kotlinCompilerExtensionVersion = "1.5.1" 44 | } 45 | packaging { 46 | resources { 47 | excludes += "/META-INF/{AL2.0,LGPL2.1}" 48 | } 49 | } 50 | } 51 | 52 | dependencies { 53 | implementation("com.journeyapps:zxing-android-embedded:4.3.0") 54 | implementation("com.google.zxing:core:3.4.1 ") 55 | implementation("androidx.core:core-ktx:1.12.0") 56 | implementation("com.github.bumptech.glide:glide:4.16.0") 57 | implementation(libs.androidx.core.ktx) 58 | implementation(libs.androidx.lifecycle.runtime.ktx) 59 | implementation(libs.androidx.activity.compose) 60 | implementation(platform(libs.androidx.compose.bom)) 61 | implementation(libs.androidx.ui) 62 | implementation(libs.androidx.ui.graphics) 63 | implementation(libs.androidx.ui.tooling.preview) 64 | implementation(libs.androidx.material3) 65 | implementation(libs.androidx.documentfile) 66 | implementation(libs.androidx.recyclerview) 67 | implementation(libs.androidx.appcompat) 68 | implementation(libs.androidx.cardview) 69 | testImplementation(libs.junit) 70 | androidTestImplementation(libs.androidx.junit) 71 | androidTestImplementation(libs.androidx.espresso.core) 72 | androidTestImplementation(platform(libs.androidx.compose.bom)) 73 | androidTestImplementation(libs.androidx.ui.test.junit4) 74 | debugImplementation(libs.androidx.ui.tooling) 75 | debugImplementation(libs.androidx.ui.test.manifest) 76 | } -------------------------------------------------------------------------------- /android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /android/app/src/androidTest/java/com/filesync/app/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.filesync.app 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.filesync.app", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 22 | 23 | 24 | 35 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/filesync/app/components/Buttons.kt: -------------------------------------------------------------------------------- 1 | package com.filesync.app.components 2 | 3 | import androidx.compose.material3.Button 4 | import androidx.compose.material3.Text 5 | import androidx.compose.runtime.Composable 6 | 7 | @Composable 8 | fun FilledButtonExample(onClick: () -> Unit) { 9 | Button(onClick = { onClick() }) { 10 | Text("Filled") 11 | } 12 | } -------------------------------------------------------------------------------- /android/app/src/main/java/com/filesync/app/components/FileItem.kt: -------------------------------------------------------------------------------- 1 | package com.filesync.app.components 2 | 3 | import java.io.File 4 | 5 | data class FileItem( 6 | val name: String, 7 | val path: String, 8 | val isDirectory: Boolean, 9 | val file: File // This is the property causing the error 10 | ) 11 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/filesync/app/components/PulsingCircle.kt: -------------------------------------------------------------------------------- 1 | package com.filesync.app.components 2 | 3 | import androidx.compose.animation.core.LinearEasing 4 | import androidx.compose.animation.core.RepeatMode 5 | import androidx.compose.animation.core.animateFloat 6 | import androidx.compose.animation.core.infiniteRepeatable 7 | import androidx.compose.animation.core.keyframes 8 | import androidx.compose.animation.core.rememberInfiniteTransition 9 | import androidx.compose.foundation.layout.Box 10 | import androidx.compose.foundation.layout.fillMaxSize 11 | import androidx.compose.runtime.Composable 12 | import androidx.compose.ui.Alignment 13 | import androidx.compose.ui.Modifier 14 | import androidx.compose.ui.unit.dp 15 | import com.filesync.app.ui.theme.Accent 16 | import androidx.compose.foundation.Canvas 17 | import androidx.compose.foundation.Image 18 | import androidx.compose.foundation.layout.fillMaxWidth 19 | import androidx.compose.foundation.layout.height 20 | import androidx.compose.foundation.layout.padding 21 | import androidx.compose.foundation.layout.size 22 | import androidx.compose.foundation.layout.width 23 | import androidx.compose.ui.res.painterResource 24 | import com.filesync.app.R 25 | import com.filesync.app.ui.theme.Accent700 26 | 27 | @Composable() 28 | fun PulsingCirclesAnimation() { 29 | val infiniteTransition = rememberInfiniteTransition(label = ""); 30 | val sizes = listOf(100.dp, 150.dp, 200.dp); 31 | val scaleFactors = List(sizes.size) { index -> 32 | infiniteTransition.animateFloat( 33 | initialValue = 1f, 34 | targetValue = 1.4f, 35 | animationSpec = infiniteRepeatable( 36 | animation = keyframes { 37 | durationMillis = 6000 38 | 0.0f at 0 with LinearEasing 39 | 0.2f at (1000 + (index + 1000)) with LinearEasing 40 | 1.2f at (2000 + (index + 1000)) with LinearEasing 41 | 1.4f at (4000 + (index + 1000)) with LinearEasing 42 | 1.6f at 6000 with LinearEasing 43 | }, 44 | repeatMode = RepeatMode.Restart 45 | ), 46 | label = "" 47 | ) 48 | 49 | } 50 | 51 | val alphaValues = List(sizes.size) { index -> 52 | infiniteTransition.animateFloat( 53 | initialValue = 0f, 54 | targetValue = 1f, 55 | animationSpec = infiniteRepeatable( 56 | animation = keyframes { 57 | durationMillis = 6000 58 | 0.0f at 0 with LinearEasing 59 | 0.2f at (2000 + (index + 1000)) with LinearEasing 60 | 0.1f at (4000 + (index + 1000)) with LinearEasing 61 | 0.0f at 6000 with LinearEasing 62 | }, 63 | repeatMode = RepeatMode.Restart, 64 | ), 65 | label = "" 66 | 67 | ) 68 | } 69 | 70 | Box( 71 | contentAlignment = Alignment.Center, 72 | modifier = Modifier 73 | .fillMaxWidth() 74 | .height(200.dp) 75 | .padding(top = 48.dp) 76 | ) { 77 | Canvas(modifier = Modifier.fillMaxSize()) { 78 | sizes.forEachIndexed { index, size -> 79 | val scale = scaleFactors[index].value 80 | val alpha = alphaValues[index].value 81 | val radius = size.toPx() / 2 82 | 83 | drawCircle( 84 | color = Accent, 85 | radius = radius * scale, 86 | center = center, 87 | alpha = alpha 88 | ) 89 | } 90 | 91 | drawCircle( 92 | color = Accent, 93 | radius = 60f, 94 | center = center 95 | ) 96 | 97 | 98 | } 99 | Image( 100 | painter = painterResource(id = R.drawable.signal), 101 | contentDescription = "Custom Vector Icon", 102 | modifier = Modifier.size(24.dp) 103 | ) 104 | } 105 | } -------------------------------------------------------------------------------- /android/app/src/main/java/com/filesync/app/components/WifiConfigModal.kt: -------------------------------------------------------------------------------- 1 | package com.filesync.app.components 2 | 3 | import androidx.compose.foundation.layout.Column 4 | import androidx.compose.foundation.layout.fillMaxWidth 5 | import androidx.compose.foundation.layout.padding 6 | import androidx.compose.material.icons.Icons 7 | import androidx.compose.material.icons.filled.Info 8 | import androidx.compose.material3.AlertDialog 9 | import androidx.compose.material3.Icon 10 | import androidx.compose.material3.Text 11 | import androidx.compose.material3.TextButton 12 | import androidx.compose.runtime.Composable 13 | import androidx.compose.ui.Modifier 14 | import androidx.compose.ui.text.font.FontWeight 15 | import androidx.compose.ui.text.style.TextAlign 16 | import androidx.compose.ui.unit.dp 17 | import androidx.compose.ui.unit.sp 18 | 19 | @Composable 20 | fun WifiConfigModal( 21 | wifiSsid: String, 22 | wifiPassword: String, 23 | onConfirm: () -> Unit, 24 | onDismiss: () -> Unit, 25 | modifier: Modifier = Modifier 26 | ) { 27 | AlertDialog( 28 | icon = { 29 | Icon( 30 | imageVector = Icons.Default.Info, 31 | contentDescription = "Info" 32 | ) 33 | }, 34 | title = { 35 | Text( 36 | text = "Connect to Wi-Fi Hotspot", 37 | fontSize = 18.sp, 38 | fontWeight = FontWeight.SemiBold, 39 | textAlign = TextAlign.Center, 40 | modifier = Modifier.fillMaxWidth() 41 | ) 42 | }, 43 | text = { 44 | Column(modifier = modifier.padding(top = 8.dp)) { 45 | Text(text = "SSID: $wifiSsid", fontSize = 14.sp) 46 | Text(text = "Password: $wifiPassword", fontSize = 14.sp) 47 | } 48 | }, 49 | onDismissRequest = onDismiss, 50 | confirmButton = { 51 | TextButton(onClick = onConfirm) { 52 | Text("Confirm") 53 | } 54 | }, 55 | dismissButton = { 56 | TextButton(onClick = onDismiss) { 57 | Text("Dismiss") 58 | } 59 | } 60 | ) 61 | } 62 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/filesync/app/hooks/DefaultFailureListener.java: -------------------------------------------------------------------------------- 1 | package com.filesync.app.hooks; 2 | 3 | 4 | 5 | import android.app.Activity; 6 | import android.os.Build; 7 | import android.widget.Toast; 8 | 9 | public class DefaultFailureListener implements APManager.OnFailureListener { 10 | public static final int REQUEST_CODE_WRITE_SETTINGS=12; 11 | private final Activity activity; 12 | 13 | public DefaultFailureListener(Activity activity) { 14 | this.activity = activity; 15 | } 16 | 17 | @Override 18 | public void onFailure(int failureCode,Exception e) { 19 | APManager.Utils utils = APManager.getApManager(activity).getUtils(); 20 | switch (failureCode) { 21 | case APManager.ERROR_DISABLE_HOTSPOT: 22 | Toast.makeText(activity, "DISABLE HOTSPOT", Toast.LENGTH_LONG).show(); 23 | activity.startActivity(utils.getTetheringSettingIntent()); 24 | break; 25 | case APManager.ERROR_DISABLE_WIFI: 26 | Toast.makeText(activity, "DISCONNECT WIFI", Toast.LENGTH_LONG).show(); 27 | utils.askForDisableWifi(activity); 28 | break; 29 | case APManager.ERROR_GPS_PROVIDER_DISABLED: 30 | Toast.makeText(activity, "ENABLE GPS", Toast.LENGTH_LONG).show(); 31 | utils.askForGpsProvider(activity); 32 | break; 33 | case APManager.ERROR_LOCATION_PERMISSION_DENIED: 34 | Toast.makeText(activity, "ALLOW LOCATION PERMISSION", Toast.LENGTH_LONG).show(); 35 | utils.askLocationPermission(activity, REQUEST_CODE_WRITE_SETTINGS); 36 | break; 37 | case APManager.ERROR_WRITE_SETTINGS_PERMISSION_REQUIRED: 38 | Toast.makeText(activity, "ALLOW WRITE SYSTEM SETTINGS PERMISSION", Toast.LENGTH_LONG).show(); 39 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 40 | utils.askWriteSettingPermission(activity); 41 | } 42 | break; 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /android/app/src/main/java/com/filesync/app/screens/MainScreen.kt: -------------------------------------------------------------------------------- 1 | 2 | import androidx.compose.foundation.BorderStroke 3 | import androidx.compose.foundation.Image 4 | import androidx.compose.foundation.clickable 5 | import androidx.compose.foundation.layout.* 6 | import androidx.compose.foundation.shape.RoundedCornerShape 7 | import androidx.compose.material3.* 8 | import androidx.compose.runtime.* 9 | import androidx.compose.ui.Alignment 10 | import androidx.compose.ui.Modifier 11 | import androidx.compose.ui.draw.shadow 12 | import androidx.compose.ui.graphics.Color 13 | import androidx.compose.ui.res.painterResource 14 | import androidx.compose.ui.text.font.FontWeight 15 | import androidx.compose.ui.text.style.TextAlign 16 | import androidx.compose.ui.unit.dp 17 | import androidx.compose.ui.unit.sp 18 | import com.filesync.app.R 19 | import com.filesync.app.components.PulsingCirclesAnimation 20 | import com.filesync.app.ui.theme.Accent 21 | import kotlinx.coroutines.launch 22 | 23 | @OptIn(ExperimentalMaterial3Api::class) 24 | @Composable 25 | fun MainScreen(qrResult: String, onScanClick: () -> Unit, wifiSsid: String, wifiPassword: String) { 26 | val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) 27 | val coroutineScope = rememberCoroutineScope() 28 | val showSheet = remember { mutableStateOf(true) } 29 | 30 | // Show bottom sheet if triggered 31 | if (showSheet.value) { 32 | ModalBottomSheet( 33 | onDismissRequest = { showSheet.value = false }, 34 | sheetState = sheetState, 35 | containerColor = MaterialTheme.colorScheme.surface, 36 | contentColor = MaterialTheme.colorScheme.onSurface, 37 | ) { 38 | Column( 39 | modifier = Modifier 40 | .fillMaxWidth() 41 | .padding(24.dp), 42 | horizontalAlignment = Alignment.CenterHorizontally 43 | ) { 44 | Text( 45 | text = "Connect to Wifi Hotspot", 46 | fontSize = 18.sp, 47 | fontWeight = FontWeight.Bold 48 | ) 49 | Spacer(modifier = Modifier.height(12.dp)) 50 | Column(modifier = Modifier) { 51 | Text(text = "SSID: $wifiSsid", fontSize = 16.sp ) 52 | Text(text = "Password: $wifiPassword", fontSize = 16.sp) 53 | 54 | } 55 | Spacer(modifier = Modifier.height(24.dp)) 56 | Button( 57 | onClick = { 58 | coroutineScope.launch { 59 | sheetState.hide() 60 | showSheet.value = false 61 | } 62 | }, 63 | shape = RoundedCornerShape(5.dp), 64 | colors = ButtonDefaults.buttonColors( 65 | containerColor = Accent, 66 | contentColor = Color.White 67 | ), 68 | border = BorderStroke(1.dp, Accent), 69 | modifier = Modifier 70 | .fillMaxWidth(0.7f) 71 | .padding(top = 12.dp) 72 | .height(48.dp) 73 | ) { 74 | 75 | Text("Dismiss") 76 | // Image( 77 | // painter = painterResource(id = R.drawable.qr_code), 78 | // contentDescription = "QR Code Icon", 79 | // modifier = Modifier.size(24.dp) 80 | // ) 81 | } 82 | } 83 | } 84 | } 85 | 86 | Surface( 87 | modifier = Modifier 88 | .fillMaxSize() 89 | .clickable { 90 | showSheet.value = true 91 | coroutineScope.launch { sheetState.show() } 92 | } 93 | ) { 94 | Box { 95 | Column( 96 | modifier = Modifier 97 | .fillMaxSize() 98 | .padding(16.dp) 99 | ) { 100 | Box(modifier = Modifier.weight(1f)) { 101 | Column( 102 | modifier = Modifier 103 | .align(Alignment.Center) 104 | .padding(bottom = 50.dp), 105 | horizontalAlignment = Alignment.CenterHorizontally 106 | ) { 107 | PulsingCirclesAnimation() 108 | } 109 | } 110 | 111 | Column( 112 | horizontalAlignment = Alignment.CenterHorizontally, 113 | modifier = Modifier 114 | .fillMaxWidth() 115 | .padding(bottom = 48.dp) 116 | ) { 117 | Text( 118 | "Waiting for connection...", 119 | fontSize = 16.sp, 120 | fontWeight = FontWeight.Medium, 121 | textAlign = TextAlign.Center 122 | ) 123 | 124 | Button( 125 | onClick = onScanClick, 126 | shape = RoundedCornerShape(5.dp), 127 | colors = ButtonDefaults.buttonColors( 128 | containerColor = Accent, 129 | contentColor = Color.White 130 | ), 131 | border = BorderStroke(1.dp, Accent), 132 | modifier = Modifier 133 | .fillMaxWidth(0.7f) 134 | .padding(top = 12.dp) 135 | .height(48.dp) 136 | .shadow(8.dp, RoundedCornerShape(50), clip = false) 137 | ) { 138 | Text("Scan QR code", fontSize = 14.sp) 139 | Image( 140 | painter = painterResource(id = R.drawable.qr_code), 141 | contentDescription = "QR Code Icon", 142 | modifier = Modifier.size(24.dp) 143 | ) 144 | } 145 | } 146 | } 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/filesync/app/ui/theme/Color.kt: -------------------------------------------------------------------------------- 1 | package com.filesync.app.ui.theme 2 | 3 | import androidx.compose.ui.graphics.Color 4 | 5 | 6 | val PurpleGrey80 = Color(0xFFCCC2DC) 7 | val Pink80 = Color(0xFFEFB8C8) 8 | 9 | 10 | val PurpleGrey40 = Color(0xFF625b71) 11 | val Pink40 = Color(0xFF7D5260) 12 | 13 | val Accent = Color(0xFF3074F5); 14 | val Accent50 = Color(0xFFDFEAFD) 15 | val Accent100 = Color(0xFFCCDDFD) 16 | val Accent200 = Color(0xFFA5C2FB) 17 | val Accent300 = Color(0xFF7EA8F9) 18 | val Accent400 = Color(0xFF578EF7) 19 | val Accent500 = Color(0xFF3074F5) 20 | val Accent600 = Color(0xFF0B55E2) 21 | val Accent700 = Color(0xFF0841AC) 22 | val Accent800 = Color(0xFF062D77) 23 | val Accent900 = Color(0xFF031941) 24 | val Accent950 = Color(0xFF020F27) 25 | val AccentDefault = Accent500 26 | val AccentDark = Color(0xFF1D232A) 27 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/filesync/app/ui/theme/Theme.kt: -------------------------------------------------------------------------------- 1 | package com.filesync.app.ui.theme 2 | 3 | import android.app.Activity 4 | import android.os.Build 5 | import androidx.compose.foundation.isSystemInDarkTheme 6 | import androidx.compose.material3.MaterialTheme 7 | import androidx.compose.material3.darkColorScheme 8 | import androidx.compose.material3.dynamicDarkColorScheme 9 | import androidx.compose.material3.dynamicLightColorScheme 10 | import androidx.compose.material3.lightColorScheme 11 | import androidx.compose.runtime.Composable 12 | import androidx.compose.runtime.SideEffect 13 | import androidx.compose.ui.graphics.Color 14 | import androidx.compose.ui.graphics.toArgb 15 | import androidx.compose.ui.platform.LocalContext 16 | import androidx.compose.ui.platform.LocalView 17 | import androidx.core.view.WindowCompat 18 | 19 | private val DarkColorScheme = darkColorScheme( 20 | primary = Accent, 21 | secondary = PurpleGrey80, 22 | tertiary = Pink80 23 | ) 24 | 25 | private val LightColorScheme = lightColorScheme( 26 | primary = Accent, 27 | secondary = PurpleGrey40, 28 | tertiary = Pink40 29 | 30 | ) 31 | 32 | @Composable 33 | fun FileSyncAndroidTheme( 34 | darkTheme: Boolean = isSystemInDarkTheme(), 35 | // Dynamic color is available on Android 12+ 36 | dynamicColor: Boolean = true, 37 | content: @Composable () -> Unit 38 | ) { 39 | val colorScheme = when { 40 | dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { 41 | val context = LocalContext.current 42 | if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) 43 | } 44 | 45 | darkTheme -> DarkColorScheme 46 | else -> LightColorScheme 47 | } 48 | val view = LocalView.current 49 | if (!view.isInEditMode) { 50 | SideEffect { 51 | val window = (view.context as Activity).window 52 | window.statusBarColor = Color(0xFF3074F5).toArgb(); 53 | WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme 54 | } 55 | } 56 | 57 | MaterialTheme( 58 | colorScheme = colorScheme, 59 | typography = Typography, 60 | content = content 61 | ) 62 | } -------------------------------------------------------------------------------- /android/app/src/main/java/com/filesync/app/ui/theme/Type.kt: -------------------------------------------------------------------------------- 1 | package com.filesync.app.ui.theme 2 | 3 | import androidx.compose.material3.Typography 4 | import androidx.compose.ui.text.TextStyle 5 | import androidx.compose.ui.text.font.FontFamily 6 | import androidx.compose.ui.text.font.FontWeight 7 | import androidx.compose.ui.unit.sp 8 | 9 | // Set of Material typography styles to start with 10 | val Typography = Typography( 11 | bodyLarge = TextStyle( 12 | fontFamily = FontFamily.Default, 13 | fontWeight = FontWeight.Normal, 14 | fontSize = 16.sp, 15 | lineHeight = 24.sp, 16 | letterSpacing = 0.5.sp 17 | ) 18 | /* Other default text styles to override 19 | titleLarge = TextStyle( 20 | fontFamily = FontFamily.Default, 21 | fontWeight = FontWeight.Normal, 22 | fontSize = 22.sp, 23 | lineHeight = 28.sp, 24 | letterSpacing = 0.sp 25 | ), 26 | labelSmall = TextStyle( 27 | fontFamily = FontFamily.Default, 28 | fontWeight = FontWeight.Medium, 29 | fontSize = 11.sp, 30 | lineHeight = 16.sp, 31 | letterSpacing = 0.5.sp 32 | ) 33 | */ 34 | ) -------------------------------------------------------------------------------- /android/app/src/main/res/assets/svg/close.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /android/app/src/main/res/assets/svg/lock.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /android/app/src/main/res/assets/svg/qr-scanner.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /android/app/src/main/res/assets/svg/qr_code.svg: -------------------------------------------------------------------------------- 1 | 3 | 5 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/res/assets/svg/signal.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /android/app/src/main/res/assets/svg/wifi.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/blue_semicircle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/cancel.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/copy.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/custom_card_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/delete.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/details.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/folder.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/gray_semisircle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/green_semicircle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/image.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opeolluwa/filesync/bfc91a48d5432ba8131c5a44cf1104d0fae2b182/android/app/src/main/res/drawable/loading.gif -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/lock.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/move.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/mp3.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/mp4.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/paste.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/pdf.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/progress_bar_custom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/purple_semicirlce.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/qr_code.xml: -------------------------------------------------------------------------------- 1 | 6 | 13 | 20 | 21 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/qr_scanner.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/rename.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/selectall.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/selected_item_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/semi_circle_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/signal.xml: -------------------------------------------------------------------------------- 1 | 6 | 13 | 14 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/storage.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/text.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/wifi.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/yellow_semisircle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android/app/src/main/res/layout/item_file.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 11 | 12 | 18 | 19 | 20 | 25 | 26 | 36 | 37 | 38 | 45 | 46 | 47 | 48 | 49 | 50 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opeolluwa/filesync/bfc91a48d5432ba8131c5a44cf1104d0fae2b182/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opeolluwa/filesync/bfc91a48d5432ba8131c5a44cf1104d0fae2b182/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opeolluwa/filesync/bfc91a48d5432ba8131c5a44cf1104d0fae2b182/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opeolluwa/filesync/bfc91a48d5432ba8131c5a44cf1104d0fae2b182/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opeolluwa/filesync/bfc91a48d5432ba8131c5a44cf1104d0fae2b182/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opeolluwa/filesync/bfc91a48d5432ba8131c5a44cf1104d0fae2b182/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opeolluwa/filesync/bfc91a48d5432ba8131c5a44cf1104d0fae2b182/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opeolluwa/filesync/bfc91a48d5432ba8131c5a44cf1104d0fae2b182/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opeolluwa/filesync/bfc91a48d5432ba8131c5a44cf1104d0fae2b182/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opeolluwa/filesync/bfc91a48d5432ba8131c5a44cf1104d0fae2b182/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /android/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | FileSync 3 | open camera 4 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | -------------------------------------------------------------------------------- /android/app/src/main/res/xml/backup_rules.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/xml/data_extraction_rules.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | 13 | 19 | -------------------------------------------------------------------------------- /android/app/src/test/java/com/filesync/app/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.filesync.app 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /android/build.gradle.kts: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | plugins { 3 | alias(libs.plugins.androidApplication) apply false 4 | alias(libs.plugins.jetbrainsKotlinAndroid) apply false 5 | } -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. For more details, visit 12 | # https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app's APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Kotlin code style for this project: "official" or "obsolete": 19 | kotlin.code.style=official 20 | # Enables namespacing of each library's R class so that its R class includes only the 21 | # resources declared in the library itself and none from the library's dependencies, 22 | # thereby reducing the size of the R class for that library 23 | android.nonTransitiveRClass=true -------------------------------------------------------------------------------- /android/gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | agp = "8.9.0" 3 | kotlin = "1.9.0" 4 | coreKtx = "1.15.0" 5 | junit = "4.13.2" 6 | junitVersion = "1.2.1" 7 | espressoCore = "3.6.1" 8 | lifecycleRuntimeKtx = "2.7.0" 9 | activityCompose = "1.7.0" 10 | composeBom = "2023.08.00" 11 | documentfile = "1.1.0" 12 | recyclerview = "1.4.0" 13 | appcompat = "1.7.0" 14 | cardview = "1.0.0" 15 | 16 | [libraries] 17 | androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } 18 | junit = { group = "junit", name = "junit", version.ref = "junit" } 19 | androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } 20 | androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } 21 | androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" } 22 | androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" } 23 | androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" } 24 | androidx-ui = { group = "androidx.compose.ui", name = "ui" } 25 | androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" } 26 | androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" } 27 | androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" } 28 | androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } 29 | androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" } 30 | androidx-material3 = { group = "androidx.compose.material3", name = "material3" } 31 | androidx-documentfile = { group = "androidx.documentfile", name = "documentfile", version.ref = "documentfile" } 32 | androidx-recyclerview = { group = "androidx.recyclerview", name = "recyclerview", version.ref = "recyclerview" } 33 | androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } 34 | androidx-cardview = { group = "androidx.cardview", name = "cardview", version.ref = "cardview" } 35 | 36 | [plugins] 37 | androidApplication = { id = "com.android.application", version.ref = "agp" } 38 | jetbrainsKotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } 39 | 40 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opeolluwa/filesync/bfc91a48d5432ba8131c5a44cf1104d0fae2b182/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Mar 20 23:56:21 WAT 2025 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /android/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | google { 4 | content { 5 | includeGroupByRegex("com\\.android.*") 6 | includeGroupByRegex("com\\.google.*") 7 | includeGroupByRegex("androidx.*") 8 | } 9 | } 10 | mavenCentral() 11 | gradlePluginPortal() 12 | } 13 | } 14 | dependencyResolutionManagement { 15 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 16 | repositories { 17 | google() 18 | mavenCentral() 19 | } 20 | } 21 | 22 | rootProject.name = "filesync android" 23 | include(":app") 24 | -------------------------------------------------------------------------------- /android/signal.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opeolluwa/filesync/bfc91a48d5432ba8131c5a44cf1104d0fae2b182/app-icon.png -------------------------------------------------------------------------------- /desktop/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /desktop/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | 13 | # MSVC Windows builds of rustc generate these, which store debugging information 14 | *.pdb 15 | 16 | node_modules 17 | 18 | 19 | dist -------------------------------------------------------------------------------- /desktop/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /desktop/.idea/desktop.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /desktop/.idea/material_theme_project_new.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | -------------------------------------------------------------------------------- /desktop/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /desktop/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /desktop/.taurignore: -------------------------------------------------------------------------------- 1 | /src 2 | /public 3 | /Cargo.toml -------------------------------------------------------------------------------- /desktop/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "filesync-ui" 3 | version = "0.5.0" 4 | edition = "2021" 5 | 6 | [workspace] 7 | members = ["src-tauri", "server", "shared"] 8 | 9 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 10 | [dependencies] 11 | console_error_panic_hook = { workspace = true } 12 | js-sys = { workspace = true } 13 | leptos = { workspace = true, features = ["csr"] } 14 | leptos-qr = { workspace = true } 15 | leptos-use = { workspace = true } 16 | leptos_router = { workspace = true } 17 | leptos_toaster = { workspace = true, features = ["csr"] } 18 | serde = { workspace = true, features = ["derive"] } 19 | serde-wasm-bindgen = { workspace = true } 20 | thaw = { workspace = true, features = ["csr"] } 21 | wasm-bindgen = { workspace = true } 22 | wasm-bindgen-futures = { workspace = true } 23 | web-sys = { workspace = true } 24 | shared.workspace = true 25 | 26 | 27 | [workspace.dependencies] 28 | thaw = { version = "0.4.0-beta-5", features = ["csr"] } 29 | leptos = { version = "0.7.0", features = ["csr"] } 30 | console_error_panic_hook = "0.1.7" 31 | js-sys = "0.3" 32 | serde = "1" 33 | serde-wasm-bindgen = "0.6" 34 | serde_json = "1" 35 | tauri = "2" 36 | tauri-build = "2" 37 | tauri-plugin-os = "2" 38 | tauri-plugin-shell = "2" 39 | wasm-bindgen = "0.2" 40 | wasm-bindgen-futures = "0.4" 41 | leptos_router = { version = "0.7.0" } 42 | leptos-use = "0.13.11" 43 | leptos_toaster = "0.1.7" 44 | web-sys = "0.3.77" 45 | leptos-qr = { git = "https://github.com/opeolluwa/leptos-qr", version = "0.2.0" } 46 | embedded_server = { path = "server" } 47 | thiserror = "2.0.12" 48 | shared = { path = "shared" } 49 | -------------------------------------------------------------------------------- /desktop/README.md: -------------------------------------------------------------------------------- 1 | # Project Title 2 | - [Description](#description) 3 | - [Getting Started](#getting-started) 4 | - [Dependencies](#dependencies) 5 | - [Installing](#installing) 6 | - [Executing program](#executing-program) 7 | - [Documentation](#documentation) 8 | - [Help](#help) 9 | - [Authors](#authors) 10 | - [Version History](#version-history) 11 | - [License](#license) 12 | - [Acknowledgments](#acknowledgments) 13 | 14 | ## Description 15 | 16 | Project description 17 | 18 | ## Getting Started 19 | 20 | ### Dependencies 21 | 22 | - Describe any prerequisites, libraries, OS version, etc., needed before installing program. 23 | - ex. Windows 10 24 | 25 | ### Installing 26 | 27 | - How/where to download your program 28 | - Any modifications needed to be made to files/folders 29 | 30 | ### Executing program 31 | 32 | - How to run the program 33 | - Step-by-step bullets 34 | 35 | ``` 36 | code blocks for commands 37 | ``` 38 | 39 | ## Documentation 40 | 41 | Describe any special instructions that are necessary to install a software package on your computer (if applicable). 42 | 43 | ## Help 44 | 45 | Any advise for common problems or issues. 46 | 47 | ``` 48 | command to run if program contains helper info 49 | ``` 50 | 51 | ## Authors 52 | 53 | Contributors names and contact info 54 | 55 | ex. Dominique Pizzie 56 | ex. [@DomPizzie](https://twitter.com/dompizzie) 57 | 58 | ## Version History 59 | 60 | - 0.2 61 | - Various bug fixes and optimizations 62 | - See [commit change]() or See [release history]() 63 | - 0.1 64 | - Initial Release 65 | 66 | ## License 67 | 68 | This project is licensed under the [NAME HERE] License - see the LICENSE.md file for details 69 | 70 | ## Acknowledgments 71 | 72 | Inspiration, code snippets, etc. 73 | -------------------------------------------------------------------------------- /desktop/Trunk.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "./index.html" 3 | filehash = false 4 | minify = "on_release" 5 | 6 | [watch] 7 | ignore = ["./src-tauri"] 8 | 9 | [serve] 10 | port = 1420 11 | open = false 12 | ws_protocol = "ws" 13 | 14 | -------------------------------------------------------------------------------- /desktop/app-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opeolluwa/filesync/bfc91a48d5432ba8131c5a44cf1104d0fae2b182/desktop/app-icon.png -------------------------------------------------------------------------------- /desktop/assets/folder-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opeolluwa/filesync/bfc91a48d5432ba8131c5a44cf1104d0fae2b182/desktop/assets/folder-icon.png -------------------------------------------------------------------------------- /desktop/assets/style/main.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | * { 6 | user-select: none !important; 7 | } 8 | 9 | :root { 10 | font-family: Inter, Avenir, Helvetica, Arial, sans-serif; 11 | font-size: 16px; 12 | line-height: 24px; 13 | font-weight: 400; 14 | font-synthesis: none; 15 | text-rendering: optimizeLegibility; 16 | -webkit-font-smoothing: antialiased; 17 | -moz-osx-font-smoothing: grayscale; 18 | -webkit-text-size-adjust: 100%; 19 | } 20 | 21 | .max-contemt { 22 | width: 100% !important; 23 | height: 100% !important; 24 | } 25 | 26 | 27 | .fab { 28 | @apply size-10 p-[10px] border-transparent text-white dark:text-gray-400 bg-app shadow-xl shadow-gray-300 fixed rounded-full bottom-10 right-4; 29 | } -------------------------------------------------------------------------------- /desktop/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Filesync 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /desktop/justfile: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | alias w:= watch 4 | alias b:= build 5 | alias install := install-dependencies 6 | alias pack := package 7 | 8 | APP_NAME := "filesync" 9 | APP_VERSION :="0.7.15" 10 | MINIMUM_STABLE_RUST_VERSION :="1.83.0" 11 | BINARIES_PATH := "bin" 12 | EXPORT_PATH := "packages" 13 | SUPPORTED_PLATFORM :="android ios macos" 14 | 15 | 16 | default: 17 | @just --list --list-heading $'Available commands\n' 18 | 19 | [doc('Install the application dependencies')] 20 | install-dependencies: 21 | echo "Installing dependencies" 22 | cargo install trunk --locked 23 | rustup target add wasm32-unknown-unknown 24 | 25 | 26 | [doc('Lint')] 27 | fmt: 28 | cargo fmt 29 | cargo clippy 30 | leptosfmt . 31 | cargo sort -w 32 | cargo group-imports --fix 33 | 34 | [doc('Run the application in watch mode')] 35 | watch target: 36 | #!/usr/bin/env sh 37 | export JAVA_HOME="/Applications/Android Studio.app/Contents/jbr/Contents/Home" 38 | export ANDROID_HOME="$HOME/Library/Android/sdk" 39 | export NDK_HOME="$ANDROID_HOME/ndk/$(ls -1 $ANDROID_HOME/ndk)" 40 | # export RUSTFLAGS='-C target-feature=+atomics,+bulk-memory,+mutable-globals' 41 | if [ {{target}} = "android" ]; then 42 | cargo tauri android dev 43 | elif [ {{target}} = "ios" ]; then 44 | cargo tauri ios dev 45 | elif [ {{target}} = "styles" ]; then 46 | npx tailwindcss -i ./assets/style/main.css -o ./assets/style/app.css --watch --minify 47 | else 48 | cargo tauri dev 49 | fi 50 | 51 | [doc('build the application ')] 52 | [group('watch')] 53 | build target: 54 | #!/usr/bin/env sh 55 | export ANDROID_HOME="$HOME/Library/Android/sdk" 56 | export NDK_HOME="$ANDROID_HOME/ndk/$(ls -1 $ANDROID_HOME/ndk)" 57 | if [ {{target}} = "all" ]; then 58 | for platform in {{SUPPORTED_PLATFORM}} 59 | do 60 | echo ">>>>>>>>>> Building $platform \n" 61 | just build $platform 62 | echo "Build completed" 63 | done 64 | elif [ {{target}} = "android" ]; then 65 | cargo tauri android build --apk 66 | elif [ {{target}} = "ios" ]; then 67 | cargo tauri ios build --aab 68 | else 69 | cargo tauri build 70 | fi 71 | 72 | 73 | [doc("Export binaries into $PWD/bin")] 74 | ship target: 75 | #!/usr/bin/env sh 76 | # mkdir bin 77 | if [ {{target}} = "all" ]; then 78 | for platform in {{SUPPORTED_PLATFORM}} 79 | do 80 | just export $platform 81 | echo "Export completed" 82 | done 83 | elif [ {{target}} = "android" ]; then 84 | cp tauri/gen/android/app/build/outputs/apk/universal/release/app-universal-release.apk {{BINARIES_PATH}}/{{APP_NAME}}.apk 85 | elif [ {{target}} = "ios" ]; then 86 | cp tauri/gen/android/app/build/outputs/bundle/universalRelease/app-universal-release.aab {{BINARIES_PATH}}/{{APP_NAME}}.aab 87 | elif [ {{target}} = "macos" ]; then 88 | cp target/release/bundle/dmg/filesync_{{APP_VERSION}}_aarch64.dmg {{BINARIES_PATH}}/{{APP_NAME}}-{{APP_VERSION}}.dmg 89 | else 90 | echo "Unspported target" 91 | exit 1; 92 | fi 93 | 94 | 95 | [doc("build and export all")] 96 | package: 97 | #!/bin/bash 98 | just build all 99 | just export all 100 | echo date > release-date.text 101 | cp "{{BINARIES_PATH}}/*" "{{EXPORT_PATH}}/*" -------------------------------------------------------------------------------- /desktop/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "filesync", 3 | "version": "1.0.0", 4 | "description": "Crossplatform filesharing application", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/opeolluwa" 12 | }, 13 | "keywords": [ 14 | "wifi", 15 | "file-sharing-mac", 16 | "android" 17 | ], 18 | "author": "Adeoye Adefemi ", 19 | "license": "MIT", 20 | "devDependencies": { 21 | "@tailwindcss/forms": "^0.5.9", 22 | "daisyui": "^4.12.14", 23 | "tailwind-plugin-ripple-effect": "^0.0.5", 24 | "tailwindcss": "^3.4.15" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /desktop/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly-2025-05-01" 3 | components = ["rust-src"] 4 | targets = ["wasm32-unknown-unknown"] 5 | 6 | [target.wasm32-unknown-unknown] 7 | rustflags = ["-C", "target-feature=+atomics,+bulk-memory"] 8 | 9 | [unstable] 10 | build-std = ["panic_abort", "std"] 11 | -------------------------------------------------------------------------------- /desktop/server/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opeolluwa/filesync/bfc91a48d5432ba8131c5a44cf1104d0fae2b182/desktop/server/.DS_Store -------------------------------------------------------------------------------- /desktop/server/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | 13 | # MSVC Windows builds of rustc generate these, which store debugging information 14 | *.pdb 15 | 16 | certificates -------------------------------------------------------------------------------- /desktop/server/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "embedded_server" 3 | description = "embedded http server for filesync app" 4 | version = "0.1.0" 5 | edition = "2021" 6 | 7 | [dependencies] 8 | shared = { workspace = true } 9 | axum = { version = "0.7.5", features = ["ws", "multipart"] } 10 | axum-extra = "0.9.3" 11 | axum-server = { version = "0.6", features = ["tls-rustls"] } 12 | axum-typed-websockets = "0.6.0" 13 | dirs = "5.0.0" 14 | filesize = "0.2.0" 15 | fs_extra = "1.3.0" 16 | futures = "0.3.28" 17 | futures-util = { version = "0.3", default-features = false, features = [ 18 | "sink", 19 | "std", 20 | ] } 21 | headers = "0.4" 22 | http = "1.1.0" 23 | include_dir = "0.7.3" 24 | infer = "0.15.0" 25 | lazy_static = "1.4.0" 26 | local-ip-address = "0.6.3" 27 | memory-serve = "0.4.5" 28 | mime_guess = "2.0.4" 29 | rand = "0.8.5" 30 | regex = "1.9.1" 31 | serde = { version = "1.0", features = ["derive"] } 32 | serde_json = "1.0" 33 | sha2 = "0.10.8" 34 | thiserror.workspace = true 35 | tokio = { version = "1.26.0", features = ["full"] } 36 | tokio-stream = "0.1" 37 | tokio-tungstenite = "0.18.0" 38 | tokio-util = { version = "0.7", features = ["io"] } 39 | tower = { version = "0.4", features = ["util"] } 40 | tower-http = { version = "0.5.0", features = ["fs", "trace", "cors"] } 41 | tracing = "0.1.37" 42 | tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } 43 | 44 | 45 | [features] 46 | default = ["server"] 47 | config = [] 48 | server = [] 49 | -------------------------------------------------------------------------------- /desktop/server/Justfile: -------------------------------------------------------------------------------- 1 | keychain: 2 | mkdir -p certificates 3 | openssl req -x509 -newkey rsa:4096 -keyout certificates/key.pem -out certificates/cert.pem -days 365 -nodes \ 4 | -subj "/CN=localhost" 5 | 6 | # openssl req -x509 -newkey rsa:4096 -days 365 -keyout certificates/key.pem -out certificates/cert.pem 7 | 8 | 9 | test-build: 10 | cargo build --manifest-path Cargo.toml -------------------------------------------------------------------------------- /desktop/server/src/errors.rs: -------------------------------------------------------------------------------- 1 | use thiserror::Error; 2 | 3 | #[derive(Error, Debug)] 4 | pub enum ServerError { 5 | #[error("Start up error due to {0}")] 6 | StartUpError(String), 7 | #[error("{0}")] 8 | ConnectionError(String), 9 | } 10 | -------------------------------------------------------------------------------- /desktop/server/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod errors; 2 | pub(crate) mod router; 3 | pub(crate) mod routes; 4 | 5 | #[cfg(feature = "config")] 6 | pub use shared::config; 7 | #[cfg(feature = "server")] 8 | pub mod server; 9 | -------------------------------------------------------------------------------- /desktop/server/src/no-tls.rs: -------------------------------------------------------------------------------- 1 | use std::net::IpAddr; 2 | use std::net::Ipv4Addr; 3 | 4 | use axum::http::Method; 5 | 6 | use serde::Deserialize; 7 | use serde::Serialize; 8 | use tower_http::cors::Any; 9 | use tower_http::cors::CorsLayer; 10 | use tower_http::trace::DefaultMakeSpan; 11 | use tower_http::trace::TraceLayer; 12 | 13 | use local_ip_address::local_ip; 14 | 15 | use axum::extract::DefaultBodyLimit; 16 | 17 | use crate::errors::ServerError; 18 | use crate::router; 19 | 20 | //TODO: run the sever can be created with multiple instances or spawn threads 21 | 22 | #[derive(Debug, Serialize, Deserialize)] 23 | pub struct EmbeddedHttpServer; 24 | 25 | impl EmbeddedHttpServer { 26 | pub async fn run() -> Result<(), ServerError> { 27 | tracing_subscriber::fmt() 28 | .with_max_level(tracing::Level::DEBUG) 29 | .init(); 30 | 31 | let cors_layer = CorsLayer::new() 32 | .allow_headers(Any) //TODO: add key gen for security 33 | .allow_methods([Method::GET, Method::POST]) 34 | .allow_origin(Any); //TODO: restrict to IP address 35 | 36 | //TODO: improve data limit it is currently set to define file limit layer as 10GB 37 | // see information here 38 | let file_size_limit = 10 * 1024 * 1024 * 1024; 39 | let file_limit = DefaultBodyLimit::max(file_size_limit); 40 | 41 | // run the https server on localhost then feed off the connection using the wifi gateway, the same way Vite/Vue CLI would do the core server 42 | // this is currently achieved by binding the server to the device default ip address 43 | 44 | let my_local_ip = local_ip().unwrap_or(IpAddr::from(Ipv4Addr::UNSPECIFIED)); 45 | let ip_address = format!("{:?}:{:?}", my_local_ip, 18005); 46 | let ip_address = ip_address 47 | .parse::() 48 | .map_err(|err| ServerError::StartUpError(err.to_string()))?; 49 | 50 | println!("my local ip is {}", ip_address); 51 | let app = router::app() 52 | .layer(file_limit) 53 | .layer(cors_layer) 54 | .layer(tower_http::trace::TraceLayer::new_for_http()) 55 | .layer( 56 | TraceLayer::new_for_http() 57 | .make_span_with(DefaultMakeSpan::default().include_headers(true)), 58 | ); 59 | 60 | // run it 61 | let listener = tokio::net::TcpListener::bind(&ip_address) 62 | .await 63 | .map_err(|err| ServerError::StartUpError(err.to_string()))?; 64 | 65 | tracing::debug!(" the server port is http://{}", ip_address); 66 | 67 | axum::serve(listener, app) 68 | .await 69 | .map_err(|err| ServerError::StartUpError(err.to_string()))?; 70 | 71 | Ok(()) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /desktop/server/src/router.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | routing::{get, post}, 3 | Router, 4 | }; 5 | 6 | use super::routes::{accept_file_upload, get_file, handle_404, health_check}; 7 | 8 | /// return all the routes as /api/ 9 | pub fn app() -> Router { 10 | Router::new() 11 | .nest( 12 | "/api", 13 | Router::new() 14 | .route("/upload", post(accept_file_upload)) 15 | .route("/ping", post(accept_file_upload).get(health_check)) 16 | .route("/file", get(get_file)), 17 | ) 18 | .fallback(handle_404) 19 | } 20 | -------------------------------------------------------------------------------- /desktop/server/src/server.rs: -------------------------------------------------------------------------------- 1 | use std::net::IpAddr; 2 | use std::path::PathBuf; 3 | use std::sync::Arc; 4 | 5 | use axum::extract::Host; 6 | use axum::handler::HandlerWithoutStateExt; 7 | use axum::response::Redirect; 8 | use axum::BoxError; 9 | use axum_server::tls_rustls::RustlsConfig; 10 | use http::StatusCode; 11 | use http::Uri; 12 | use serde::Deserialize; 13 | use serde::Serialize; 14 | use tower_http::cors::Any; 15 | use tower_http::cors::CorsLayer; 16 | use tower_http::trace::DefaultMakeSpan; 17 | use tower_http::trace::TraceLayer; 18 | 19 | use axum::extract::DefaultBodyLimit; 20 | use axum::http::Method; 21 | 22 | use crate::errors::ServerError; 23 | use crate::router; 24 | 25 | use shared::config::Ports; 26 | use shared::config::KEY_CHAIN_PATH; 27 | 28 | #[derive(Debug, Serialize, Deserialize)] 29 | pub struct EmbeddedHttpServer; 30 | 31 | impl EmbeddedHttpServer { 32 | pub async fn run(socket_address: Arc) -> Result<(), ServerError> { 33 | tracing_subscriber::fmt() 34 | .with_max_level(tracing::Level::DEBUG) 35 | .init(); 36 | 37 | let cors_layer = CorsLayer::new() 38 | .allow_headers(Any) //TODO: add key gen for security and middleware 39 | .allow_methods([Method::GET, Method::POST]) 40 | .allow_origin(Any); //TODO: restrict to IP address 41 | 42 | //TODO: improve data limit it is currently set to define file limit layer as 10GB 43 | // see information here 44 | let file_size_limit = 10 * 1024 * 1024 * 1024; 45 | let file_limit = DefaultBodyLimit::max(file_size_limit); 46 | 47 | // optional: spawn a second server to redirect http requests to this server 48 | let ports = Ports::default(); 49 | tokio::spawn(Self::redirect_http_to_https( 50 | ports, 51 | Arc::clone(&socket_address), 52 | )); 53 | 54 | // configure certificate and private key used by https 55 | let config = RustlsConfig::from_pem_file( 56 | PathBuf::from(env!("CARGO_MANIFEST_DIR")) 57 | .join(KEY_CHAIN_PATH) 58 | .join("cert.pem"), 59 | PathBuf::from(env!("CARGO_MANIFEST_DIR")) 60 | .join(KEY_CHAIN_PATH) 61 | .join("key.pem"), 62 | ) 63 | .await 64 | .map_err(|err| ServerError::StartUpError(err.to_string()))?; 65 | 66 | // run the https server on localhost then feed off the connection using the wifi gateway, the same way Vite/Vue CLI would do the core server 67 | // this is currently achieved by binding the server to the device default ip address 68 | 69 | let socket_address = format!("{:?}:{:?}", socket_address, ports.https); 70 | let socket_address = socket_address 71 | .parse::() 72 | .map_err(|err| ServerError::StartUpError(err.to_string()))?; 73 | 74 | println!("my local ip is {}", socket_address); 75 | let app = router::app() 76 | .layer(file_limit) 77 | .layer(cors_layer) 78 | .layer(tower_http::trace::TraceLayer::new_for_http()) 79 | .layer( 80 | TraceLayer::new_for_http() 81 | .make_span_with(DefaultMakeSpan::default().include_headers(true)), 82 | ); 83 | 84 | tracing::debug!(" the server port is http://{}", socket_address); 85 | axum_server::bind_rustls(socket_address, config) 86 | .serve(app.into_make_service()) 87 | .await 88 | .map_err(|err| ServerError::StartUpError(err.to_string()))?; 89 | 90 | Ok(()) 91 | } 92 | 93 | async fn redirect_http_to_https( 94 | ports: Ports, 95 | socket_address: Arc, 96 | ) -> Result<(), ServerError> { 97 | fn make_https(host: String, uri: Uri, ports: Ports) -> Result { 98 | let mut parts = uri.into_parts(); 99 | 100 | parts.scheme = Some(axum::http::uri::Scheme::HTTPS); 101 | 102 | if parts.path_and_query.is_none() { 103 | parts.path_and_query = Some("/".parse().unwrap()); 104 | } 105 | 106 | let https_host = host.replace(&ports.http.to_string(), &ports.https.to_string()); 107 | parts.authority = Some(https_host.parse()?); 108 | 109 | Ok(Uri::from_parts(parts)?) 110 | } 111 | 112 | let redirect = move |Host(host): Host, uri: Uri| async move { 113 | match make_https(host, uri, ports) { 114 | Ok(uri) => Ok(Redirect::permanent(&uri.to_string())), 115 | Err(error) => { 116 | tracing::warn!(%error, "failed to convert URI to HTTPS"); 117 | Err(StatusCode::BAD_REQUEST) 118 | } 119 | } 120 | }; 121 | 122 | let socket_address = format!("{:?}:{:?}", socket_address, ports.http); 123 | let socket_address = socket_address 124 | .parse::() 125 | .map_err(|err| ServerError::StartUpError(err.to_string()))?; 126 | 127 | let listener = tokio::net::TcpListener::bind(socket_address).await.unwrap(); 128 | tracing::debug!("listening on {}", listener.local_addr().unwrap()); 129 | axum::serve(listener, redirect.into_make_service()) 130 | .await 131 | .unwrap(); 132 | 133 | Ok(()) 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /desktop/shared/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "shared" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [dependencies] 7 | local-ip-address = "0.6.5" 8 | serde = { workspace = true, features = ["derive"] } 9 | tauri-interop = { version = "2.2.2" } 10 | 11 | # host target 12 | [target.'cfg(not(target_family = "wasm"))'.dependencies] 13 | tauri = { workspace = true } 14 | 15 | #wasm target 16 | [target.'cfg(target_family = "wasm")'.dependencies] 17 | 18 | -------------------------------------------------------------------------------- /desktop/shared/Justfile: -------------------------------------------------------------------------------- 1 | test-build: 2 | cargo build --manifest-path Cargo.toml --features state,config -------------------------------------------------------------------------------- /desktop/shared/src/cmd.rs: -------------------------------------------------------------------------------- 1 | use crate::config::EmbeddedServerConfig; 2 | 3 | #[tauri_interop::command] 4 | pub fn greet(name: &str) -> String { 5 | format!("Hello, {}! You've been greeted from Rust!", name) 6 | } 7 | 8 | #[tauri_interop::command] 9 | pub async fn extract_connection() -> EmbeddedServerConfig { 10 | EmbeddedServerConfig::default() 11 | } 12 | 13 | tauri_interop::collect_commands!(); 14 | -------------------------------------------------------------------------------- /desktop/shared/src/config.rs: -------------------------------------------------------------------------------- 1 | use local_ip_address::local_ip; 2 | use serde::{Deserialize, Serialize}; 3 | use std::{ 4 | fmt::Display, 5 | net::{IpAddr, Ipv4Addr}, 6 | }; 7 | 8 | #[derive(Debug, Serialize, Deserialize, Clone)] 9 | pub struct EmbeddedServerConfig { 10 | pub ip_address: String, 11 | pub ports: Ports, 12 | } 13 | 14 | impl Display for EmbeddedServerConfig { 15 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 16 | write!(f, "host: {}, ports: {}", self.ip_address, self.ports) 17 | } 18 | } 19 | 20 | impl EmbeddedServerConfig { 21 | pub fn setup() -> Self { 22 | Self { 23 | ip_address: "".to_string(), 24 | ports: Ports { http: 0, https: 0 }, 25 | } 26 | } 27 | } 28 | 29 | impl Default for EmbeddedServerConfig { 30 | fn default() -> Self { 31 | let local_ip = local_ip().unwrap_or(IpAddr::from(Ipv4Addr::UNSPECIFIED)); 32 | 33 | Self { 34 | //TODO: add ports 35 | ip_address: local_ip.to_string(), 36 | ports: Ports::default(), 37 | } 38 | } 39 | } 40 | 41 | pub const HTTP_PORT: u64 = 18005; 42 | pub const HTTPS_PORT: u64 = 18006; 43 | pub const KEY_CHAIN_PATH: &str = "certificates"; 44 | #[derive(Clone, Copy, Serialize, Deserialize, Debug)] 45 | pub struct Ports { 46 | pub http: u16, 47 | pub https: u16, 48 | } 49 | 50 | impl Default for Ports { 51 | fn default() -> Self { 52 | Self { 53 | http: HTTP_PORT as u16, 54 | https: HTTPS_PORT as u16, 55 | } 56 | } 57 | } 58 | 59 | impl Display for Ports { 60 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 61 | write!(f, "http:{}, https:{}", self.http, self.https) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /desktop/shared/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod cmd; 2 | 3 | // #[cfg(not(target_family = "wasm"))] 4 | // #[cfg(feature = "state")] 5 | pub mod state; 6 | 7 | // #[cfg(not(target_family = "wasm"))] 8 | // #[cfg(feature = "config")] 9 | pub mod config; 10 | 11 | 12 | // pub use crate::; 13 | // pub mod cmd_wasm_bindgen; -------------------------------------------------------------------------------- /desktop/shared/src/state.rs: -------------------------------------------------------------------------------- 1 | use crate::config::EmbeddedServerConfig; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | #[derive(Debug, Serialize, Deserialize, Clone)] 5 | pub struct AppState { 6 | pub server_config: EmbeddedServerConfig, 7 | 8 | } 9 | -------------------------------------------------------------------------------- /desktop/src-tauri/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Generated by Tauri 6 | # will have schema files for capabilities auto-completion 7 | /gen/schemas 8 | -------------------------------------------------------------------------------- /desktop/src-tauri/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "filesync" 3 | version = "0.1.0" 4 | description = "A Tauri App" 5 | authors = ["you"] 6 | edition = "2021" 7 | 8 | [lib] 9 | name = "filesync_lib" 10 | crate-type = ["staticlib", "cdylib", "rlib"] 11 | 12 | [build-dependencies] 13 | tauri-build = { workspace = true, features = [] } 14 | 15 | [dependencies] 16 | dirs = "6.0.0" 17 | embedded_server = { workspace = true, features = ["config", "server"] } 18 | local-ip-address = "0.6.4" 19 | rand = { version = "0.9.0", features = ["small_rng"] } 20 | random_str = "0.1.2" 21 | serde = { workspace = true, features = ["derive"] } 22 | serde_json = { workspace = true } 23 | shared = { workspace = true } 24 | tauri = { workspace = true, features = [] } 25 | tauri-plugin-fs = "2" 26 | tauri-plugin-os = { workspace = true } 27 | tauri-plugin-shell = { workspace = true } 28 | tauri-plugin-sql = { version = "2", features = ["sqlite"] } 29 | thiserror = "2.0.12" 30 | tokio = "1.43.0" 31 | uuid = { version = "1.15.1", features = ["v4"] } 32 | [target.'cfg(any(target_os = "android", target_os = "ios"))'.dependencies] 33 | tauri-plugin-barcode-scanner = "2" 34 | -------------------------------------------------------------------------------- /desktop/src-tauri/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | tauri_build::build() 3 | } 4 | -------------------------------------------------------------------------------- /desktop/src-tauri/capabilities/default.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../gen/schemas/desktop-schema.json", 3 | "identifier": "default", 4 | "description": "Capability for the main window", 5 | "windows": ["main"], 6 | "permissions": [ 7 | "core:default", 8 | "shell:allow-open", 9 | "os:default", 10 | "sql:allow-load", 11 | "sql:allow-execute", 12 | "sql:allow-select", 13 | "sql:default", 14 | "sql:allow-close", 15 | "fs:allow-app-read-recursive", 16 | "fs:allow-app-meta-recursive", 17 | "fs:allow-audio-read-recursive", 18 | "fs:allow-audio-write-recursive", 19 | "fs:allow-video-read-recursive", 20 | "fs:allow-video-write-recursive", 21 | "fs:allow-public-read-recursive", 22 | "fs:allow-public-write-recursive", 23 | "fs:allow-document-read-recursive", 24 | "fs:allow-document-write-recursive", 25 | { 26 | "identifier": "fs:scope", 27 | "allow": [ 28 | { "path": "$APPDATA" }, 29 | { "path": "$HOME" }, 30 | { "path": "$PUBLIC" }, 31 | { "path": "$AUDIO" }, 32 | { "path": "$VIDEO" }, 33 | { "path": "$DOCUMENT" } 34 | ] 35 | } 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /desktop/src-tauri/capabilities/mobile.json: -------------------------------------------------------------------------------- 1 | { 2 | "identifier": "mobile-capability", 3 | "platforms": ["android", "iOS"], 4 | "windows": ["main"], 5 | "permissions": ["barcode-scanner:default"] 6 | } 7 | -------------------------------------------------------------------------------- /desktop/src-tauri/icons/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opeolluwa/filesync/bfc91a48d5432ba8131c5a44cf1104d0fae2b182/desktop/src-tauri/icons/128x128.png -------------------------------------------------------------------------------- /desktop/src-tauri/icons/128x128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opeolluwa/filesync/bfc91a48d5432ba8131c5a44cf1104d0fae2b182/desktop/src-tauri/icons/128x128@2x.png -------------------------------------------------------------------------------- /desktop/src-tauri/icons/32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opeolluwa/filesync/bfc91a48d5432ba8131c5a44cf1104d0fae2b182/desktop/src-tauri/icons/32x32.png -------------------------------------------------------------------------------- /desktop/src-tauri/icons/Square107x107Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opeolluwa/filesync/bfc91a48d5432ba8131c5a44cf1104d0fae2b182/desktop/src-tauri/icons/Square107x107Logo.png -------------------------------------------------------------------------------- /desktop/src-tauri/icons/Square142x142Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opeolluwa/filesync/bfc91a48d5432ba8131c5a44cf1104d0fae2b182/desktop/src-tauri/icons/Square142x142Logo.png -------------------------------------------------------------------------------- /desktop/src-tauri/icons/Square150x150Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opeolluwa/filesync/bfc91a48d5432ba8131c5a44cf1104d0fae2b182/desktop/src-tauri/icons/Square150x150Logo.png -------------------------------------------------------------------------------- /desktop/src-tauri/icons/Square284x284Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opeolluwa/filesync/bfc91a48d5432ba8131c5a44cf1104d0fae2b182/desktop/src-tauri/icons/Square284x284Logo.png -------------------------------------------------------------------------------- /desktop/src-tauri/icons/Square30x30Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opeolluwa/filesync/bfc91a48d5432ba8131c5a44cf1104d0fae2b182/desktop/src-tauri/icons/Square30x30Logo.png -------------------------------------------------------------------------------- /desktop/src-tauri/icons/Square310x310Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opeolluwa/filesync/bfc91a48d5432ba8131c5a44cf1104d0fae2b182/desktop/src-tauri/icons/Square310x310Logo.png -------------------------------------------------------------------------------- /desktop/src-tauri/icons/Square44x44Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opeolluwa/filesync/bfc91a48d5432ba8131c5a44cf1104d0fae2b182/desktop/src-tauri/icons/Square44x44Logo.png -------------------------------------------------------------------------------- /desktop/src-tauri/icons/Square71x71Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opeolluwa/filesync/bfc91a48d5432ba8131c5a44cf1104d0fae2b182/desktop/src-tauri/icons/Square71x71Logo.png -------------------------------------------------------------------------------- /desktop/src-tauri/icons/Square89x89Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opeolluwa/filesync/bfc91a48d5432ba8131c5a44cf1104d0fae2b182/desktop/src-tauri/icons/Square89x89Logo.png -------------------------------------------------------------------------------- /desktop/src-tauri/icons/StoreLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opeolluwa/filesync/bfc91a48d5432ba8131c5a44cf1104d0fae2b182/desktop/src-tauri/icons/StoreLogo.png -------------------------------------------------------------------------------- /desktop/src-tauri/icons/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opeolluwa/filesync/bfc91a48d5432ba8131c5a44cf1104d0fae2b182/desktop/src-tauri/icons/icon.icns -------------------------------------------------------------------------------- /desktop/src-tauri/icons/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opeolluwa/filesync/bfc91a48d5432ba8131c5a44cf1104d0fae2b182/desktop/src-tauri/icons/icon.ico -------------------------------------------------------------------------------- /desktop/src-tauri/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opeolluwa/filesync/bfc91a48d5432ba8131c5a44cf1104d0fae2b182/desktop/src-tauri/icons/icon.png -------------------------------------------------------------------------------- /desktop/src-tauri/src/commands/app.rs: -------------------------------------------------------------------------------- 1 | use tauri::{Config, Runtime}; 2 | 3 | #[tauri::command] 4 | pub async fn get_app_config(app: tauri::AppHandle) -> Config { 5 | let app_config = app.config(); 6 | app_config.to_owned() 7 | } 8 | 9 | -------------------------------------------------------------------------------- /desktop/src-tauri/src/commands/keygen.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, Serialize, Deserialize)] 2 | pub struct WifiCredentials { 3 | pub ssid: u16, 4 | pub passkey: String, 5 | } 6 | use serde::{Deserialize, Serialize}; 7 | 8 | use crate::utils::generator::{generate_passkey, generate_random_digits}; 9 | 10 | #[tauri::command] 11 | pub fn generate_android_wifi_credentials() -> WifiCredentials { 12 | let ssid = generate_random_digits(); 13 | let passkey = generate_passkey(); 14 | 15 | WifiCredentials { ssid, passkey } 16 | } 17 | -------------------------------------------------------------------------------- /desktop/src-tauri/src/commands/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod app; 2 | pub mod keygen; 3 | pub mod server; -------------------------------------------------------------------------------- /desktop/src-tauri/src/commands/server.rs: -------------------------------------------------------------------------------- 1 | // use embedded_server::config::EmbeddedServerConfig; 2 | // use tauri::{Manager, Runtime}; 3 | 4 | // use shared::state::AppState; 5 | 6 | // #[tauri::command] 7 | // pub async fn extract_connection( 8 | // app: tauri::AppHandle, 9 | // ) -> Result { 10 | // let data = app.state::(); 11 | // let AppState { server_config } = data.inner().clone(); 12 | // Ok(server_config) 13 | // } 14 | -------------------------------------------------------------------------------- /desktop/src-tauri/src/database/db.rs: -------------------------------------------------------------------------------- 1 | use std::{env, fs}; 2 | 3 | use sqlx::{Pool, Sqlite}; 4 | use tauri::AppHandle; 5 | 6 | use crate::error::StartupError; 7 | 8 | pub struct Database { 9 | pub pool: Pool, 10 | } 11 | 12 | impl Database { 13 | pub async fn new(app_handle: &AppHandle) -> Result { 14 | let app_dir = app_handle 15 | .path_resolver() 16 | .app_data_dir() 17 | .expect("failed to get app dir"); 18 | 19 | // Ensure the app directory exists 20 | fs::create_dir_all(&app_dir) 21 | .map_err(|error| StartupError::ProcessFailed(error.to_string()))?; 22 | 23 | let db_path = app_dir.join("grid_search.db"); 24 | 25 | // Set the DATABASE_URL environment variable to point to this SQLite file 26 | env::set_var("DATABASE_URL", format!("sqlite://{}", db_path.display())); 27 | 28 | let connection_options = sqlx::sqlite::SqliteConnectOptions::new() 29 | .filename(&db_path) 30 | .create_if_missing(true) 31 | .journal_mode(sqlx::sqlite::SqliteJournalMode::Wal); 32 | todo!() 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /desktop/src-tauri/src/database/migrations.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opeolluwa/filesync/bfc91a48d5432ba8131c5a44cf1104d0fae2b182/desktop/src-tauri/src/database/migrations.rs -------------------------------------------------------------------------------- /desktop/src-tauri/src/database/mod.rs: -------------------------------------------------------------------------------- 1 | // pub mod db; 2 | pub mod models; 3 | -------------------------------------------------------------------------------- /desktop/src-tauri/src/database/models/mod.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /desktop/src-tauri/src/error.rs: -------------------------------------------------------------------------------- 1 | use thiserror::Error; 2 | 3 | #[derive(Error, Debug)] 4 | pub enum StartupError {} 5 | -------------------------------------------------------------------------------- /desktop/src-tauri/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Learn more about Tauri commands at https://tauri.app/develop/calling-rust/ 2 | mod commands; 3 | mod database; 4 | mod error; 5 | mod utils; 6 | use embedded_server::server::EmbeddedHttpServer; 7 | use local_ip_address::local_ip; 8 | use shared::config::EmbeddedServerConfig; 9 | use shared::state::AppState; 10 | use std::{ 11 | net::{IpAddr, Ipv4Addr}, 12 | sync::Arc, 13 | }; 14 | use tauri::Manager; 15 | use tauri_plugin_sql::{Migration, MigrationKind}; 16 | 17 | #[cfg_attr(mobile, tauri::mobile_entry_point)] 18 | pub fn run() { 19 | let migrations = vec![Migration { 20 | version: 1, 21 | description: "create_transfer_history_table", 22 | sql: "CREATE TABLE transfer_history (id TEXT PRIMARY KEY, file_name TEXT, sender TEXT, receiver TEXT, file_size VARCHAR, date TEXT, status VARCHAR );", 23 | kind: MigrationKind::Up, 24 | }]; 25 | 26 | let local_ip = local_ip().unwrap_or(IpAddr::from(Ipv4Addr::UNSPECIFIED)); 27 | let app_state = AppState { 28 | server_config: EmbeddedServerConfig::default(), 29 | 30 | }; 31 | 32 | tauri::async_runtime::spawn(EmbeddedHttpServer::run(Arc::new(local_ip))); 33 | tauri::Builder::default() 34 | .setup(move |app| { 35 | app.manage(app_state); 36 | Ok(()) 37 | }) 38 | .plugin( 39 | tauri_plugin_sql::Builder::default() 40 | .add_migrations("sqlite:filesync.db", migrations) 41 | .build(), 42 | ) 43 | .plugin(tauri_plugin_os::init()) 44 | .plugin(tauri_plugin_shell::init()) 45 | .invoke_handler(shared::cmd::get_handlers()) 46 | // .invoke_handler(tauri::generate_handler![]) 47 | .run(tauri::generate_context!()) 48 | .expect("error while running tauri application"); 49 | } 50 | -------------------------------------------------------------------------------- /desktop/src-tauri/src/main.rs: -------------------------------------------------------------------------------- 1 | // Prevents additional console window on Windows in release, DO NOT REMOVE!! 2 | #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] 3 | 4 | fn main() { 5 | filesync_lib::run() 6 | } 7 | -------------------------------------------------------------------------------- /desktop/src-tauri/src/utils/generator.rs: -------------------------------------------------------------------------------- 1 | use random_str as random; 2 | 3 | pub fn generate_random_digits() -> u16 { 4 | let min = 1000; 5 | let max = 9999; 6 | let random_digit = random::get_int(min, max); 7 | random_digit as u16 8 | } 9 | 10 | pub fn generate_passkey() -> String { 11 | let lowercase = true; 12 | let uppercase = true; 13 | let length = 8; 14 | let numbers = true; 15 | let symbols = true; 16 | 17 | random::get_string(length, lowercase, uppercase, numbers, symbols) 18 | } 19 | 20 | #[cfg(test)] 21 | mod test { 22 | use super::*; 23 | 24 | #[test] 25 | fn test_random_digit_generator() { 26 | let digit = generate_random_digits(); 27 | assert!(digit.to_string().len() >= 4); 28 | } 29 | 30 | #[test] 31 | fn test_random_letters_generator() { 32 | let passkey = generate_passkey(); 33 | assert_eq!(passkey.len(), 8); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /desktop/src-tauri/src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod generator; 2 | -------------------------------------------------------------------------------- /desktop/src-tauri/tauri.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.tauri.app/config/2", 3 | "productName": "filesync", 4 | "version": "0.7.16", 5 | "identifier": "com.filesync.app", 6 | "build": { 7 | "beforeDevCommand": "trunk serve", 8 | "devUrl": "http://localhost:1420", 9 | "beforeBuildCommand": "trunk build", 10 | "frontendDist": "../dist" 11 | }, 12 | 13 | "app": { 14 | "withGlobalTauri": true, 15 | "windows": [ 16 | { 17 | "title": "Filesync", 18 | "width": 750, 19 | "height": 600 20 | } 21 | ], 22 | "security": { 23 | "csp": null 24 | } 25 | }, 26 | "bundle": { 27 | "active": true, 28 | "targets": "all", 29 | "icon": [ 30 | "icons/32x32.png", 31 | "icons/128x128.png", 32 | "icons/128x128@2x.png", 33 | "icons/icon.icns", 34 | "icons/icon.ico" 35 | ] 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /desktop/src/app.rs: -------------------------------------------------------------------------------- 1 | use leptos::*; 2 | use leptos_router::{ 3 | components::{Route, Router, Routes}, 4 | path, 5 | }; 6 | use thaw::ConfigProvider; 7 | 8 | // use crate::screens::about::AboutScreen; 9 | // use crate::screens::history::HistoryScreen; 10 | // use crate::screens::settings::SettingsScreen; 11 | // use crate::screens::share::ShareScreen; 12 | // use crate::screens::welcome::choose_platform::SelectPlatformScreen; 13 | // use crate::screens::welcome::receive::ReceiveScreen; 14 | // use crate::screens::welcome::select_action::TransferScreen; 15 | // use crate::screens::welcome::send::SendScreen; 16 | use crate::screens::welcome::WelcomeScreen; 17 | use crate::{layouts::default_layout::DefaultLayout, screens::home::HomeScreen}; 18 | 19 | #[component] 20 | pub fn App() -> impl leptos::IntoView { 21 | view! { 22 | 23 | 24 | 25 | 26 | 27 | 28 | // 29 | // 30 | // 31 | // 32 | // 33 | // 34 | // 35 | 36 | 37 | 38 | 39 | 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /desktop/src/components/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod toolbar; 2 | -------------------------------------------------------------------------------- /desktop/src/components/toolbar.rs: -------------------------------------------------------------------------------- 1 | use leptos::prelude::{ClassAttribute, ElementChild}; 2 | use leptos::{view, IntoView}; 3 | 4 | use crate::icons::chevron::ChevronUpDownIcon; 5 | use crate::icons::cog::SettingsIconOutline; 6 | use crate::icons::download_icon::DownloadIcon; 7 | use crate::icons::home_icon::HomeIcon; 8 | use crate::icons::upload_icon::UploadIcon; 9 | 10 | use crate::routes::{HISTORY_ROUTE, HOME_ROUTE, RECEIVE_ROUTE, SEND_ROUTE, SETTINGS_ROUTE}; 11 | 12 | #[leptos::component] 13 | pub fn ToolbarItem( 14 | label: &'static str, 15 | href: &'static str, 16 | icon: F, 17 | #[prop(optional)] class: &'static str, 18 | ) -> impl leptos::IntoView 19 | where 20 | F: IntoView + 'static, 21 | { 22 | view! { 23 | 30 | {icon} 31 | {label} 32 | 33 | } 34 | } 35 | 36 | #[leptos::component] 37 | pub fn Toolbar() -> impl leptos::IntoView { 38 | let home_icon = HomeIcon(); 39 | let settings_icon = SettingsIconOutline(); 40 | let upload_icon = UploadIcon(); 41 | let download_icon = DownloadIcon(); 42 | let history_icon = ChevronUpDownIcon(); 43 | 44 | view! { 45 |
46 | 47 | 48 | 49 | 50 | 56 |
57 | } 58 | } 59 | -------------------------------------------------------------------------------- /desktop/src/icons/arrow_left_right_icon.rs: -------------------------------------------------------------------------------- 1 | use leptos::prelude::{ClassAttribute, CustomAttribute, ElementChild}; 2 | use leptos::view; 3 | 4 | #[leptos::component] 5 | pub fn ArrowLeftRightIconSolid() -> impl leptos::IntoView { 6 | view! { 7 | 15 | 20 | 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /desktop/src/icons/calendar_icon.rs: -------------------------------------------------------------------------------- 1 | use leptos::prelude::{ClassAttribute, CustomAttribute, ElementChild}; 2 | use leptos::view; 3 | 4 | #[leptos::component] 5 | pub fn CalendarIconOutline() -> impl leptos::IntoView { 6 | view! { 7 | 15 | 20 | 21 | } 22 | } 23 | 24 | 25 | 26 | #[leptos::component] 27 | pub fn CalendarIconSolid() -> impl leptos::IntoView { 28 | view! { 29 | 35 | 40 | 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /desktop/src/icons/chevron.rs: -------------------------------------------------------------------------------- 1 | use leptos::prelude::{ClassAttribute, CustomAttribute, ElementChild}; 2 | use leptos::view; 3 | 4 | #[leptos::component] 5 | pub fn ChevronLeftIcon() -> impl leptos::IntoView { 6 | view! { 7 | 15 | 16 | 17 | } 18 | } 19 | 20 | #[leptos::component] 21 | pub fn ChevronUpDownIcon() -> impl leptos::IntoView { 22 | view! { 23 | 31 | 36 | 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /desktop/src/icons/cloud.rs: -------------------------------------------------------------------------------- 1 | use leptos::prelude::{ClassAttribute, CustomAttribute, ElementChild}; 2 | use leptos::view; 3 | 4 | #[leptos::component] 5 | pub fn CloudUploadIcon(#[prop(optional)] class: &'static str) -> impl leptos::IntoView { 6 | view! { 7 | 15 | 20 | 21 | } 22 | } 23 | 24 | #[leptos::component] 25 | pub fn CloudDownloadIconSolid() -> impl leptos::IntoView { 26 | view! { 27 | 33 | 38 | 39 | } 40 | } 41 | 42 | #[leptos::component] 43 | pub fn CloudUploadIconSolid() -> impl leptos::IntoView { 44 | view! { 45 | 51 | 56 | 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /desktop/src/icons/cog.rs: -------------------------------------------------------------------------------- 1 | use leptos::prelude::{ClassAttribute, CustomAttribute, ElementChild}; 2 | use leptos::view; 3 | 4 | #[leptos::component] 5 | pub fn SettingsIconOutline() -> impl leptos::IntoView { 6 | view! { 7 | 15 | 20 | 25 | 26 | } 27 | } 28 | 29 | #[leptos::component] 30 | pub fn CogSolid() -> impl leptos::IntoView { 31 | view! { 32 | 38 | 43 | 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /desktop/src/icons/dots.rs: -------------------------------------------------------------------------------- 1 | use leptos::prelude::{ClassAttribute, CustomAttribute, ElementChild}; 2 | use leptos::view; 3 | 4 | #[leptos::component] 5 | pub fn DotVertical() -> impl leptos::IntoView { 6 | view! { 7 | 15 | 20 | 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /desktop/src/icons/download_icon.rs: -------------------------------------------------------------------------------- 1 | use leptos::prelude::{ClassAttribute, CustomAttribute, ElementChild}; 2 | use leptos::view; 3 | 4 | #[leptos::component] 5 | pub fn DownloadIcon() -> impl leptos::IntoView { 6 | view! { 7 | 15 | 20 | 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /desktop/src/icons/history_icon.rs: -------------------------------------------------------------------------------- 1 | use leptos::prelude::{ClassAttribute, CustomAttribute, ElementChild}; 2 | use leptos::view; 3 | 4 | #[leptos::component] 5 | pub fn HistoryIcon() -> impl leptos::IntoView { 6 | view! { 7 | 15 | 20 | 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /desktop/src/icons/home_icon.rs: -------------------------------------------------------------------------------- 1 | use leptos::prelude::{ClassAttribute, CustomAttribute, ElementChild}; 2 | use leptos::{view, IntoView}; 3 | 4 | #[leptos::component] 5 | pub fn HomeIcon() -> impl leptos::IntoView { 6 | view! { 7 | 13 | 14 | 15 | } 16 | } 17 | 18 | #[leptos::component] 19 | pub fn HomeIconSolid() -> impl IntoView { 20 | view! { 21 | 27 | 28 | 29 | 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /desktop/src/icons/info_icon.rs: -------------------------------------------------------------------------------- 1 | use leptos::prelude::{ClassAttribute, CustomAttribute, ElementChild}; 2 | use leptos::view; 3 | 4 | #[leptos::component] 5 | pub fn InformationIconOutline() -> impl leptos::IntoView { 6 | view! { 7 | 15 | 20 | 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /desktop/src/icons/menu_icon.rs: -------------------------------------------------------------------------------- 1 | use leptos::prelude::{ClassAttribute, CustomAttribute, ElementChild}; 2 | use leptos::view; 3 | 4 | #[leptos::component] 5 | pub fn MenuIcon() -> impl leptos::IntoView { 6 | view! { 7 | 15 | 20 | 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /desktop/src/icons/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod arrow_left_right_icon; 2 | pub mod chevron; 3 | pub mod cloud; 4 | pub mod cog; 5 | pub mod dots; 6 | pub mod download_icon; 7 | pub mod history_icon; 8 | pub mod home_icon; 9 | pub mod info_icon; 10 | pub mod menu_icon; 11 | pub mod moon_icon; 12 | pub mod platform; 13 | pub mod scan_qr_icon; 14 | pub mod share_icon; 15 | pub mod sun_icon; 16 | pub mod upload_icon; 17 | -------------------------------------------------------------------------------- /desktop/src/icons/moon_icon.rs: -------------------------------------------------------------------------------- 1 | use leptos::prelude::{ClassAttribute, CustomAttribute, ElementChild}; 2 | use leptos::view; 3 | 4 | #[leptos::component] 5 | pub fn MoonIconOutline() -> impl leptos::IntoView { 6 | view! { 7 | 15 | 20 | 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /desktop/src/icons/plus_icon.rs: -------------------------------------------------------------------------------- 1 | use leptos::prelude::{ClassAttribute, CustomAttribute, ElementChild}; 2 | use leptos::view; 3 | 4 | #[leptos::component] 5 | pub fn PlusIcon() -> impl leptos::IntoView { 6 | view! { 7 | 13 | 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /desktop/src/icons/scan_qr_icon.rs: -------------------------------------------------------------------------------- 1 | use leptos::prelude::{ClassAttribute, CustomAttribute, ElementChild}; 2 | use leptos::view; 3 | 4 | #[leptos::component] 5 | pub fn ScanQrIcon() -> impl leptos::IntoView { 6 | view! { 7 | 13 | 14 | 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /desktop/src/icons/share_icon.rs: -------------------------------------------------------------------------------- 1 | use leptos::prelude::{ClassAttribute, CustomAttribute, ElementChild}; 2 | use leptos::view; 3 | 4 | #[leptos::component] 5 | pub fn ShareIconSolid() -> impl leptos::IntoView { 6 | view! { 7 | 15 | 20 | 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /desktop/src/icons/sun_icon.rs: -------------------------------------------------------------------------------- 1 | use leptos::prelude::{ClassAttribute, CustomAttribute, ElementChild}; 2 | use leptos::view; 3 | 4 | #[leptos::component] 5 | pub fn SunIconOutline() -> impl leptos::IntoView { 6 | view! { 7 | 15 | 20 | 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /desktop/src/icons/upload_icon.rs: -------------------------------------------------------------------------------- 1 | use leptos::prelude::{ClassAttribute, CustomAttribute, ElementChild}; 2 | use leptos::view; 3 | 4 | #[leptos::component] 5 | pub fn UploadIcon() -> impl leptos::IntoView { 6 | view! { 7 | 15 | 20 | 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /desktop/src/icons/user_icon.rs: -------------------------------------------------------------------------------- 1 | use leptos::prelude::{ClassAttribute, CustomAttribute, ElementChild}; 2 | use leptos::view; 3 | 4 | #[leptos::component] 5 | pub fn UserIconMultipleOutline() -> impl leptos::IntoView { 6 | view! { 7 | 15 | 20 | 21 | } 22 | } 23 | 24 | #[leptos::component] 25 | pub fn userIconMultipleSolid() -> impl leptos::IntoView { 26 | view! { 27 | 33 | 34 | 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /desktop/src/js_bindgen/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "js_bindgen" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | wasm-bindgen = { workspace = true } 8 | wasm-bindgen-futures = { workspace = true } 9 | web-sys = { version = "0.3.76", features = [ 10 | "Document", 11 | "Window", 12 | "Node", 13 | "HtmlElement", 14 | ] } 15 | -------------------------------------------------------------------------------- /desktop/src/js_bindgen/api_request.js: -------------------------------------------------------------------------------- 1 | const request_path = (path) => `https://vulkan-8vfs.shuttle.app/v1/${path}`; 2 | // const request_path = (path) => `http://127.0.0.1:4576/v1/${path}`; 3 | 4 | async function post(payload, endpoint) { 5 | try { 6 | const url = request_path(endpoint); 7 | const response = await fetch(url, { 8 | method: "POST", 9 | headers: { 10 | "Content-Type": "application/json", 11 | }, 12 | body: JSON.stringify(payload), 13 | }); 14 | 15 | if (!response.ok) { 16 | throw new Error(`Response status: ${response.status}`); 17 | } 18 | 19 | const json = await response.json(); 20 | console.log(json); 21 | } catch (error) { 22 | console.error(error.message); 23 | } 24 | } 25 | 26 | async function get( endpoint) { 27 | try { 28 | const url = request_path(endpoint); 29 | const response = await fetch("http://127.0.0.1:4576/v1/health", { 30 | method: "GET", 31 | headers: { 32 | "Content-Type": "application/json", 33 | }, 34 | }); 35 | 36 | if (!response.ok) { 37 | throw new Error(`Response status: ${response.status}`); 38 | } 39 | 40 | const json = await response.text(); 41 | console.log(json); 42 | } catch (error) { 43 | console.error(error.message); 44 | } 45 | } 46 | 47 | post( 48 | { 49 | "email": "test@mailer.com", 50 | "first_name": "test", 51 | "last_name": "test", 52 | "password": "test", 53 | }, 54 | "users/register" 55 | ); 56 | 57 | // get("health"); 58 | -------------------------------------------------------------------------------- /desktop/src/js_bindgen/api_request.rs: -------------------------------------------------------------------------------- 1 | use wasm_bindgen::prelude::*; 2 | 3 | // create rust functions from the javascript functions 4 | #[wasm_bindgen(module = "/src/js_bindgen/api_request.js")] 5 | extern "C" { 6 | pub async fn post(payload: JsValue, endpoint: &str) -> JsValue; 7 | } 8 | -------------------------------------------------------------------------------- /desktop/src/js_bindgen/go_to_prev_location.js: -------------------------------------------------------------------------------- 1 | function go_to_prev_location() { 2 | window.history.back(); 3 | } 4 | -------------------------------------------------------------------------------- /desktop/src/js_bindgen/go_to_prev_location.rs: -------------------------------------------------------------------------------- 1 | use wasm_bindgen::prelude::*; 2 | 3 | #[wasm_bindgen(module = "/src/js_bindgen/go_to_prev_location.js")] 4 | extern "C" { 5 | pub fn prev_location(); 6 | } 7 | -------------------------------------------------------------------------------- /desktop/src/js_bindgen/is_active_route.js: -------------------------------------------------------------------------------- 1 | 2 | export function is_active_route(target_route) { 3 | let current_route = window.location.pathname; 4 | 5 | return ( 6 | current_route.toLowerCase().trim() === target_route.toLowerCase().trim() 7 | ); 8 | } 9 | -------------------------------------------------------------------------------- /desktop/src/js_bindgen/is_active_route.rs: -------------------------------------------------------------------------------- 1 | use wasm_bindgen::prelude::*; 2 | 3 | #[wasm_bindgen(module = "/src/js_bindgen/is_active_route.js")] 4 | extern "C" { 5 | pub fn is_active_route(route: &str) -> bool; 6 | } 7 | -------------------------------------------------------------------------------- /desktop/src/js_bindgen/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod api_request; 2 | pub mod go_to_prev_location; 3 | pub mod is_active_route; 4 | pub mod navigate; 5 | pub mod window_location; 6 | -------------------------------------------------------------------------------- /desktop/src/js_bindgen/navigate.js: -------------------------------------------------------------------------------- 1 | export function change_location_to(location) { 2 | window.location.replace(location); 3 | } 4 | -------------------------------------------------------------------------------- /desktop/src/js_bindgen/navigate.rs: -------------------------------------------------------------------------------- 1 | use wasm_bindgen::prelude::*; 2 | 3 | #[wasm_bindgen(module = "/src/js_bindgen/navigate.js")] 4 | extern "C" { 5 | pub fn change_location_to(location: &str); 6 | } 7 | -------------------------------------------------------------------------------- /desktop/src/js_bindgen/window_location.js: -------------------------------------------------------------------------------- 1 | export function get_window_location() { 2 | return window.location.pathname; 3 | } 4 | -------------------------------------------------------------------------------- /desktop/src/js_bindgen/window_location.rs: -------------------------------------------------------------------------------- 1 | use wasm_bindgen::prelude::*; 2 | 3 | // create rust functions from the javascript functions 4 | #[wasm_bindgen(module = "/src/js_bindgen/window_location.js")] 5 | extern "C" { 6 | pub fn get_window_location() -> String; 7 | } 8 | -------------------------------------------------------------------------------- /desktop/src/layouts/default_layout.rs: -------------------------------------------------------------------------------- 1 | use leptos::prelude::ElementChild; 2 | use leptos::{ 3 | children::Children, 4 | prelude::{ClassAttribute, StyleAttribute}, 5 | view, 6 | }; 7 | 8 | #[leptos::component] 9 | pub fn DefaultLayout(children: Children) -> impl leptos::IntoView { 10 | let children = children(); 11 | view! { 12 |
16 |
{children}
17 |
18 | } 19 | } 20 | -------------------------------------------------------------------------------- /desktop/src/layouts/desktop_layout.rs: -------------------------------------------------------------------------------- 1 | use leptos::prelude::{ClassAttribute, ElementChild, OnAttribute}; 2 | use leptos::{prelude::Children, view}; 3 | 4 | use crate::icons::chevron::ChevronLeftIcon; 5 | use crate::icons::cloud::CloudUploadIcon; 6 | use crate::js_bindgen::navigate::change_location_to; 7 | 8 | use crate::components::toolbar::Toolbar; 9 | 10 | #[leptos::component] 11 | pub fn DesktopLayout(children: Children) -> impl leptos::IntoView { 12 | let children = children(); 13 | view! { 14 |
15 | 16 | 17 |
18 |
{children}
19 |
20 | 21 |
22 | } 23 | } 24 | -------------------------------------------------------------------------------- /desktop/src/layouts/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod default_layout; 2 | pub(crate) mod desktop_layout; 3 | pub(crate) mod welcome_screen_layout; 4 | -------------------------------------------------------------------------------- /desktop/src/layouts/welcome_screen_layout.rs: -------------------------------------------------------------------------------- 1 | use leptos::prelude::CustomAttribute; 2 | use leptos::prelude::{ClassAttribute, ElementChild, OnAttribute}; 3 | use leptos::{prelude::Children, view}; 4 | 5 | use crate::js_bindgen::navigate::change_location_to; 6 | 7 | #[leptos::component] 8 | pub fn WelcomeScreenLayout( 9 | children: Children, 10 | #[prop(optional)] label: &'static str, 11 | #[prop(optional)] show_label: bool, 12 | #[prop(optional)] class: &'static str, 13 | ) -> impl leptos::IntoView { 14 | let children = children(); 15 | 16 | let _transfer_action = label.to_string(); 17 | let _show_label = show_label; 18 | 19 | view! { 20 |
24 | 25 | 33 | 38 | 39 | 40 |
41 |
45 | 46 | {children} 47 |
48 | } 49 | } 50 | -------------------------------------------------------------------------------- /desktop/src/main.rs: -------------------------------------------------------------------------------- 1 | mod app; 2 | mod components; 3 | mod icons; 4 | mod js_bindgen; 5 | mod layouts; 6 | mod routes; 7 | mod screens; 8 | 9 | use app::*; 10 | 11 | use leptos::*; 12 | use mount::mount_to_body; 13 | 14 | fn main() { 15 | console_error_panic_hook::set_once(); 16 | mount_to_body(|| { 17 | view! { } 18 | }) 19 | } 20 | -------------------------------------------------------------------------------- /desktop/src/routes.rs: -------------------------------------------------------------------------------- 1 | pub(crate) const SEND_ROUTE: &str = "/send"; 2 | pub(crate) const RECEIVE_ROUTE: &str = "/receive"; 3 | pub(crate) const HOME_ROUTE: &str = "/home"; 4 | pub(crate) const SETTINGS_ROUTE: &str = "/settings"; 5 | pub(crate) const HISTORY_ROUTE: &str = "/history"; 6 | // pub(crate) const SELECT_PLATFORM_ROUTE: &str = "/platform"; 7 | -------------------------------------------------------------------------------- /desktop/src/screens/about.rs: -------------------------------------------------------------------------------- 1 | use leptos::prelude::ClassAttribute; 2 | use leptos::prelude::CustomAttribute; 3 | use leptos::prelude::ElementChild; 4 | use leptos::{leptos_dom::logging::console_log, prelude::signal, task::spawn_local, view}; 5 | use thaw::{Flex, FlexAlign, FlexJustify, Image, Text}; 6 | 7 | #[leptos::component] 8 | pub fn AboutScreen() -> impl leptos::IntoView { 9 | let (app_name, _set_app_name) = signal("Filesync"); 10 | let (app_version, _set_app_version) = signal("0.7.9"); 11 | 12 | // spawn_local(async move { 13 | // let app_config = invoke_tauri_command_without_args("get_app_config").await; 14 | // console_log(&app_config.as_string().unwrap()); 15 | // }); 16 | 17 | view! { 18 | 19 | 20 | 21 | {app_name} {" "} {app_version} 22 | 23 |
24 | 25 | "https://github.com/opeolluwa/filesync" 26 | 27 | 33 | 34 | 35 | 36 | 37 |
38 |
39 | } 40 | } 41 | -------------------------------------------------------------------------------- /desktop/src/screens/ddwelcome/choose_platform.rs: -------------------------------------------------------------------------------- 1 | use crate::layouts::welcome_screen_layout::WelcomeScreenLayout; 2 | use filesync_icons::platform::{AndroidLogo, LinuxLogo, MacOsLogo, WindowsPlatformLogo}; 3 | use leptos::leptos_dom::logging::console_log; 4 | use leptos::prelude::{signal, ClassAttribute, ElementChild, Get, OnAttribute, RwSignal, Set}; 5 | use leptos::task::spawn_local; 6 | use leptos::view; 7 | use leptos_qr::QrCode; 8 | use tauri_bindgen::wifi_bindgen::WifiCredentials; 9 | use tauri_sys::core::invoke; 10 | use tauri_wasm_bindgen::api::invoke::invoke_tauri_command_without_args; 11 | use thaw::{Dialog, DialogBody, DialogContent, DialogSurface, DialogTitle, Theme}; 12 | 13 | #[leptos::component] 14 | pub fn SelectPlatformScreen() -> impl leptos::IntoView { 15 | let platform_logo_class_rules = "dark:bg-gray-700 bg-gray-200 text-gray-400 hover:bg-app-50 hover:text-app-600 transition-all duration-200 p-4 rounded-xl shadow hover:shadow-none cursor-pointer dark:hover:bg-gray-700/50"; 16 | 17 | let android = AndroidLogo(); 18 | let macOs = MacOsLogo(); 19 | let windows = WindowsPlatformLogo(); 20 | let linux = LinuxLogo(); 21 | 22 | let (wifi_creds, set_wifi_creds) = signal::(WifiCredentials::default()); 23 | let open_android_qr_modal = RwSignal::new(false); 24 | 25 | let _current_theme = Theme::use_rw_theme().get().name; 26 | 27 | // let creds = wifi_creds.get(); 28 | let manage_android_click_event = move || { 29 | open_android_qr_modal.set(true); 30 | 31 | spawn_local(async move { 32 | let wifi_creds = 33 | invoke::("generate_android_wifi_credentials", &()).await; 34 | set_wifi_creds.set(wifi_creds); 35 | }); 36 | }; 37 | view! { 38 | 39 | 40 |
41 |
42 |

43 | "Select platform of the other device" 44 |

45 | 46 |
47 | 53 | 54 | 55 | 56 |
57 |
58 |
59 | 60 | 61 | 62 | 63 | 64 | "Scan QR code to connect Android device" 65 | 66 | 67 |
68 | 75 |
76 |
77 | 78 |
79 |
80 |
81 |
82 | } 83 | } 84 | -------------------------------------------------------------------------------- /desktop/src/screens/ddwelcome/default.rs: -------------------------------------------------------------------------------- 1 | use leptos::prelude::CustomAttribute; 2 | use leptos::prelude::{ClassAttribute, ElementChild, OnAttribute}; 3 | use leptos::view; 4 | use leptos_qr::QrCode; 5 | 6 | use js_bindgen::navigate::change_location_to; 7 | 8 | use crate::routes::{RECEIVE_ROUTE, SELECT_PLATFORM_ROUTE}; 9 | 10 | #[leptos::component] 11 | pub fn DefaultScreen() -> impl leptos::IntoView { 12 | let app_action_css_rule ="dark:bg-gray-700 dark:hover:bg-gray-700/50 bg-gray-200 text-gray-400 hover:bg-app-50 hover:text-app-600 transition-all duration-200 p-4 rounded-xl shadow hover:shadow-none cursor-pointer flex flex-col ites-center justify-center"; 13 | 14 | view! { 15 |
16 |
17 |
18 | 25 |
26 | 27 |

28 | "Scan QR code to pair mobile device" 29 |

30 |

31 | "Open the mobile companion app and follow prompt to scan QR code" 32 |

33 | 34 |
35 |
36 | } 37 | } 38 | -------------------------------------------------------------------------------- /desktop/src/screens/ddwelcome/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod choose_platform; 2 | pub mod default; 3 | pub mod receive; 4 | pub mod select_action; 5 | pub mod send; 6 | -------------------------------------------------------------------------------- /desktop/src/screens/ddwelcome/receive.rs: -------------------------------------------------------------------------------- 1 | use leptos::prelude::{ClassAttribute, ElementChild}; 2 | use leptos::view; 3 | 4 | use crate::layouts::welcome_screen_layout::WelcomeScreenLayout; 5 | 6 | #[leptos::component] 7 | pub fn ReceiveScreen() -> impl leptos::IntoView { 8 | view! { 9 | 10 |
11 |

heheh

12 | "hh" 13 |
14 | 15 |
16 | } 17 | } 18 | -------------------------------------------------------------------------------- /desktop/src/screens/ddwelcome/select_action.rs: -------------------------------------------------------------------------------- 1 | use leptos::{view, IntoView}; 2 | 3 | use crate::screens::welcome::default::DefaultScreen; 4 | 5 | #[leptos::component] 6 | pub fn TransferScreen() -> impl IntoView { 7 | view! { } 8 | } 9 | -------------------------------------------------------------------------------- /desktop/src/screens/ddwelcome/send.rs: -------------------------------------------------------------------------------- 1 | use leptos::prelude::{ClassAttribute, CustomAttribute, ElementChild}; 2 | use leptos::view; 3 | use thaw::{FileList, Upload, UploadDragger}; 4 | use web_sys::console; 5 | 6 | use js_bindgen::navigate::change_location_to; 7 | 8 | use crate::layouts::welcome_screen_layout::WelcomeScreenLayout; 9 | 10 | #[leptos::component] 11 | pub fn SendScreen() -> impl leptos::IntoView { 12 | let custom_request = move |file_list: FileList| { 13 | let total_number_of_files = file_list.length(); 14 | 15 | //TODO: use parallel processing 16 | for index in 0..=total_number_of_files { 17 | let file_name = file_list.item(index).unwrap().name(); 18 | let _web_sysfile_blob: js_sys::Promise = file_list.item(index).unwrap().array_buffer(); 19 | change_location_to("/home"); 20 | console::log_1(&file_name.into()); 21 | } 22 | 23 | console::log_1(&"Hello using web-sys".into()); 24 | console::log_1(&file_list.into()); 25 | }; 26 | 27 | view! { 28 | 29 | 34 | 35 |
36 | 44 | 49 | 50 | 51 |

Drag and drop your files here

52 |
53 |
54 |
55 |
56 | } 57 | 58 | // let send_steps = [ 59 | // "Create Wi-fi hostpot on yor phone", 60 | // "Connect your Laptop the phone hotspot", 61 | // "Open Filesync mobile", 62 | // "Initialize a receive action", 63 | // "Scan Qr code below", 64 | // ]; 65 | 66 | // view! { 67 | // 68 | //
69 | // 70 | // Connect mobile 71 | // 72 | //
73 | //
    74 | // {send_steps.map(|step| view! {
  1. {step}
  2. }).collect_view()} 75 | //
76 | //
77 | // 86 | //
87 | //
88 | // } 89 | } 90 | -------------------------------------------------------------------------------- /desktop/src/screens/history.rs: -------------------------------------------------------------------------------- 1 | use leptos::{view, IntoView}; 2 | use thaw::Text; 3 | 4 | use crate::layouts::desktop_layout::DesktopLayout; 5 | 6 | #[leptos::component] 7 | pub fn HistoryScreen() -> impl IntoView { 8 | view! { 9 | 10 | 11 | 12 | Transfer history 13 | 14 | 15 | 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /desktop/src/screens/home.rs: -------------------------------------------------------------------------------- 1 | use leptos::view; 2 | 3 | use crate::layouts::desktop_layout::DesktopLayout; 4 | 5 | #[leptos::component] 6 | pub fn HomeScreen() -> impl leptos::IntoView { 7 | view! { hhhhh } 8 | } 9 | -------------------------------------------------------------------------------- /desktop/src/screens/mod.rs: -------------------------------------------------------------------------------- 1 | // pub mod about; 2 | // pub mod history; 3 | pub mod home; 4 | // pub mod settings; 5 | // pub mod share; 6 | pub mod welcome; 7 | -------------------------------------------------------------------------------- /desktop/src/screens/settings.rs: -------------------------------------------------------------------------------- 1 | use leptos::prelude::ClassAttribute; 2 | use leptos::prelude::CustomAttribute; 3 | use leptos::prelude::ElementChild; 4 | use leptos::{leptos_dom::logging::console_log, prelude::signal, task::spawn_local, view}; 5 | use thaw::{Flex, FlexAlign, FlexJustify, Image, Text}; 6 | 7 | use crate::layouts::welcome_screen_layout::WelcomeScreenLayout; 8 | use crate::tauri_wasm_bindgen::invoke::invoke_tauri_command_without_args; 9 | 10 | #[leptos::component] 11 | pub fn SettingsScreen() -> impl leptos::IntoView { 12 | let (app_name, _set_app_name) = signal("Filesync"); 13 | let (app_version, _set_app_version) = signal("0.7.9"); 14 | 15 | spawn_local(async move { 16 | let app_config = invoke_tauri_command_without_args("get_app_config").await; 17 | console_log(&app_config.as_string().unwrap()); 18 | }); 19 | 20 | view! { 21 | 22 | 23 | 24 | 25 | {app_name} {" "} {app_version} 26 | 27 |
28 | 29 | "https://github.com/opeolluwa/filesync" 30 | 31 | 37 | 38 | 39 | 40 | 41 |
42 |
43 | 44 |
45 | } 46 | } 47 | -------------------------------------------------------------------------------- /desktop/src/screens/share.rs: -------------------------------------------------------------------------------- 1 | use leptos::prelude::{ClassAttribute, CustomAttribute, ElementChild}; 2 | use leptos::view; 3 | use thaw::{FileList, Upload, UploadDragger}; 4 | 5 | #[leptos::component] 6 | pub fn ShareScreen() -> impl leptos::IntoView { 7 | let custom_request = move |file_list: FileList| { 8 | let _len = file_list.length(); 9 | println!("heheh") 10 | }; 11 | 12 | view! { 13 | 18 | 19 |
20 | 28 | 33 | 34 | 35 |

Drag and drop your files here

36 |
37 |
38 |
39 | } 40 | } 41 | -------------------------------------------------------------------------------- /desktop/src/screens/welcome.rs: -------------------------------------------------------------------------------- 1 | use crate::leptos_dom::logging::console_log; 2 | use leptos::prelude::*; 3 | use leptos::task::spawn_local; 4 | use leptos::view; 5 | use leptos_qr::QrCode; 6 | 7 | #[leptos::component] 8 | pub fn WelcomeScreen() -> impl leptos::IntoView { 9 | let (greet_msg, set_greet_msg) = signal(String::new()); 10 | let (server_config, set_server_config) = signal::(String::new()); 11 | 12 | //TODO: add validation to ensure the server is not a loop back address 13 | 14 | spawn_local(async move { 15 | let result = call_greet("alex").await; 16 | set_greet_msg.set(result); 17 | let server_config = cmd_extract_connection().await; 18 | set_server_config.set(format!("{:#?}", server_config.to_string())); 19 | }); 20 | 21 | view! { 22 |
23 | 24 |
25 | 32 |
33 | 34 |
35 | "Scan QR code to pair mobile device" 36 |
37 | 38 | 41 | 42 |
43 | } 44 | } 45 | 46 | async fn call_greet(name: &str) -> String { 47 | shared::cmd::greet(&name).await 48 | } 49 | 50 | use shared::cmd; 51 | use shared::config::EmbeddedServerConfig; 52 | 53 | pub async fn cmd_extract_connection() -> EmbeddedServerConfig { 54 | cmd::extract_connection().await 55 | } 56 | -------------------------------------------------------------------------------- /desktop/tailwind.config.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | 3 | const colors = require("tailwindcss/colors"); 4 | const plugin = require("tailwindcss/plugin"); 5 | 6 | module.exports = { 7 | content: [ 8 | "./index.html", 9 | "./src/**/*.rs", 10 | "./mobile/**/*.rs", 11 | "desktop/**/*.rs", 12 | ], 13 | theme: { 14 | colors: { 15 | transparent: "transparent", 16 | current: "currentColor", 17 | black: colors.black, 18 | white: colors.white, 19 | gray: colors.gray, 20 | emerald: colors.emerald, 21 | indigo: colors.indigo, 22 | yellow: colors.yellow, 23 | dark: colors.neutral, 24 | red: colors.red, 25 | accent: "rgba(226,233,252,255)", 26 | card: "#f9fbfe", 27 | slate: colors.slate, 28 | app: { 29 | DEFAULT: "#3074F5", 30 | 50: "#DFEAFD", 31 | 100: "#CCDDFD", 32 | 200: "#A5C2FB", 33 | 300: "#7EA8F9", 34 | 400: "#578EF7", 35 | 500: "#3074F5", 36 | 600: "#0B55E2", 37 | 700: "#0841AC", 38 | 800: "#062D77", 39 | 900: "#031941", 40 | 950: "#020F27", 41 | dark: "#1d232a", 42 | }, 43 | }, 44 | extend: {}, 45 | }, 46 | plugins: [ 47 | require("@tailwindcss/forms"), 48 | require("daisyui"), 49 | require("tailwind-plugin-ripple-effect"), 50 | ], 51 | }; 52 | -------------------------------------------------------------------------------- /screenshots/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opeolluwa/filesync/bfc91a48d5432ba8131c5a44cf1104d0fae2b182/screenshots/.DS_Store -------------------------------------------------------------------------------- /screenshots/filesync-0.7.16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opeolluwa/filesync/bfc91a48d5432ba8131c5a44cf1104d0fae2b182/screenshots/filesync-0.7.16.png -------------------------------------------------------------------------------- /screenshots/filesync-new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opeolluwa/filesync/bfc91a48d5432ba8131c5a44cf1104d0fae2b182/screenshots/filesync-new.png -------------------------------------------------------------------------------- /screenshots/filesync.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opeolluwa/filesync/bfc91a48d5432ba8131c5a44cf1104d0fae2b182/screenshots/filesync.png -------------------------------------------------------------------------------- /screenshots/home-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opeolluwa/filesync/bfc91a48d5432ba8131c5a44cf1104d0fae2b182/screenshots/home-dark.png -------------------------------------------------------------------------------- /screenshots/home-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opeolluwa/filesync/bfc91a48d5432ba8131c5a44cf1104d0fae2b182/screenshots/home-light.png -------------------------------------------------------------------------------- /screenshots/mobile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opeolluwa/filesync/bfc91a48d5432ba8131c5a44cf1104d0fae2b182/screenshots/mobile.png -------------------------------------------------------------------------------- /screenshots/music-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opeolluwa/filesync/bfc91a48d5432ba8131c5a44cf1104d0fae2b182/screenshots/music-dark.png -------------------------------------------------------------------------------- /screenshots/music-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opeolluwa/filesync/bfc91a48d5432ba8131c5a44cf1104d0fae2b182/screenshots/music-light.png -------------------------------------------------------------------------------- /screenshots/share-files-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opeolluwa/filesync/bfc91a48d5432ba8131c5a44cf1104d0fae2b182/screenshots/share-files-light.png -------------------------------------------------------------------------------- /screenshots/share-files.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opeolluwa/filesync/bfc91a48d5432ba8131c5a44cf1104d0fae2b182/screenshots/share-files.png -------------------------------------------------------------------------------- /tmp/.zcrash: -------------------------------------------------------------------------------- 1 | https://dribbble.com/shots/6660249-File-Share-App-Ui-Desgin 2 | 3 | https://www.behance.net/gallery/201636587/Smart-Swtich?tracking_source=search_projects|file+transfer&l=9 4 | 5 | https://www.behance.net/gallery/200607041/EasyShare-File-Transfer-App-Ui-Design?tracking_source=search_projects|file+transfer&l=1 6 | 7 | desktop https://dribbble.com/shots/21154209-Incloud-Cloud-Storage-Dashboard 8 | 9 | Please enter a password to protect the secret key. Password: Password (one more 10 | time): Deriving a key from the password in order to encrypt the secret key... 11 | done 12 | 13 | ## Your keypair was generated successfully Private: /Users/USER/.tauri/filesync.key (Keep it secret!) Public: /Users/USER/.tauri/filesync.key.pub 14 | 15 | Environment variables used to sign: `TAURI_SIGNING_PRIVATE_KEY` Path or String 16 | of your private key `TAURI_SIGNING_PRIVATE_KEY_PASSWORD` Your private key 17 | password (optional) 18 | 19 | ## ATTENTION: If you lose your private key OR password, you'll not be able to sign your update package and updates will not work. 20 | 21 | USER@Uranium filesync % ls ~/.tauri/myapp.key ls: /Users/USER/.tauri/myapp.key: 22 | No such file or directory USER@Uranium filesync % 23 | 24 | - History restored 25 | 26 | desktop inspitarion 27 | https://dribbble.com/shots/14167930-Perpail-File-Sharing-Web-App 28 | 29 | mobile https://dribbble.com/shots/15162851-Files-Manager-Mobile-App 30 | https://dribbble.com/shots/19319600-Halver-File-transfer-app-UI 31 | https://dribbble.com/shots/14536756-File-Manager-Mobile-App-Exploration?utm_source=pinterest&utm_campaign=pinterest_shot&utm_content=File%20Manager%20Mobile%20App%20Exploration&utm_medium=Social_Share 32 | https://dribbble.com/shots/15089330/attachments/6820385?mode=media 33 | 34 | https://dribbble.com/shots/20336532-Cloud-Storage-Mobile-App 35 | https://dribbble.com/shots/4620760-Contact-App-Share-Options/attachments/1044517?mode=media 36 | 37 | 38 | 39 | 40 | 000000 41 | CF0F47 42 | FF0B55 43 | FFDEDE -------------------------------------------------------------------------------- /tmp/design.zcrash: -------------------------------------------------------------------------------- 1 | https://dribbble.com/shots/19708255-Storage-App-Ui-Design 2 | 3 | 4 | barcode scanner mobile 5 | https://dribbble.com/shots/12986128-Self-checkout-smart-shopping-app 6 | https://dribbble.com/shots/20812421-Scanny-Barcode-QR-Code-Reader --------------------------------------------------------------------------------