├── .codeclimate.yml ├── .editorconfig ├── .gitignore ├── .travis.yml ├── .tx └── config ├── LICENSE ├── README.md ├── brunch-config.coffee ├── coffeelint.json ├── config.xml ├── fastlane ├── AppFile ├── Deliverfile ├── Fastfile ├── README.md ├── metadata │ ├── android │ │ └── en-US │ │ │ ├── changelogs │ │ │ ├── 10.txt │ │ │ ├── 104018.txt │ │ │ ├── 104028.txt │ │ │ ├── 104038.txt │ │ │ ├── 11.txt │ │ │ ├── 110.txt │ │ │ ├── 111.txt │ │ │ ├── 112.txt │ │ │ ├── 113.txt │ │ │ ├── 114.txt │ │ │ ├── 116.txt │ │ │ ├── 117.txt │ │ │ ├── 1188.txt │ │ │ ├── 12.txt │ │ │ ├── 13.txt │ │ │ ├── 14.txt │ │ │ ├── 16.txt │ │ │ ├── 18.txt │ │ │ ├── 19.txt │ │ │ ├── 2.txt │ │ │ ├── 3.txt │ │ │ ├── 4.txt │ │ │ ├── 5.txt │ │ │ ├── 6.txt │ │ │ ├── 7.txt │ │ │ ├── 8.txt │ │ │ └── 9.txt │ │ │ ├── full_description.txt │ │ │ ├── images │ │ │ └── icon.png │ │ │ ├── short_description.txt │ │ │ ├── title.txt │ │ │ └── video.txt │ └── ios │ │ ├── copyright.txt │ │ ├── en-US │ │ ├── description.txt │ │ ├── keywords.txt │ │ ├── marketing_url.txt │ │ ├── name.txt │ │ ├── privacy_url.txt │ │ ├── release_notes.txt │ │ └── support_url.txt │ │ ├── primary_category.txt │ │ └── secondary_category.txt └── screenshots │ └── ios │ ├── README.txt │ └── en-US │ ├── 1_ipadPro_1.1-ipad-welcome.png │ ├── 1_iphone6Plus_1.1-welcome.png │ ├── 2_ipadPro_2.2-ipad-access-files.png │ ├── 2_iphone6Plus_2.2-access-files.png │ ├── 3_ipadPro_3.3-ipad-folder.png │ ├── 3_iphone6Plus_3.3-folder.png │ ├── 4_ipadPro_4.4-ipad-files.png │ ├── 4_iphone6Plus_4.4-files.png │ ├── 5_ipadPro_5.5-ipad-config.png │ └── 5_iphone6Plus_5.5-config.png ├── hooks └── README.md ├── keys ├── README.md ├── android │ └── .gitkeep └── ios │ └── .gitkeep ├── package.json ├── res ├── icon.png ├── icon.svg ├── icons │ ├── android │ │ ├── icon-144-xxhdpi.png │ │ ├── icon-192-xxxhdpi.png │ │ ├── icon-36-ldpi.png │ │ ├── icon-48-mdpi.png │ │ ├── icon-72-hdpi.png │ │ └── icon-96-xhdpi.png │ └── ios │ │ ├── icon-40-2x.png │ │ ├── icon-40.png │ │ ├── icon-50-2x.png │ │ ├── icon-50.png │ │ ├── icon-57-2x.png │ │ ├── icon-57.png │ │ ├── icon-60-2x.png │ │ ├── icon-60-3x.png │ │ ├── icon-60.png │ │ ├── icon-72-2x.png │ │ ├── icon-72.png │ │ ├── icon-76-2x.png │ │ ├── icon-76.png │ │ ├── icon-small-2x.png │ │ └── icon-small.png ├── screens │ ├── android │ │ ├── screen-hdpi-landscape.png │ │ ├── screen-hdpi-portrait.png │ │ ├── screen-ldpi-landscape.png │ │ ├── screen-ldpi-portrait.png │ │ ├── screen-mdpi-landscape.png │ │ ├── screen-mdpi-portrait.png │ │ ├── screen-xhdpi-landscape.png │ │ ├── screen-xhdpi-portrait.png │ │ ├── screen-xxhdpi-landscape.png │ │ ├── screen-xxhdpi-portrait.png │ │ ├── screen-xxxhdpi-landscape.png │ │ └── screen-xxxhdpi-portrait.png │ └── ios │ │ ├── screen-ipad-landscape-2x.png │ │ ├── screen-ipad-landscape.png │ │ ├── screen-ipad-portrait-2x.png │ │ ├── screen-ipad-portrait.png │ │ ├── screen-iphone-568h-2x.png │ │ ├── screen-iphone-landscape-736h.png │ │ ├── screen-iphone-portrait-2x.png │ │ ├── screen-iphone-portrait-667h.png │ │ ├── screen-iphone-portrait-736h.png │ │ └── screen-iphone-portrait.png ├── splash.png └── splash.svg ├── src ├── app │ ├── application.coffee │ ├── background_service.coffee │ ├── components │ │ └── information │ │ │ ├── information.model.coffee │ │ │ ├── information.style.styl │ │ │ ├── information.template.jade │ │ │ └── information.view.coffee │ ├── lib │ │ ├── android_calendar_handler.coffee │ │ ├── check_platform_versions.coffee │ │ ├── config.coffee │ │ ├── connection_handler.coffee │ │ ├── database.coffee │ │ ├── deleted_document.coffee │ │ ├── device_status.coffee │ │ ├── file_cache_handler.coffee │ │ ├── first_replication.coffee │ │ ├── initialize.coffee │ │ ├── log_sender.coffee │ │ ├── media │ │ │ ├── media_uploader.coffee │ │ │ └── picture_handler.coffee │ │ ├── mimetype.coffee │ │ ├── notification_handler.coffee │ │ ├── path.coffee │ │ ├── permission.coffee │ │ ├── persistent_log.coffee │ │ ├── random.coffee │ │ ├── remote_request.coffee │ │ ├── request.js │ │ ├── request_cozy.coffee │ │ ├── synchronization.coffee │ │ ├── toast.coffee │ │ ├── translation.coffee │ │ ├── url_validator.coffee │ │ └── utils.coffee │ ├── locales │ │ ├── de.json │ │ ├── en.json │ │ ├── es.json │ │ ├── fr.json │ │ ├── ja.json │ │ ├── ko.json │ │ └── ru.json │ ├── migrations │ │ ├── 0.3.0.coffee │ │ ├── 1.0.0.coffee │ │ ├── 1.1.1.coffee │ │ ├── 1.2.0.coffee │ │ ├── 1.3.0.coffee │ │ ├── 2.0.0.coffee │ │ └── migration.coffee │ ├── models │ │ ├── file.coffee │ │ ├── file_list.coffee │ │ └── service_manager.coffee │ ├── replicator │ │ ├── change │ │ │ ├── change_contact_handler.coffee │ │ │ ├── change_dispatcher.coffee │ │ │ ├── change_event_handler.coffee │ │ │ ├── change_file_handler.coffee │ │ │ ├── change_folder_handler.coffee │ │ │ ├── change_notification_handler.coffee │ │ │ ├── change_tag_handler.coffee │ │ │ └── conflicts_handler.coffee │ │ ├── design_documents.coffee │ │ ├── filesystem.coffee │ │ ├── filter_manager.coffee │ │ ├── fromDevice │ │ │ ├── android_account.coffee │ │ │ ├── changes_importer.coffee │ │ │ ├── contact_importer.coffee │ │ │ ├── event_importer.coffee │ │ │ └── notification_importer.coffee │ │ ├── main.coffee │ │ ├── replication_launcher.coffee │ │ └── transformer │ │ │ ├── cozy_to_android_calendar.coffee │ │ │ ├── cozy_to_android_contact.coffee │ │ │ └── cozy_to_android_event.coffee │ ├── router.coffee │ ├── styles │ │ ├── application.styl │ │ ├── base │ │ │ ├── _background.styl │ │ │ ├── _buttons.styl │ │ │ ├── _colors.styl │ │ │ ├── _foreground.styl │ │ │ ├── _form.styl │ │ │ ├── _icons.styl │ │ │ ├── _page.styl │ │ │ └── _variable.styl │ │ └── components │ │ │ ├── _errors.styl │ │ │ ├── _file_viewer.styl │ │ │ ├── _list-item.styl │ │ │ ├── _mediaPlayer.styl │ │ │ ├── _menu.styl │ │ │ ├── _splash.styl │ │ │ ├── _toolbar.styl │ │ │ └── _wizard.styl │ ├── templates │ │ ├── breadcrumbs.jade │ │ ├── config.jade │ │ ├── file_viewer.jade │ │ ├── layout │ │ │ ├── header.jade │ │ │ ├── layout.jade │ │ │ └── layout_with_header.jade │ │ ├── media_player.jade │ │ └── onboarding │ │ │ ├── check_credentials.jade │ │ │ ├── password.jade │ │ │ ├── permission_calendars.jade │ │ │ ├── permission_contacts.jade │ │ │ ├── permission_files.jade │ │ │ ├── permission_photos.jade │ │ │ ├── url.jade │ │ │ └── welcome.jade │ └── views │ │ ├── breadcrumbs.coffee │ │ ├── config.coffee │ │ ├── file_viewer.coffee │ │ ├── layout │ │ ├── base_view.coffee │ │ ├── header.coffee │ │ ├── layout.coffee │ │ └── layout_with_header.coffee │ │ ├── media_player.coffee │ │ └── onboarding │ │ ├── check_credentials.coffee │ │ ├── password.coffee │ │ ├── permission.coffee │ │ ├── url.coffee │ │ └── welcome.coffee ├── assets │ ├── backgroundservice.html │ ├── fonts │ │ ├── fonts.css │ │ ├── material-icons │ │ ├── mdi │ │ │ ├── css │ │ │ │ └── materialdesignicons.css │ │ │ └── fonts │ │ ├── roboto │ │ └── sourcesanspro-regular.woff │ ├── img │ │ ├── background.jpg │ │ ├── glyphicons-halflings-white.png │ │ ├── glyphicons-halflings.png │ │ ├── icon-calendar.svg │ │ ├── icon-contact.svg │ │ ├── icon-files.svg │ │ ├── icon-photos.svg │ │ ├── icon.png │ │ ├── menu-icon.png │ │ ├── menu-icon.svg │ │ ├── mini_logo.png │ │ ├── spinner-grey.svg │ │ ├── spinner.svg │ │ ├── sprite-files-type.svg │ │ └── sync.png │ ├── index.html │ └── viewer ├── modules │ ├── async.js │ ├── node-polyglot.js │ ├── path.js │ ├── pouchdb.js │ ├── process.js │ ├── semver.js │ └── validator.js ├── test │ ├── application.coffee │ ├── assets │ │ ├── _tests.html │ │ └── manual_test_guide.md │ ├── contacts.coffee │ ├── fixtures │ │ ├── cordova_contact.json │ │ ├── cozy_calendar.json │ │ ├── cozy_contact.json │ │ ├── event_punctual_android.json │ │ ├── event_punctual_androidcreated.json │ │ ├── event_punctual_cozy.json │ │ ├── event_recurring_android.json │ │ ├── event_recurring_cozy.json │ │ ├── event_recurringallday_android.json │ │ ├── event_recurringallday_androidcreated.json │ │ └── event_recurringallday_cozy.json │ ├── helper │ │ ├── config.coffee │ │ ├── database.coffee │ │ ├── fixture.coffee │ │ └── helper.coffee │ ├── initialize.coffee │ └── unit │ │ ├── lib │ │ ├── config.coffee │ │ ├── connection_handler.coffee │ │ ├── database.coffee │ │ ├── file_cache_handler.coffee │ │ ├── translation.coffee │ │ ├── url_validator.coffee │ │ └── utils.coffee │ │ └── replicator │ │ ├── change │ │ ├── change_dispatcher.coffee │ │ └── change_notification_handler.coffee │ │ ├── design_documents.coffee │ │ ├── filter_manager.coffee │ │ ├── main.coffee │ │ ├── replication_launcher.coffee │ │ └── transformer │ │ ├── cozy_to_android_calendar.coffee │ │ ├── cozy_to_android_contact.coffee │ │ └── cozy_to_android_event.coffee └── vendor │ ├── css │ ├── animate.css │ ├── materialize.css │ └── snap.css │ └── scripts │ ├── backbone.js │ ├── jquery.js │ ├── materialize.js │ ├── moment-timezone-with-data.js │ ├── moment.js │ ├── snap.js │ └── underscore.js └── yarn.lock /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | languages: 4 | JavaScript: true 5 | 6 | exclude_paths: 7 | - src/vendor 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | # editorconfig is a unified configuration file that all editors can take 3 | # into account 4 | root = true 5 | 6 | [*] 7 | charset = utf-8 8 | end_of_line = lf 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | indent_style = space 12 | indent_size = 4 13 | 14 | [*.jade] 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | platforms 2 | plugins 3 | keys/android/* 4 | !keys/android/.gitkeep 5 | keys/ios/* 6 | !keys/ios/.gitkeep 7 | *.transifex.coffee 8 | www 9 | node_modules 10 | src/coverage 11 | src/node_modules 12 | build 13 | res/drawable* 14 | res/mipmap* 15 | 16 | # fastlane 17 | /fastlane/report.xml 18 | /fastlane/Error*.png 19 | /fastlane/Preview.html 20 | Preview.html 21 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | language: android 4 | 5 | sudo: false 6 | 7 | jdk: 8 | - oraclejdk8 9 | 10 | env: 11 | - TRAVIS_NODE_VERSION="6.9.1" 12 | 13 | android: 14 | components: 15 | - tools # to get the new `repository-11.xml` 16 | - tools # to install Android SDK tools 25.1.x (x = 6 right now) 17 | - build-tools-25.0.0 18 | - android-25 19 | 20 | cache: 21 | directories: 22 | - node_modules 23 | - platforms 24 | - plugins 25 | 26 | install: 27 | - nvm install $TRAVIS_NODE_VERSION 28 | - npm install 29 | 30 | script: 31 | - npm run lint 32 | - npm run test:coverage 33 | - ./node_modules/.bin/cordova build android 34 | 35 | after_success: 36 | - ./node_modules/.bin/codecov 37 | -------------------------------------------------------------------------------- /.tx/config: -------------------------------------------------------------------------------- 1 | [main] 2 | host = https://www.transifex.com 3 | 4 | [cozy-mobile.enjson] 5 | file_filter = src/app/locales/.json 6 | source_file = src/app/locales/en.json 7 | source_lang = en 8 | type = KEYVALUEJSON 9 | -------------------------------------------------------------------------------- /brunch-config.coffee: -------------------------------------------------------------------------------- 1 | module.exports.config = 2 | 3 | # See docs at https://github.com/brunch/brunch/blob/stable/docs/config.md. 4 | 5 | paths: 6 | public: './www' 7 | watched: ['src/app', 'src/modules', 'src/vendor', 'src/assets'] 8 | 9 | conventions: 10 | assets: /^src\/assets/ 11 | 12 | plugins: 13 | coffeelint: 14 | options: 15 | indentation: value: 4 16 | level: 'error' 17 | 18 | modules: 19 | nameCleaner: (path) -> 20 | path.replace /^src\/(modules|app)\//, '' 21 | .replace /process\/browser.js$/, 'process.js' 22 | 23 | files: 24 | javascripts: 25 | joinTo: 26 | 'javascripts/app.js': /^src\/app/ 27 | 'javascripts/modules.js': /^src\/modules/ 28 | 'javascripts/vendor.js': /^src\/vendor/ 29 | order: 30 | # Files in `vendor` directories are compiled before other files 31 | # even if they aren't specified in order. 32 | before: [ 33 | 'src/vendor/scripts/jquery.js' 34 | 'src/vendor/scripts/underscore.js' 35 | 'src/vendor/scripts/backbone.js' 36 | 'src/vendor/scripts/moment.js' 37 | 'src/vendor/scripts/moment-timezone-with-data.js' 38 | 'src/vendor/scripts/materialize.js' 39 | 'src/vendor/scripts/snap.js' 40 | ] 41 | 42 | stylesheets: 43 | joinTo: 44 | 'stylesheets/app.css' 45 | order: 46 | before: [ 47 | 'src/vendor/css/animate.css' 48 | 'src/vendor/css/materialize.css' 49 | 'src/vendor/css/snap.css' 50 | ] 51 | 52 | templates: 53 | joinTo: 'javascripts/app.js' 54 | -------------------------------------------------------------------------------- /coffeelint.json: -------------------------------------------------------------------------------- 1 | { 2 | "arrow_spacing": { 3 | "level": "ignore" 4 | }, 5 | "braces_spacing": { 6 | "level": "ignore", 7 | "spaces": 0, 8 | "empty_object_spaces": 0 9 | }, 10 | "camel_case_classes": { 11 | "level": "error" 12 | }, 13 | "coffeescript_error": { 14 | "level": "error" 15 | }, 16 | "colon_assignment_spacing": { 17 | "level": "ignore", 18 | "spacing": { 19 | "left": 0, 20 | "right": 0 21 | } 22 | }, 23 | "cyclomatic_complexity": { 24 | "level": "ignore", 25 | "value": 10 26 | }, 27 | "duplicate_key": { 28 | "level": "error" 29 | }, 30 | "empty_constructor_needs_parens": { 31 | "level": "ignore" 32 | }, 33 | "ensure_comprehensions": { 34 | "level": "warn" 35 | }, 36 | "eol_last": { 37 | "level": "ignore" 38 | }, 39 | "indentation": { 40 | "value": 4, 41 | "level": "error" 42 | }, 43 | "line_endings": { 44 | "level": "ignore", 45 | "value": "unix" 46 | }, 47 | "max_line_length": { 48 | "value": 80, 49 | "level": "error", 50 | "limitComments": true 51 | }, 52 | "missing_fat_arrows": { 53 | "level": "ignore", 54 | "is_strict": false 55 | }, 56 | "newlines_after_classes": { 57 | "value": 3, 58 | "level": "ignore" 59 | }, 60 | "no_backticks": { 61 | "level": "error" 62 | }, 63 | "no_debugger": { 64 | "level": "warn", 65 | "console": false 66 | }, 67 | "no_empty_functions": { 68 | "level": "ignore" 69 | }, 70 | "no_empty_param_list": { 71 | "level": "ignore" 72 | }, 73 | "no_implicit_braces": { 74 | "level": "ignore", 75 | "strict": true 76 | }, 77 | "no_implicit_parens": { 78 | "level": "ignore", 79 | "strict": true 80 | }, 81 | "no_interpolation_in_single_quotes": { 82 | "level": "ignore" 83 | }, 84 | "no_nested_string_interpolation": { 85 | "level": "warn" 86 | }, 87 | "no_plusplus": { 88 | "level": "ignore" 89 | }, 90 | "no_private_function_fat_arrows": { 91 | "level": "warn" 92 | }, 93 | "no_stand_alone_at": { 94 | "level": "ignore" 95 | }, 96 | "no_tabs": { 97 | "level": "error" 98 | }, 99 | "no_this": { 100 | "level": "ignore" 101 | }, 102 | "no_throwing_strings": { 103 | "level": "error" 104 | }, 105 | "no_trailing_semicolons": { 106 | "level": "error" 107 | }, 108 | "no_trailing_whitespace": { 109 | "level": "error", 110 | "allowed_in_comments": false, 111 | "allowed_in_empty_lines": true 112 | }, 113 | "no_unnecessary_double_quotes": { 114 | "level": "ignore" 115 | }, 116 | "no_unnecessary_fat_arrows": { 117 | "level": "warn" 118 | }, 119 | "non_empty_constructor_needs_parens": { 120 | "level": "ignore" 121 | }, 122 | "prefer_english_operator": { 123 | "level": "ignore", 124 | "doubleNotLevel": "ignore" 125 | }, 126 | "space_operators": { 127 | "level": "ignore" 128 | }, 129 | "spacing_after_comma": { 130 | "level": "ignore" 131 | }, 132 | "transform_messes_up_line_numbers": { 133 | "level": "warn" 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /fastlane/AppFile: -------------------------------------------------------------------------------- 1 | app_identifier "io.cozy.mobile.sync" 2 | apple_id "simon@cozycloud.cc" 3 | team_name "CozyCloud" 4 | itc_team_name "CozyCloud" 5 | -------------------------------------------------------------------------------- /fastlane/Deliverfile: -------------------------------------------------------------------------------- 1 | ###################### More Options ###################### 2 | # If you want to have even more control, check out the documentation 3 | # https://github.com/fastlane/fastlane/blob/master/deliver/Deliverfile.md 4 | 5 | 6 | ###################### Automatically generated ###################### 7 | # Feel free to remove the following line if you use fastlane (which you should) 8 | 9 | app_identifier "io.cozy.mobile.sync" # The bundle identifier of your app 10 | username "simon@cozycloud.cc" # your Apple ID user 11 | 12 | metadata_path "./fastlane/metadata/ios" 13 | screenshots_path "./fastlane/screenshots/ios" 14 | submit_for_review false 15 | -------------------------------------------------------------------------------- /fastlane/Fastfile: -------------------------------------------------------------------------------- 1 | fastlane_version "1.99.0" 2 | default_platform :ios 3 | 4 | APP_NAME = "Cozy Mobile" 5 | APP_FILENAME = "CozyMobile" 6 | PLATFORM = { :ios => "platforms/ios", :android => "platforms/android" } 7 | SECURITY = { :ios => "keys/ios", :android => "keys/android" } 8 | BUILD = { :ios => "build/ios", :android => "build/android" } 9 | 10 | 11 | def build(platform) 12 | sh " cd .. && ./node_modules/.bin/brunch build && ./node_modules/.bin/cordova build #{platform} --release" 13 | end 14 | 15 | 16 | platform :ios do 17 | 18 | 19 | # path to xcodeproj folder 20 | xcodeproj = "#{PLATFORM[:ios]}/#{APP_NAME}.xcodeproj" 21 | 22 | 23 | desc "Create an app identifier on the developer member center and iTunes Connect" 24 | lane :create do 25 | produce(app_name: APP_NAME) 26 | end 27 | 28 | 29 | desc "Codesign, Archive and upload the app to Testflight" 30 | lane :pushtest do 31 | 32 | # Get certificate 33 | cert(output_path: "#{SECURITY[:ios]}") 34 | 35 | # Get provisioning profile 36 | sigh(output_path: "#{SECURITY[:ios]}/testflight") 37 | 38 | # Build the 39 | build('ios') 40 | 41 | # Recreate schemes to ensure a smooth transition from cordova to gym 42 | recreate_schemes(project: xcodeproj) 43 | 44 | # Archive app into ipa 45 | gym(scheme: APP_NAME, project: xcodeproj, output_directory: "#{BUILD[:ios]}", output_name: APP_FILENAME) 46 | 47 | # Upload to Testflight 48 | pilot(distribute_external: false, ipa: "#{BUILD[:ios]}/#{APP_FILENAME}.ipa") 49 | 50 | end 51 | 52 | end 53 | -------------------------------------------------------------------------------- /fastlane/README.md: -------------------------------------------------------------------------------- 1 | fastlane documentation 2 | ================ 3 | # Installation 4 | ``` 5 | sudo gem install fastlane 6 | ``` 7 | # Available Actions 8 | ## iOS 9 | ### ios create 10 | ``` 11 | fastlane ios create 12 | ``` 13 | Create an app identifier on the developer member center and iTunes Connect 14 | ### ios pushtest 15 | ``` 16 | fastlane ios pushtest 17 | ``` 18 | Codesign, Archive and upload the app to Testflight 19 | 20 | ---- 21 | 22 | This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run. 23 | More information about fastlane can be found on [https://fastlane.tools](https://fastlane.tools). 24 | The documentation of fastlane can be found on [GitHub](https://github.com/fastlane/fastlane/tree/master/fastlane). 25 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/10.txt: -------------------------------------------------------------------------------- 1 | Manage files versions file. -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/104018.txt: -------------------------------------------------------------------------------- 1 | - Fix repeated bug on background service -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/104028.txt: -------------------------------------------------------------------------------- 1 | - Fix display file with special characters. 2 | - Fix bug when service is launch upload picture can't finish, so now is synchronous. -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/104038.txt: -------------------------------------------------------------------------------- 1 | - Fix minimum compatibility to android 4.4 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/11.txt: -------------------------------------------------------------------------------- 1 | Improve first synchronisation -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/110.txt: -------------------------------------------------------------------------------- 1 | * Bug fix when user doesn't synchronize his contacts. -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/111.txt: -------------------------------------------------------------------------------- 1 | * Improve application design. 2 | * Bug fixes for contacts synchronization. -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/112.txt: -------------------------------------------------------------------------------- 1 | Database migration. -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/113.txt: -------------------------------------------------------------------------------- 1 | Database migration. -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/114.txt: -------------------------------------------------------------------------------- 1 | Database migration. -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/116.txt: -------------------------------------------------------------------------------- 1 | * Calendar synchronization 2 | * Add devices permissions 3 | * Change mails for debug logs. -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/117.txt: -------------------------------------------------------------------------------- 1 | * Debug for password with specific characters. -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/1188.txt: -------------------------------------------------------------------------------- 1 | Improve search -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/12.txt: -------------------------------------------------------------------------------- 1 | Add breadcrumb 2 | Works vertically and horizontally -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/13.txt: -------------------------------------------------------------------------------- 1 | Improve performances 2 | Small UI fixes -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/14.txt: -------------------------------------------------------------------------------- 1 | Temporary cozy notifications synchronization 2 | Photos synchronization thanks a deamon service 3 | Add a infinite scroll for big folder 4 | Fix duplicated folder 5 | Fix synchronization problems (for cozy to phone) 6 | Some UI improvements -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/16.txt: -------------------------------------------------------------------------------- 1 | Add contacts synchronization. -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/18.txt: -------------------------------------------------------------------------------- 1 | * Few bugs fixes for contacts synchronization. 2 | * New: report a bug! In the configuration screen, scroll all the way down, and send us the logs. -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/19.txt: -------------------------------------------------------------------------------- 1 | * Bug fix for password with accents. 2 | * Improve logs 3 | * Improve user feedbacks -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/2.txt: -------------------------------------------------------------------------------- 1 | Thanks @mihneadb for the bug reports 2 | - change color to cozy's 3 | - close keyboard after search 4 | - close side-menu when appropriate 5 | - prefetch folder to improve snappyness -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/3.txt: -------------------------------------------------------------------------------- 1 | Use views for faster initial replication 2 | Full control of local vs cloud files -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/4.txt: -------------------------------------------------------------------------------- 1 | Synchronize images from your phone to your cozy -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/5.txt: -------------------------------------------------------------------------------- 1 | Synchronize images from your phone to your cozy -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/6.txt: -------------------------------------------------------------------------------- 1 | Synchronize images from your phone to your cozy -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/7.txt: -------------------------------------------------------------------------------- 1 | Bugfix on folder sorting -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/8.txt: -------------------------------------------------------------------------------- 1 | Some bug fixes -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/9.txt: -------------------------------------------------------------------------------- 1 | Fix bug to display files, improve login design. -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/full_description.txt: -------------------------------------------------------------------------------- 1 | Cozy.io is a platform that brings all your web services in the same private space. With it, your web apps and your devices can share data easily, providing you with a new experience. You can install Cozy on your own hardware where no one profiles you. 2 | 3 | This is the Android mobile client to your personnal Cozy server. Connect it to your Cozy server and browse through your cozy files from your Android device. You can also synchronize your contacts and your calendars. 4 | 5 | Use github issues to report bugs and vote for next features : https://github.com/cozy/cozy-mobile/issues -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/fastlane/metadata/android/en-US/images/icon.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/short_description.txt: -------------------------------------------------------------------------------- 1 | Cozy.io files management app. -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/title.txt: -------------------------------------------------------------------------------- 1 | Cozy -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/video.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/fastlane/metadata/android/en-US/video.txt -------------------------------------------------------------------------------- /fastlane/metadata/ios/copyright.txt: -------------------------------------------------------------------------------- 1 | LGPL-3.0 2 | -------------------------------------------------------------------------------- /fastlane/metadata/ios/en-US/description.txt: -------------------------------------------------------------------------------- 1 | Cozy.io is a platform that brings all your web services in the same private space. With it, your web apps and your devices can share data easily, providing you with a new experience. You can install Cozy on your own hardware where no one profiles you. 2 | 3 | This is the iOS mobile client to your personnal Cozy server. Connect it to your Cozy server and browse through your cozy files from your iOS device. 4 | 5 | Use github issues to report bugs and vote for next features : https://github.com/cozy/cozy-mobile/issues 6 | -------------------------------------------------------------------------------- /fastlane/metadata/ios/en-US/keywords.txt: -------------------------------------------------------------------------------- 1 | cozy cloud sync files 2 | -------------------------------------------------------------------------------- /fastlane/metadata/ios/en-US/marketing_url.txt: -------------------------------------------------------------------------------- 1 | https://cozy.io 2 | -------------------------------------------------------------------------------- /fastlane/metadata/ios/en-US/name.txt: -------------------------------------------------------------------------------- 1 | Cozy Mobile 2 | -------------------------------------------------------------------------------- /fastlane/metadata/ios/en-US/privacy_url.txt: -------------------------------------------------------------------------------- 1 | https://cozy.io 2 | -------------------------------------------------------------------------------- /fastlane/metadata/ios/en-US/release_notes.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fastlane/metadata/ios/en-US/support_url.txt: -------------------------------------------------------------------------------- 1 | https://github.com/cozy/cozy-mobile/issues 2 | -------------------------------------------------------------------------------- /fastlane/metadata/ios/primary_category.txt: -------------------------------------------------------------------------------- 1 | MZGenre.Productivity 2 | -------------------------------------------------------------------------------- /fastlane/metadata/ios/secondary_category.txt: -------------------------------------------------------------------------------- 1 | MZGenre.Photography 2 | -------------------------------------------------------------------------------- /fastlane/screenshots/ios/README.txt: -------------------------------------------------------------------------------- 1 | Put all screenshots you want to use inside the folder of its language (e.g. en-US). 2 | The device type will automatically be recognized using the image resolution. Apple TV screenshots 3 | should be stored in a subdirectory named appleTV with language folders inside of it. 4 | 5 | The screenshots can be named whatever you want, but keep in mind they are sorted alphabetically. -------------------------------------------------------------------------------- /fastlane/screenshots/ios/en-US/1_ipadPro_1.1-ipad-welcome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/fastlane/screenshots/ios/en-US/1_ipadPro_1.1-ipad-welcome.png -------------------------------------------------------------------------------- /fastlane/screenshots/ios/en-US/1_iphone6Plus_1.1-welcome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/fastlane/screenshots/ios/en-US/1_iphone6Plus_1.1-welcome.png -------------------------------------------------------------------------------- /fastlane/screenshots/ios/en-US/2_ipadPro_2.2-ipad-access-files.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/fastlane/screenshots/ios/en-US/2_ipadPro_2.2-ipad-access-files.png -------------------------------------------------------------------------------- /fastlane/screenshots/ios/en-US/2_iphone6Plus_2.2-access-files.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/fastlane/screenshots/ios/en-US/2_iphone6Plus_2.2-access-files.png -------------------------------------------------------------------------------- /fastlane/screenshots/ios/en-US/3_ipadPro_3.3-ipad-folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/fastlane/screenshots/ios/en-US/3_ipadPro_3.3-ipad-folder.png -------------------------------------------------------------------------------- /fastlane/screenshots/ios/en-US/3_iphone6Plus_3.3-folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/fastlane/screenshots/ios/en-US/3_iphone6Plus_3.3-folder.png -------------------------------------------------------------------------------- /fastlane/screenshots/ios/en-US/4_ipadPro_4.4-ipad-files.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/fastlane/screenshots/ios/en-US/4_ipadPro_4.4-ipad-files.png -------------------------------------------------------------------------------- /fastlane/screenshots/ios/en-US/4_iphone6Plus_4.4-files.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/fastlane/screenshots/ios/en-US/4_iphone6Plus_4.4-files.png -------------------------------------------------------------------------------- /fastlane/screenshots/ios/en-US/5_ipadPro_5.5-ipad-config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/fastlane/screenshots/ios/en-US/5_ipadPro_5.5-ipad-config.png -------------------------------------------------------------------------------- /fastlane/screenshots/ios/en-US/5_iphone6Plus_5.5-config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/fastlane/screenshots/ios/en-US/5_iphone6Plus_5.5-config.png -------------------------------------------------------------------------------- /hooks/README.md: -------------------------------------------------------------------------------- 1 | 21 | # Cordova Hooks 22 | 23 | This directory may contain scripts used to customize cordova commands. This 24 | directory used to exist at `.cordova/hooks`, but has now been moved to the 25 | project root. Any scripts you add to these directories will be executed before 26 | and after the commands corresponding to the directory name. Useful for 27 | integrating your own build systems or integrating with version control systems. 28 | 29 | __Remember__: Make your scripts executable. 30 | 31 | ## Hook Directories 32 | The following subdirectories will be used for hooks: 33 | 34 | after_build/ 35 | after_compile/ 36 | after_docs/ 37 | after_emulate/ 38 | after_platform_add/ 39 | after_platform_rm/ 40 | after_platform_ls/ 41 | after_plugin_add/ 42 | after_plugin_ls/ 43 | after_plugin_rm/ 44 | after_plugin_search/ 45 | after_prepare/ 46 | after_run/ 47 | after_serve/ 48 | before_build/ 49 | before_compile/ 50 | before_docs/ 51 | before_emulate/ 52 | before_platform_add/ 53 | before_platform_rm/ 54 | before_platform_ls/ 55 | before_plugin_add/ 56 | before_plugin_ls/ 57 | before_plugin_rm/ 58 | before_plugin_search/ 59 | before_prepare/ 60 | before_run/ 61 | before_serve/ 62 | pre_package/ <-- Windows 8 and Windows Phone only. 63 | 64 | ## Script Interface 65 | 66 | All scripts are run from the project's root directory and have the root directory passes as the first argument. All other options are passed to the script using environment variables: 67 | 68 | * CORDOVA_VERSION - The version of the Cordova-CLI. 69 | * CORDOVA_PLATFORMS - Comma separated list of platforms that the command applies to (e.g.: android, ios). 70 | * CORDOVA_PLUGINS - Comma separated list of plugin IDs that the command applies to (e.g.: org.apache.cordova.file, org.apache.cordova.file-transfer) 71 | * CORDOVA_HOOK - Path to the hook that is being executed. 72 | * CORDOVA_CMDLINE - The exact command-line arguments passed to cordova (e.g.: cordova run ios --emulate) 73 | 74 | If a script returns a non-zero exit code, then the parent cordova command will be aborted. 75 | 76 | 77 | ## Writing hooks 78 | 79 | We highly recommend writting your hooks using Node.js so that they are 80 | cross-platform. Some good examples are shown here: 81 | 82 | [http://devgirl.org/2013/11/12/three-hooks-your-cordovaphonegap-project-needs/](http://devgirl.org/2013/11/12/three-hooks-your-cordovaphonegap-project-needs/) 83 | 84 | -------------------------------------------------------------------------------- /keys/README.md: -------------------------------------------------------------------------------- 1 | # Generate signed release (Cozy staff only) 2 | 3 | We use `fastlane` to generate, sign, upload release :package:. 4 | See more information on `fastlane/README.md`. 5 | 6 | 7 | ## Android 8 | 9 | Place to `android` folder this files: 10 | 11 | cozy-play-store.keystore 12 | cozy-play-store.password 13 | cozy-play-store.json 14 | 15 | Now you can use the npm command to create android release: 16 | 17 | npm run release:android 18 | 19 | 20 | ## iOS 21 | 22 | All keys was imported by `fastlane` automatically. 23 | 24 | 25 | ### Internal 26 | 27 | ### :family: User on testflight 28 | 29 | Internal users go to [iTunes Connect](https://itunesconnect.apple.com/WebObjects/iTunesConnect.woa/ra/ng/users_roles). 30 | 31 | External users: 32 | 33 | # Add user 34 | pilot add email@invite.com 35 | 36 | # Find a tester 37 | pilot find email@invite.com 38 | 39 | # Remove user 40 | pilot remove email@invite.com 41 | -------------------------------------------------------------------------------- /keys/android/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/keys/android/.gitkeep -------------------------------------------------------------------------------- /keys/ios/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/keys/ios/.gitkeep -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cozy-mobile", 3 | "description": "This is the native mobile client for Cozy.", 4 | "version": "2.1.0", 5 | "license": "LGPL-3.0", 6 | "engines": { 7 | "node": "~6.9.1" 8 | }, 9 | "scripts": { 10 | "postinstall": "npm run build && cordova prepare", 11 | "build": "brunch build", 12 | "watch": "brunch watch", 13 | "android": "cordova run android --device", 14 | "android-emulator": "cordova run android --emulator", 15 | "ios": "cordova run ios --device", 16 | "ios-emulator": "cordova run ios --emulator", 17 | "lint": "coffeelint -r src/app src/test *.coffee", 18 | "test": "mocha src/test/unit/ --recursive --reporter spec --compilers coffee:coffee-script/register --require coffee-coverage/register-istanbul", 19 | "test:coverage": "npm run test && istanbul report", 20 | "test:watch": "npm run test -- --watch --watch-extensions coffee", 21 | "prerelease:android": "rm -rf www/ && npm run build && cordova build android --release", 22 | "release:android": "jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore ./keys/android/cozy-play-store.keystore -storepass `cat ./keys/android/cozy-play-store.password` ./platforms/android/build/outputs/apk/android-release-unsigned.apk cozy-play-store && zipalign -v 4 ./platforms/android/build/outputs/apk/android-release-unsigned.apk ./build/android/CozyMobile.apk", 23 | "postrelease:android": "PACKAGE_VERSION=$(node -p -e \"require('./package.json').version\"); mv -f ./build/android/CozyMobile.apk ./build/android/CozyMobile_$PACKAGE_VERSION.apk; md5 ./build/android/CozyMobile_$PACKAGE_VERSION.apk; shasum ./build/android/CozyMobile_$PACKAGE_VERSION.apk" 24 | }, 25 | "dependencies": { 26 | "animate.css": "3.5.2", 27 | "async": "2.1.4", 28 | "backbone": "1.3.3", 29 | "jquery": "3.1.1", 30 | "material-design-icons": "3.0.1", 31 | "materialize-css": "https://github.com/cozy/materialize.git", 32 | "mdi": "1.7.22", 33 | "moment": "2.17.1", 34 | "moment-timezone": "0.5.11", 35 | "node-polyglot": "2.2.1", 36 | "node-viewerjs": "https://github.com/cozy/ViewerJS.git", 37 | "path-browser": "2.2.1", 38 | "pouchdb": "6.1.0", 39 | "semver": "5.3.0", 40 | "snap.js": "https://github.com/kosssi/snap.js.git#develop", 41 | "underscore": "1.8.3", 42 | "validator": "6.2.0" 43 | }, 44 | "devDependencies": { 45 | "brunch": "2.9.1", 46 | "chai": "3.5.0", 47 | "clean-css-brunch": "2.0.0", 48 | "codecov": "1.0.1", 49 | "coffee-coverage": "1.0.1", 50 | "coffee-script": "1.12.2", 51 | "coffee-script-brunch": "2.1.0", 52 | "coffeelint": "1.16.0", 53 | "cordova": "6.4.0", 54 | "css-brunch": "2.6.1", 55 | "istanbul": "0.4.5", 56 | "jade-brunch": "2.8.0", 57 | "javascript-brunch": "2.0.0", 58 | "json-brunch": "1.5.4", 59 | "memdown": "1.2.4", 60 | "mocha": "3.2.0", 61 | "mockery": "2.0.0", 62 | "stylus-brunch": "2.8.0", 63 | "xmlhttprequest": "1.8.0" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /res/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/res/icon.png -------------------------------------------------------------------------------- /res/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | cozy-lg 3 | 4 | 5 | background 6 | 7 | 8 | 9 | Layer 1 10 | 11 | 12 | -------------------------------------------------------------------------------- /res/icons/android/icon-144-xxhdpi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/res/icons/android/icon-144-xxhdpi.png -------------------------------------------------------------------------------- /res/icons/android/icon-192-xxxhdpi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/res/icons/android/icon-192-xxxhdpi.png -------------------------------------------------------------------------------- /res/icons/android/icon-36-ldpi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/res/icons/android/icon-36-ldpi.png -------------------------------------------------------------------------------- /res/icons/android/icon-48-mdpi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/res/icons/android/icon-48-mdpi.png -------------------------------------------------------------------------------- /res/icons/android/icon-72-hdpi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/res/icons/android/icon-72-hdpi.png -------------------------------------------------------------------------------- /res/icons/android/icon-96-xhdpi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/res/icons/android/icon-96-xhdpi.png -------------------------------------------------------------------------------- /res/icons/ios/icon-40-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/res/icons/ios/icon-40-2x.png -------------------------------------------------------------------------------- /res/icons/ios/icon-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/res/icons/ios/icon-40.png -------------------------------------------------------------------------------- /res/icons/ios/icon-50-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/res/icons/ios/icon-50-2x.png -------------------------------------------------------------------------------- /res/icons/ios/icon-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/res/icons/ios/icon-50.png -------------------------------------------------------------------------------- /res/icons/ios/icon-57-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/res/icons/ios/icon-57-2x.png -------------------------------------------------------------------------------- /res/icons/ios/icon-57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/res/icons/ios/icon-57.png -------------------------------------------------------------------------------- /res/icons/ios/icon-60-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/res/icons/ios/icon-60-2x.png -------------------------------------------------------------------------------- /res/icons/ios/icon-60-3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/res/icons/ios/icon-60-3x.png -------------------------------------------------------------------------------- /res/icons/ios/icon-60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/res/icons/ios/icon-60.png -------------------------------------------------------------------------------- /res/icons/ios/icon-72-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/res/icons/ios/icon-72-2x.png -------------------------------------------------------------------------------- /res/icons/ios/icon-72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/res/icons/ios/icon-72.png -------------------------------------------------------------------------------- /res/icons/ios/icon-76-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/res/icons/ios/icon-76-2x.png -------------------------------------------------------------------------------- /res/icons/ios/icon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/res/icons/ios/icon-76.png -------------------------------------------------------------------------------- /res/icons/ios/icon-small-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/res/icons/ios/icon-small-2x.png -------------------------------------------------------------------------------- /res/icons/ios/icon-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/res/icons/ios/icon-small.png -------------------------------------------------------------------------------- /res/screens/android/screen-hdpi-landscape.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/res/screens/android/screen-hdpi-landscape.png -------------------------------------------------------------------------------- /res/screens/android/screen-hdpi-portrait.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/res/screens/android/screen-hdpi-portrait.png -------------------------------------------------------------------------------- /res/screens/android/screen-ldpi-landscape.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/res/screens/android/screen-ldpi-landscape.png -------------------------------------------------------------------------------- /res/screens/android/screen-ldpi-portrait.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/res/screens/android/screen-ldpi-portrait.png -------------------------------------------------------------------------------- /res/screens/android/screen-mdpi-landscape.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/res/screens/android/screen-mdpi-landscape.png -------------------------------------------------------------------------------- /res/screens/android/screen-mdpi-portrait.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/res/screens/android/screen-mdpi-portrait.png -------------------------------------------------------------------------------- /res/screens/android/screen-xhdpi-landscape.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/res/screens/android/screen-xhdpi-landscape.png -------------------------------------------------------------------------------- /res/screens/android/screen-xhdpi-portrait.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/res/screens/android/screen-xhdpi-portrait.png -------------------------------------------------------------------------------- /res/screens/android/screen-xxhdpi-landscape.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/res/screens/android/screen-xxhdpi-landscape.png -------------------------------------------------------------------------------- /res/screens/android/screen-xxhdpi-portrait.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/res/screens/android/screen-xxhdpi-portrait.png -------------------------------------------------------------------------------- /res/screens/android/screen-xxxhdpi-landscape.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/res/screens/android/screen-xxxhdpi-landscape.png -------------------------------------------------------------------------------- /res/screens/android/screen-xxxhdpi-portrait.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/res/screens/android/screen-xxxhdpi-portrait.png -------------------------------------------------------------------------------- /res/screens/ios/screen-ipad-landscape-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/res/screens/ios/screen-ipad-landscape-2x.png -------------------------------------------------------------------------------- /res/screens/ios/screen-ipad-landscape.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/res/screens/ios/screen-ipad-landscape.png -------------------------------------------------------------------------------- /res/screens/ios/screen-ipad-portrait-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/res/screens/ios/screen-ipad-portrait-2x.png -------------------------------------------------------------------------------- /res/screens/ios/screen-ipad-portrait.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/res/screens/ios/screen-ipad-portrait.png -------------------------------------------------------------------------------- /res/screens/ios/screen-iphone-568h-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/res/screens/ios/screen-iphone-568h-2x.png -------------------------------------------------------------------------------- /res/screens/ios/screen-iphone-landscape-736h.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/res/screens/ios/screen-iphone-landscape-736h.png -------------------------------------------------------------------------------- /res/screens/ios/screen-iphone-portrait-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/res/screens/ios/screen-iphone-portrait-2x.png -------------------------------------------------------------------------------- /res/screens/ios/screen-iphone-portrait-667h.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/res/screens/ios/screen-iphone-portrait-667h.png -------------------------------------------------------------------------------- /res/screens/ios/screen-iphone-portrait-736h.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/res/screens/ios/screen-iphone-portrait-736h.png -------------------------------------------------------------------------------- /res/screens/ios/screen-iphone-portrait.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/res/screens/ios/screen-iphone-portrait.png -------------------------------------------------------------------------------- /res/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/res/splash.png -------------------------------------------------------------------------------- /res/splash.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | background 5 | 6 | 7 | 8 | Layer 1 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/app/application.coffee: -------------------------------------------------------------------------------- 1 | # intialize module which initialize global vars. 2 | require './lib/utils' 3 | toast = require './lib/toast' 4 | Initialize = require './lib/initialize' 5 | Synchronization = require './lib/synchronization' 6 | ServiceManager = require './models/service_manager' 7 | log = require('./lib/persistent_log') 8 | prefix: "application" 9 | date: true 10 | processusTag: "Application" 11 | 12 | 13 | module.exports = 14 | 15 | 16 | initialize: -> 17 | log.debug "initialize" 18 | 19 | @name = 'APP' 20 | @init = new Initialize @ 21 | @init.initConfig => 22 | Backbone.history.start() 23 | @startLayout() 24 | @startSynchronization() 25 | # The ServiceManager is a flag for the background plugin to 26 | # know if it's the service or the application, 27 | # see https://git.io/vVjJO 28 | @serviceManager = new ServiceManager() 29 | 30 | 31 | startLayout: -> 32 | log.debug "startLayout" 33 | 34 | Router = require './router' 35 | @router = new Router() 36 | @router.init @init.config.get 'state' 37 | 38 | 39 | startSynchronization: -> 40 | log.info 'startSynchronization' 41 | 42 | @synchro = new Synchronization() 43 | @synchro.sync() 44 | 45 | 46 | setListeners: -> 47 | log.debug "setListeners" 48 | 49 | document.addEventListener "resume", => 50 | log.info "RESUME EVENT" 51 | @state = 'resume' 52 | @init.config.set 'appState', 'launch', => 53 | @init.trigger 'resume' 54 | , false 55 | 56 | document.addEventListener "pause", => 57 | log.info "PAUSE EVENT" 58 | toast.hide() 59 | @state = 'pause' 60 | @init.config.set 'appState', 'pause', => 61 | @init.trigger 'pause' 62 | , false 63 | 64 | 65 | exit: (err) -> 66 | log.debug "exit" 67 | 68 | if err 69 | log.error err 70 | msg = err.message or err 71 | msg += "\n #{t('error try restart')}" 72 | navigator.notification.alert msg 73 | navigator.app.exitApp() 74 | 75 | 76 | addDeviceListener: -> 77 | log.debug "addDeviceListener" 78 | 79 | document.addEventListener 'deviceready', => 80 | window.open = cordova.InAppBrowser.open 81 | @initialize() 82 | , false 83 | -------------------------------------------------------------------------------- /src/app/background_service.coffee: -------------------------------------------------------------------------------- 1 | # intialize module which initialize global vars. 2 | require './lib/utils' 3 | 4 | Initialize = require './lib/initialize' 5 | Synchronization = require './lib/synchronization' 6 | 7 | log = require('./lib/persistent_log') 8 | prefix: "application" 9 | date: true 10 | processusTag: "BackgroundService" 11 | 12 | 13 | # This service will be started in it's own browser instance by the 14 | # JSBackgroundService. It take place of traditionnal application object, on the 15 | # widnow.app field too. It's allow to re-use the application's code without 16 | # changes. 17 | # Be carreful with shared resources, between application and services, as they 18 | # may run simultaneously (but in independant browsers). 19 | module.exports = BackgroundService = 20 | 21 | initialize: -> 22 | log.debug "initialize" 23 | 24 | @name = 'SERVICE' 25 | @init = new Initialize @ 26 | @init.initConfig => 27 | @startSynchronization() 28 | 29 | 30 | startSynchronization: -> 31 | log.info 'startSynchronization' 32 | 33 | @synchro = new Synchronization() 34 | syncLoop = false 35 | @synchro.sync syncLoop, => 36 | @exit() 37 | 38 | 39 | startMainActivity: (err)-> 40 | log.debug "startMainActivity" 41 | 42 | log.error err if err 43 | # Start activity to initialize app 44 | # or update permissions 45 | JSBackgroundService.startMainActivity (err)-> 46 | log.error err if err 47 | # Then shutdown service 48 | window.service.workDone() 49 | 50 | exit: (err) -> 51 | log.debug "exit" 52 | 53 | log.error err if err 54 | # give some time to finish and close things. 55 | setTimeout -> 56 | # call this javabinding directly on object to avoid 57 | # Error 'NPMethod called on non-NPObject' 58 | window.service.workDone() 59 | , 5 * 1000 60 | 61 | 62 | addDeviceListener: -> 63 | log.debug "addDeviceListener" 64 | 65 | document.addEventListener 'deviceready', => 66 | try 67 | @initialize() 68 | 69 | catch error 70 | log.error 'EXCEPTION SERVICE INITIALIZATION : ', error 71 | 72 | finally 73 | # "Watchdog" : in all cases, kill service after 14' 74 | setTimeout -> 75 | # call this javabinding directly on object to avoid 76 | # Error 'NPMethod called on non-NPObject' 77 | window.service.workDone() 78 | , 14 * 60 * 1000 79 | -------------------------------------------------------------------------------- /src/app/components/information/information.model.coffee: -------------------------------------------------------------------------------- 1 | PictureHandler = require '../../lib/media/picture_handler' 2 | FirstReplication = require '../../lib/first_replication' 3 | 4 | 5 | module.exports = class Information extends Backbone.Model 6 | 7 | 8 | initialize: -> 9 | @firstReplication = new FirstReplication() 10 | @listenTo @firstReplication, "change:queue", (object, @task) => 11 | if @task 12 | @set text: t 'config_loading_' + @task, progress: 0 13 | else 14 | @set text: '', progress: 0 15 | @listenTo @firstReplication, "change:total", (object, total) => 16 | if @task is 'files' 17 | @total = total 18 | else 19 | @total = total * 2 20 | @listenTo @firstReplication, "change:progress", (object, progress) => 21 | @set progress: progress * 100 / @total 22 | 23 | @pictureHandler = new PictureHandler() 24 | @listenTo @pictureHandler, "change:total", (object, @total) => 25 | @set text: t 'information_backup_picture' 26 | @listenTo @pictureHandler, "change:progress", (object, progress) => 27 | if progress is @total 28 | @set text: '', progress: 0 29 | else 30 | @set progress: progress * 100 / @total 31 | 32 | 33 | defaults: -> 34 | display: true 35 | text: '' 36 | progress: 0 37 | -------------------------------------------------------------------------------- /src/app/components/information/information.style.styl: -------------------------------------------------------------------------------- 1 | .information 2 | position absolute 3 | overflow hidden 4 | bottom 0 5 | left 0 6 | right 0 7 | background-color #F1F2F4 8 | color #3A3A42 9 | font-weight bold 10 | height 60px 11 | 12 | .progress 13 | margin 0 14 | background-color #E2E2E2 15 | height 6px 16 | 17 | .determinate 18 | background-color #34A7FF 19 | 20 | .info 21 | margin 15px 25px 22 | bottom: 0; 23 | position: absolute; 24 | -------------------------------------------------------------------------------- /src/app/components/information/information.template.jade: -------------------------------------------------------------------------------- 1 | if text && display 2 | .information 3 | if progress 4 | .progress 5 | .determinate(style='width: ' + progress + '%') 6 | .info= text 7 | -------------------------------------------------------------------------------- /src/app/components/information/information.view.coffee: -------------------------------------------------------------------------------- 1 | Model = require './information.model' 2 | 3 | 4 | module.exports = class Information extends Backbone.View 5 | 6 | 7 | template: require './information.template' 8 | 9 | 10 | initialize: -> 11 | @model = new Model() 12 | @listenTo @model, 'change', @render 13 | @listenTo @model, 'change:text', (object, text) -> 14 | if text is '' 15 | $('body').removeClass 'import' 16 | else 17 | $('body').addClass 'import' 18 | @ 19 | 20 | 21 | render: -> 22 | @$el.html @template @model.toJSON() 23 | @ 24 | 25 | 26 | hide: -> 27 | @model.set 'display', false 28 | 29 | 30 | show: -> 31 | @model.set 'display', true 32 | -------------------------------------------------------------------------------- /src/app/lib/check_platform_versions.coffee: -------------------------------------------------------------------------------- 1 | semver = require 'semver' 2 | log = require('../lib/persistent_log') 3 | prefix: "CheckPlatformVersions" 4 | date: true 5 | 6 | 7 | PLATFORM_VERSIONS = 8 | 'proxy': '>=2.1.11' 9 | 'data-system': '>=2.5.13' 10 | 11 | 12 | module.exports = 13 | 14 | checkPlatformVersions: (callback) -> 15 | log.debug 'checkPlatformVersions' 16 | 17 | config = app.init.config 18 | requestCozy = app.init.requestCozy 19 | options = 20 | method: 'get' 21 | url: "#{config.get 'cozyURL'}/versions" 22 | retry: 3 23 | 24 | requestCozy.request options, (err, response, body) -> 25 | return callback err if err # TODO i18n ? 26 | 27 | for item in body 28 | [s, app, version] = item.match /([^:]+): ([\d\.]+)/ 29 | if app of PLATFORM_VERSIONS 30 | unless semver.satisfies(version, PLATFORM_VERSIONS[app]) 31 | msg = t 'error need min %version for %app' 32 | msg = msg.replace '%app', app 33 | msg = msg.replace '%version', PLATFORM_VERSIONS[app] 34 | return callback new Error msg 35 | 36 | # Everything fine 37 | callback() 38 | -------------------------------------------------------------------------------- /src/app/lib/connection_handler.coffee: -------------------------------------------------------------------------------- 1 | log = require('./persistent_log') 2 | prefix: 'ConnectionHandler' 3 | date: true 4 | instance = null 5 | 6 | 7 | module.exports = class ConnectionHandler 8 | 9 | 10 | constructor: (@ConnectionState) -> 11 | return instance if instance 12 | instance = @ 13 | 14 | # Connection is from cordova-plugin-network-information 15 | @ConnectionState ?= Connection 16 | @connected = @_getConnected() 17 | 18 | log.info @connected 19 | 20 | document.addEventListener 'offline', @_offline, false 21 | document.addEventListener 'online', @_online, false 22 | 23 | 24 | _online: -> 25 | unless @connected 26 | log.info 'online' 27 | @connected = true 28 | 29 | 30 | _offline: -> 31 | if @connected 32 | log.info 'offline' 33 | @connected = false 34 | 35 | 36 | _getConnected: -> 37 | navigator.connection.type isnt @ConnectionState.NONE 38 | 39 | 40 | isConnected: -> 41 | connected = @_getConnected() 42 | if connected isnt @connected 43 | if connected 44 | @_online() 45 | else 46 | @_offline() 47 | 48 | @connected 49 | 50 | 51 | isWifi: -> 52 | @isConnected() and navigator.connection.type is @ConnectionState.WIFI 53 | -------------------------------------------------------------------------------- /src/app/lib/database.coffee: -------------------------------------------------------------------------------- 1 | PouchDB = require 'pouchdb' 2 | semver = require 'semver' 3 | log = require("./persistent_log") 4 | prefix: "Database" 5 | date: true 6 | 7 | 8 | class Database 9 | 10 | 11 | @REPLICATE_DB: 'cozy-files.db' 12 | @LOCAL_DB: 'cozy-photos.db' 13 | 14 | 15 | # Create databases 16 | # 17 | # adapter: 18 | # 'websql': for iOS and Android < 4.4.0 19 | # 'idb': for Android >= 4.4.0 20 | # 21 | # To test: 22 | # options = db: require 'memdown' 23 | constructor: (options) -> 24 | options ?= _getOptions() 25 | @replicateDb = new PouchDB Database.REPLICATE_DB, options 26 | @localDb = new PouchDB Database.LOCAL_DB, options 27 | 28 | 29 | setRemoteDatabase: (cozyUrl) -> 30 | log.debug "setRemoteDatabase" 31 | 32 | @remoteDb = new PouchDB "#{cozyUrl}/replication" 33 | 34 | 35 | module.exports = Database 36 | 37 | 38 | _getOptions = -> 39 | options = 40 | adapter: 'websql' 41 | location: 'default' 42 | auto_compaction: true 43 | 44 | 45 | if device.platform is "Android" 46 | # device.version is not a semantic version: 47 | # - Froyo OS would return "2.2" 48 | # - Eclair OS would return "2.1", "2.0.1", or "2.0" 49 | # - Version can also return update level "2.1-update1" 50 | version = device.version.split('-')[0] 51 | version += '.0' unless semver.valid version 52 | 53 | # https://pouchdb.com/adapters.html 54 | log.debug "Android version:", device.version, version 55 | if semver.valid(version) and semver.gte version, '4.4.0' 56 | log.info 'Require idb' 57 | options.adapter = 'idb' 58 | 59 | return options 60 | -------------------------------------------------------------------------------- /src/app/lib/deleted_document.coffee: -------------------------------------------------------------------------------- 1 | log = require('./persistent_log') 2 | prefix: "DeletedDocument" 3 | date: true 4 | instance = null 5 | 6 | 7 | module.exports = class DeletedDocument 8 | 9 | 10 | ###* 11 | * Create a ChangeFileHandler. 12 | ### 13 | constructor: -> 14 | return instance if instance 15 | instance = @ 16 | 17 | @remoteDb = app.init.database.remoteDb 18 | 19 | 20 | getDocBeforeDeleted: (docId, callback) -> 21 | notFoundError = new Error "Document isn't found." 22 | getDoc = (docWithRevs, index, callback) => 23 | shortRev = parseInt(docWithRevs._rev[0], 10) - index 24 | return callback notFoundError if shortRev < 1 25 | 26 | revId = docWithRevs._revisions.ids[index] 27 | revision = "#{shortRev}-#{revId}" 28 | @remoteDb.get docWithRevs._id, rev: revision, (err, doc) -> 29 | return callback err if err 30 | 31 | if doc._deleted 32 | getDoc docWithRevs, index + 1, callback 33 | else 34 | callback null, doc 35 | 36 | options = 37 | revs: true 38 | open_revs: 'all' 39 | @remoteDb.get docId, options, (err, response) -> 40 | return callback err if err 41 | return callback notFoundError unless response.length is 1 42 | doc = response[0].ok 43 | return callback notFoundError unless doc 44 | 45 | getDoc doc, 1, callback 46 | 47 | -------------------------------------------------------------------------------- /src/app/lib/initialize.coffee: -------------------------------------------------------------------------------- 1 | AndroidAccount = require '../replicator/fromDevice/android_account' 2 | Config = require './config' 3 | Database = require './database' 4 | DesignDocuments = require '../replicator/design_documents' 5 | DeviceStatus = require './device_status' 6 | FilterManager = require '../replicator/filter_manager' 7 | FileCacheHandler = require './file_cache_handler' 8 | Replicator = require '../replicator/main' 9 | RequestCozy = require './request_cozy' 10 | Translation = require './translation' 11 | ConnectionHandler = require './connection_handler' 12 | toast = require './toast' 13 | AndroidAccount = require '../replicator/fromDevice/android_account' 14 | 15 | 16 | log = require('./persistent_log') 17 | prefix: "Initialize" 18 | date: true 19 | 20 | module.exports = class Initialize 21 | 22 | _.extend Initialize.prototype, Backbone.Events 23 | 24 | constructor: (@app) -> 25 | log.debug "constructor" 26 | 27 | @connection = new ConnectionHandler() 28 | @translation = new Translation() 29 | @database = new Database() 30 | @config = new Config @database, @ 31 | @replicator = new Replicator() 32 | @requestCozy = new RequestCozy @config 33 | @fileCacheHandler = new FileCacheHandler @database.localDb, \ 34 | @database.replicateDb, @requestCozy 35 | androidAccount = new AndroidAccount() 36 | androidAccount.create (err) -> 37 | log.info err if err 38 | @ 39 | 40 | 41 | initConfig: (callback) -> 42 | DeviceStatus.initialize() 43 | 44 | @translation.setDeviceLocale => 45 | @config.load => 46 | state = if @app.name is 'APP' then 'launch' else 'service' 47 | 48 | @filterManager = new FilterManager @config, @requestCozy, \ 49 | @database.replicateDb 50 | @config.set 'appState', state, => 51 | @fileCacheHandler.load => 52 | @replicator.initConfig @config, @requestCozy, \ 53 | @database, @fileCacheHandler 54 | @replicator.initFileSystem -> 55 | callback() 56 | 57 | 58 | upsertLocalDesignDocuments: (callback = ->) -> 59 | designDocs = 60 | new DesignDocuments @database.replicateDb, @database.localDb 61 | designDocs.createOrUpdateAllDesign callback -------------------------------------------------------------------------------- /src/app/lib/log_sender.coffee: -------------------------------------------------------------------------------- 1 | log = require('./persistent_log') 2 | prefix: "log sender" 3 | date: true 4 | 5 | SUPPORT_MAIL = 'log-mobile@cozycloud.cc' 6 | 7 | module.exports = 8 | 9 | getData: -> 10 | config = window.app.init.config 11 | 12 | return { 13 | subject: "Log from cozy-mobile v#{config.get 'appVersion'}" 14 | body: """ 15 | #{t('send log please describe problem')} 16 | 17 | 18 | ######################## 19 | # #{device.platform}: #{device.version} 20 | # url: '#{config.get 'cozyURL'}' 21 | # deviceName: '#{config.get 'deviceName'}' 22 | # version: '#{config.get 'appVersion'}' 23 | # 24 | # syncContacts: '#{config.get 'syncContacts'}' 25 | # syncCalendars: '#{config.get 'syncCalendars'}' 26 | # syncNotifications: '#{config.get 'cozyNotifications'}' 27 | # 28 | # syncImages: '#{config.get 'syncImages'}' 29 | # syncOnWifi: '#{config.get 'syncOnWifi'}' 30 | # 31 | # firstSyncFiles: '#{config.get 'firstSyncFiles'}' 32 | # firstSyncContacts: '#{config.get 'firstSyncContacts'}' 33 | # firstSyncCalendars: '#{config.get 'firstSyncCalendars'}' 34 | # 35 | # #{t('send log trace begin')} 36 | ## 37 | 38 | #{log.getTraces().join('\n')} 39 | 40 | ## 41 | # #{t('send log trace end')} 42 | ######################## 43 | 44 | 45 | #{t('send log please describe problem')} 46 | 47 | """ 48 | } 49 | 50 | send: -> 51 | data = @getData() 52 | 53 | query = "subject=#{encodeURI(data.subject)}" + \ 54 | "&body=#{encodeURI(data.body)}" 55 | 56 | window.open "mailto:#{SUPPORT_MAIL}?" + query, "_system" 57 | 58 | 59 | share: -> 60 | data = @getData() 61 | options = 62 | subject: data.subject 63 | message: data.body 64 | onSuccess = (result) -> 65 | log.info "Share completed? " + result.completed 66 | log.info "Shared to app: " + result.app 67 | onError = (err) -> 68 | log.info "Sharing failed with message: " + err 69 | socialSharing = window.plugins.socialsharing 70 | socialSharing.shareWithOptions options, onSuccess, onError 71 | 72 | -------------------------------------------------------------------------------- /src/app/lib/path.coffee: -------------------------------------------------------------------------------- 1 | module.exports = 2 | 3 | 4 | getDirName: (path) -> 5 | path.replace(/\\/g,'/').replace(/\/[^\/]*$/, '') 6 | 7 | 8 | getFileName: (path) -> 9 | path.replace(/^.*[\\\/]/, '') 10 | -------------------------------------------------------------------------------- /src/app/lib/permission.coffee: -------------------------------------------------------------------------------- 1 | instance = null 2 | log = require('./persistent_log') 3 | prefix: "Permission" 4 | date: true 5 | 6 | 7 | module.exports = class Permission 8 | 9 | 10 | constructor: () -> 11 | return instance if instance 12 | instance = @ 13 | 14 | @config = app.init.config 15 | @permissions = cordova.plugins.permissions 16 | @synchro = app.synchro 17 | 18 | 19 | checkPermission: (type, success, callback) -> 20 | 21 | if device.platform is 'iOS' 22 | return success true 23 | 24 | if type is 'calendars' 25 | permission = 'CALENDAR' 26 | else if type is 'files' or type is 'photos' 27 | permission = 'EXTERNAL_STORAGE' 28 | else if type is 'contacts' 29 | permission = 'CONTACTS' 30 | 31 | readPermission = @permissions['READ_' + permission] 32 | writePermission = @permissions['WRITE_' + permission] 33 | 34 | error = (err) => 35 | log.info 'err', err 36 | @config.removeSync type 37 | callback false 38 | 39 | read = (status) => 40 | if (!status.hasPermission) 41 | @permissions.requestPermission readPermission, (status) -> 42 | if status.hasPermission 43 | checkWritePermission() 44 | else 45 | error() 46 | , error 47 | else 48 | checkWritePermission() 49 | 50 | write = (status) => 51 | if (!status.hasPermission) 52 | @permissions.requestPermission writePermission, (status) -> 53 | if status.hasPermission then success true else error() 54 | , error 55 | else 56 | success true 57 | 58 | checkWritePermission = => 59 | @permissions.hasPermission writePermission, write, error 60 | 61 | @permissions.hasPermission readPermission, read, error 62 | -------------------------------------------------------------------------------- /src/app/lib/persistent_log.coffee: -------------------------------------------------------------------------------- 1 | module.exports = (options) -> new Logger options 2 | 3 | LOG_SIZE = 1000 4 | 5 | colors = 6 | blue: ['\x1B[34m', '\x1B[39m'] 7 | cyan: ['\x1B[36m', '\x1B[39m'] 8 | green: ['\x1B[32m', '\x1B[39m'] 9 | magenta: ['\x1B[36m', '\x1B[39m'] 10 | red: ['\x1B[31m', '\x1B[39m'] 11 | yellow: ['\x1B[33m', '\x1B[39m'] 12 | 13 | levelColors = 14 | error: colors.red 15 | debug: colors.green 16 | warn: colors.yellow 17 | info: colors.blue 18 | 19 | class Logger 20 | 21 | constructor: (@options) -> 22 | @options ?= {} 23 | if 'processusTag' of @options 24 | Logger.processusTag = @options.processusTag 25 | 26 | # If no localStorage, should be test environment : deactivate log. 27 | unless localStorage? 28 | @noLog = true 29 | 30 | 31 | stringify: (text) -> 32 | if text instanceof Error 33 | err = text 34 | text = err.message 35 | if err.stack? 36 | text += "\n" + err.stack 37 | else if text instanceof Object 38 | text = JSON.stringify text 39 | return text 40 | 41 | 42 | format: (level, texts) -> 43 | text = (@stringify text for text in texts).join(" ") 44 | text = "#{@options.prefix} | #{text}" if @options.prefix? 45 | text = "#{level} - #{text}" if level 46 | text = "#{Logger.processusTag}> #{text}" if Logger.processusTag 47 | if @options.date 48 | date = new Date().toISOString() 49 | text = "[#{date}] #{text}" 50 | return text 51 | 52 | info: (texts...) -> 53 | return if @noLog 54 | text = @format 'info', texts 55 | @persist text 56 | console.info text 57 | 58 | warn: (texts...) -> 59 | return if @noLog 60 | text = @format 'warn', texts 61 | @persist text 62 | console.warn text 63 | 64 | error: (texts...) -> 65 | return if @noLog 66 | text = @format 'error', texts 67 | @persist text 68 | console.error text 69 | 70 | debug: (texts...) -> 71 | return if @noLog 72 | text = @format 'debug', texts 73 | @persist text 74 | console.info text 75 | 76 | raw: (texts...) -> 77 | return if @noLog 78 | console.log.apply console, texts 79 | 80 | lineBreak: (text) -> 81 | return if @noLog 82 | text = Array(80).join("*") 83 | @raw text 84 | window.logTrace.push text 85 | 86 | persist: (text) -> 87 | logIndex = +localStorage.getItem "log_index" 88 | logIndex = (logIndex + 1) % LOG_SIZE 89 | localStorage.setItem "log_#{logIndex}", text 90 | localStorage.setItem "log_index", '' + logIndex 91 | 92 | getTraces: -> 93 | logIndex = +localStorage.getItem "log_index" 94 | i = (logIndex + 1) % LOG_SIZE 95 | 96 | traces = [] 97 | while i != logIndex 98 | log = localStorage.getItem "log_#{i}" 99 | traces.push log if log 100 | i = (i + 1) % LOG_SIZE 101 | 102 | return traces 103 | -------------------------------------------------------------------------------- /src/app/lib/random.coffee: -------------------------------------------------------------------------------- 1 | module.exports.randomString = (length=32) -> 2 | string = "" 3 | string += Math.random().toString(36).substr(2) while string.length < length 4 | string.substr 0, length 5 | -------------------------------------------------------------------------------- /src/app/lib/remote_request.coffee: -------------------------------------------------------------------------------- 1 | async = require 'async' 2 | log = require('./persistent_log') 3 | prefix: "RemoteRequest" 4 | date: true 5 | instance = null 6 | 7 | 8 | all = (doc) -> emit doc._id, doc._id 9 | config = 10 | file: all: all 11 | folder: 12 | all: all 13 | byFullPath: (doc) -> emit (doc.path + '/' + doc.name), doc._id 14 | contact: all: all 15 | event: all: all 16 | tag: byname: (doc) -> emit doc.name, doc._id 17 | notification: all: all 18 | 19 | 20 | 21 | module.exports = class RemoteRequest 22 | 23 | 24 | constructor: (@requestCozy) -> 25 | return instance if instance 26 | instance = @ 27 | 28 | @requestCozy ?= app.init.requestCozy 29 | 30 | 31 | putRequest: (docType, filterName, callback) -> 32 | options = 33 | method: 'put' 34 | type: 'data-system' 35 | path: "/request/#{docType}/#{filterName}/" 36 | body: 37 | map: """ 38 | function (doc) { 39 | if (doc.docType.toLowerCase() === "#{docType}") { 40 | filter = #{config[docType][filterName].toString()}; 41 | filter(doc); 42 | } 43 | } 44 | """ 45 | retry: 3 46 | @requestCozy.request options, callback 47 | 48 | 49 | fetchAll: (doc, callback) -> 50 | @putRequest doc.docType, 'all', (err) => 51 | return log.error err if err 52 | 53 | options = 54 | method: 'post' 55 | type: 'data-system' 56 | path: "/request/#{doc.docType}/all/" 57 | body: 58 | include_docs: true 59 | show_revs: true 60 | retry: doc.retry 61 | @requestCozy.request options, (err, res, rows) -> 62 | if not err and res.statusCode isnt 200 63 | err = new Error res.statusCode, res.reason 64 | 65 | callback err, rows 66 | -------------------------------------------------------------------------------- /src/app/lib/request_cozy.coffee: -------------------------------------------------------------------------------- 1 | request = require './request' 2 | log = require('./persistent_log') 3 | prefix: "RequestCozy" 4 | date: true 5 | 6 | 7 | # private 8 | 9 | 10 | get = (options, callback) -> 11 | request.get options, callback 12 | 13 | put = (options, callback) -> 14 | request.put options, callback 15 | 16 | post = (options, callback) -> 17 | request.post options, callback 18 | 19 | del = (options, callback) -> 20 | request.del options, callback 21 | 22 | 23 | # public 24 | 25 | 26 | module.exports = class RequestCozy 27 | 28 | constructor: (@config) -> 29 | 30 | request: (options, callback) -> 31 | optionsCopy = JSON.parse(JSON.stringify(options)) 32 | delete options.retry 33 | options.json = true unless options.json 34 | 35 | method = options.method 36 | delete options.method 37 | 38 | if options.path 39 | path = options.path 40 | delete options.path 41 | 42 | unless options.url 43 | switch options.type 44 | when 'data-system' 45 | options.url = @getDataSystemUrl path 46 | when 'replication' 47 | options.url = "#{@config.get 'cozyURL'}/replication#{path}" 48 | delete options.type 49 | 50 | unless options.auth 51 | options.auth = 52 | username: @config.get 'deviceName' 53 | password: @config.get 'devicePassword' 54 | 55 | if optionsCopy.retry 56 | cb = (err) => 57 | if err 58 | log.debug 'retry' 59 | optionsCopy.retry-- 60 | @request optionsCopy, callback 61 | else 62 | callback.apply @, arguments 63 | else 64 | cb = callback 65 | 66 | eval(method)(options, cb) 67 | 68 | getDataSystemUrl: (path, withUrlAuth) -> 69 | if withUrlAuth 70 | url = @config.getCozyUrl() 71 | else 72 | url = @config.get 'cozyURL' 73 | "#{url}/ds-api#{path}" 74 | 75 | getDataSystemOption: (path, withUrlAuth) -> 76 | json: true 77 | auth: 78 | username: @config.get 'deviceName' 79 | password: @config.get 'devicePassword' 80 | url: @getDataSystemUrl path, withUrlAuth 81 | -------------------------------------------------------------------------------- /src/app/lib/toast.coffee: -------------------------------------------------------------------------------- 1 | log = require('./persistent_log') 2 | prefix: "toast" 3 | date: true 4 | 5 | 6 | module.exports = 7 | 8 | 9 | info: (msg, duration = 5000) -> 10 | options = @_getDefaultOptions() 11 | options.message = t msg 12 | options.duration = duration 13 | @display options 14 | 15 | 16 | valid: (msg, duration = 5000) -> 17 | options = @_getDefaultOptions() 18 | options.message = t msg 19 | options.duration = duration 20 | options.styling.backgroundColor = '#68a581' 21 | @display options 22 | 23 | 24 | warn: (msg, duration = 5000) -> 25 | options = @_getDefaultOptions() 26 | options.message = t msg 27 | options.duration = duration 28 | options.styling.backgroundColor = '#ffa500' 29 | @display options 30 | 31 | 32 | error: (msg, duration = 5000) -> 33 | options = @_getDefaultOptions() 34 | options.message = t msg 35 | options.duration = duration 36 | options.styling.backgroundColor = '#e13a36' 37 | @display options 38 | 39 | 40 | hide: -> 41 | window.plugins.toast.hide() 42 | 43 | 44 | display: (options) -> 45 | if app.name is 'APP' and app.state isnt 'pause' 46 | window.plugins.toast.showWithOptions options 47 | 48 | 49 | _getDefaultOptions: -> 50 | position: "bottom" 51 | addPixelsY: -200 52 | styling: 53 | cornerRadius: 30 54 | -------------------------------------------------------------------------------- /src/app/lib/translation.coffee: -------------------------------------------------------------------------------- 1 | Polyglot = require 'node-polyglot' 2 | log = require('./persistent_log') 3 | prefix: "Translation" 4 | date: true 5 | 6 | module.exports = class Translation 7 | DEFAULT_LANGUAGE: 'en' 8 | 9 | constructor: -> 10 | @polyglot = new Polyglot() 11 | 12 | setLocale: (locale) -> 13 | log.debug "setLocale: #{locale.value}" 14 | 15 | [@language] = locale.value.split '-' 16 | translations = 17 | try 18 | require '../locales/'+ @language 19 | catch e 20 | @language = @DEFAULT_LANGUAGE 21 | require '../locales/' + @language 22 | 23 | @polyglot.extend translations 24 | 25 | getTranslate: -> 26 | @polyglot.t.bind @polyglot 27 | 28 | setDeviceLocale: (callback) -> 29 | log.debug "setDeviceLocale" 30 | 31 | # Monkey patch for browser debugging 32 | if window.isBrowserDebugging 33 | window.navigator = window.navigator or {} 34 | window.navigator.globalization = 35 | window.navigator.globalization or {} 36 | window.navigator.globalization.getPreferredLanguage = (cb) => 37 | cb value: @DEFAULT_LANGUAGE 38 | 39 | # Use the device's locale until we get the config document. 40 | navigator.globalization.getPreferredLanguage (properties) => 41 | @setLocale(properties) 42 | window.t = @getTranslate() 43 | callback() 44 | -------------------------------------------------------------------------------- /src/app/lib/url_validator.coffee: -------------------------------------------------------------------------------- 1 | validator = require 'validator' 2 | 3 | module.exports = 4 | 5 | 6 | validUrl: (url) -> 7 | validator.isURL url, protocols: ['https'] 8 | 9 | 10 | cleanUrl: (url) -> 11 | # trim white space 12 | url = url.replace /^\s+|\s+$/g,'' 13 | 14 | # add .cozycloud.cc if the user only input name 15 | if url.indexOf('.') is -1 and url.length > 0 16 | url = url + ".cozycloud.cc" 17 | 18 | # Add https on the hostname 19 | if url[0..3] isnt 'http' 20 | url = 'https://' + url 21 | 22 | # remove trailing slash 23 | if url[url.length-1] is '/' 24 | url = url[..-2] 25 | 26 | return url 27 | -------------------------------------------------------------------------------- /src/app/lib/utils.coffee: -------------------------------------------------------------------------------- 1 | 2 | # Add setImmediate, because its one argument signature is cleaner to write 3 | # with coffee script. 4 | window.setImmediate = window.setImmediate or (callback) -> 5 | setTimeout callback, 1 6 | 7 | # Simple continue on error helper, to use with async for example. 8 | # How to use : 9 | # Initialize a 'continueOnError' helper on top of the module, giving 10 | # the specific logger. Then wrap the callback with it in the code : 11 | # Typycally continueOnError(cb)(err) 12 | module.exports.continueOnError = (log) -> 13 | (callback) -> 14 | (err) -> 15 | if err 16 | log.error 'Continue on error:', err 17 | callback null # continue 18 | else 19 | callback.apply @, arguments 20 | -------------------------------------------------------------------------------- /src/app/migrations/0.3.0.coffee: -------------------------------------------------------------------------------- 1 | module.exports = 2 | 3 | 4 | migrate: (callback) -> 5 | navigator.notification.alert t 'version too old' 6 | app.exit() 7 | -------------------------------------------------------------------------------- /src/app/migrations/1.0.0.coffee: -------------------------------------------------------------------------------- 1 | module.exports = 2 | 3 | 4 | migrate: (callback) -> 5 | config = app.init.config 6 | defaultConfig = config.getDefault() 7 | replicateDb = app.init.database.replicateDb 8 | newConfig = {} 9 | 10 | replicateDb.get defaultConfig._id, (err, doc) -> 11 | return callback err if err 12 | 13 | # remove auth 14 | # remove lastInitState 15 | # add state 16 | for key of defaultConfig 17 | if doc[key] isnt undefined 18 | newConfig[key] = doc[key] 19 | else 20 | newConfig[key] = defaultConfig[key] 21 | 22 | newConfig._rev = doc._rev 23 | if doc.lastBackup > 0 or doc.lastSync > 0 24 | newConfig.state = 'syncCompleted' 25 | 26 | config.setConfigValue newConfig 27 | db.put newConfig, (err) => 28 | return callback err if err 29 | @setCozyUrl @get('cozyURL'), callback 30 | -------------------------------------------------------------------------------- /src/app/migrations/1.1.1.coffee: -------------------------------------------------------------------------------- 1 | log = require('../lib/persistent_log') 2 | prefix: "migration 1.1.1" 3 | date: true 4 | 5 | 6 | module.exports = 7 | 8 | 9 | migrate: (callback) -> 10 | config = app.init.config 11 | cozyUrl = config.get 'cozyURL' 12 | config.setCozyUrl cozyUrl, (err) -> 13 | log.error err if err 14 | config.set 'state', 'syncCompleted', callback 15 | -------------------------------------------------------------------------------- /src/app/migrations/1.2.0.coffee: -------------------------------------------------------------------------------- 1 | FilterManager = require '../replicator/filter_manager' 2 | 3 | 4 | module.exports = 5 | 6 | 7 | migrate: (callback) -> 8 | config = app.init.config 9 | if config.get 'cozyNotifications' 10 | db = app.init.database.replicateDb 11 | requestCozy = app.init.requestCozy 12 | filterManager = new FilterManager config, requestCozy, db 13 | filterManager.setFilter callback 14 | else 15 | callback() 16 | -------------------------------------------------------------------------------- /src/app/migrations/1.3.0.coffee: -------------------------------------------------------------------------------- 1 | async = require 'async' 2 | DesignDocuments = require '../replicator/design_documents' 3 | fs = require '../replicator/filesystem' 4 | FileCacheHandler = require '../lib/file_cache_handler' 5 | log = require('../lib/persistent_log') 6 | prefix: "migration 1.3.0" 7 | date: true 8 | 9 | 10 | module.exports = 11 | 12 | 13 | migrate: (callback) -> 14 | @fileCacheHandler = new FileCacheHandler() 15 | @_findFolders (err, folders) => 16 | return callback err if err 17 | return callback() if folders.length is 0 18 | 19 | @_getCozyFiles folders, (err, cozyFiles) => 20 | return callback err if err 21 | return callback() if not cozyFiles or cozyFiles.length is 0 22 | 23 | async.eachSeries folders, (folder, cb) => 24 | id = folder.name.split('-')[0] 25 | cozyFile = cozyFiles[id] 26 | @_moveFile folder, cozyFile, (err) => 27 | if err 28 | fs.rmrf folder, -> 29 | log.warn 'Error move file in new folder.' 30 | log.warn err, cozyFile, folder 31 | return cb() 32 | @fileCacheHandler.saveInCache cozyFile, true, (err) -> 33 | if err 34 | log.warn 'Error save in new cache.' 35 | log.warn err, cozyFile, folder 36 | fs.rmrf folder, (err) -> 37 | if err 38 | log.warn 'Error remove old folder.' 39 | log.warn err, cozyFile, folder 40 | cb() 41 | , callback 42 | 43 | 44 | _findFolders: (callback) -> 45 | fs.initialize (err, downloads) => 46 | return callback err if err 47 | @downloads = downloads 48 | fs.getChildren @downloads, callback 49 | 50 | 51 | _getCozyFiles: (folders, callback) -> 52 | ids = [] 53 | for folder in folders 54 | ids.push folder.name.split('-')[0] 55 | 56 | db = app.init.database.replicateDb 57 | options = include_docs: true 58 | db.query DesignDocuments.PATH_TO_BINARY, options, (err, results) -> 59 | return callback err if err 60 | return callback() if results.rows.length is 0 61 | 62 | cozyFiles = [] 63 | for row in results.rows 64 | cozyFile = row.doc 65 | if cozyFile.binary?.file?.id in ids 66 | cozyFiles[cozyFile.binary.file.id] = cozyFile 67 | cozyFiles.push cozyFile 68 | 69 | callback null, cozyFiles 70 | 71 | 72 | _moveFile: (folder, cozyFile, callback) -> 73 | fileName = @fileCacheHandler.getFileName cozyFile 74 | folderName = @fileCacheHandler.getFolderName cozyFile 75 | 76 | fs.getOrCreateSubFolder @downloads, folderName, (err, newFolder) -> 77 | return callback err if err 78 | fs.getFile folder, fileName, (err, file) -> 79 | return callback err if err 80 | fs.moveTo file, newFolder, fileName, callback 81 | -------------------------------------------------------------------------------- /src/app/migrations/2.0.0.coffee: -------------------------------------------------------------------------------- 1 | module.exports = 2 | 3 | 4 | migrate: (callback) -> 5 | config = app.init.config 6 | defaultConfig = config.getDefault() 7 | replicateDb = app.init.database.replicateDb 8 | newConfig = {} 9 | 10 | replicateDb.get defaultConfig._id, (err, doc) -> 11 | return callback err if err 12 | 13 | # remove auth 14 | # remove lastInitState 15 | # add state 16 | for key of defaultConfig 17 | if key of doc 18 | newConfig[key] = doc[key] 19 | else 20 | newConfig[key] = defaultConfig[key] 21 | 22 | if newConfig.state is 'syncCompleted' 23 | newConfig.state = 'appConfigured' 24 | 25 | newConfig._rev = doc._rev 26 | 27 | config.setConfigValue newConfig 28 | replicateDb.put newConfig, callback 29 | -------------------------------------------------------------------------------- /src/app/migrations/migration.coffee: -------------------------------------------------------------------------------- 1 | async = require 'async' 2 | toast = require '../lib/toast' 3 | semver = require 'semver' 4 | log = require('../lib/persistent_log') 5 | prefix: "Migration" 6 | date: true 7 | 8 | 9 | # corresponding of migration file 10 | migrations = [ 11 | '2.0.0' 12 | '1.3.0' 13 | '1.2.0' 14 | '1.1.1' 15 | '1.0.0' 16 | '0.3.0' 17 | ] 18 | 19 | 20 | module.exports = 21 | 22 | 23 | migrate: (oldVersion, callback) -> 24 | log.debug 'migrate' 25 | 26 | migrationsSorted = migrations.sort (a, b) -> semver.compare a, b 27 | config = app.init.config 28 | 29 | async.eachSeries migrationsSorted, (version, cb) -> 30 | if semver.gt(version, oldVersion) 31 | log.debug "migration #{version}" 32 | toast.info "toast_migration", 180000 33 | 34 | migration = require "./#{version}" 35 | migration.migrate (err) -> 36 | log.warn err if err 37 | config.set 'appVersion', version, cb 38 | else 39 | cb() 40 | , -> 41 | toast.hide() 42 | config.updateVersion callback 43 | -------------------------------------------------------------------------------- /src/app/models/file.coffee: -------------------------------------------------------------------------------- 1 | module.exports = class File extends Backbone.Model 2 | 3 | idAttribute: "_id" 4 | 5 | defaults: -> 6 | incache: 'loading' 7 | version: false 8 | 9 | initialize: -> 10 | @isDeviceFolder = @isFolder() and 11 | @wholePath() is window.app.init.config.get 'deviceName' 12 | 13 | isFolder: -> 14 | @get('docType')?.toLowerCase() is 'folder' 15 | 16 | wholePath: -> 17 | name = @get('name') 18 | if path = @get('path') then "#{path.slice(1)}/#{name}" 19 | else name 20 | -------------------------------------------------------------------------------- /src/app/models/file_list.coffee: -------------------------------------------------------------------------------- 1 | module.exports = class FileList extends Backbone.Model 2 | 3 | defaults: -> 4 | loading: false 5 | files: [] 6 | -------------------------------------------------------------------------------- /src/app/models/service_manager.coffee: -------------------------------------------------------------------------------- 1 | # Service lifecycle 2 | # 3 | # Service is started on : 4 | # Repeatedly 5 | # 6 | # if syncImage 7 | # New Picture Intent 8 | # New connection on Wifi 9 | # 10 | # Service do 11 | # if Wifi 12 | # Backup images (if syncImages) 13 | # Sync 14 | # Update cache 15 | # 16 | # if cozyNotifications 17 | # Sync notifications 18 | # Display notifications 19 | # 20 | 21 | log = require('../lib/persistent_log') 22 | prefix: "ServiceManager" 23 | date: true 24 | 25 | repeatingPeriod = 15 * 60 * 1000 26 | 27 | module.exports = class ServiceManager extends Backbone.Model 28 | 29 | defaults: -> 30 | daemonActivated: false 31 | 32 | initialize: -> 33 | log.debug "initialize" 34 | 35 | config = app.init.config 36 | # Initialize plugin with current config values. 37 | @listenNewPictures config, config.get 'syncImages' 38 | @toggle config, true # force activate. 39 | 40 | # Listen to updates. 41 | @listenTo config, "change:syncImages", @listenNewPictures 42 | 43 | @checkActivated() 44 | 45 | 46 | isActivated: -> 47 | log.debug "isActivated" 48 | 49 | return @get 'daemonActivated' 50 | 51 | checkActivated: -> 52 | log.debug "checkActivated" 53 | 54 | window.JSBackgroundService.isRepeating (err, isRepeating) => 55 | if err 56 | log.error err 57 | isRepeating = false 58 | 59 | log.debug "isRepeating=#{isRepeating}" 60 | 61 | @set 'daemonActivated', isRepeating 62 | 63 | 64 | activate: (repeatingPeriod) -> 65 | log.debug "activate: repeatingPeriod=#{repeatingPeriod}" 66 | 67 | window.JSBackgroundService.setRepeating repeatingPeriod, (err) => 68 | return log.error err if err 69 | @checkActivated() 70 | 71 | deactivate: -> 72 | log.debug "deactivate" 73 | 74 | window.JSBackgroundService.cancelRepeating (err) => 75 | return log.error err if err 76 | @checkActivated() 77 | 78 | toggle: (config, activate) -> 79 | log.debug "toggle: activate=#{activate}" 80 | 81 | if activate 82 | @activate repeatingPeriod 83 | else 84 | @deactivate() 85 | 86 | listenNewPictures: (config, listen) -> 87 | log.debug "listenNewPictures: listen=#{listen}" 88 | 89 | window.JSBackgroundService.listenNewPictures listen, (err) -> 90 | log.error err if err 91 | 92 | isRunning: (callback) -> 93 | log.debug "isRunning" 94 | 95 | window.JSBackgroundService.isRunning callback 96 | -------------------------------------------------------------------------------- /src/app/replicator/change/change_dispatcher.coffee: -------------------------------------------------------------------------------- 1 | ChangeFileHandler = require "./change_file_handler" 2 | ChangeFolderHandler = require "./change_folder_handler" 3 | ChangeEventHandler = require "./change_event_handler" 4 | ChangeContactHandler = require "./change_contact_handler" 5 | ChangeTagHandler = require "./change_tag_handler" 6 | ChangeNotificationHandler = require "./change_notification_handler" 7 | log = require('../../lib/persistent_log') 8 | prefix: "ChangeDispatcher" 9 | date: true 10 | 11 | ###* 12 | * ChangeDispatcher allows to launch the good manager with specific state 13 | * 14 | * @class ChangeDispatcher 15 | ### 16 | module.exports = class ChangeDispatcher 17 | 18 | 19 | ###* 20 | * Create a ChangeDispatcher. 21 | * 22 | * @param {ReplicatorConfig} config - it's replication config. 23 | ### 24 | constructor: -> 25 | @changeHandlers = 26 | "file": new ChangeFileHandler() 27 | "folder": new ChangeFolderHandler() 28 | "event": new ChangeEventHandler() 29 | "contact": new ChangeContactHandler() 30 | "tag": new ChangeTagHandler() 31 | "notification": new ChangeNotificationHandler() 32 | 33 | 34 | ###* 35 | * Launch the good handler with specific state. 36 | * 37 | * @param {Object} doc - it's a pouchdb file document. 38 | * @param {Function} callback 39 | ### 40 | dispatch: (doc, callback = ->) -> 41 | docType = doc.docType.toLowerCase() 42 | if @changeHandlers[docType]? 43 | @changeHandlers[docType].dispatch doc, callback 44 | else 45 | callback new Error 'No dispatcher for this document' 46 | 47 | ###* 48 | * Check if a doc is authorized to be dispatched 49 | * 50 | * @param {Object} doc - it's a pouchdb file document. 51 | * 52 | * @return {Boolean} 53 | ### 54 | isDispatched: (doc) -> 55 | log.debug "isDispatched" 56 | 57 | return doc?.docType?.toLowerCase() of @changeHandlers 58 | -------------------------------------------------------------------------------- /src/app/replicator/change/change_folder_handler.coffee: -------------------------------------------------------------------------------- 1 | DeletedDocument = require '../../lib/deleted_document' 2 | log = require('../../lib/persistent_log') 3 | prefix: "ChangeFolderHandler" 4 | date: true 5 | instance = null 6 | 7 | 8 | ###* 9 | * ChangeFolderHandler allows to dispatch event when folder change. 10 | * 11 | * @class ChangeFolderHandler 12 | ### 13 | module.exports = class ChangeFolderHandler 14 | 15 | 16 | ###* 17 | * Create a ChangeFolderHandler. 18 | ### 19 | constructor: -> 20 | return instance if instance 21 | instance = @ 22 | 23 | _.extend @, Backbone.Events 24 | @deletedDocument = new DeletedDocument() 25 | 26 | 27 | ###* 28 | * When replication change a folder. 29 | * 30 | * @param {Object} doc - it's a pouchdb file document. 31 | ### 32 | dispatch: (doc, callback) -> 33 | log.debug "dispatch" 34 | 35 | cb = (err, doc) => 36 | @trigger "change:path", @, doc.path if doc?.path or doc?.path is "" 37 | callback err 38 | 39 | if doc._deleted 40 | @deletedDocument.getDocBeforeDeleted doc._id, cb 41 | else 42 | cb null, doc 43 | -------------------------------------------------------------------------------- /src/app/replicator/change/change_notification_handler.coffee: -------------------------------------------------------------------------------- 1 | NotificationHandler = require '../../lib/notification_handler' 2 | 3 | log = require('../../lib/persistent_log') 4 | prefix: "ChangeNotificationHandler" 5 | date: true 6 | 7 | 8 | ###* 9 | * ChangeNotificationHandler Can create, update or delete a notification on 10 | * your device 11 | * 12 | * @class ChangeNotificationHandler 13 | ### 14 | module.exports = class ChangeNotificationHandler 15 | 16 | 17 | constructor: (@notifHandler) -> 18 | @notifHandler ?= new NotificationHandler() 19 | 20 | 21 | dispatch: (cozyNotification, callback) -> 22 | log.debug "dispatch" 23 | 24 | if cozyNotification._deleted 25 | @notifHandler.removeCordovaNotification cozyNotification, callback 26 | else 27 | @notifHandler.displayCordovaNotification cozyNotification, callback 28 | -------------------------------------------------------------------------------- /src/app/replicator/change/change_tag_handler.coffee: -------------------------------------------------------------------------------- 1 | AndroidAccount = require "../fromDevice/android_account" 2 | AndroidCalendarHandler = require "../../lib/android_calendar_handler" 3 | CozyToAndroidCalendar = require "../transformer/cozy_to_android_calendar" 4 | log = require('../../lib/persistent_log') 5 | prefix: "ChangeTagHandler" 6 | date: true 7 | 8 | ###* 9 | * ChangeTagHandler Can only update color of calendar. 10 | * For now, calendar is only a tag, so we do it this hack. 11 | * Calendar is created when an event is created. 12 | * Calendar is removed when last event of calendar is removed 13 | * 14 | * @class ChangeEventHandler 15 | ### 16 | module.exports = class ChangeTagHandler 17 | 18 | constructor: -> 19 | @androidCalendarHandler = new AndroidCalendarHandler() 20 | @cozyToAndroidCalendar = new CozyToAndroidCalendar() 21 | 22 | dispatch: (cozyCalendar) -> 23 | log.debug "dispatch" 24 | 25 | @androidCalendarHandler.getByName cozyCalendar.name, \ 26 | (err, androidCalendar) => 27 | # if androidCalendar is not find, this tag is for another thing 28 | if androidCalendar 29 | calendar = @cozyToAndroidCalendar.transform cozyCalendar, \ 30 | AndroidAccount.ACCOUNT, androidCalendar 31 | if calendar.calendar_color isnt androidCalendar.calendar_color 32 | @androidCalendarHandler.update calendar, (err) -> 33 | log.error err if err 34 | -------------------------------------------------------------------------------- /src/app/replicator/change/conflicts_handler.coffee: -------------------------------------------------------------------------------- 1 | async = require 'async' 2 | 3 | log = require('../../lib/persistent_log') 4 | prefix: "ConflictsHandler" 5 | date: true 6 | 7 | module.exports = class ConflictsHandler 8 | 9 | constructor: (@db)-> 10 | 11 | handleConflicts: (doc, callback) -> 12 | log.debug "handleConflicts" 13 | 14 | # Get the doc with conflicts (and revs) infos from Pouch 15 | @db.get doc._id, { conflicts: true, open_revs: "all" }, (err, local) => 16 | return callback err if err 17 | 18 | if local._conflicts? 19 | @_handleConflicts doc, local, callback 20 | else 21 | callback null, doc 22 | 23 | 24 | ###* 25 | * Resolve the conflicts, always keeping the given cozy's doc. 26 | * @param doc just received from cozy 27 | * @param local the version in the pouch, with conflicts and revs infos 28 | * @param callback with return the keeped doc (the cozy's one) 29 | ### 30 | _handleConflicts: (doc, local, callback) -> 31 | log.info "fixes conflicts for #{doc._id}" 32 | 33 | # Collect all conflicts to remove, excluding version from cozy 34 | revsToDelete = local._conflicts.filter (rev) -> rev isnt doc._rev 35 | 36 | # If pouch had chosen to keep its version, the cozy's rev is in 37 | # conflicts. We have to remove the other one 38 | if revsToDelete.length isnt local._conflicts.length 39 | revNum = parseInt doc._rev.split('-')[0] 40 | # That's how revisions are classed in conflicts array 41 | idx = local._revisions.start - revNum 42 | revsToDelete.push "#{revNum}-#{local._revisions.ids[idx]}" 43 | 44 | # Apply clean up. 45 | async.each revsToDelete, (rev, cb) => 46 | log.info "remove revision #{rev}" 47 | @db.remove doc._id, rev, cb 48 | , (err) -> 49 | callback err, doc 50 | -------------------------------------------------------------------------------- /src/app/replicator/fromDevice/android_account.coffee: -------------------------------------------------------------------------------- 1 | log = require('../../lib/persistent_log') 2 | prefix: "AndroidAccount" 3 | date: true 4 | 5 | module.exports = class AndroidAccount 6 | 7 | @NAME: 'myCozy' 8 | @TYPE: 'io.cozy' 9 | @ACCOUNT: 10 | accountName: AndroidAccount.NAME 11 | accountType: AndroidAccount.TYPE 12 | 13 | create: (callback) -> 14 | log.debug "create" 15 | 16 | navigator.contacts.createAccount AndroidAccount.TYPE 17 | , AndroidAccount.NAME, -> 18 | callback null 19 | , callback 20 | -------------------------------------------------------------------------------- /src/app/replicator/fromDevice/changes_importer.coffee: -------------------------------------------------------------------------------- 1 | async = require 'async' 2 | 3 | ContactImporter = require './contact_importer' 4 | EventImporter = require "./event_importer" 5 | NotificationImporter = require "./notification_importer" 6 | 7 | log = require('../../lib/persistent_log') 8 | prefix: "ChangesImporter" 9 | date: true 10 | 11 | 12 | module.exports = class ChangesImporter 13 | 14 | 15 | constructor: (@config, @eventImporter, @contactImporter, @notifImporter) -> 16 | @config ?= app.init.config 17 | @eventImporter ?= new EventImporter() 18 | @contactImporter ?= new ContactImporter() 19 | @notifImporter ?= new NotificationImporter() 20 | 21 | 22 | synchronize: (callback) -> 23 | async.series [ 24 | (cb) => 25 | if @config.get('syncCalendars') 26 | @eventImporter.synchronize cb 27 | else cb() 28 | 29 | (cb) => 30 | if @config.get('syncContacts') 31 | @contactImporter.synchronize cb 32 | else cb() 33 | 34 | (cb) => 35 | if @config.get('cozyNotifications') 36 | @notifImporter.synchronize cb 37 | else cb() 38 | 39 | ], callback 40 | -------------------------------------------------------------------------------- /src/app/replicator/fromDevice/notification_importer.coffee: -------------------------------------------------------------------------------- 1 | NotificationHandler = require '../../lib/notification_handler' 2 | 3 | log = require('../../lib/persistent_log') 4 | prefix: "NotificationImporter" 5 | date: true 6 | 7 | 8 | module.exports = class NotificationImporter 9 | 10 | 11 | constructor: -> 12 | @notificationHandler = new NotificationHandler() 13 | 14 | 15 | synchronize: (callback) -> 16 | @notificationHandler.deletesIfIsNotPresent callback 17 | -------------------------------------------------------------------------------- /src/app/replicator/transformer/cozy_to_android_calendar.coffee: -------------------------------------------------------------------------------- 1 | log = require('../../lib/persistent_log') 2 | prefix: "CozyToAndroidCalendar" 3 | date: true 4 | 5 | ###* 6 | * CozyToAndroidCalendar transform a cozy calendar to a android calendar and 7 | * reverse transform an android calendar to a cozy calendar. 8 | * 9 | * @class CozyToAndroidCalendar 10 | ### 11 | module.exports = class CozyToAndroidCalendar 12 | 13 | ###* 14 | * @param {Object} cozyCalendar - it's a cozy calendar 15 | * @param {Object} account - it's android account 16 | * @param {Object} androidCalendar - it's an android calendar (optional) 17 | ### 18 | transform: (cozyCalendar, account, androidCalendar = undefined) -> 19 | log.debug "transform" 20 | 21 | return { 22 | _id: if androidCalendar then androidCalendar._id else undefined 23 | account_name: account.accountName 24 | account_type: account.accountType 25 | ownerAccount: account.accountName 26 | name: cozyCalendar.name.replace /\s/g, '' 27 | calendar_displayName: cozyCalendar.name 28 | calendar_color: @_color2Android(cozyCalendar.color) 29 | # No specific needs (?) 30 | #calendar_timezone: null 31 | 32 | # http://developer.android.com/reference/android/provider/ 33 | # CalendarContract.CalendarColumns.html#CALENDAR_ACCESS_LEVEL 34 | calendar_access_level: 700 35 | sync_events: 1 36 | # METHOD_ALERT, METHOD_EMAIL 37 | allowedReminders: "1,2" 38 | allowedAvailability: "0" # Deactivated. 39 | allowedAttendeeTypes: "0" # Deactivated. 40 | } 41 | 42 | reverseTransform: (androidCalendar) -> 43 | log.debug "reverseTransform" 44 | 45 | new Error "TODO" 46 | 47 | _color2Android: (color) -> 48 | color = '#2979FF' if color is undefined 49 | if color[0] is '#' 50 | return parseInt color.replace(/[^0-9A-Fa-f]/g, ''), 16 51 | else if color[0] is 'r' 52 | rgb = color.match /(\d+)/g 53 | return rgb[0] * 256 * 256 + rgb[1] * 256 + rgb[2] * 1 54 | -------------------------------------------------------------------------------- /src/app/router.coffee: -------------------------------------------------------------------------------- 1 | Layout = require './views/layout/layout' 2 | LayoutWithHeader = require './views/layout/layout_with_header' 3 | 4 | # onboarding 5 | WelcomeView = require './views/onboarding/welcome' 6 | UrlView = require './views/onboarding/url' 7 | PasswordView = require './views/onboarding/password' 8 | CheckCredentialsView = require './views/onboarding/check_credentials' 9 | PermissionsView = require './views/onboarding/permission' 10 | 11 | # others 12 | FileViewer = require './views/file_viewer' 13 | MediaPlayerView = require './views/media_player' 14 | ConfigView = require './views/config' 15 | 16 | log = require('./lib/persistent_log') 17 | prefix: "router" 18 | date: true 19 | 20 | 21 | module.exports = class Router extends Backbone.Router 22 | 23 | 24 | routes: 25 | 'onboarding/welcome' : 'welcome' 26 | 'onboarding/url' : 'url' 27 | 'onboarding/password' : 'password' 28 | 'onboarding/check' : 'checkCredentials' 29 | 'permissions/*step' : 'permissions' 30 | 31 | 'folder*path' : 'fileViewer' 32 | 'media/*mimetype//*path' : 'media' 33 | 'config' : 'config' 34 | 35 | 36 | init: (state) -> 37 | if state is 'appConfigured' 38 | @navigate 'folder/' 39 | return @fileViewer '/' 40 | 41 | @layout = new Layout() 42 | @layoutWithHeader = new LayoutWithHeader() 43 | $('body').html @layout.render().el 44 | 45 | if state is 'deviceCreated' 46 | @permissions 'files' 47 | else 48 | @welcome() 49 | 50 | 51 | welcome: -> 52 | @layout.display new WelcomeView() 53 | 54 | 55 | url: -> 56 | @layout.display new UrlView() 57 | 58 | 59 | password: (error) -> 60 | @layout.display new PasswordView error 61 | 62 | 63 | checkCredentials: (password) -> 64 | @layout.display new CheckCredentialsView password 65 | 66 | 67 | permissions: (step) -> 68 | @layout.display new PermissionsView step 69 | 70 | 71 | 72 | fileViewer: (path) -> 73 | log.info 'foleViewer', path 74 | 75 | unless @layout is undefined or @layout instanceof LayoutWithHeader 76 | @layout.destroy() 77 | @layout = undefined 78 | 79 | if @layout is undefined 80 | log.info 'create LayoutWithHeader' 81 | if @layoutWithHeader 82 | @layout = @layoutWithHeader 83 | else 84 | @layout = new LayoutWithHeader() 85 | $('body').html @layout.render().el 86 | return @layout.alredyLoad = false if @layout.alredyLoad 87 | if @layout.back 88 | @layout.goBack() 89 | else 90 | @layout.updateHeader path: path, displaySearch: true 91 | @layout.display new FileViewer path: path 92 | 93 | 94 | media: (mimetype, path) -> 95 | @layout.display new MediaPlayerView path, mimetype 96 | 97 | 98 | config: -> 99 | @layout.updateHeader title: t('config'), displaySearch: false 100 | @layout.display new ConfigView() 101 | -------------------------------------------------------------------------------- /src/app/styles/base/_background.styl: -------------------------------------------------------------------------------- 1 | // shadows 2 | depth01 = 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24) 3 | depth02 = 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23) 4 | depth03 = 0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23) 5 | -------------------------------------------------------------------------------- /src/app/styles/base/_buttons.styl: -------------------------------------------------------------------------------- 1 | /* 2 | * BUTTONS 3 | * ======= 4 | * 5 | * This file contains every styles related to buttons. 6 | */ 7 | 8 | .button 9 | text-transform uppercase 10 | font-weight bold 11 | box-shadow depth01 12 | // overrides ionic default 13 | font-size 14px 14 | 15 | &.button-energized 16 | background-color red 17 | border-color red - 20% 18 | 19 | &.button-balanced 20 | background-color blue 21 | border-color blue - 10% 22 | 23 | &.button-grey 24 | background-color grey-04 25 | border-color grey-04 - 10% 26 | color white 27 | 28 | &.button-dark.button-clear 29 | // overrides ionic default 30 | background grey-01 31 | border-color grey-02 32 | 33 | &.button-calm 34 | background-color blue 35 | 36 | .button-full-width 37 | width 100% 38 | 39 | .button-bar > .button 40 | &:first-child 41 | // overrides ionic default 42 | border-radius 2px 43 | margin-right 4px 44 | 45 | &:last-child 46 | // overrides ionic default 47 | border-right-width 0 48 | border-radius 2px 49 | margin-left 4px 50 | 51 | .item-options .button 52 | line-height 50px 53 | background-color grey-01 54 | color grey-08 55 | 56 | #btn-save 57 | #btn-back 58 | margin-top 30px 59 | 60 | .bar-header 61 | .button 62 | background-color transparent 63 | box-shadow 0 0 64 | border 0 65 | 66 | .btn-menu 67 | z-index 100 68 | flex none 69 | width 42px 70 | height 38px 71 | margin-right 0.5em 72 | @extend $icon-menu 73 | 74 | .btn-search 75 | z-index 100 76 | opacity .4 77 | position absolute 78 | right 12px 79 | width 42px 80 | height 46px 81 | @extend $icon-magnifier 82 | -------------------------------------------------------------------------------- /src/app/styles/base/_colors.styl: -------------------------------------------------------------------------------- 1 | /* 2 | * COLORS 3 | * ====== 4 | * 5 | * This file contains colors declarations and tools. 6 | */ 7 | 8 | // defaults 9 | black = #000 10 | white = #fff 11 | 12 | // 50 (well, in fact, mostly 8) shades of grey 13 | grey-01 = #EAEEF2 14 | grey-02 = #DDE6EF 15 | grey-03 = #C8D5DF 16 | grey-04 = #ACB8C5 17 | grey-05 = #92A0B2 18 | grey-06 = #748192 19 | grey-07 = #4F5B69 20 | grey-08 = #32363F 21 | 22 | // more of cozy UI 23 | blue = #33A6FF 24 | dark-blue = blue - 20% 25 | red = #FF3713 26 | green = #16D943 27 | 28 | body 29 | background-color #32363F 30 | color grey-08 31 | 32 | .bg-cozy-color 33 | background-color blue !important 34 | .cozy-color, .text-cozy 35 | color blue 36 | .spinner-cozy 37 | border-color blue 38 | .spinner-white 39 | border-color white 40 | -------------------------------------------------------------------------------- /src/app/styles/base/_foreground.styl: -------------------------------------------------------------------------------- 1 | html, body 2 | height 100% 3 | 4 | body 5 | font-size 16px 6 | -------------------------------------------------------------------------------- /src/app/styles/base/_form.styl: -------------------------------------------------------------------------------- 1 | 2 | // Text input 3 | // -------------------------------------------------------- 4 | 5 | .card input 6 | padding 6px 0 7 | min-height 58px 8 | border-bottom 1px solid grey-03 9 | font-size 1rem 10 | 11 | &:focus 12 | border-color blue 13 | 14 | // A weird block appears on the login screen when an input 15 | // is on focus and we scroll. 16 | // The display none allows to hide it. 17 | .card .cloned-text-input 18 | display none 19 | 20 | #deviceNamePicker .item-stacked-label input 21 | // overrides ionic default 22 | padding-left 0 23 | 24 | 25 | 26 | // search input 27 | // -------------------------------------------------------- 28 | .menu .item-input-inset .item-input-wrapper 29 | input 30 | padding 0 31 | min-height 48px 32 | border-bottom 1px solid grey-08 33 | font-size 1rem 34 | color grey-01 35 | 36 | &:focus 37 | border-color blue 38 | 39 | &::-webkit-input-placeholder 40 | color grey-05 41 | 42 | 43 | 44 | // Checkboxes 45 | // -------------------------------------------------------- 46 | .checkbox 47 | padding 14px 48 | display block 49 | height 52px 50 | 51 | .checkbox label 52 | font-size 16px 53 | padding-left 15px 54 | vertical-align top 55 | 56 | .checkbox input 57 | // overrides ionic default 58 | width 24px 59 | height 24px 60 | 61 | &:before 62 | box-shadow inset 0 0 0 2px grey-03 63 | transition all 350ms cubic-bezier(0, 0.89, 0.44, 1) 64 | // overrides ionic default 65 | border 0 66 | border-radius 2px 67 | 68 | &:checked:before 69 | box-shadow inset 0 0 0 30px blue 70 | 71 | &:after 72 | opacity 0 73 | transition opacity .2s 74 | // overrides ionic default 75 | top 24% 76 | left 20% 77 | width 17px 78 | height 8px 79 | border-width 2px 80 | 81 | 82 | &:checked:after 83 | opacity 1 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /src/app/styles/base/_page.styl: -------------------------------------------------------------------------------- 1 | .page 2 | position absolute 3 | top 0 4 | left 0 5 | width 100% 6 | height 100% 7 | -webkit-transform translate3d(0, 0, 0) 8 | transform translate3d(0, 0, 0) 9 | -webkit-transition-duration 5s 10 | transition-duration 5s 11 | 12 | 13 | .page.posLeft1 14 | left -100% 15 | 16 | 17 | .page.posCenter1 18 | left 0 19 | 20 | 21 | .page.posRight1 22 | left 100% 23 | 24 | 25 | #layout 26 | height 100% 27 | 28 | 29 | #layout-with-menu 30 | #contentContainer 31 | position fixed 32 | top 56px 33 | bottom 0 34 | left 0 35 | right 0 36 | background-color white 37 | 38 | .snap-drawers 39 | position absolute 40 | 41 | .snap-content 42 | position absolute 43 | border none 44 | border-radius 0 45 | 46 | nav .button-collapse 47 | margin 0 3px 48 | 49 | #sidenav-overlay 50 | top 56px 51 | 52 | .configClass 53 | overflow auto 54 | width 100% 55 | height 100% 56 | 57 | .collection 58 | z-index 2 59 | 60 | h2.display-icon 61 | i 62 | font-size 150px 63 | color grey 64 | -------------------------------------------------------------------------------- /src/app/styles/base/_variable.styl: -------------------------------------------------------------------------------- 1 | ios-header = 20px 2 | -------------------------------------------------------------------------------- /src/app/styles/components/_errors.styl: -------------------------------------------------------------------------------- 1 | // Errors 2 | // ---------------------------------------------------------------------------- 3 | 4 | .error-msg 5 | margin 1em 0 6 | padding 1em 7 | 8 | background-color red 9 | border-radius 4px 10 | 11 | color white 12 | font-size 1em 13 | line-height 1.3 14 | 15 | a 16 | color white 17 | text-decoration underline 18 | 19 | // notifications 20 | .bar.bar-calm 21 | background-color grey-04 22 | border-color grey-04 23 | background-image none 24 | -------------------------------------------------------------------------------- /src/app/styles/components/_file_viewer.styl: -------------------------------------------------------------------------------- 1 | .import 2 | #contentContainer 3 | bottom 60px!important 4 | 5 | .download 6 | 7 | .actions 8 | position absolute 9 | top 0 10 | right 0 11 | border none 12 | bottom 0 13 | background transparent 14 | 15 | .mdi 16 | background transparent 17 | color #808080 18 | box-shadow none 19 | 20 | .modal 21 | z-index 600!important 22 | 23 | .modal-overlay 24 | z-index 300!important 25 | 26 | .modal 27 | 28 | &.cache 29 | .no-cache 30 | display none 31 | &.no-cache 32 | .cache 33 | display none 34 | 35 | .big 36 | display none!important 37 | opacity 0.5 38 | 39 | &.is-big 40 | .no-big 41 | display none!important 42 | .big 43 | display block!important 44 | 45 | .collection 46 | border none 47 | margin 0 48 | padding 0 49 | background-color transparent 50 | 51 | .collection-header 52 | border-bottom 1px solid #808080 53 | background-color transparent!important 54 | font-weight bold 55 | 56 | .name 57 | padding-top 10px 58 | 59 | .collection-item 60 | border 0 61 | color #808080 62 | margin-top 10px 63 | background-color transparent!important 64 | min-height 40px 65 | padding-right 0 66 | 67 | .icon 68 | color #808080 69 | font-size 20px 70 | -------------------------------------------------------------------------------- /src/app/styles/components/_list-item.styl: -------------------------------------------------------------------------------- 1 | .item-complex.item-icon-left .item-content 2 | // overrides ionic default 3 | padding-left 55px 4 | -------------------------------------------------------------------------------- /src/app/styles/components/_mediaPlayer.styl: -------------------------------------------------------------------------------- 1 | .mediaPicture 2 | background-size contain 3 | position fixed 4 | top 56px 5 | bottom 0 6 | left 0 7 | right 0 8 | z-index 299 9 | background-color black 10 | color white 11 | background-repeat no-repeat 12 | background-position center, center 13 | 14 | .mediaHeader 15 | transition-duration: 0.3s; 16 | position fixed 17 | top 0 18 | left 0 19 | right 0 20 | 21 | .btn-floating 22 | background-color transparent 23 | box-shadow inherit 24 | 25 | span 26 | position absolute 27 | top 0 28 | left 55px 29 | right 55px 30 | height 56px 31 | overflow hidden 32 | white-space nowrap 33 | text-overflow ellipsis 34 | 35 | .mediaContainer 36 | height 100% 37 | 38 | iframe 39 | border none 40 | width 100% 41 | height 100% 42 | 43 | .actions 44 | position: absolute 45 | top 0 46 | right 0 47 | padding-left 15px 48 | border-radius 28px 49 | 50 | nav 51 | background-color #000 52 | 53 | a 54 | height 56px 55 | -------------------------------------------------------------------------------- /src/app/styles/components/_splash.styl: -------------------------------------------------------------------------------- 1 | .splash-screen 2 | width 33% 3 | position absolute 4 | top 45% 5 | left 50% 6 | margin-left -16.5% 7 | margin-top -16.5% 8 | 9 | .splash-message 10 | width 66% 11 | position absolute 12 | bottom 20% 13 | left 50% 14 | margin-left -33% 15 | color white 16 | font-family sans-serif 17 | font-size 18px 18 | text-align center 19 | -------------------------------------------------------------------------------- /src/app/styles/components/_toolbar.styl: -------------------------------------------------------------------------------- 1 | // Header 2 | 3 | header-size = 48px 4 | 5 | 6 | .bar 7 | // overrides inonic default header's height 8 | height header-size 9 | 10 | .has-header 11 | // overrides inonic default header's height 12 | top header-size 13 | 14 | 15 | .bar-header 16 | background-image none 17 | background-color blue 18 | box-shadow depth01 19 | 20 | h1.title 21 | padding-left 2em 22 | 23 | text-align left 24 | color white 25 | font-size (20/16)em 26 | line-height 48px 27 | 28 | .spinner 29 | position absolute 30 | right 0 31 | margin 6px 10px 32 | height 26px 33 | width 26px 34 | 35 | img 36 | width 100% 37 | 38 | // logo icon used in the setup screens (on boarding) 39 | .icon-logo 40 | width 42px 41 | height 38px 42 | @extend $icon-logo 43 | 44 | 45 | 46 | // breadcrumbs 47 | #breadcrumbs 48 | display flex 49 | align-items center 50 | width 100% 51 | 52 | a 53 | color #fff 54 | text-decoration none 55 | 56 | .home 57 | flex none 58 | position relative 59 | font-weight bold 60 | line-height 28px 61 | 62 | #crumbs 63 | width 52vw 64 | margin-left 24px 65 | 66 | &::-webkit-scrollbar 67 | display none 68 | 69 | ul 70 | list-style none 71 | white-space nowrap 72 | 73 | li 74 | display none 75 | overflow hidden 76 | text-overflow ellipsis 77 | color #fff 78 | 79 | &:last-child 80 | display block 81 | 82 | .white-arrow 83 | z-index 1 84 | position absolute 85 | top 5px 86 | right -17px 87 | width 10px 88 | height 20px 89 | 90 | border-top 10px solid transparent 91 | border-bottom 10px solid transparent 92 | border-left 10px solid white 93 | 94 | .blue-arrow 95 | z-index 2 96 | position absolute 97 | top 5px 98 | right -15px 99 | width 10px 100 | height 20px 101 | 102 | border-top 10px solid transparent 103 | border-bottom 10px solid transparent 104 | border-left 10px solid blue 105 | -------------------------------------------------------------------------------- /src/app/templates/breadcrumbs.jade: -------------------------------------------------------------------------------- 1 | a.home(href="#folder/") 2 | .span= t('files') 3 | .arrow(style='display: none;') 4 | .blue-arrow 5 | .white-arrow 6 | 7 | //- Current folder 8 | #crumbs 9 | if hasFolder 10 | ul 11 | li 12 | a(href="#folder#{folder.path}") 13 | | #{folder.name} 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/app/templates/config.jade: -------------------------------------------------------------------------------- 1 | ul.collection.with-header 2 | li.collection-item 3 | div 4 | = deviceName 5 | .secondary-content 6 | i.material-icons stay_current_portrait 7 | li.collection-item 8 | div v 9 | = appVersion 10 | .secondary-content 11 | i.material-icons info 12 | if device.platform === 'Android' 13 | li.collection-header 14 | h5= t('synchronisation title') 15 | li.collection-item 16 | p 17 | input#contactSyncCheck(type="checkbox", checked=syncContacts, disabled= !firstSync) 18 | label(for='contactSyncCheck')= t('contacts sync label') 19 | p 20 | input#calendarSyncCheck(type="checkbox", checked=syncCalendars, disabled= !firstSync) 21 | label(for='calendarSyncCheck')= t('calendars sync label') 22 | p 23 | input#cozyNotificationsCheck(type="checkbox", checked=cozyNotifications) 24 | label(for='cozyNotificationsCheck')= t('cozy notifications sync label') 25 | 26 | li.collection-header 27 | h5= t('backup title') 28 | li.collection-item 29 | if device.platform === 'Android' 30 | p 31 | input#imageSyncCheck(type="checkbox", checked=syncImages) 32 | label(for='imageSyncCheck')= t('images sync label') 33 | p 34 | input#wifiSyncCheck(type="checkbox", checked=syncOnWifi) 35 | label(for='wifiSyncCheck')= t('wifi sync label') 36 | 37 | 38 | li.collection-header 39 | h5= t('reset title') 40 | 41 | li.collection-item 42 | p= t('synchro warning') 43 | p.center 44 | button#synchrobtn.waves-effect.waves-light.btn.bg-cozy-color 45 | i.material-icons.left cloud_download 46 | = t('retry synchro') 47 | 48 | 49 | li.collection-header 50 | h5= t('support') 51 | 52 | li.collection-item 53 | p= t('send log info') 54 | p.center 55 | button#sendlogbtn.waves-effect.waves-light.btn.bg-cozy-color 56 | i.material-icons.left send 57 | = t('config_log_send') 58 | p.center 59 | button#sharelogbtn.waves-effect.waves-light.btn.bg-cozy-color 60 | i.material-icons.left share 61 | = t('config_log_share') 62 | -------------------------------------------------------------------------------- /src/app/templates/file_viewer.jade: -------------------------------------------------------------------------------- 1 | .collection.files.z-depth-2.snap-content 2 | if loading 3 | .valign-wrapper 4 | .center.cozy-color 5 | .row 6 | .col.s12 7 | .preloader-wrapper.active 8 | .spinner-layer.spinner-cozy 9 | .circle-clipper.left 10 | .circle 11 | .gap-patch 12 | .circle 13 | .circle-clipper.right 14 | .circle 15 | .col.s12 16 | span= t('config_loading_files') 17 | else 18 | each file in files 19 | a.collection-item.download(href=file.link, data-key=file._id, data-doctype=file.docType, data-type=file.icon, data-fullpath=file.fullPath, data-name=file.name, data-is-cached=file.isCached?'true':'false', data-is-compatible-viewer=file.isCompatibleViewer?'true':'false', data-is-big=file.isBig?'true':'false') 20 | if file.isCached 21 | i(class="icon mdi mdi-#{file.icon} is-cached") 22 | else 23 | i(class="icon mdi mdi-#{file.icon}") 24 | span.title.truncate= file.name 25 | 26 | if file.docType.toLowerCase() == 'file' 27 | button.actions 28 | i.mdi.mdi-dots-vertical.btn-floating.waves-effect 29 | 30 | .progress 31 | .determinate.fileProgress(style='width: 0%') 32 | 33 | else 34 | .valign-wrapper 35 | .center.cozy-color 36 | i.mdi.mdi-file 37 | span Aucun fichier 38 | 39 | 40 | #actions-modal.modal.bottom-sheet 41 | .modal-content 42 | .collection.files 43 | .collection-header 44 | i.file-icon 45 | .name.truncate 46 | a.collection-item.waves-effect.no-cache.actionDownload 47 | i.icon.mdi.mdi-cloud-download 48 | span= t('modal_action_file_download') 49 | if isViewerCompatible 50 | a.collection-item.waves-effect.cache.no-big.actionDisplay.in 51 | i.icon.mdi.mdi-play-circle-outline 52 | span= t('modal_action_file_display_in') 53 | .collection-item.cache.big 54 | i.icon.mdi.mdi-play-circle-outline 55 | span= t('modal_action_file_big') 56 | a.collection-item.waves-effect.cache.actionDisplay 57 | i.icon.mdi.mdi-open-in-new 58 | span= t('modal_action_file_display_out') 59 | a.collection-item.waves-effect.cache.actionRemove 60 | i.icon.large.material-icons phonelink_erase 61 | span= t('modal_action_file_remove_device') 62 | 63 | 64 | #open-error-modal.modal 65 | .modal-content 66 | h4= t('modal_file_open_error_title') 67 | p= t('modal_file_open_error_description') 68 | .modal-footer 69 | a.modal-action.modal-close.waves-effect.waves-green.btn-flat(href='#!') OK 70 | -------------------------------------------------------------------------------- /src/app/templates/layout/header.jade: -------------------------------------------------------------------------------- 1 | #slide-out.side-nav 2 | ul 3 | li 4 | a.truncate(href=userUrl, onclick="open('#{userUrl}', '_system', 'location=yes')") 5 | i.material-icons cloud 6 | = userUrl 7 | li 8 | .divider 9 | li 10 | a.truncate(href='#folder/', onclick="$('#slide-out').sideNav('hide')") 11 | i.material-icons folder 12 | = t('files') 13 | li 14 | a.truncate(href='#config', onclick="$('#slide-out').sideNav('hide')") 15 | i.material-icons settings 16 | = t('config') 17 | if displayRender 18 | li 19 | .divider 20 | li 21 | a.truncate#senderBtn 22 | i.material-icons send 23 | = t('config_log_send') 24 | li 25 | a.truncate#shareBtn 26 | i.material-icons share 27 | = t('config_log_share') 28 | footer#displaySender 29 | 30 | 31 | #header.navbar-fixed.header-file 32 | nav.open 33 | .nav-wrapper.bg-cozy-color 34 | 35 | if displaySearch 36 | ul.right.search 37 | li.toggleSearch 38 | a#searchButton 39 | i.material-icons search 40 | 41 | if title 42 | a.brand-logo.center(href='#')= title 43 | 44 | ul.left.file 45 | li 46 | a#menuButton.button-collapse(href='#', data-activates='slide-out') 47 | i.material-icons menu 48 | 49 | nav.close 50 | .nav-wrapper.bg-cozy-color 51 | form#searchForm 52 | .input-field 53 | input#searchInput(type='search', required='') 54 | label(for='searchInput') 55 | i.material-icons search 56 | i.toggleSearch.material-icons close 57 | -------------------------------------------------------------------------------- /src/app/templates/layout/layout.jade: -------------------------------------------------------------------------------- 1 | #contentContainer 2 | -------------------------------------------------------------------------------- /src/app/templates/layout/layout_with_header.jade: -------------------------------------------------------------------------------- 1 | #headerContainer 2 | #contentContainer 3 | -------------------------------------------------------------------------------- /src/app/templates/media_player.jade: -------------------------------------------------------------------------------- 1 | #mediaPicture.mediaPicture 2 | .mediaHeader 3 | nav 4 | .nav-wrapper 5 | ul.left 6 | li 7 | a#exit 8 | i.material-icons.left arrow_back 9 | li 10 | span= fileName 11 | 12 | a.actions.waves-effect.waves-light 13 | i.mdi.mdi-dots-vertical.btn-floating 14 | 15 | .mediaContainer 16 | iframe(allowfullscreen="true", src="viewer/index.html?mimetype=" + mimetype + "#" + path) 17 | 18 | 19 | #actions-modal.modal.bottom-sheet 20 | .modal-content 21 | .collection.files.cache 22 | .collection-header 23 | i(class='icon mdi mdi-' + icon) 24 | .name.truncate= fileName 25 | a.collection-item.waves-effect.cache.actionDisplay 26 | i.icon.mdi.mdi-open-in-new 27 | span= t('modal_action_file_display_out') 28 | a.collection-item.waves-effect.cache.actionRemove 29 | i.icon.large.material-icons phonelink_erase 30 | span= t('modal_action_file_remove_device') 31 | 32 | 33 | #open-error-modal.modal 34 | .modal-content 35 | h4= t('modal_file_open_error_title') 36 | p= t('modal_file_open_error_description') 37 | .modal-footer 38 | a.modal-action.modal-close.waves-effect.waves-green.btn-flat(href='#!') OK 39 | -------------------------------------------------------------------------------- /src/app/templates/onboarding/check_credentials.jade: -------------------------------------------------------------------------------- 1 | #check.valign-wrapper.loader 2 | .center.row 3 | .col.s12 4 | .preloader-wrapper.active 5 | .spinner-layer.spinner-white 6 | .circle-clipper.left 7 | .circle 8 | .gap-patch 9 | .circle 10 | .circle-clipper.right 11 | .circle 12 | .col.s12 13 | span= t('onboarding_device_create') 14 | -------------------------------------------------------------------------------- /src/app/templates/onboarding/password.jade: -------------------------------------------------------------------------------- 1 | #password.wizard-step.fWizardPassword 2 | .block-100.valign-wrapper 3 | .valign.center 4 | form 5 | .row 6 | .input-field.col.s12 7 | i.material-icons.prefix https 8 | input#input-password(type='password', value=password, class=(error?'invalid':'')) 9 | label(for='input-password', data-error=(error?t(error):' ')) 10 | = t('password placeholder') 11 | .input-field.col.s12 12 | input#display-password(type="checkbox") 13 | label(for='display-password')= t('onboarding_password_display') 14 | 15 | p.flow-text 16 | = t('password help', {url: cozyUrl}) 17 | 18 | a#btn-back(href='#onboarding/url') 19 | = t('wrong cozy address') 20 | 21 | a#btn-password.button.waves-effect.waves-light.btn-large.white(href='#onboarding/check') 22 | = t('login') 23 | -------------------------------------------------------------------------------- /src/app/templates/onboarding/permission_calendars.jade: -------------------------------------------------------------------------------- 1 | .wizard-step.fWizardCalendars 2 | .big-icon-container 3 | img(src="./img/icon-calendar.svg") 4 | 5 | h2= t('calendars header') 6 | 7 | p= t('calendars explanation') 8 | 9 | .button-block 10 | button#btn-nope= t('skip') 11 | button#btn-yep= t('authorize') 12 | -------------------------------------------------------------------------------- /src/app/templates/onboarding/permission_contacts.jade: -------------------------------------------------------------------------------- 1 | .wizard-step.fWizardContacts 2 | .big-icon-container 3 | img(src="./img/icon-contact.svg") 4 | 5 | h2= t('contacts header') 6 | 7 | p= t('contacts explanation') 8 | 9 | .button-block 10 | button#btn-nope= t('skip') 11 | button#btn-yep= t('authorize') 12 | -------------------------------------------------------------------------------- /src/app/templates/onboarding/permission_files.jade: -------------------------------------------------------------------------------- 1 | .wizard-step.fWizardFiles 2 | .big-icon-container 3 | img(src="./img/icon-files.svg") 4 | 5 | h2= t('files header') 6 | 7 | p= t('files explanation') 8 | 9 | .button-block 10 | button#btn-yep= t('next') 11 | -------------------------------------------------------------------------------- /src/app/templates/onboarding/permission_photos.jade: -------------------------------------------------------------------------------- 1 | .wizard-step.fWizardPhotos 2 | .big-icon-container 3 | img(src="./img/icon-photos.svg") 4 | 5 | h2= t('pictures header') 6 | 7 | p= t('pictures explanation') 8 | 9 | .button-block 10 | button#btn-nope= t('skip') 11 | button#btn-yep= t('authorize') 12 | -------------------------------------------------------------------------------- /src/app/templates/onboarding/url.jade: -------------------------------------------------------------------------------- 1 | #url.wizard-step.fWizardURL 2 | .block-100.valign-wrapper 3 | .valign.center 4 | form 5 | .row 6 | .input-field.col.s12 7 | i.material-icons.prefix account_circle 8 | input#input-url(type='url', value=cozyUrl, class=(error?'invalid':'')) 9 | label(for='input-url', data-error=(error?t(error):' ')) 10 | = t('url placeholder') 11 | 12 | p.flow-text 13 | = t('url help') 14 | 15 | a#btn-register(href="#", onclick="open('https://cozy.io/en/try-it/', '_system', 'location=yes')") 16 | = t('no account register') 17 | 18 | a#btn-url.button.waves-effect.waves-light.btn-large.white.text-cozy(href='#onboarding/password')= t('next') 19 | -------------------------------------------------------------------------------- /src/app/templates/onboarding/welcome.jade: -------------------------------------------------------------------------------- 1 | #welcome.wizard-step.fWizardWelcome 2 | .block-100.valign-wrapper 3 | .big-icon-container.valign.center-align 4 | .row 5 | .col.s12.m6 6 | img.big-icon(src="./img/icon.png") 7 | .col.s12.m6 8 | br.hide-on-small-only 9 | br.hide-on-small-only 10 | h1 11 | = t('your own private cloud') 12 | a.waves-effect.waves-light.btn-large.bg-cozy-color(href="#onboarding/url") 13 | = t('sign in to your cozy') 14 | -------------------------------------------------------------------------------- /src/app/views/breadcrumbs.coffee: -------------------------------------------------------------------------------- 1 | BaseView = require './layout/base_view' 2 | 3 | module.exports = class BreadcrumbsView extends BaseView 4 | 5 | id: 'breadcrumbs' 6 | template: require '../templates/breadcrumbs' 7 | 8 | initialize: (options) -> 9 | if options.path? 10 | @folder = 11 | name: options.path.split('/')[-1..-1][0] 12 | path: options.path 13 | 14 | getRenderData: -> 15 | return hasFolder: @folder?, folder: @folder 16 | 17 | afterRender: -> 18 | if @folder 19 | @$('#crumbs').show() 20 | @$('.home .arrow').show() 21 | else 22 | @$('#crumbs').hide() 23 | @$('.home .arrow').hide() 24 | return @ 25 | -------------------------------------------------------------------------------- /src/app/views/layout/base_view.coffee: -------------------------------------------------------------------------------- 1 | module.exports = class BaseView extends Backbone.View 2 | 3 | 4 | template: -> 5 | 6 | 7 | initialize: (@options) -> 8 | 9 | 10 | getRenderData: -> 11 | model: @model?.toJSON() 12 | 13 | 14 | render: -> 15 | @beforeRender() 16 | @$el.html @template(@getRenderData()) 17 | @bindRefs() if @refs 18 | @$el.prop 'className', @className() if typeof @className is 'function' 19 | @afterRender() 20 | @ 21 | 22 | 23 | bindRefs: -> 24 | for ref, selector of @refs 25 | @[ref] = @$ selector 26 | 27 | 28 | setState: (key, value) -> 29 | clearTimeout @dirtyTimeout 30 | @[key] = value 31 | @dirtyTimeout = setTimeout (=> @render()), 1 32 | 33 | 34 | beforeRender: -> 35 | 36 | 37 | afterRender: -> 38 | 39 | 40 | destroyWithDelay: (delay = 1000) -> 41 | setTimeout => 42 | @destroy() 43 | , delay 44 | 45 | 46 | destroy: -> 47 | @undelegateEvents() 48 | @$el.removeData().unbind() 49 | @remove() 50 | Backbone.View::remove.call @ 51 | 52 | 53 | onBackButtonClicked: (event) => 54 | @options.fsm.trigger 'clickBack' 55 | -------------------------------------------------------------------------------- /src/app/views/layout/header.coffee: -------------------------------------------------------------------------------- 1 | BaseView = require './base_view' 2 | pathHelper = require '../../lib/path' 3 | logSender = require '../../lib/log_sender' 4 | log = require('../../lib/persistent_log') 5 | prefix: "Header" 6 | date: true 7 | 8 | 9 | module.exports = class Header extends BaseView 10 | 11 | 12 | template: require '../../templates/layout/header' 13 | refs: 14 | menuButton: '#menuButton' 15 | searchInput: '#searchInput' 16 | searchForm: '#searchForm' 17 | headerDiv: '#header' 18 | 19 | 20 | initialize: -> 21 | @config ?= app.init.config 22 | @router ?= app.router 23 | @displaySender = 0 24 | 25 | 26 | events: -> 27 | 'click .toggleSearch': 'toggleSearch' 28 | 'submit #searchForm': 'searchSubmit' 29 | 'click #displaySender': 'displaySender' 30 | 'click #senderBtn': -> logSender.send() 31 | 'click #shareBtn': -> logSender.share() 32 | 33 | 34 | hide: -> 35 | @headerDiv.hide() 36 | 37 | 38 | show: -> 39 | @headerDiv.show() 40 | 41 | 42 | update: (options) -> 43 | @displaySearch = false #options.displaySearch 44 | if options.path 45 | @parentPath = pathHelper.getDirName options.path 46 | @title = pathHelper.getFileName options.path 47 | @title = t('files') if @parentPath is '' and @title is '' 48 | @path = options.path 49 | @title = options.title if options.title 50 | 51 | @render() 52 | 53 | 54 | getRenderData: -> 55 | parentPath: @parentPath 56 | title: @title 57 | displaySearch: @displaySearch 58 | userUrl: @config.get 'cozyURL' 59 | displayRender: @displaySender > 1 60 | 61 | 62 | toggleSearch: (event) -> 63 | console.log event 64 | nav = $('.header-file') 65 | nav.toggleClass 'search-open' 66 | if nav.hasClass 'search-open' 67 | setTimeout => 68 | @searchInput.focus() 69 | , 100 70 | 71 | 72 | searchSubmit: (event) -> 73 | if @searchInput.val() 74 | @router.navigate '#search/' + @searchInput.val() 75 | return false 76 | 77 | 78 | displaySender: -> 79 | @displaySender++ 80 | @render() 81 | 82 | 83 | afterRender: -> 84 | setTimeout => 85 | if $('.drag-target').length > 0 86 | @menuButton.sideNav('destroy') 87 | setTimeout => 88 | @menuButton.sideNav() 89 | , 100 90 | else 91 | @menuButton.sideNav() 92 | , 100 -------------------------------------------------------------------------------- /src/app/views/layout/layout.coffee: -------------------------------------------------------------------------------- 1 | BaseView = require './base_view' 2 | 3 | 4 | log = require('../../lib/persistent_log') 5 | prefix: "Layout" 6 | date: true 7 | 8 | 9 | module.exports = class Layout extends BaseView 10 | 11 | 12 | id: 'layout' 13 | template: require '../../templates/layout/layout' 14 | refs: 15 | contentContainer: '#contentContainer' 16 | 17 | 18 | initialize: -> 19 | @back = false 20 | document.addEventListener "backbutton", @onBackButtonClicked, false 21 | 22 | 23 | onBackButtonClicked: (event) => 24 | log.info 'onBackButtonClicked' 25 | 26 | if @currentView.backExit 27 | if window.confirm t "confirm exit message" 28 | navigator.app.exitApp() 29 | else 30 | @back = true 31 | window.history.back() 32 | 33 | 34 | display: (@view, back = false) -> 35 | log.info 'display' 36 | animationEnd = 'webkitAnimationEnd mozAnimationEnd MSAnimationEnd' + \ 37 | ' oanimationend animationend' 38 | 39 | animationExit = 40 | 'animated ' + (@currentView?.animationExit or 'slideOutLeft') 41 | animationEntrance = 42 | 'animated ' + (@view?.animationEntrance or 'slideInRight') 43 | 44 | if @back 45 | animationExit = animationExit.replace('Left', 'Right') 46 | animationEntrance = animationEntrance.replace('Right', 'Left') 47 | @back = false 48 | 49 | @oldView = @currentView 50 | @currentView = @view 51 | @contentContainer.append @view.render().$el 52 | 53 | 54 | if $("#contentContainer > div").length > 1 55 | oldPage = $("#contentContainer > div:first-child") 56 | newPage = $("#contentContainer > div:last-child") 57 | newPage 58 | .addClass(animationEntrance) 59 | .one animationEnd, -> 60 | newPage.removeClass animationEntrance 61 | oldPage.addClass(animationExit) 62 | @oldView.destroyWithDelay() 63 | else 64 | newPage = $("#contentContainer > div:first-child") 65 | setTimeout -> 66 | newPage 67 | .addClass(animationEntrance) 68 | .one animationEnd, -> 69 | newPage.removeClass animationEntrance 70 | , 100 71 | 72 | 73 | destroy: -> 74 | document.removeEventListener "backbutton", @onBackButtonClicked, false 75 | @undelegateEvents() 76 | @$el.removeData().unbind() 77 | @remove() 78 | -------------------------------------------------------------------------------- /src/app/views/layout/layout_with_header.coffee: -------------------------------------------------------------------------------- 1 | Layout = require './layout' 2 | Header = require './header' 3 | FileViewer = require '../file_viewer' 4 | InformationView = require '../../components/information/information.view' 5 | MediaPlayerView = require '../media_player' 6 | 7 | 8 | log = require('../../lib/persistent_log') 9 | prefix: "LayoutWithHeader" 10 | date: true 11 | 12 | 13 | module.exports = class LayoutWithHeader extends Layout 14 | 15 | 16 | id: 'layout-with-menu' 17 | template: require '../../templates/layout/layout_with_header' 18 | refs: 19 | headerContainer: '#headerContainer' 20 | contentContainer: '#contentContainer' 21 | menuContainer: '#menuContainer' 22 | 23 | 24 | initialize: -> 25 | super 26 | @header = new Header() 27 | @information = new InformationView() 28 | @views = [] 29 | @router = app.router 30 | @alredyLoad = false 31 | 32 | 33 | afterRender: -> 34 | @headerContainer.html @header.render().$el 35 | @$el.append @information.render().$el 36 | 37 | 38 | updateHeader: (options) -> 39 | @header.update options 40 | 41 | 42 | showHeader: -> 43 | @header.show() 44 | 45 | 46 | hideHeader: -> 47 | @header.hide() 48 | 49 | 50 | display: (@view) -> 51 | log.info 'display' 52 | 53 | @oldView = @currentView 54 | @currentView = @view 55 | 56 | @currentView.backExit = true if @oldView is undefined 57 | 58 | if @currentView instanceof MediaPlayerView 59 | @information.hide() 60 | 61 | if @currentView.append 62 | @views.push @currentView 63 | @contentContainer.append @currentView.render().$el 64 | else 65 | for @view in @views 66 | @view.destroy() 67 | @views.push @currentView 68 | @contentContainer.html @currentView.render().$el 69 | 70 | 71 | goBack: -> 72 | @oldView = @views.pop() 73 | 74 | if @oldView instanceof MediaPlayerView 75 | @information.show() 76 | 77 | @oldView.destroy() 78 | @back = false 79 | @currentView = @views[@views.length - 1] 80 | options = 81 | path: @currentView.options.path or 'files' 82 | displaySearch: true 83 | @updateHeader options 84 | if @views.length > 1 85 | @oldView = @views[@views.length - 2] 86 | else 87 | @oldView = undefined 88 | 89 | 90 | onBackButtonClicked: (event) => 91 | if @currentView.backExit 92 | if window.confirm t "confirm exit message" 93 | navigator.app.exitApp() 94 | else 95 | if @currentView instanceof FileViewer 96 | @goBack() 97 | @router.navigate 'folder' + @currentView.path, trigger: false 98 | else 99 | @back = true 100 | window.history.back() 101 | -------------------------------------------------------------------------------- /src/app/views/media_player.coffee: -------------------------------------------------------------------------------- 1 | BaseView = require './layout/base_view' 2 | FileCacheHandler = require '../lib/file_cache_handler' 3 | pathHelper = require '../lib/path' 4 | mimetype = require '../lib/mimetype' 5 | 6 | log = require('../lib/persistent_log') 7 | prefix: "MediaPlayerView" 8 | date: true 9 | 10 | module.exports = class MediaPlayerView extends BaseView 11 | 12 | btnBackEnabled: true 13 | template: require '../templates/media_player' 14 | append: true 15 | refs: 16 | picture: '#mediaPicture' 17 | container: '.mediaContainer' 18 | modal: '#actions-modal' 19 | 20 | 21 | initialize: (@path, @mimetype) -> 22 | @fileCacheHandler = new FileCacheHandler() 23 | @fileName = pathHelper.getFileName @path 24 | @layout = app.router.layout 25 | @icon = mimetype.getIcon docType: 'file', mime: @mimetype 26 | $('body').removeClass 'bg-cozy-color' 27 | 28 | 29 | events: -> 30 | 'click #exit': 'onClickExit' 31 | 'click .actionDisplay': 'onClickOpen' 32 | 'click .actionRemove': 'removeFile' 33 | 'click .actions': 'displayActions' 34 | 35 | 36 | beforeRender: -> 37 | StatusBar.backgroundColorByHexString "#000" 38 | @layout.hideHeader() 39 | 40 | 41 | onClickOpen: (e) -> 42 | e.preventDefault() 43 | @modal.modal 'close' 44 | @fileCacheHandler.open @path, (err) => 45 | if err isnt 'OK' 46 | log.warn err 47 | @openErrorModal.modal({ending_top: '20%'}).modal 'open' 48 | 49 | 50 | onClickExit: (event) -> 51 | event.preventDefault() if event 52 | @layout.showHeader() 53 | window.history.back() 54 | @layout.alredyLoad = true 55 | @layout.views.pop() 56 | @layout.information.show() 57 | @destroy() 58 | 59 | 60 | removeFile: -> 61 | cozyFileId = pathHelper.getFileName pathHelper.getDirName @path 62 | cozyFile = _id: cozyFileId 63 | 64 | @fileCacheHandler.removeLocal cozyFile, => 65 | @onClickExit() 66 | $("[data-key=#{cozyFileId}]").attr 'data-is-cached', 'false' 67 | $("[data-key=#{cozyFileId}] .is-cached").removeClass 'is-cached' 68 | 69 | 70 | getRenderData: -> 71 | mimetype: @mimetype 72 | path: @path 73 | fileName: @fileName 74 | icon: @icon 75 | 76 | 77 | displayActions: (event) -> 78 | log.debug 'displayActions' 79 | 80 | event.preventDefault() 81 | event.stopPropagation() 82 | @modal.modal().modal 'open' 83 | 84 | 85 | destroy: -> 86 | StatusBar.backgroundColorByHexString "#33A6FF" 87 | $('body').addClass 'bg-cozy-color' 88 | super 89 | -------------------------------------------------------------------------------- /src/app/views/onboarding/check_credentials.coffee: -------------------------------------------------------------------------------- 1 | BaseView = require '../layout/base_view' 2 | FirstReplication = require '../../lib/first_replication' 3 | 4 | 5 | module.exports = class CheckCredentials extends BaseView 6 | 7 | 8 | className: 'page' 9 | template: require '../../templates/onboarding/check_credentials' 10 | backExit: true 11 | 12 | 13 | initialize: (@password) -> 14 | @config ?= app.init.config 15 | @router ?= app.router 16 | @database ?= app.init.database 17 | @canRedirect = false 18 | @firstReplication = new FirstReplication() 19 | @replicator = app.init.replicator 20 | StatusBar.backgroundColorByHexString '#33A6FF' 21 | 22 | setTimeout => 23 | @canRedirect = true 24 | , 1500 25 | 26 | cozyUrl = @config.get 'cozyURL' 27 | deviceName = @config.get 'deviceName' 28 | 29 | @replicator.registerRemoteSafe cozyUrl, @password, deviceName, \ 30 | (err, body) => 31 | if err 32 | @goTo => 33 | @router.password err.message 34 | else 35 | @config.set 'state', 'deviceCreated' 36 | @config.set 'deviceName', body.login 37 | @config.set 'devicePassword', body.password 38 | @config.set 'devicePermissions', body.permissions 39 | @database.setRemoteDatabase @config.getCozyUrl() 40 | @goTo => 41 | @router.navigate '#permissions/files', trigger: true 42 | @firstReplication.addTask 'files', => 43 | @replicator.updateIndex -> 44 | 45 | 46 | goTo: (callback) -> 47 | if @canRedirect 48 | return callback() 49 | 50 | setTimeout => 51 | @goTo callback 52 | , 100 53 | -------------------------------------------------------------------------------- /src/app/views/onboarding/password.coffee: -------------------------------------------------------------------------------- 1 | BaseView = require '../layout/base_view' 2 | ConnectionHandler = require '../../lib/connection_handler' 3 | 4 | 5 | module.exports = class Password extends BaseView 6 | 7 | 8 | className: 'page' 9 | template: require '../../templates/onboarding/password' 10 | refs: 11 | inputPassword: '#input-password' 12 | displayPassword: '#display-password' 13 | content: '.wizard-step' 14 | btnPassword: '#btn-password' 15 | 16 | 17 | 18 | initialize: (@error = '') -> 19 | @config ?= app.init.config 20 | @cozyUrl = @config.get 'cozyURL' 21 | @password = '' 22 | StatusBar.backgroundColorByHexString '#4DCEC5' 23 | @connectionHandler = new ConnectionHandler() 24 | 25 | if @error and @error.startsWith 'CORS request rejected' 26 | @error = 'connexion error' 27 | 28 | 29 | getRenderData: -> 30 | error: @error 31 | cozyUrl: @cozyUrl 32 | password: @password 33 | 34 | 35 | events: -> 36 | 'click #display-password': 'toggleInputType' 37 | 'click #btn-password': 'validPassword' 38 | 'blur #input-password': 'changePassword' 39 | 'change #input-password': 'changePassword' 40 | 41 | 42 | toggleInputType: -> 43 | if @displayPassword.is(':checked') 44 | @inputPassword.attr 'type', 'text' 45 | else 46 | @inputPassword.attr 'type', 'password' 47 | 48 | 49 | changePassword: -> 50 | @password = @inputPassword.val() 51 | @error = '' if @error 52 | 53 | 54 | validPassword: (e) -> 55 | e.preventDefault() 56 | @password = @inputPassword.val() 57 | 58 | unless @password 59 | @error = 'onboarding_password_empty' 60 | return @render() 61 | 62 | unless @connectionHandler.isConnected() 63 | @error = 'connection disable' 64 | return @render() 65 | 66 | app.router.checkCredentials @password 67 | -------------------------------------------------------------------------------- /src/app/views/onboarding/permission.coffee: -------------------------------------------------------------------------------- 1 | BaseView = require '../layout/base_view' 2 | FirstReplication = require '../../lib/first_replication' 3 | CheckPermission = require '../../lib/permission' 4 | 5 | 6 | module.exports = class Permission extends BaseView 7 | 8 | 9 | 10 | className: 'page' 11 | templates: 12 | 'files': require '../../templates/onboarding/permission_files' 13 | 'contacts': require '../../templates/onboarding/permission_contacts' 14 | 'calendars': require '../../templates/onboarding/permission_calendars' 15 | 'photos': require '../../templates/onboarding/permission_photos' 16 | colors: 17 | 'files': '#9169F2' 18 | 'contacts': '#FD7461' 19 | 'calendars': '#34D882' 20 | 'photos': '#FFAE5F' 21 | refs: 22 | noBtn: '#btn-nope' 23 | yesBtn: '#btn-yep' 24 | 25 | 26 | template: (data) -> 27 | @templates[@step](data) 28 | 29 | 30 | initialize: (@step) -> 31 | if @step is 'files' 32 | @backExit = true 33 | else 34 | @backExit = false 35 | @config ?= app.init.config 36 | @router ?= app.router 37 | @platform ?= device.platform 38 | StatusBar.backgroundColorByHexString @colors[@step] 39 | @firstReplication = new FirstReplication() 40 | @checkPermission = new CheckPermission() 41 | 42 | 43 | 44 | events: -> 45 | 'click #btn-yep': => @setPermission true 46 | 'click #btn-nope': => @setPermission false 47 | 48 | 49 | setPermission: (value) -> 50 | route = switch @step 51 | when 'files' then 'permissions/contacts' 52 | when 'contacts' then 'permissions/calendars' 53 | when 'calendars' then 'permissions/photos' 54 | when 'photos' then 'folder/' 55 | 56 | route = 'folder/' if @platform is 'iOS' 57 | 58 | if route is 'folder/' 59 | StatusBar.backgroundColorByHexString '#33A6FF' 60 | @config.set 'state', 'appConfigured' 61 | 62 | if @step is 'files' 63 | return @router.navigate route, trigger: true 64 | 65 | next = (status) => 66 | switch @step 67 | when 'contacts' 68 | @config.set 'syncContacts', status 69 | when 'calendars' 70 | @config.set 'syncCalendars', status 71 | when 'photos' 72 | @config.set 'syncImages', status 73 | 74 | if status and (@step is 'contacts' or @step is 'calendars') 75 | @firstReplication.addTask @step, -> 76 | 77 | @router.navigate route, trigger: true 78 | 79 | if value 80 | @checkPermission.checkPermission @step, next, next 81 | else 82 | next false 83 | -------------------------------------------------------------------------------- /src/app/views/onboarding/url.coffee: -------------------------------------------------------------------------------- 1 | BaseView = require '../layout/base_view' 2 | urlValidator = require '../../lib/url_validator' 3 | 4 | 5 | module.exports = class Url extends BaseView 6 | 7 | 8 | className: 'page' 9 | template: require '../../templates/onboarding/url' 10 | backExit: true 11 | refs: 12 | inputUrl: '#input-url' 13 | buttonUrl: '#btn-url' 14 | content: '.wizard-step' 15 | 16 | 17 | initialize: (@config) -> 18 | @config ?= app.init.config 19 | @cozyUrl = @config.get 'cozyURL' 20 | @error = '' 21 | StatusBar.backgroundColorByHexString '#33A6FF' 22 | 23 | 24 | getRenderData: -> 25 | error: @error 26 | cozyUrl: @cozyUrl 27 | 28 | 29 | events: -> 30 | 'blur #input-url': 'onURLBlur' 31 | 'change #input-url': -> @error = '' if @error 32 | 'click #btn-url': 'validUrl' 33 | 34 | 35 | onURLBlur: -> 36 | @cozyUrl = @inputUrl.val() 37 | return unless @cozyUrl 38 | @inputUrl.val urlValidator.cleanUrl @inputUrl.val() 39 | @cozyUrl = @inputUrl.val() 40 | unless urlValidator.validUrl @cozyUrl 41 | @error = "url invalid" 42 | @render() 43 | 44 | 45 | validUrl: (e) -> 46 | unless @inputUrl.val() is '' 47 | @inputUrl.val urlValidator.cleanUrl @inputUrl.val() 48 | @cozyUrl = @inputUrl.val() 49 | if urlValidator.validUrl @cozyUrl 50 | @config.setCozyUrl @cozyUrl 51 | else 52 | e.preventDefault() 53 | @error = "url invalid" 54 | @render() 55 | -------------------------------------------------------------------------------- /src/app/views/onboarding/welcome.coffee: -------------------------------------------------------------------------------- 1 | BaseView = require '../layout/base_view' 2 | 3 | 4 | module.exports = class Welcome extends BaseView 5 | 6 | 7 | className: 'page' 8 | template: require '../../templates/onboarding/welcome' 9 | animationEntrance: 'slideInDown' 10 | animationExit: 'fadeOutLeft' 11 | 12 | initialize: -> 13 | @backExit = true 14 | screen.lockOrientation 'portrait' 15 | StatusBar.backgroundColorByHexString '#fff' 16 | -------------------------------------------------------------------------------- /src/assets/backgroundservice.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Cozy Files Service 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/assets/fonts/fonts.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Source Sans Pro 3 | * 4 | * Use for: labor 5 | * Font-family: 'Source Sans Pro', sans-serif 6 | * Available styles: 7 | * - font-style: normal / font-weight: normal 8 | * - font-style: italic / font-weight: normal 9 | * - font-style: normal / font-weight: bold 10 | * - font-style: italic / font-weight: bold 11 | */ 12 | @font-face { 13 | font-family: 'Source Sans Pro'; 14 | font-style: normal; 15 | font-weight: 400; 16 | src: local('Source Sans Pro'), 17 | local('SourceSansPro-Regular'), 18 | url('./sourcesanspro-regular.woff') format('woff'); 19 | } 20 | -------------------------------------------------------------------------------- /src/assets/fonts/material-icons: -------------------------------------------------------------------------------- 1 | ../../../node_modules/material-design-icons/iconfont/ -------------------------------------------------------------------------------- /src/assets/fonts/mdi/css/materialdesignicons.css: -------------------------------------------------------------------------------- 1 | ../../../../../node_modules/mdi/css/materialdesignicons.css -------------------------------------------------------------------------------- /src/assets/fonts/mdi/fonts: -------------------------------------------------------------------------------- 1 | ../../../../node_modules/mdi/fonts/ -------------------------------------------------------------------------------- /src/assets/fonts/roboto: -------------------------------------------------------------------------------- 1 | ../../../node_modules/materialize-css/dist/fonts/roboto/ -------------------------------------------------------------------------------- /src/assets/fonts/sourcesanspro-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/src/assets/fonts/sourcesanspro-regular.woff -------------------------------------------------------------------------------- /src/assets/img/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/src/assets/img/background.jpg -------------------------------------------------------------------------------- /src/assets/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/src/assets/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /src/assets/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/src/assets/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /src/assets/img/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/src/assets/img/icon.png -------------------------------------------------------------------------------- /src/assets/img/menu-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/src/assets/img/menu-icon.png -------------------------------------------------------------------------------- /src/assets/img/menu-icon.svg: -------------------------------------------------------------------------------- 1 | image/svg+xml -------------------------------------------------------------------------------- /src/assets/img/mini_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/src/assets/img/mini_logo.png -------------------------------------------------------------------------------- /src/assets/img/spinner-grey.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/assets/img/spinner.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/assets/img/sync.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-mobile/77cd1f1cd72478480677acc53a6c39f5a99f9586/src/assets/img/sync.png -------------------------------------------------------------------------------- /src/assets/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Cozy Files 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/assets/viewer: -------------------------------------------------------------------------------- 1 | ../../node_modules/node-viewerjs/release/ -------------------------------------------------------------------------------- /src/modules/async.js: -------------------------------------------------------------------------------- 1 | ../../node_modules/async/dist/async.js -------------------------------------------------------------------------------- /src/modules/node-polyglot.js: -------------------------------------------------------------------------------- 1 | ../../node_modules/node-polyglot/build/polyglot.js -------------------------------------------------------------------------------- /src/modules/path.js: -------------------------------------------------------------------------------- 1 | ../../node_modules/path-browser/path.js -------------------------------------------------------------------------------- /src/modules/pouchdb.js: -------------------------------------------------------------------------------- 1 | ../../node_modules/pouchdb/dist/pouchdb.js -------------------------------------------------------------------------------- /src/modules/process.js: -------------------------------------------------------------------------------- 1 | ../../node_modules/process/browser.js -------------------------------------------------------------------------------- /src/modules/semver.js: -------------------------------------------------------------------------------- 1 | ../../node_modules/semver/semver.js -------------------------------------------------------------------------------- /src/modules/validator.js: -------------------------------------------------------------------------------- 1 | ../../node_modules/validator/validator.js -------------------------------------------------------------------------------- /src/test/application.coffee: -------------------------------------------------------------------------------- 1 | should = chai.should() 2 | 3 | application = require 'application' 4 | 5 | 6 | module.exports = describe 'Application Controler', -> 7 | 8 | describe 'When application starts', -> 9 | Router = require 'routes' 10 | LayoutView = require 'views/app_layout' 11 | 12 | before -> 13 | application.triggerMethod 'before:start' 14 | 15 | it 'should contains a router', -> 16 | application.should.have.property('router').to.be.an.instanceof \ 17 | Router 18 | it 'should contains a layerView', -> 19 | application.should.have.property('layout').to.be.an.instanceof \ 20 | LayoutView 21 | -------------------------------------------------------------------------------- /src/test/assets/_tests.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Mocha Tests 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /src/test/assets/manual_test_guide.md: -------------------------------------------------------------------------------- 1 | # Cozy Mobile manual test suite 2 | 3 | Standard use case to test, and by version specific tests. 4 | 5 | # Standard 6 | 7 | # Upgrade scenario 8 | 9 | - [ ] upgrade application 10 | - [ ] start application 11 | - [ ] view backup label 12 | - [ ] navigate through folders 13 | - [ ] download and open a file 14 | - [ ] check contacts account 15 | - [ ] check agenda account 16 | - [ ] use a password with special caracters (accent AND punctuation) 17 | 18 | # Install scenario 19 | 20 | - [ ] uninstall the app from the device 21 | - [ ] install the app 22 | - [ ] choose to sync everithing, and wait for first sync 23 | 24 | # Advanced scenarios 25 | 26 | ## File replication 27 | 28 | - [ ] Add a file to the cozy 29 | - [ ] Delete a file on the cozy 30 | - [ ] Add a file in cache 31 | - [ ] Rename this file in cache 32 | - [ ] Delete this file in cache 33 | - [ ] Add a folder 34 | - [ ] Add a folder in cache 35 | - [ ] Add a file in the folder 36 | - [ ] Change the file in the folder 37 | - [ ] Delete the folder 38 | - [ ] Delete Photos directory, and let the app recreate it. 39 | 40 | ## Service 41 | 42 | - [ ] close the app, and take a picture 43 | 44 | ## Contacts sync 45 | 46 | Before each check, perform a sync in the app. 47 | 48 | - [ ] Add a contact on cozy 49 | - [ ] Add a contact on the device 50 | - [ ] Change a contact on the cozy 51 | - [ ] Change a contat on the device 52 | - [ ] Delete a contact on the cozy 53 | - [ ] Delete a contact on the device 54 | 55 | ## Calendar sync 56 | 57 | - [ ] Add a event on cozy 58 | - [ ] Add a event on the device 59 | - [ ] Change a event on the cozy 60 | - [ ] Change a contat on the device 61 | - [ ] Delete a event on the cozy 62 | - [ ] Delete a event on the device 63 | - [ ] Add a event on a new calendar on the cozy 64 | - [ ] Change the color of a calendar on the cozy 65 | - [ ] Change the name of a calendar on the cozy 66 | - [ ] Delete a calendar on the cozy 67 | 68 | 69 | ### Device --> Cozy 70 | 71 | - [ ] Add a event 72 | - [ ] Change time (start/end) 73 | - [ ] Change day 74 | - [ ] Change recurring (with and without end) 75 | - [ ] Change reminders (add, delete, change type) 76 | - [ ] Change invitation 77 | - [ ] Change invitation, status 78 | - [ ] Change allday 79 | - [ ] Change description 80 | - [ ] Change location 81 | - [ ] Change one event in a reccuring 82 | - [ ] Delete event 83 | 84 | ### Cozy --> Device 85 | - [ ] Add a event 86 | - [ ] Change time (start/end) 87 | - [ ] Change day 88 | - [ ] Change recurring (with and without end) 89 | - [ ] Change reminders (add, delete, change type) 90 | - [ ] Add event with more than 5 reminders (Android limitation) 91 | - [ ] Change invitation 92 | - [ ] Change invitation, status 93 | - [ ] Change allday 94 | - [ ] Change description 95 | - [ ] Change location 96 | - [ ] Change Calendar 97 | - [ ] Delete event 98 | 99 | ### Conflicts 100 | 101 | - [ ] Change two distincts events 102 | - [ ] Change two fields of an event 103 | - [ ] Change the same field of the same event. 104 | -------------------------------------------------------------------------------- /src/test/fixtures/cozy_calendar.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "CozyCalendarName" 3 | } 4 | -------------------------------------------------------------------------------- /src/test/fixtures/event_punctual_android.json: -------------------------------------------------------------------------------- 1 | { 2 | "_id": 4346, 3 | "calendar_id": 58, 4 | "organizer": "myCozy", 5 | "title": "Dentist", 6 | "eventLocation": "Dentist office", 7 | "description": "", 8 | "dtstart": 1415196000000, 9 | "dtend": 1415199600000, 10 | "eventTimezone": "UTC", 11 | "allDay": 0, 12 | "accessLevel": 0, 13 | "availability": 0, 14 | "guestsCanModify": 0, 15 | "guestsCanInviteOthers": 1, 16 | "guestsCanSeeGuests": 1, 17 | "deleted": 0, 18 | "_sync_id": "d8dde00c9ba594f7d8d242d7a200b46c", 19 | "sync_data2": "1-e8e99869019a07830118f93cf252c817", 20 | "sync_data5": "2015-10-30T09:05:34.247Z", 21 | "attendees": [], 22 | "reminders": [ 23 | { 24 | "_id": 1550, 25 | "event_id": 4346, 26 | "minutes": 15, 27 | "method": 1 28 | }, 29 | { 30 | "_id": 1551, 31 | "event_id": 4346, 32 | "minutes": 1440, 33 | "method": 2 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /src/test/fixtures/event_punctual_androidcreated.json: -------------------------------------------------------------------------------- 1 | { 2 | "_id": 4352, 3 | "calendar_id": 58, 4 | "organizer": "myCozy", 5 | "title": "Dentist", 6 | "eventLocation": "Dentist office", 7 | "description": "", 8 | "dtstart": 1415196000000, 9 | "dtend": 1415199600000, 10 | "eventTimezone": "Europe/Amsterdam", 11 | "allDay": 0, 12 | "accessLevel": 0, 13 | "availability": 0, 14 | "guestsCanModify": 0, 15 | "guestsCanInviteOthers": 1, 16 | "guestsCanSeeGuests": 1, 17 | "dirty": 0, 18 | "deleted": 0, 19 | "_sync_id": "220928F5-6C81-186A-A3A7-61679C6D6590", 20 | "sync_data2": "1-0c539ebf0464548ca68428f365001db8", 21 | "sync_data9": "0", 22 | "attendees": [], 23 | "reminders": [ 24 | { 25 | "_id": 1554, 26 | "event_id": 4352, 27 | "minutes": 15, 28 | "method": 1 29 | }, 30 | { 31 | "_id": 1553, 32 | "event_id": 4352, 33 | "minutes": 1440, 34 | "method": 2 35 | } 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /src/test/fixtures/event_punctual_cozy.json: -------------------------------------------------------------------------------- 1 | { 2 | "_id": "d8dde00c9ba594f7d8d242d7a200b46c", 3 | "_rev": "1-e8e99869019a07830118f93cf252c817", 4 | "start": "2014-11-05T14:00:00.000Z", 5 | "end": "2014-11-05T15:00:00.000Z", 6 | "place": "Dentist office", 7 | "details": "", 8 | "description": "Dentist", 9 | "rrule": "", 10 | "tags": [ 11 | "calendar 1" 12 | ], 13 | "attendees": [ 14 | ], 15 | "related": null, 16 | "alarms": [ 17 | { 18 | "trigg": "-PT15M", 19 | "action": "DISPLAY" 20 | }, 21 | { 22 | "trigg": "-P1D", 23 | "action": "EMAIL" 24 | } 25 | ], 26 | "created": "2015-10-30T09:05:34.247Z", 27 | "lastModification": "2015-10-30T09:05:34.247Z", 28 | "docType": "event" 29 | } 30 | -------------------------------------------------------------------------------- /src/test/fixtures/event_recurring_android.json: -------------------------------------------------------------------------------- 1 | { 2 | "_id": 4347, 3 | "calendar_id": 58, 4 | "organizer": "myCozy", 5 | "title": "Recurring event", 6 | "eventLocation": "Hidden dungeon", 7 | "description": "Crawling a hidden dungeon", 8 | "dtstart": 1415271600000, 9 | "eventTimezone": "Europe/Paris", 10 | "duration": "PT1H", 11 | "allDay": 0, 12 | "rrule": "FREQ=WEEKLY;INTERVAL=1;UNTIL=20150101T000000Z;BYDAY=TH", 13 | "accessLevel": 0, 14 | "availability": 0, 15 | "guestsCanModify": 0, 16 | "guestsCanInviteOthers": 1, 17 | "guestsCanSeeGuests": 1, 18 | "deleted": 0, 19 | "_sync_id": "d8dde00c9ba594f7d8d242d7a200b897", 20 | "sync_data2": "1-4e6e627c30ff3df264112c16a819bd2c", 21 | "sync_data5": "2015-10-30T09:05:34.278Z", 22 | "attendees": [], 23 | "reminders": [] 24 | } 25 | -------------------------------------------------------------------------------- /src/test/fixtures/event_recurring_cozy.json: -------------------------------------------------------------------------------- 1 | { 2 | "_id": "d8dde00c9ba594f7d8d242d7a200b897", 3 | "_rev": "1-4e6e627c30ff3df264112c16a819bd2c", 4 | "start": "2014-11-06T12:00:00.000", 5 | "end": "2014-11-06T13:00:00.000", 6 | "place": "Hidden dungeon", 7 | "details": "Crawling a hidden dungeon", 8 | "description": "Recurring event", 9 | "rrule": "FREQ=WEEKLY;INTERVAL=1;UNTIL=20150101T000000Z;BYDAY=TH", 10 | "tags": [ 11 | "calendar 1" 12 | ], 13 | "attendees": [ 14 | ], 15 | "related": null, 16 | "timezone": "Europe/Paris", 17 | "alarms": [ 18 | ], 19 | "created": "2015-10-30T09:05:34.278Z", 20 | "lastModification": "2015-10-30T09:05:34.278Z", 21 | "docType": "event" 22 | } 23 | -------------------------------------------------------------------------------- /src/test/fixtures/event_recurringallday_android.json: -------------------------------------------------------------------------------- 1 | { 2 | "_id": 4348, 3 | "calendar_id": 58, 4 | "organizer": "myCozy", 5 | "title": "Friend's birthday", 6 | "eventLocation": "Friend's appartment", 7 | "description": "Bring a present!", 8 | "dtstart": 1415318400000, 9 | "duration": "PT24H", 10 | "allDay": 1, 11 | "eventTimezone": "UTC", 12 | "rrule": "FREQ=YEARLY;INTERVAL=1", 13 | "accessLevel": 0, 14 | "availability": 0, 15 | "guestsCanModify": 0, 16 | "guestsCanInviteOthers": 1, 17 | "guestsCanSeeGuests": 1, 18 | "deleted": 0, 19 | "_sync_id": "d8dde00c9ba594f7d8d242d7a200bfb2", 20 | "sync_data2": "1-396cb5fe83923904a38e0a8a4265e307", 21 | "sync_data5": "2015-10-30T09:05:34.323Z", 22 | "attendees": [ 23 | { 24 | "_id": 6909, 25 | "event_id": 4348, 26 | "attendeeEmail": "randomgirl@provider.tld", 27 | "attendeeRelationship": 0, 28 | "attendeeType": 0, 29 | "attendeeStatus": 4 30 | }, 31 | { 32 | "_id": 6910, 33 | "event_id": 4348, 34 | "attendeeEmail": "randomguy@provider.tld", 35 | "attendeeRelationship": 0, 36 | "attendeeType": 0, 37 | "attendeeStatus": 4 38 | } 39 | ], 40 | "reminders": [ 41 | { 42 | "_id": 1552, 43 | "event_id": 4348, 44 | "minutes": 10080, 45 | "method": 1 46 | } 47 | ] 48 | } 49 | -------------------------------------------------------------------------------- /src/test/fixtures/event_recurringallday_androidcreated.json: -------------------------------------------------------------------------------- 1 | { 2 | "_id": 4353, 3 | "calendar_id": 58, 4 | "organizer": "myCozy", 5 | "title": "Friend's birthday", 6 | "eventLocation": "Friend's appartment", 7 | "description": "Bring a present!", 8 | "dtstart": 1415318400000, 9 | "eventTimezone": "UTC", 10 | "duration": "P1D", 11 | "allDay": 1, 12 | "rrule": "FREQ=YEARLY;WKST=MO", 13 | "accessLevel": 0, 14 | "availability": 1, 15 | "guestsCanModify": 0, 16 | "guestsCanInviteOthers": 1, 17 | "guestsCanSeeGuests": 1, 18 | "dirty": 1, 19 | "deleted": 0, 20 | "mutators": "com.android.providers.calendar", 21 | "sync_data9": "4", 22 | "attendees": [ 23 | { 24 | "_id": 6913, 25 | "event_id": 4353, 26 | "attendeeEmail": "myCozy", 27 | "attendeeRelationship": 2, 28 | "attendeeType": 1, 29 | "attendeeStatus": 1 30 | }, 31 | { 32 | "_id": 6914, 33 | "event_id": 4353, 34 | "attendeeName": "randomgirl@provider.tld", 35 | "attendeeEmail": "randomgirl@provider.tld", 36 | "attendeeRelationship": 0, 37 | "attendeeType": 1, 38 | "attendeeStatus": 3 39 | }, 40 | { 41 | "_id": 6915, 42 | "event_id": 4353, 43 | "attendeeName": "randomguy@provider.tld", 44 | "attendeeEmail": "randomguy@provider.tld", 45 | "attendeeRelationship": 0, 46 | "attendeeType": 1, 47 | "attendeeStatus": 3 48 | } 49 | ], 50 | "reminders": [ 51 | { 52 | "_id": 1553, 53 | "event_id": 4353, 54 | "minutes": 9540, 55 | "method": 1 56 | } 57 | ] 58 | } 59 | -------------------------------------------------------------------------------- /src/test/fixtures/event_recurringallday_cozy.json: -------------------------------------------------------------------------------- 1 | { 2 | "_id": "d8dde00c9ba594f7d8d242d7a200bfb2", 3 | "_rev": "1-396cb5fe83923904a38e0a8a4265e307", 4 | "start": "2014-11-07", 5 | "end": "2014-11-08", 6 | "place": "Friend's appartment", 7 | "details": "Bring a present!", 8 | "description": "Friend's birthday", 9 | "rrule": "FREQ=YEARLY;INTERVAL=1", 10 | "tags": [ 11 | "calendar 1" 12 | ], 13 | "attendees": [ 14 | { 15 | "id": 1, 16 | "email": "randomgirl@provider.tld", 17 | "contactid": null, 18 | "status": "INVITATION-NOT-SENT" 19 | }, 20 | { 21 | "id": 2, 22 | "email": "randomguy@provider.tld", 23 | "contactid": null, 24 | "status": "INVITATION-NOT-SENT" 25 | } 26 | ], 27 | "related": null, 28 | "alarms": [ 29 | { 30 | "trigg": "-P1W", 31 | "action": "DISPLAY" 32 | } 33 | ], 34 | "created": "2015-10-30T09:05:34.323Z", 35 | "lastModification": "2015-10-30T09:05:34.323Z", 36 | "docType": "event" 37 | } 38 | -------------------------------------------------------------------------------- /src/test/helper/config.coffee: -------------------------------------------------------------------------------- 1 | Config = require '../../app/lib/config' 2 | 3 | 4 | module.exports = 5 | 6 | 7 | url: 'https://test.cozycloud.cc' 8 | 9 | 10 | get: (database) -> 11 | initialize = 12 | upsertLocalDesignDocuments: (callback) -> 13 | callback() 14 | new Config database, initialize 15 | 16 | 17 | getLoaded: (database, callback) -> 18 | config = @get database 19 | config.load -> 20 | callback config 21 | 22 | 23 | getLoadedWithUrl: (database, callback) -> 24 | @getLoaded database, (config) => 25 | config.setCozyUrl @url, -> 26 | callback config 27 | -------------------------------------------------------------------------------- /src/test/helper/database.coffee: -------------------------------------------------------------------------------- 1 | PouchDB = require 'pouchdb' 2 | Database = require '../../app/lib/database' 3 | options = db: require 'memdown' 4 | 5 | module.exports = 6 | 7 | 8 | get: (name) -> 9 | name ?= new Date().toISOString() 10 | database = new Database options 11 | if name 12 | database.replicateDb = new PouchDB "#{name}.replicateDb", options 13 | database.localDb = new PouchDB "#{name}.localDb", options 14 | return database 15 | -------------------------------------------------------------------------------- /src/test/helper/fixture.coffee: -------------------------------------------------------------------------------- 1 | module.exports = 2 | 3 | 4 | getAccount: -> 5 | accountName: 'accountName' 6 | accountType: 'accountType' 7 | 8 | 9 | getCozyCalendar: -> 10 | require '../fixtures/cozy_calendar' 11 | 12 | 13 | getCozyContact: -> 14 | require '../fixtures/cozy_contact' 15 | 16 | 17 | getAndroidDevice: -> 18 | platform: 'Android' 19 | manufacturer: 'HTC' 20 | model: 'Passion' 21 | -------------------------------------------------------------------------------- /src/test/helper/helper.coffee: -------------------------------------------------------------------------------- 1 | database = require './database' 2 | fixture = require './fixture' 3 | config = require './config' 4 | 5 | 6 | module.exports = 7 | 8 | 9 | requireTestFile: (filename) -> 10 | require '../../app' + filename.split('unit')[1] 11 | 12 | 13 | database: database 14 | fixture: fixture 15 | config: config 16 | -------------------------------------------------------------------------------- /src/test/initialize.coffee: -------------------------------------------------------------------------------- 1 | mocha.setup 2 | ui: 'bdd' 3 | 4 | 5 | application = require 'test/application' 6 | 7 | 8 | module.exports = -> 9 | mocha.checkLeaks() 10 | mocha.run() 11 | -------------------------------------------------------------------------------- /src/test/unit/lib/config.coffee: -------------------------------------------------------------------------------- 1 | helper = require '../../helper/helper' 2 | should = require('chai').should() 3 | Config = helper.requireTestFile __filename 4 | 5 | 6 | global._ = require 'underscore' 7 | global.Backbone = require 'backbone' 8 | global.device = helper.fixture.getAndroidDevice() 9 | initialize = upsertLocalDesignDocuments: (callback) -> callback() 10 | 11 | 12 | module.exports = describe 'Config Test', -> 13 | 14 | 15 | describe 'Config initialization', (done) -> 16 | 17 | database = helper.database.get() 18 | config = new Config database, initialize 19 | 20 | it 'should have database variable', -> 21 | config.database.should.be.exist 22 | 23 | it 'should not have config variable', -> 24 | should.not.exist config.config 25 | 26 | it 'is not loaded', -> 27 | config.loaded.should.be.false 28 | 29 | 30 | describe 'Config load', (done) -> 31 | 32 | database = helper.database.get() 33 | config = new Config database, initialize 34 | 35 | it 'is loaded', (done) -> 36 | 37 | config.load -> 38 | config.loaded.should.be.true 39 | should.not.exist database.remoteDB 40 | done() 41 | return 42 | 43 | it 'can get variable', -> 44 | config.get('deviceName').should.exist 45 | 46 | it 'can set variable', (done) -> 47 | config.set 'deviceName', 'Android-HTC-Passion2', -> 48 | config.get('deviceName').should.equal 'Android-HTC-Passion2' 49 | done() 50 | return 51 | 52 | 53 | describe 'Config Cozy Url', -> 54 | 55 | database = helper.database.get() 56 | config = new Config database, initialize 57 | 58 | it 'should not have cozy url', -> 59 | config.getCozyUrl().should.equal '' 60 | 61 | it 'can set cozy url', (done) -> 62 | config.load -> 63 | url = 'https://test.cozycloud.cc' 64 | config.setCozyUrl url, -> 65 | config.getCozyUrl().should.equal url 66 | done() 67 | return 68 | 69 | 70 | describe 'Config version', -> 71 | 72 | database = helper.database.get() 73 | config = new Config database, initialize 74 | 75 | it 'should have same version by default', (done) -> 76 | config.load -> 77 | config.isNewVersion().should.be.false 78 | done() 79 | return 80 | 81 | it 'should be false when not the same version', (done) -> 82 | config.set 'appVersion', '1.0.0', -> 83 | config.isNewVersion().should.be.true 84 | done() 85 | return 86 | 87 | it 'can upgrade version', (done) -> 88 | config.updateVersion -> 89 | config.isNewVersion().should.be.false 90 | done() 91 | return 92 | -------------------------------------------------------------------------------- /src/test/unit/lib/connection_handler.coffee: -------------------------------------------------------------------------------- 1 | should = require('chai').should() 2 | ConnectionHandler = require '../../../app/lib/connection_handler' 3 | Connection = require '../../../../plugins/' + 4 | 'cordova-plugin-network-information/www/Connection' 5 | 6 | global.navigator = 7 | connection: 8 | type: 'none' 9 | global.document = 10 | addEventListener: -> 11 | global.app = 12 | init: 13 | currentState: '' 14 | 15 | 16 | module.exports = describe 'ConnectionHandler Test', -> 17 | 18 | 19 | connectionHandler = new ConnectionHandler Connection 20 | 21 | 22 | it 'should have connected variable', -> 23 | connectionHandler.connected.should.be.exist 24 | 25 | 26 | it 'should be false when device is offline.', -> 27 | connectionHandler.isConnected().should.be.false 28 | 29 | 30 | it 'should be true when device is online.', -> 31 | global.navigator.connection.type = 'wifi' 32 | connectionHandler.isConnected().should.be.true 33 | 34 | 35 | it 'should be return true when is on wifi.', -> 36 | connectionHandler.isWifi().should.be.true 37 | -------------------------------------------------------------------------------- /src/test/unit/lib/database.coffee: -------------------------------------------------------------------------------- 1 | should = require('chai').should() 2 | Database = require '../../../app/lib/database' 3 | 4 | options = db: require 'memdown' 5 | url = 'cozyUrlForTest' 6 | 7 | 8 | module.exports = describe 'Database Service Test', -> 9 | 10 | 11 | describe 'constants', -> 12 | 13 | it 'should get replicate database name', -> 14 | Database.REPLICATE_DB.should.be.equal 'cozy-files.db' 15 | 16 | it 'should get local database name', -> 17 | Database.LOCAL_DB.should.be.equal 'cozy-photos.db' 18 | 19 | 20 | describe 'Create databases', -> 21 | 22 | database = new Database options 23 | 24 | it 'must have replicate database', -> 25 | database.replicateDb.should.be.an.Object 26 | 27 | it 'must have local database', -> 28 | database.localDb.should.be.an.Object 29 | 30 | it 'must not have remote database', -> 31 | (database.remoteDb is undefined).should.be.true 32 | 33 | it 'must have remote database after init it', -> 34 | database.setRemoteDatabase url 35 | database.remoteDb.should.be.an.Object 36 | -------------------------------------------------------------------------------- /src/test/unit/lib/translation.coffee: -------------------------------------------------------------------------------- 1 | should = require('chai').should() 2 | Translation = require '../../../app/lib/translation' 3 | 4 | module.exports = describe 'Translation Service Test', -> 5 | 6 | 7 | describe 'Before set locale', -> 8 | 9 | notInitialize = new Translation() 10 | 11 | it 'should not have language property before set locale', -> 12 | notInitialize.should.not.have.property 'language' 13 | 14 | it 'should have polyglot property before set locale', -> 15 | notInitialize.should.have.property 'polyglot' 16 | 17 | it 'should have english default language', -> 18 | notInitialize.should.have.property 'DEFAULT_LANGUAGE' 19 | notInitialize.DEFAULT_LANGUAGE.should.equal 'en' 20 | 21 | 22 | describe 'After set locale', -> 23 | 24 | initializeError = new Translation() 25 | initializeError.setLocale {value: 'af-ZA'} 26 | 27 | it 'should have english language when locale is not supported', -> 28 | initializeError.should.have.property 'language' 29 | initializeError.language.should.equal 'en' 30 | 31 | initializeFr = new Translation() 32 | initializeFr.setLocale {value: 'fr-FR'} 33 | 34 | it 'should have fr language when locale is fr-FR', -> 35 | initializeFr.should.have.property 'language' 36 | initializeFr.language.should.equal 'fr' 37 | 38 | t = initializeFr.getTranslate() 39 | it 'should have a function to translate', -> 40 | t.should.be.a 'function' 41 | 42 | it 'should be easy to translate', -> 43 | t('error try restart').should.equal \ 44 | 'Essayez de redémarrer l\'application.' 45 | -------------------------------------------------------------------------------- /src/test/unit/lib/url_validator.coffee: -------------------------------------------------------------------------------- 1 | should = require('chai').should() 2 | urlValidator = require '../../../app/lib/url_validator' 3 | url = 'https://test.cozycloud.cc' 4 | 5 | 6 | module.exports = describe 'urlValidator Test', -> 7 | 8 | 9 | describe 'Valid URL', -> 10 | 11 | it 'should accept https url', -> 12 | urlValidator.validUrl(url).should.be.true 13 | 14 | it 'refuse http url', -> 15 | urlValidator.validUrl('http://test.cozy').should.be.false 16 | 17 | 18 | describe 'Clean URL', -> 19 | 20 | it 'should remove space chars', -> 21 | urlValidator.cleanUrl(" #{url} ").should.be.equal url 22 | 23 | it 'should remove / on end url', -> 24 | urlValidator.cleanUrl("#{url}/").should.be.equal url 25 | 26 | it 'should add https when is not present', -> 27 | urlValidator.cleanUrl("test.cozycloud.cc").should.be.equal url 28 | 29 | it 'should add .cozycloud.cc when not dash is present', -> 30 | urlValidator.cleanUrl("https://test").should.be.equal url 31 | -------------------------------------------------------------------------------- /src/test/unit/lib/utils.coffee: -------------------------------------------------------------------------------- 1 | should = require('chai').should() 2 | 3 | # Stubed window global object. 4 | global.window = {} 5 | Utils = require '../../../app/lib/utils' 6 | 7 | module.exports = describe 'Utils Test', -> 8 | 9 | 10 | # 'setImmediate' should be tested in a browser environment. 11 | 12 | 13 | describe 'continueOnError', -> 14 | logger = error: -> 15 | 16 | continueOnError = Utils.continueOnError(logger) 17 | 18 | it 'should continue on error', (done) -> 19 | callback = (err) -> 20 | should.not.exist err 21 | done() 22 | continueOnError(callback)('error') 23 | 24 | it 'should transmit arguments on no errors', (done) -> 25 | callback = (err, arg1, arg2) -> 26 | should.not.exist err 27 | arg1.should.equal 'arg1' 28 | arg2.should.equal 'arg2' 29 | done() 30 | 31 | continueOnError(callback)(null, 'arg1', 'arg2') 32 | 33 | it 'should use the specified log', (done) -> 34 | log = error : (msg1, msg2)-> 35 | 36 | "#{msg1} #{msg2}".should.equal 'Continue on error: error' 37 | 38 | callback = done 39 | 40 | Utils.continueOnError(log)(callback)('error') 41 | 42 | -------------------------------------------------------------------------------- /src/test/unit/replicator/change/change_dispatcher.coffee: -------------------------------------------------------------------------------- 1 | should = require('chai').should() 2 | mockery = require 'mockery' 3 | global.app = init: database: remoteDb: get: (docId, options, cb) -> cb() 4 | 5 | 6 | module.exports = describe 'ChangeDispatcher Test', -> 7 | 8 | config = {} 9 | 10 | before -> 11 | mockery.enable 12 | warnOnReplace: false 13 | warnOnUnregistered: false 14 | useCleanCache: true 15 | 16 | changeHandlerMock = () -> 17 | dispatch: (doc, callback) -> callback() 18 | 19 | mockery.registerMock './change_file_handler', changeHandlerMock 20 | mockery.registerMock './change_event_handler', changeHandlerMock 21 | mockery.registerMock './change_contact_handler', changeHandlerMock 22 | mockery.registerMock './change_tag_handler', changeHandlerMock 23 | mockery.registerMock './change_notification_handler', changeHandlerMock 24 | @ChangeDispatcher = require \ 25 | '../../../../app/replicator/change/change_dispatcher' 26 | 27 | after -> 28 | mockery.deregisterAll() 29 | delete @ChangeDispatcher 30 | mockery.disable() 31 | 32 | describe '[When all is ok]', -> 33 | describe 'isDispatched', -> 34 | 35 | it 'return true if this doc is dispatched', -> 36 | changeDispatcher = new @ChangeDispatcher config 37 | changeDispatcher.isDispatched(docType: 'file').should.be.true 38 | 39 | it 'return false if this doc isnt dispatched', -> 40 | changeDispatcher = new @ChangeDispatcher config 41 | changeDispatcher.isDispatched(docType: 'email').should.be.false 42 | 43 | it 'is case insentivie on docType', -> 44 | changeDispatcher = new @ChangeDispatcher config 45 | changeDispatcher.isDispatched(docType: 'File').should.be.true 46 | changeDispatcher.isDispatched(docType: 'file').should.be.true 47 | 48 | # errors : doc has no doctype 49 | 50 | describe 'dispatch', -> 51 | # TODO : it 'dispatch to the expected dispatcher', -> 52 | 53 | it 'throw error on unexpected document', (done) -> 54 | changeDispatcher = new @ChangeDispatcher config 55 | changeDispatcher.dispatch { docType: 'email' }, (err) -> 56 | err.should.exist 57 | done() 58 | 59 | it 'is case insentivie on docType', (done) -> 60 | changeDispatcher = new @ChangeDispatcher config 61 | changeDispatcher.dispatch { docType: 'File' }, (err) -> 62 | should.not.exist err 63 | done() 64 | 65 | describe '[All errors]', -> 66 | 67 | it 'handle document without doctype', -> 68 | changeDispatcher = new @ChangeDispatcher config 69 | changeDispatcher.isDispatched(noDocType: true).should.be.false 70 | -------------------------------------------------------------------------------- /src/test/unit/replicator/change/change_notification_handler.coffee: -------------------------------------------------------------------------------- 1 | should = require('chai').should() 2 | ChangeNotificationHandler = 3 | require '../../../../app/replicator/change/change_notification_handler' 4 | 5 | 6 | module.exports = describe 'ChangeNotificationHandler Test', -> 7 | 8 | 9 | notifHandler = 10 | removeCordovaNotification: -> false 11 | displayCordovaNotification: -> true 12 | 13 | changeNotificationHandler = new ChangeNotificationHandler notifHandler 14 | 15 | 16 | it 'can delete notification', -> 17 | changeNotificationHandler.dispatch({_deleted: true}).should.be.false 18 | 19 | 20 | it 'can add notification', -> 21 | changeNotificationHandler.dispatch({}).should.be.true 22 | -------------------------------------------------------------------------------- /src/test/unit/replicator/design_documents.coffee: -------------------------------------------------------------------------------- 1 | async = require 'async' 2 | PouchDB = require 'pouchdb' 3 | should = require('chai').should() 4 | DesignDocuments = require '../../../app/replicator/design_documents' 5 | 6 | module.exports = describe 'DesignDocuments Test', -> 7 | 8 | cozyDB = new PouchDB 'cozyDB', {db: require 'memdown'} 9 | internalDB = new PouchDB 'internalDB', {db: require 'memdown'} 10 | designDocs = new DesignDocuments cozyDB, internalDB 11 | 12 | it 'should be possible to create all design', (done) -> 13 | designDocs.createOrUpdateAllDesign (error, responses) -> 14 | async.series [ 15 | (next) -> cozyDB.allDocs {}, (error, response) -> 16 | response.total_rows.should.be.equal 7 17 | next() 18 | (next) -> internalDB.allDocs {}, (error, response) -> 19 | response.total_rows.should.be.equal 2 20 | next() 21 | ], done 22 | 23 | it 'should be possible to update one design', (done) -> 24 | DesignDocuments.PicturesDesignDoc.version++ 25 | designDocs.createOrUpdateAllDesign (error, responses) -> 26 | updated = responses.filter((doc) -> doc.id != undefined).length 27 | updated.should.be.equal 1 28 | done() 29 | -------------------------------------------------------------------------------- /src/test/unit/replicator/filter_manager.coffee: -------------------------------------------------------------------------------- 1 | should = require('chai').should() 2 | FilterManager = require '../../../app/replicator/filter_manager' 3 | 4 | module.exports = describe 'FilterManager Test', -> 5 | 6 | deviceName = "my-device" 7 | 8 | getConfig = (deviceName, syncContacts, syncCalendars, cozyNotifications) -> 9 | deviceName: deviceName 10 | syncContacts: syncContacts 11 | syncCalendars: syncCalendars 12 | cozyNotifications: cozyNotifications 13 | get: (value) -> @[value] 14 | getRequestCozy = (err, res, body) -> 15 | err: err 16 | res: res 17 | body: body 18 | request: (options, callback) -> callback err, res, body 19 | getDb = (getErr, putErr, existing) -> 20 | getErr: getErr 21 | putErr: putErr 22 | existing: existing 23 | get: (filterId, callback) -> 24 | callback getErr, existing 25 | put: (doc, callback) -> 26 | callback putErr 27 | 28 | before -> 29 | @config = getConfig deviceName, true, true, true 30 | @requestCozy = getRequestCozy false, 2, success: true 31 | @db = getDb null, null, true 32 | @filterManager = new FilterManager @config, @requestCozy, @db 33 | 34 | after -> 35 | delete @filterManager 36 | 37 | describe '[When all is ok]', -> 38 | 39 | it "getFilterName return the filter name", -> 40 | name = @filterManager.getFilterName() 41 | name.should.be.equal "filter-#{deviceName}-config/config" 42 | 43 | it "setFilter return true", (done) -> 44 | @filterManager.setFilter (err, response) -> 45 | response.should.be.equal true 46 | done() 47 | 48 | 49 | describe '[All errors]', -> 50 | 51 | it "When API have an requestCozy error setFilter return err", (done) -> 52 | @requestCozy = getRequestCozy 'err', null, null 53 | @filterManager = new FilterManager @config, @requestCozy, @db 54 | 55 | @filterManager.setFilter (err, response) -> 56 | err.should.not.to.be.null 57 | done() 58 | 59 | it "When API have an db error setFilter return err", (done) -> 60 | @db = getDb null, 'err', true 61 | @filterManager = new FilterManager @config, @requestCozy, @db 62 | 63 | @filterManager.setFilter (err, response) -> 64 | err.should.not.to.be.null 65 | done() 66 | -------------------------------------------------------------------------------- /src/test/unit/replicator/main.coffee: -------------------------------------------------------------------------------- 1 | helper = require '../../helper/helper' 2 | should = require('chai').should() 3 | Replicator = helper.requireTestFile __filename 4 | 5 | 6 | global._ = require 'underscore' 7 | global.Backbone = require 'backbone' 8 | 9 | 10 | module.exports = describe 'Replicator Test', -> 11 | 12 | 13 | it 'can be init config', -> 14 | # arrange 15 | config = "config" 16 | requestCozy = "requestCozy" 17 | database = "database" 18 | fileCacheHandler = "fileCacheHandler" 19 | replicator = new Replicator() 20 | 21 | # act 22 | replicator.initConfig config, requestCozy, database, fileCacheHandler 23 | 24 | # assert 25 | replicator.config.should.equal config 26 | replicator.requestCozy.should.equal requestCozy 27 | replicator.database.should.equal database 28 | replicator.fileCacheHandler.should.equal fileCacheHandler 29 | 30 | 31 | it 'can get remote checkpoint', (done) -> 32 | # arrange 33 | replicator = new Replicator() 34 | err = undefined 35 | res = undefined 36 | body = last_seq: 1 37 | replicator.requestCozy = 38 | request: (options, callback) -> 39 | callback err, res, body 40 | 41 | # act 42 | replicator.getRemoteCheckpoint (err, checkpoint) -> 43 | 44 | # assert 45 | checkpoint.should.be.equal body.last_seq 46 | done() 47 | 48 | 49 | it 'can update index of pouchdb', (done) -> 50 | # arrange 51 | replicator = new Replicator() 52 | replicator.replicateDb = 53 | query: (name, options, cb) -> 54 | cb() 55 | 56 | # act 57 | replicator.updateIndex -> 58 | 59 | # assert 60 | done() 61 | 62 | 63 | it 'can update permissions', (done) -> 64 | # arrange 65 | replicator = new Replicator() 66 | database = helper.database.get() 67 | helper.config.getLoadedWithUrl database, (config) -> 68 | config.set 'devicePermissions', {}, -> 69 | replicator.config = config 70 | replicator.config.hasPermissions().should.be.false 71 | replicator.requestCozy = 72 | request: (options, callback) -> 73 | permissions = permissions:config.getDefaultPermissions() 74 | callback undefined, undefined, permissions 75 | 76 | # act 77 | replicator.updatePermissions '', (err) -> 78 | 79 | # assert 80 | replicator.config.hasPermissions().should.be.true 81 | done() 82 | return 83 | -------------------------------------------------------------------------------- /src/test/unit/replicator/transformer/cozy_to_android_calendar.coffee: -------------------------------------------------------------------------------- 1 | should = require('chai').should() 2 | helper = require '../../../helper/helper' 3 | Transformer = helper.requireTestFile __filename 4 | 5 | 6 | module.exports = describe 'Cozy To Android Calendar Transformer Test', -> 7 | 8 | 9 | transformer = new Transformer() 10 | 11 | 12 | it 'can transform cozy to android', -> 13 | cozyCalendar = helper.fixture.getCozyCalendar() 14 | account = helper.fixture.getAccount() 15 | androidCalendar = transformer.transform cozyCalendar, account 16 | should.not.exist androidCalendar._id 17 | 18 | 19 | it 'can transform cozy to android with old androidCalendar', -> 20 | cozyCalendar = helper.fixture.getCozyCalendar() 21 | account = helper.fixture.getAccount() 22 | androidCalendar = _id: "id" 23 | androidCalendar = 24 | transformer.transform cozyCalendar, account, androidCalendar 25 | androidCalendar._id.should.exist 26 | -------------------------------------------------------------------------------- /src/test/unit/replicator/transformer/cozy_to_android_contact.coffee: -------------------------------------------------------------------------------- 1 | helper = require '../../../helper/helper' 2 | Transformer = helper.requireTestFile __filename 3 | 4 | 5 | pluginPath = '../../../../../plugins/io.cozy.contacts/www/' 6 | global.ContactName = require "#{pluginPath}ContactName" 7 | global.ContactOrganization = require "#{pluginPath}ContactOrganization" 8 | global.ContactField = require "#{pluginPath}ContactField" 9 | global.ContactAddress = require "#{pluginPath}ContactAddress" 10 | global.window = 11 | atob: (data) -> 12 | new Buffer(data, 'base64').toString() 13 | 14 | 15 | module.exports = describe 'Cozy To Android Contact Transformer Test', -> 16 | 17 | 18 | transformer = new Transformer() 19 | 20 | 21 | it 'can transform cozy to android', -> 22 | cozyContact = helper.fixture.getCozyContact() 23 | cordovaContact = transformer.transform cozyContact 24 | cordovaContact.should.be.exist 25 | 26 | 27 | it 'can transform cozy to android', -> 28 | cozyContact = helper.fixture.getCozyContact() 29 | simple = transformer.transform cozyContact 30 | cozyContact.datapoints.push cozyContact.datapoints[0] # email 31 | cozyContact.datapoints.push cozyContact.datapoints[11] # tel 32 | cozyContact.datapoints.push cozyContact.datapoints[19] # address 33 | duplicate = transformer.transform cozyContact 34 | 35 | simple.emails.length.should.be.equal duplicate.emails.length 36 | simple.phoneNumbers.length.should.be.equal duplicate.phoneNumbers.length 37 | simple.addresses.length.should.be.equal duplicate.addresses.length 38 | 39 | 40 | it 'can transform android to cozy', (done) -> 41 | cordovaContact = require '../../../fixtures/cordova_contact.json' 42 | transformer.reverseTransform cordovaContact, (err, cozyContact) -> 43 | cozyContact.should.be.exist 44 | done() 45 | return 46 | -------------------------------------------------------------------------------- /src/vendor/css/animate.css: -------------------------------------------------------------------------------- 1 | ../../../node_modules/animate.css/animate.css -------------------------------------------------------------------------------- /src/vendor/css/materialize.css: -------------------------------------------------------------------------------- 1 | ../../../node_modules/materialize-css/dist/css/materialize.css -------------------------------------------------------------------------------- /src/vendor/css/snap.css: -------------------------------------------------------------------------------- 1 | ../../../node_modules/snap.js/dist/snap.css -------------------------------------------------------------------------------- /src/vendor/scripts/backbone.js: -------------------------------------------------------------------------------- 1 | ../../../node_modules/backbone/backbone.js -------------------------------------------------------------------------------- /src/vendor/scripts/jquery.js: -------------------------------------------------------------------------------- 1 | ../../../node_modules/jquery/dist/jquery.js -------------------------------------------------------------------------------- /src/vendor/scripts/materialize.js: -------------------------------------------------------------------------------- 1 | ../../../node_modules/materialize-css/dist/js/materialize.js -------------------------------------------------------------------------------- /src/vendor/scripts/moment-timezone-with-data.js: -------------------------------------------------------------------------------- 1 | ../../../node_modules/moment-timezone/builds/moment-timezone-with-data.js -------------------------------------------------------------------------------- /src/vendor/scripts/moment.js: -------------------------------------------------------------------------------- 1 | ../../../node_modules/moment/moment.js -------------------------------------------------------------------------------- /src/vendor/scripts/snap.js: -------------------------------------------------------------------------------- 1 | ../../../node_modules/snap.js/dist/snap.js -------------------------------------------------------------------------------- /src/vendor/scripts/underscore.js: -------------------------------------------------------------------------------- 1 | ../../../node_modules/underscore/underscore.js --------------------------------------------------------------------------------