├── .editorconfig
├── .github
├── ISSUE_TEMPLATE.md
├── gdc-create-credentials.png
├── gdc-oauth-client-id-creation.png
├── screenshot.png
├── setup1.png
├── setup2.png
├── setup3.png
└── wmail_wavebox.gif
├── .gitignore
├── .travis.yml
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── assets
├── fonts
│ ├── fontawesome
│ │ ├── FontAwesome.otf
│ │ ├── font-awesome.min.css
│ │ ├── fontawesome-webfont.eot
│ │ ├── fontawesome-webfont.svg
│ │ ├── fontawesome-webfont.ttf
│ │ ├── fontawesome-webfont.woff
│ │ └── fontawesome-webfont.woff2
│ ├── materialdesign
│ │ ├── MaterialIcons-Regular.ttf
│ │ └── material-icons.css
│ └── roboto
│ │ ├── .gitignore
│ │ ├── Roboto-Black.ttf
│ │ ├── Roboto-BlackItalic.ttf
│ │ ├── Roboto-Bold.ttf
│ │ ├── Roboto-BoldItalic.ttf
│ │ ├── Roboto-Light.ttf
│ │ ├── Roboto-LightItalic.ttf
│ │ ├── Roboto-Medium.ttf
│ │ ├── Roboto-MediumItalic.ttf
│ │ ├── Roboto-Regular.ttf
│ │ ├── Roboto-Thin.ttf
│ │ ├── Roboto-ThinItalic.ttf
│ │ └── roboto-fontface.css
├── icons
│ ├── app.icns
│ ├── app.ico
│ ├── app.png
│ ├── app.svg
│ ├── app_128.png
│ ├── app_16.png
│ ├── app_24.png
│ ├── app_256.png
│ ├── app_32.png
│ ├── app_48.png
│ ├── app_512.png
│ ├── app_64.png
│ └── app_96.png
├── images
│ ├── ginbox_icon_512.png
│ ├── ginbox_mode_full_small.png
│ ├── ginbox_mode_inbox.png
│ ├── ginbox_mode_unreadunbundled.png
│ ├── ginbox_mode_zero_small.png
│ ├── gmail_icon_512.png
│ ├── gmail_inbox_categories_small.png
│ ├── gmail_inbox_priority_small.png
│ ├── gmail_inbox_unread_small.png
│ ├── google_services
│ │ ├── logo_calendar_128px.png
│ │ ├── logo_calendar_32px.png
│ │ ├── logo_calendar_48px.png
│ │ ├── logo_calendar_64px.png
│ │ ├── logo_contacts_128px.png
│ │ ├── logo_contacts_32px.png
│ │ ├── logo_contacts_48px.png
│ │ ├── logo_contacts_64px.png
│ │ ├── logo_drive_128px.png
│ │ ├── logo_drive_32px.png
│ │ ├── logo_drive_48px.png
│ │ ├── logo_drive_64px.png
│ │ ├── logo_hangouts_128px.png
│ │ ├── logo_hangouts_16px.png
│ │ ├── logo_hangouts_24px.png
│ │ ├── logo_hangouts_32px.png
│ │ ├── logo_hangouts_48px.png
│ │ ├── logo_hangouts_64px.png
│ │ ├── logo_keep_128px.png
│ │ ├── logo_keep_32px.png
│ │ ├── logo_keep_48px.png
│ │ └── logo_keep_64px.png
│ └── mailbox_right_click_settings.png
└── webpack.config.js
├── package.json
├── src
├── app
│ ├── package.json
│ ├── src
│ │ ├── app
│ │ │ ├── AppAnalytics.js
│ │ │ ├── AppPrimaryMenu.js
│ │ │ ├── AuthGoogle.js
│ │ │ ├── AuthHTTP.html
│ │ │ ├── AuthHTTP.js
│ │ │ ├── KeyboardShortcuts.js
│ │ │ ├── main.js
│ │ │ ├── storage
│ │ │ │ ├── StorageBucket.js
│ │ │ │ ├── StorageBucketAppMutable.js
│ │ │ │ ├── appStorage.js
│ │ │ │ ├── avatarStorage.js
│ │ │ │ ├── index.js
│ │ │ │ ├── mailboxStorage.js
│ │ │ │ └── settingStorage.js
│ │ │ ├── stores
│ │ │ │ ├── mailboxStore.js
│ │ │ │ └── settingStore.js
│ │ │ └── windows
│ │ │ │ ├── ContentWindow.js
│ │ │ │ ├── MailboxesSessionManager.js
│ │ │ │ ├── MailboxesWindow.js
│ │ │ │ ├── WMailWindow.js
│ │ │ │ └── WindowManager.js
│ │ └── index.js
│ └── webpack.config.js
├── scenes
│ ├── mailboxes
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── Components
│ │ │ │ ├── ColorPickerButton.js
│ │ │ │ ├── Grid
│ │ │ │ │ ├── Col.js
│ │ │ │ │ ├── Container.js
│ │ │ │ │ ├── Row.js
│ │ │ │ │ └── index.js
│ │ │ │ ├── TrayIconEditor.js
│ │ │ │ ├── TrayPreview.js
│ │ │ │ ├── TrayRenderer.js
│ │ │ │ ├── WebView.js
│ │ │ │ └── index.js
│ │ │ ├── Dispatch
│ │ │ │ ├── index.js
│ │ │ │ ├── mailboxDispatch.js
│ │ │ │ └── navigationDispatch.js
│ │ │ ├── Notifications
│ │ │ │ ├── Notification.js
│ │ │ │ └── UnreadNotifications.js
│ │ │ ├── ReactComponents.less
│ │ │ ├── index.js
│ │ │ ├── mailboxes.html
│ │ │ ├── notification.html
│ │ │ ├── offline.html
│ │ │ ├── reporter.js
│ │ │ ├── stores
│ │ │ │ ├── StorageBucket.js
│ │ │ │ ├── alt.js
│ │ │ │ ├── appWizard
│ │ │ │ │ ├── appWizardActions.js
│ │ │ │ │ ├── appWizardStore.js
│ │ │ │ │ └── index.js
│ │ │ │ ├── compose
│ │ │ │ │ ├── composeActions.js
│ │ │ │ │ ├── composeStore.js
│ │ │ │ │ └── index.js
│ │ │ │ ├── dictionaries
│ │ │ │ │ ├── dictionariesActions.js
│ │ │ │ │ ├── dictionariesStore.js
│ │ │ │ │ └── index.js
│ │ │ │ ├── google
│ │ │ │ │ ├── GoogleHTTPTransporter.js
│ │ │ │ │ ├── googleActions.js
│ │ │ │ │ ├── googleHTTP.js
│ │ │ │ │ ├── googleStore.js
│ │ │ │ │ └── index.js
│ │ │ │ ├── http
│ │ │ │ │ ├── httpActions.js
│ │ │ │ │ ├── httpStore.js
│ │ │ │ │ └── index.js
│ │ │ │ ├── mailbox
│ │ │ │ │ ├── avatarPersistence.js
│ │ │ │ │ ├── index.js
│ │ │ │ │ ├── mailboxActions.js
│ │ │ │ │ ├── mailboxPersistence.js
│ │ │ │ │ ├── mailboxStore.js
│ │ │ │ │ └── migration.js
│ │ │ │ ├── mailboxWizard
│ │ │ │ │ ├── Configurations.js
│ │ │ │ │ ├── index.js
│ │ │ │ │ ├── mailboxWizardActions.js
│ │ │ │ │ └── mailboxWizardStore.js
│ │ │ │ ├── platform
│ │ │ │ │ ├── index.js
│ │ │ │ │ ├── platformActions.js
│ │ │ │ │ └── platformStore.js
│ │ │ │ └── settings
│ │ │ │ │ ├── index.js
│ │ │ │ │ ├── migration.js
│ │ │ │ │ ├── settingsActions.js
│ │ │ │ │ ├── settingsPersistence.js
│ │ │ │ │ └── settingsStore.js
│ │ │ └── ui
│ │ │ │ ├── App.js
│ │ │ │ ├── AppBadge.js
│ │ │ │ ├── AppContent.js
│ │ │ │ ├── AppWizard
│ │ │ │ ├── AppWizard.js
│ │ │ │ ├── AppWizardComplete.js
│ │ │ │ ├── AppWizardMailto.js
│ │ │ │ ├── AppWizardStart.js
│ │ │ │ ├── AppWizardTray.js
│ │ │ │ └── index.js
│ │ │ │ ├── DictionaryInstaller
│ │ │ │ ├── DictionaryInstallHandler.js
│ │ │ │ └── DictionaryInstallStepper.js
│ │ │ │ ├── Mailbox
│ │ │ │ ├── Google
│ │ │ │ │ ├── GoogleMailboxCalendarTab.js
│ │ │ │ │ ├── GoogleMailboxCommunicationTab.js
│ │ │ │ │ ├── GoogleMailboxContactsTab.js
│ │ │ │ │ ├── GoogleMailboxMailTab.js
│ │ │ │ │ ├── GoogleMailboxNotesTab.js
│ │ │ │ │ └── GoogleMailboxStorageTab.js
│ │ │ │ ├── MailboxComposePicker.js
│ │ │ │ ├── MailboxSearch.js
│ │ │ │ ├── MailboxTab.js
│ │ │ │ ├── MailboxTabSleepable.js
│ │ │ │ ├── MailboxTargetUrl.js
│ │ │ │ ├── MailboxWindows.js
│ │ │ │ └── mailboxWindow.less
│ │ │ │ ├── MailboxWizard
│ │ │ │ ├── AddMailboxWizardDialog.js
│ │ │ │ ├── ConfigureCompleteWizardDialog.js
│ │ │ │ ├── ConfigureGinboxMailboxWizard.js
│ │ │ │ ├── ConfigureGmailMailboxWizard.js
│ │ │ │ ├── ConfigureMailboxServicesDialog.js
│ │ │ │ ├── ConfigureMailboxWizardDialog.js
│ │ │ │ ├── MailboxWizard.js
│ │ │ │ └── index.js
│ │ │ │ ├── NewsDialog.js
│ │ │ │ ├── NewsDialog.less
│ │ │ │ ├── Settings
│ │ │ │ ├── AccountSettings.js
│ │ │ │ ├── Accounts
│ │ │ │ │ ├── AccountAdvancedSettings.js
│ │ │ │ │ ├── AccountAvatarSettings.js
│ │ │ │ │ ├── AccountCustomCodeSettings.js
│ │ │ │ │ ├── AccountManagementSettings.js
│ │ │ │ │ ├── AccountServiceSettings.js
│ │ │ │ │ ├── AccountUnreadSettings.js
│ │ │ │ │ └── CustomCodeEditingModal.js
│ │ │ │ ├── AdvancedSettings.js
│ │ │ │ ├── General
│ │ │ │ │ ├── DownloadSettingsSection.js
│ │ │ │ │ ├── InfoSettingsSection.js
│ │ │ │ │ ├── LanguageSettingsSection.js
│ │ │ │ │ ├── NotificationSettingsSection.js
│ │ │ │ │ ├── PlatformSettingsSection.js
│ │ │ │ │ ├── TraySettingsSection.js
│ │ │ │ │ └── UISettingsSection.js
│ │ │ │ ├── GeneralSettings.js
│ │ │ │ ├── SettingsDialog.js
│ │ │ │ └── settingStyles.js
│ │ │ │ ├── Sidelist
│ │ │ │ ├── Sidelist.js
│ │ │ │ ├── SidelistItemAddMailbox.js
│ │ │ │ ├── SidelistItemMailbox
│ │ │ │ │ ├── SidelistItemMailbox.js
│ │ │ │ │ ├── SidelistItemMailboxAvatar.js
│ │ │ │ │ ├── SidelistItemMailboxPopover.js
│ │ │ │ │ ├── SidelistItemMailboxService.js
│ │ │ │ │ ├── SidelistItemMailboxServices.js
│ │ │ │ │ └── index.js
│ │ │ │ ├── SidelistItemNews.js
│ │ │ │ ├── SidelistItemSettings.js
│ │ │ │ ├── SidelistItemWizard.js
│ │ │ │ ├── SidelistMailboxes.js
│ │ │ │ ├── SidelistStyles.js
│ │ │ │ ├── SidelistStyles.less
│ │ │ │ └── index.js
│ │ │ │ ├── Tray.js
│ │ │ │ ├── UpdateCheckDialog.js
│ │ │ │ ├── Welcome
│ │ │ │ └── Welcome.js
│ │ │ │ ├── appContent.less
│ │ │ │ ├── appTheme.js
│ │ │ │ └── layout.less
│ │ └── webpack.config.js
│ └── platform
│ │ ├── src
│ │ ├── nativeRequire.js
│ │ └── webviewInjection
│ │ │ ├── Browser
│ │ │ ├── Browser.js
│ │ │ ├── ContextMenu.js
│ │ │ ├── DictionaryLoad.js
│ │ │ ├── KeyboardNavigator.js
│ │ │ └── Spellchecker.js
│ │ │ ├── Google
│ │ │ ├── GinboxApi.js
│ │ │ ├── GinboxChangeEmitter.js
│ │ │ ├── GmailApiExtras.js
│ │ │ ├── GmailChangeEmitter.js
│ │ │ ├── GoogleMail.js
│ │ │ ├── GoogleService.js
│ │ │ └── GoogleWindowOpen.js
│ │ │ ├── WMail
│ │ │ ├── CustomCode.js
│ │ │ └── WMail.js
│ │ │ ├── elconsole.js
│ │ │ ├── googleMail.js
│ │ │ ├── googleService.js
│ │ │ └── injector.js
│ │ └── webpack.config.js
└── shared
│ ├── Models
│ ├── Mailbox
│ │ ├── Google.js
│ │ ├── Mailbox.js
│ │ ├── MailboxServices.js
│ │ ├── MailboxTypes.js
│ │ └── index.js
│ ├── Model.js
│ ├── Settings
│ │ ├── AppSettings.js
│ │ ├── LanguageSettings.js
│ │ ├── NewsSettings.js
│ │ ├── OSSettings.js
│ │ ├── ProxySettings.js
│ │ ├── SettingsIdent.js
│ │ ├── TraySettings.js
│ │ ├── UISettings.js
│ │ └── index.js
│ └── index.js
│ ├── b64Assets.js
│ ├── constants.js
│ ├── dictionaries.js
│ ├── dictionaryExcludes.js
│ └── index.js
└── webpack.config.js
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | indent_size = 2
6 | end_of_line = lf
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 |
11 | [{package.json,*.yml}]
12 | indent_style = space
13 | indent_size = 2
14 | trim_trailing_whitespace = true
15 | insert_final_newline = true
16 |
17 | [*.md]
18 | trim_trailing_whitespace = false
19 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | * Which version of WMail are you using? [e.g. 1.3.1]
2 |
3 | * Which Operating System are you using? [e.g. Windows 10, Mac OSX 10, Ubuntu 14.04]
4 |
--------------------------------------------------------------------------------
/.github/gdc-create-credentials.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/.github/gdc-create-credentials.png
--------------------------------------------------------------------------------
/.github/gdc-oauth-client-id-creation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/.github/gdc-oauth-client-id-creation.png
--------------------------------------------------------------------------------
/.github/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/.github/screenshot.png
--------------------------------------------------------------------------------
/.github/setup1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/.github/setup1.png
--------------------------------------------------------------------------------
/.github/setup2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/.github/setup2.png
--------------------------------------------------------------------------------
/.github/setup3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/.github/setup3.png
--------------------------------------------------------------------------------
/.github/wmail_wavebox.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/.github/wmail_wavebox.gif
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | credentials.js
2 | node_modules
3 | bin
4 | dist
5 | WMail-darwin-x64
6 | WMail-linux-ia32/
7 | WMail-linux-x64/
8 | WMail-win32-ia32/
9 | WMail-win32-x64/
10 | WMail-win32-ia32-Installer/
11 | *.log
12 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: required
2 | install:
3 | - sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y
4 | - sudo apt-get update -qq
5 | - if [ "$CXX" = "g++" ]; then sudo apt-get install -qq g++-4.8; fi
6 | - if [ "$CXX" = "g++" ]; then export CXX="g++-4.8" CC="gcc-4.8"; fi
7 | - npm install -g standard
8 |
9 | language: node_js
10 | node_js:
11 | - "6.2.0"
12 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Pull Requests
2 |
3 | By submitting a pull request, you represent that you have the right to license your contribution to us and the community, and agree by submitting the patch that your contributions are licensed under the MPL-2.0 license.
4 |
5 | # Raising a new issue & requesting Features
6 |
7 | Thanks for being an A* tester and helping to make WMail better by either reporting a bug or raising an issue! Before you create a new issue here are a few things that might be worth checking first...
8 |
9 | 1. Make sure you're using the [latest version and also check the latest pre-releases](https://github.com/Thomas101/wmail/releases). You may find that your bug has been fixed or feature added in a pre-release. You can find the full list of all releases available to download along with the changelog for each release on the [releases page](https://github.com/Thomas101/wmail/releases)
10 |
11 | 2. Take a quick look to see if somebody else has already raised an issue on the [issues page](https://github.com/Thomas101/wmail/issues). It makes it much easier for me to track discussion around a single issue rather than dealing with duplicates
12 |
13 | 3. Check the [closed issues with the "waiting release" tag](https://github.com/Thomas101/wmail/issues?q=is%3Aissue+label%3AWaiting-release+is%3Aclosed). These are bugs that have been fixed or features that have been added that haven't quite made it into a release just yet. Once an issue is given the waiting release tag it will make it out into a release shortly
14 |
15 | 4. Some commonly raised issues have their own section in the [FAQs](https://github.com/Thomas101/wmail/wiki/FAQs). The answer to your question may be there already!
16 |
17 | Once you're happy that your issue / feature hasn't already been raised or fixed feel free to raise it!
18 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # We've moved!
2 |
3 | Hi! This repository is no longer being used and has been archived for historical purposes.
4 |
5 | Find out more at [https://wavebox.io](https://wavebox.io/)
6 |
--------------------------------------------------------------------------------
/assets/fonts/fontawesome/FontAwesome.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/fonts/fontawesome/FontAwesome.otf
--------------------------------------------------------------------------------
/assets/fonts/fontawesome/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/fonts/fontawesome/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/assets/fonts/fontawesome/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/fonts/fontawesome/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/assets/fonts/fontawesome/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/fonts/fontawesome/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/assets/fonts/fontawesome/fontawesome-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/fonts/fontawesome/fontawesome-webfont.woff2
--------------------------------------------------------------------------------
/assets/fonts/materialdesign/MaterialIcons-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/fonts/materialdesign/MaterialIcons-Regular.ttf
--------------------------------------------------------------------------------
/assets/fonts/materialdesign/material-icons.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'Material Icons';
3 | font-style: normal;
4 | font-weight: 400;
5 | src: url(MaterialIcons-Regular.ttf) format('truetype');
6 | }
7 |
8 | .material-icons {
9 | font-family: 'Material Icons';
10 | font-weight: normal;
11 | font-style: normal;
12 | font-size: 24px; /* Preferred icon size */
13 | display: inline-block;
14 | width: 1em;
15 | height: 1em;
16 | line-height: 1;
17 | text-transform: none;
18 | letter-spacing: normal;
19 | word-wrap: normal;
20 | white-space: nowrap;
21 | direction: ltr;
22 |
23 | /* Support for all WebKit browsers. */
24 | -webkit-font-smoothing: antialiased;
25 | /* Support for Safari and Chrome. */
26 | text-rendering: optimizeLegibility;
27 |
28 | /* Support for Firefox. */
29 | -moz-osx-font-smoothing: grayscale;
30 |
31 | /* Support for IE. */
32 | font-feature-settings: 'liga';
33 | }
34 |
--------------------------------------------------------------------------------
/assets/fonts/roboto/.gitignore:
--------------------------------------------------------------------------------
1 | # OS specific trash
2 | .DS_Store
3 | ._.DS_Store
4 | Thumbs.db
5 |
6 | # Sass-specific
7 | .sass-cache
8 |
--------------------------------------------------------------------------------
/assets/fonts/roboto/Roboto-Black.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/fonts/roboto/Roboto-Black.ttf
--------------------------------------------------------------------------------
/assets/fonts/roboto/Roboto-BlackItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/fonts/roboto/Roboto-BlackItalic.ttf
--------------------------------------------------------------------------------
/assets/fonts/roboto/Roboto-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/fonts/roboto/Roboto-Bold.ttf
--------------------------------------------------------------------------------
/assets/fonts/roboto/Roboto-BoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/fonts/roboto/Roboto-BoldItalic.ttf
--------------------------------------------------------------------------------
/assets/fonts/roboto/Roboto-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/fonts/roboto/Roboto-Light.ttf
--------------------------------------------------------------------------------
/assets/fonts/roboto/Roboto-LightItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/fonts/roboto/Roboto-LightItalic.ttf
--------------------------------------------------------------------------------
/assets/fonts/roboto/Roboto-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/fonts/roboto/Roboto-Medium.ttf
--------------------------------------------------------------------------------
/assets/fonts/roboto/Roboto-MediumItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/fonts/roboto/Roboto-MediumItalic.ttf
--------------------------------------------------------------------------------
/assets/fonts/roboto/Roboto-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/fonts/roboto/Roboto-Regular.ttf
--------------------------------------------------------------------------------
/assets/fonts/roboto/Roboto-Thin.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/fonts/roboto/Roboto-Thin.ttf
--------------------------------------------------------------------------------
/assets/fonts/roboto/Roboto-ThinItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/fonts/roboto/Roboto-ThinItalic.ttf
--------------------------------------------------------------------------------
/assets/fonts/roboto/roboto-fontface.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'Roboto';
3 | src: url('Roboto-Thin.ttf') format('truetype');
4 | font-weight: 100;
5 | font-style: normal;
6 | }
7 |
8 | @font-face {
9 | font-family: 'Roboto-Thin';
10 | src: url('Roboto-Thin.ttf') format('truetype');
11 | }
12 |
13 | @font-face {
14 | font-family: 'Roboto';
15 | src: url('Roboto-ThinItalic.ttf') format('truetype');
16 | font-weight: 100;
17 | font-style: italic;
18 | }
19 |
20 | @font-face {
21 | font-family: 'Roboto-ThinItalic';
22 | src: url('Roboto-ThinItalic.ttf') format('truetype');
23 | }
24 |
25 | @font-face {
26 | font-family: 'Roboto';
27 | src: url('Roboto-Light.ttf') format('truetype');
28 | font-weight: 300;
29 | font-style: normal;
30 | }
31 |
32 | @font-face {
33 | font-family: 'Roboto-Light';
34 | src: url('Roboto-Light.ttf') format('truetype');
35 | }
36 |
37 | @font-face {
38 | font-family: 'Roboto';
39 | src: url('Roboto-LightItalic.ttf') format('truetype');
40 | font-weight: 300;
41 | font-style: italic;
42 | }
43 |
44 | @font-face {
45 | font-family: 'Roboto-LightItalic';
46 | src: url('Roboto-LightItalic.ttf') format('truetype');
47 | }
48 |
49 | @font-face {
50 | font-family: 'Roboto';
51 | src: url('Roboto-Regular.ttf') format('truetype');
52 | font-weight: 400;
53 | font-style: normal;
54 | }
55 |
56 | @font-face {
57 | font-family: 'Roboto-Regular';
58 | src: url('Roboto-Regular.ttf') format('truetype');
59 | }
60 |
61 | @font-face {
62 | font-family: 'Roboto';
63 | src: url('Roboto-RegularItalic.ttf') format('truetype');
64 | font-weight: 400;
65 | font-style: italic;
66 | }
67 |
68 | @font-face {
69 | font-family: 'Roboto-RegularItalic';
70 | src: url('Roboto-RegularItalic.ttf') format('truetype');
71 | }
72 |
73 | @font-face {
74 | font-family: 'Roboto';
75 | src: url('Roboto-Medium.ttf') format('truetype');
76 | font-weight: 500;
77 | font-style: normal;
78 | }
79 |
80 | @font-face {
81 | font-family: 'Roboto-Medium';
82 | src: url('Roboto-Medium.ttf') format('truetype');
83 | }
84 |
85 | @font-face {
86 | font-family: 'Roboto';
87 | src: url('Roboto-MediumItalic.ttf') format('truetype');
88 | font-weight: 500;
89 | font-style: italic;
90 | }
91 |
92 | @font-face {
93 | font-family: 'Roboto-MediumItalic';
94 | src: url('Roboto-MediumItalic.ttf') format('truetype');
95 | }
96 |
97 | @font-face {
98 | font-family: 'Roboto';
99 | src: url('Roboto-Bold.ttf') format('truetype');
100 | font-weight: 700;
101 | font-style: normal;
102 | }
103 |
104 | @font-face {
105 | font-family: 'Roboto-Bold';
106 | src: url('Roboto-Bold.ttf') format('truetype');
107 | }
108 |
109 | @font-face {
110 | font-family: 'Roboto';
111 | src: url('Roboto-BoldItalic.ttf') format('truetype');
112 | font-weight: 700;
113 | font-style: italic;
114 | }
115 |
116 | @font-face {
117 | font-family: 'Roboto-BoldItalic';
118 | src: url('Roboto-BoldItalic.ttf') format('truetype');
119 | }
120 |
121 | @font-face {
122 | font-family: 'Roboto';
123 | src: url('Roboto-Black.ttf') format('truetype');
124 | font-weight: 900;
125 | font-style: normal;
126 | }
127 |
128 | @font-face {
129 | font-family: 'Roboto-Black';
130 | src: url('Roboto-Black.ttf') format('truetype');
131 | }
132 |
133 | @font-face {
134 | font-family: 'Roboto';
135 | src: url('Roboto-BlackItalic.ttf') format('truetype');
136 | font-weight: 900;
137 | font-style: italic;
138 | }
139 |
140 | @font-face {
141 | font-family: 'Roboto-BlackItalic';
142 | src: url('Roboto-BlackItalic.ttf') format('truetype');
143 | }
144 |
--------------------------------------------------------------------------------
/assets/icons/app.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/icons/app.icns
--------------------------------------------------------------------------------
/assets/icons/app.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/icons/app.ico
--------------------------------------------------------------------------------
/assets/icons/app.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/icons/app.png
--------------------------------------------------------------------------------
/assets/icons/app.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/assets/icons/app_128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/icons/app_128.png
--------------------------------------------------------------------------------
/assets/icons/app_16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/icons/app_16.png
--------------------------------------------------------------------------------
/assets/icons/app_24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/icons/app_24.png
--------------------------------------------------------------------------------
/assets/icons/app_256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/icons/app_256.png
--------------------------------------------------------------------------------
/assets/icons/app_32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/icons/app_32.png
--------------------------------------------------------------------------------
/assets/icons/app_48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/icons/app_48.png
--------------------------------------------------------------------------------
/assets/icons/app_512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/icons/app_512.png
--------------------------------------------------------------------------------
/assets/icons/app_64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/icons/app_64.png
--------------------------------------------------------------------------------
/assets/icons/app_96.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/icons/app_96.png
--------------------------------------------------------------------------------
/assets/images/ginbox_icon_512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/images/ginbox_icon_512.png
--------------------------------------------------------------------------------
/assets/images/ginbox_mode_full_small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/images/ginbox_mode_full_small.png
--------------------------------------------------------------------------------
/assets/images/ginbox_mode_inbox.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/images/ginbox_mode_inbox.png
--------------------------------------------------------------------------------
/assets/images/ginbox_mode_unreadunbundled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/images/ginbox_mode_unreadunbundled.png
--------------------------------------------------------------------------------
/assets/images/ginbox_mode_zero_small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/images/ginbox_mode_zero_small.png
--------------------------------------------------------------------------------
/assets/images/gmail_icon_512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/images/gmail_icon_512.png
--------------------------------------------------------------------------------
/assets/images/gmail_inbox_categories_small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/images/gmail_inbox_categories_small.png
--------------------------------------------------------------------------------
/assets/images/gmail_inbox_priority_small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/images/gmail_inbox_priority_small.png
--------------------------------------------------------------------------------
/assets/images/gmail_inbox_unread_small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/images/gmail_inbox_unread_small.png
--------------------------------------------------------------------------------
/assets/images/google_services/logo_calendar_128px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/images/google_services/logo_calendar_128px.png
--------------------------------------------------------------------------------
/assets/images/google_services/logo_calendar_32px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/images/google_services/logo_calendar_32px.png
--------------------------------------------------------------------------------
/assets/images/google_services/logo_calendar_48px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/images/google_services/logo_calendar_48px.png
--------------------------------------------------------------------------------
/assets/images/google_services/logo_calendar_64px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/images/google_services/logo_calendar_64px.png
--------------------------------------------------------------------------------
/assets/images/google_services/logo_contacts_128px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/images/google_services/logo_contacts_128px.png
--------------------------------------------------------------------------------
/assets/images/google_services/logo_contacts_32px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/images/google_services/logo_contacts_32px.png
--------------------------------------------------------------------------------
/assets/images/google_services/logo_contacts_48px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/images/google_services/logo_contacts_48px.png
--------------------------------------------------------------------------------
/assets/images/google_services/logo_contacts_64px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/images/google_services/logo_contacts_64px.png
--------------------------------------------------------------------------------
/assets/images/google_services/logo_drive_128px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/images/google_services/logo_drive_128px.png
--------------------------------------------------------------------------------
/assets/images/google_services/logo_drive_32px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/images/google_services/logo_drive_32px.png
--------------------------------------------------------------------------------
/assets/images/google_services/logo_drive_48px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/images/google_services/logo_drive_48px.png
--------------------------------------------------------------------------------
/assets/images/google_services/logo_drive_64px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/images/google_services/logo_drive_64px.png
--------------------------------------------------------------------------------
/assets/images/google_services/logo_hangouts_128px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/images/google_services/logo_hangouts_128px.png
--------------------------------------------------------------------------------
/assets/images/google_services/logo_hangouts_16px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/images/google_services/logo_hangouts_16px.png
--------------------------------------------------------------------------------
/assets/images/google_services/logo_hangouts_24px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/images/google_services/logo_hangouts_24px.png
--------------------------------------------------------------------------------
/assets/images/google_services/logo_hangouts_32px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/images/google_services/logo_hangouts_32px.png
--------------------------------------------------------------------------------
/assets/images/google_services/logo_hangouts_48px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/images/google_services/logo_hangouts_48px.png
--------------------------------------------------------------------------------
/assets/images/google_services/logo_hangouts_64px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/images/google_services/logo_hangouts_64px.png
--------------------------------------------------------------------------------
/assets/images/google_services/logo_keep_128px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/images/google_services/logo_keep_128px.png
--------------------------------------------------------------------------------
/assets/images/google_services/logo_keep_32px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/images/google_services/logo_keep_32px.png
--------------------------------------------------------------------------------
/assets/images/google_services/logo_keep_48px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/images/google_services/logo_keep_48px.png
--------------------------------------------------------------------------------
/assets/images/google_services/logo_keep_64px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/images/google_services/logo_keep_64px.png
--------------------------------------------------------------------------------
/assets/images/mailbox_right_click_settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Thomas101/wmail/e389fc56296e74a033785b7dcc455b65a714bd46/assets/images/mailbox_right_click_settings.png
--------------------------------------------------------------------------------
/assets/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const ROOT_DIR = path.resolve(path.join(__dirname, '../'))
3 | const BIN_DIR = path.join(ROOT_DIR, 'bin')
4 | const devRequire = (n) => require(path.join(ROOT_DIR, 'node_modules', n))
5 |
6 | const CleanWebpackPlugin = devRequire('clean-webpack-plugin')
7 | const CopyWebpackPlugin = devRequire('copy-webpack-plugin')
8 |
9 | module.exports = {
10 | output: {
11 | path: BIN_DIR,
12 | filename: '__.js'
13 | },
14 | plugins: [
15 | new CleanWebpackPlugin(['fonts', 'icons'], {
16 | root: BIN_DIR,
17 | verbose: true,
18 | dry: false
19 | }),
20 | new CopyWebpackPlugin([
21 | { from: path.join(__dirname, 'fonts'), to: 'fonts', force: true },
22 | { from: path.join(__dirname, 'icons'), to: 'icons', force: true },
23 | { from: path.join(__dirname, 'images'), to: 'images', force: true }
24 | ], {
25 | ignore: [ '.DS_Store' ]
26 | })
27 | ]
28 | }
29 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "wmail",
3 | "version": "2.3.1",
4 | "prerelease": true,
5 | "description": "The missing desktop client for Gmail and Google Inbox",
6 | "scripts": {
7 | "prestart": "webpack",
8 | "start": "electron bin/app/index.js",
9 | "test": "standard",
10 | "install-all": "echo ':wmail' && npm install && cd src/app && echo ':wmail-app' && npm install && cd ../../src/scenes/mailboxes && echo ':wmail-scenes-mailboxes' && npm install",
11 | "outdated-all": "echo ':wmail' && npm outdated && cd src/app && echo ':wmail-app' && npm outdated && cd ../../src/scenes/mailboxes && echo ':wmail-scenes-mailboxes' && npm outdated",
12 | "dev:platform": "webpack --task=platform && electron bin/app/index.js",
13 | "dev:app": "webpack --task=app && electron bin/app/index.js",
14 | "dev:mailboxes": "webpack --task=mailboxes && electron bin/app/index.js",
15 | "dev:assets": "webpack --task=assets && electron bin/app/index.js",
16 | "dev:run": "electron bin/app/index.js"
17 | },
18 | "keywords": [],
19 | "author": {
20 | "name": "Thomas Beverley",
21 | "email": "tom.beverley.wmail@gmail.com",
22 | "url": "https://github.com/Thomas101/"
23 | },
24 | "homepage": "https://thomas101.github.io/wmail/",
25 | "license": "MPL-2.0",
26 | "repository": "https://github.com/Thomas101/wmail",
27 | "main": "bin/app/index.js",
28 | "dependencies": {
29 | "babel": "6.23.0",
30 | "babel-core": "6.23.1",
31 | "babel-loader": "6.3.2",
32 | "babel-preset-es2015": "6.22.0",
33 | "babel-preset-react": "6.23.0",
34 | "babel-preset-stage-0": "6.22.0",
35 | "clean-webpack-plugin": "0.1.15",
36 | "copy-webpack-plugin": "4.0.1",
37 | "css-loader": "0.26.1",
38 | "electron": "1.6.1",
39 | "extract-text-webpack-plugin": "1.0.1",
40 | "file-loader": "0.10.0",
41 | "json-loader": "0.5.4",
42 | "jsx-loader": "0.13.2",
43 | "less": "2.7.2",
44 | "less-loader": "2.2.3",
45 | "style-loader": "0.13.1",
46 | "uglify-js": "mishoo/UglifyJS2#3ee46e91e802fb8bf20656bce115375c5f624052",
47 | "url-loader": "0.5.7",
48 | "uuid": "3.0.1",
49 | "webpack": "1.14.0",
50 | "webpack-target-electron-renderer": "0.4.0"
51 | },
52 | "devDependencies": {
53 | "standard": "8.6.0"
54 | },
55 | "standard": {
56 | "ignore": [
57 | "src/app/lib/"
58 | ]
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "wmail-app",
3 | "keywords": [],
4 | "author": "Thomas Beverley",
5 | "license": "MPL-2.0",
6 | "repository": "https://github.com/Thomas101/wmail",
7 | "description": "The mail app code for the WMail app",
8 | "dependencies": {
9 | "appdirectory": "0.1.0",
10 | "dictionary-en-us": "1.2.0",
11 | "escape-html": "1.0.3",
12 | "fs-extra": "2.0.0",
13 | "gmail-js": "0.6.8",
14 | "googleapis": "16.1.0",
15 | "home-dir": "1.0.0",
16 | "https-proxy-agent": "1.0.0",
17 | "jquery": "3.1.1",
18 | "minivents": "2.0.2",
19 | "mkdirp": "0.5.1",
20 | "node-fetch": "1.6.3",
21 | "os-locale": "2.0.0",
22 | "request": "2.79.0",
23 | "unused-filename": "0.1.0",
24 | "uuid": "3.0.1",
25 | "windows-shortcuts": "Thomas101/windows-shortcuts#0.1.4",
26 | "wmail-spellchecker": "Thomas101/wmail-spellchecker#1.0.5",
27 | "write-file-atomic": "1.3.1",
28 | "yargs": "6.6.0"
29 | },
30 | "devDependencies": {
31 | "standard": "8.6.0"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/app/src/app/AuthHTTP.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | WMail Authentication Required
6 |
58 |
59 |
77 |
78 |
79 | WMail Authentication Required
80 |
81 | requires a username and password
82 |
83 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/src/app/src/app/AuthHTTP.js:
--------------------------------------------------------------------------------
1 | const {BrowserWindow} = require('electron')
2 |
3 | class AuthHTTP {
4 |
5 | /* ****************************************************************************/
6 | // Lifecycle
7 | /* ****************************************************************************/
8 |
9 | /**
10 | * @param callback: callback to execute with username and password
11 | */
12 | constructor (url, callback) {
13 | this.window = new BrowserWindow({
14 | width: 400,
15 | height: 300,
16 | frame: false,
17 | center: true,
18 | show: true,
19 | resizable: false,
20 | alwaysOnTop: true,
21 | autoHideMenuBar: true
22 | })
23 | this.window.loadURL(`file://${__dirname}/AuthHTTP.html`)
24 | this.window.webContents.on('did-finish-load', () => {
25 | this.window.webContents.send('requestor', url)
26 | })
27 | }
28 | }
29 |
30 | module.exports = AuthHTTP
31 |
--------------------------------------------------------------------------------
/src/app/src/app/KeyboardShortcuts.js:
--------------------------------------------------------------------------------
1 | const {globalShortcut} = require('electron')
2 |
3 | /*
4 | * KeyboardShortcuts registers additional keyboard shortcuts.
5 | * Note that most keyboard shortcuts are configured with the AppPrimaryMenu.
6 | */
7 | class KeyboardShortcuts {
8 |
9 | /* ****************************************************************************/
10 | // Lifecycle
11 | /* ****************************************************************************/
12 |
13 | constructor (selectors) {
14 | this._selectors = selectors
15 | this._shortcuts = []
16 | }
17 |
18 | /* ****************************************************************************/
19 | // Creating
20 | /* ****************************************************************************/
21 |
22 | /**
23 | * Registers global keyboard shortcuts.
24 | */
25 | register () {
26 | let shortcuts = new Map([
27 | ['CmdOrCtrl+{', this._selectors.prevMailbox],
28 | ['CmdOrCtrl+}', this._selectors.nextMailbox]
29 | ])
30 | this.unregister()
31 | shortcuts.forEach((callback, accelerator) => {
32 | globalShortcut.register(accelerator, callback)
33 | this._shortcuts.push(accelerator)
34 | })
35 | }
36 |
37 | /**
38 | * Unregisters any previously registered global keyboard shortcuts.
39 | */
40 | unregister () {
41 | this._shortcuts.forEach((accelerator) => {
42 | globalShortcut.unregister(accelerator)
43 | })
44 | this._shortcuts = []
45 | }
46 |
47 | }
48 |
49 | module.exports = KeyboardShortcuts
50 |
--------------------------------------------------------------------------------
/src/app/src/app/storage/StorageBucketAppMutable.js:
--------------------------------------------------------------------------------
1 | const StorageBucket = require('./StorageBucket')
2 |
3 | class StorageBucketAppMutable extends StorageBucket {
4 | /**
5 | * @param k: the key to set
6 | * @param v: the value to set
7 | * @return v
8 | */
9 | setItem (k, v) { return this._setItem(k, v) }
10 |
11 | /**
12 | * @param k: the key to set
13 | * @param v: the value to set
14 | * @return v
15 | */
16 | setJSONItem (k, v) { return this._setItem(k, JSON.stringify(v)) }
17 |
18 | /**
19 | * @param k: the key to remove
20 | */
21 | removeItem (k) { return this._removeItem(k) }
22 | }
23 |
24 | module.exports = StorageBucketAppMutable
25 |
--------------------------------------------------------------------------------
/src/app/src/app/storage/appStorage.js:
--------------------------------------------------------------------------------
1 | const StorageBucket = require('./StorageBucketAppMutable')
2 | module.exports = new StorageBucket('app')
3 |
--------------------------------------------------------------------------------
/src/app/src/app/storage/avatarStorage.js:
--------------------------------------------------------------------------------
1 | const StorageBucket = require('./StorageBucket')
2 | module.exports = new StorageBucket('avatar')
3 |
--------------------------------------------------------------------------------
/src/app/src/app/storage/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | appStorage: require('./appStorage'),
3 | avatarStorage: require('./avatarStorage'),
4 | mailboxStorage: require('./mailboxStorage'),
5 | settingStorage: require('./settingStorage')
6 | }
7 |
--------------------------------------------------------------------------------
/src/app/src/app/storage/mailboxStorage.js:
--------------------------------------------------------------------------------
1 | const StorageBucket = require('./StorageBucket')
2 | module.exports = new StorageBucket('mailboxes')
3 |
--------------------------------------------------------------------------------
/src/app/src/app/storage/settingStorage.js:
--------------------------------------------------------------------------------
1 | const StorageBucket = require('./StorageBucket')
2 | module.exports = new StorageBucket('settings')
3 |
--------------------------------------------------------------------------------
/src/app/src/app/stores/mailboxStore.js:
--------------------------------------------------------------------------------
1 | const persistence = require('../storage/mailboxStorage')
2 | const Minivents = require('minivents')
3 | const Mailbox = require('../../shared/Models/Mailbox/Mailbox')
4 | const { MAILBOX_INDEX_KEY } = require('../../shared/constants')
5 |
6 | class MailboxStore {
7 | /* ****************************************************************************/
8 | // Lifecycle
9 | /* ****************************************************************************/
10 |
11 | constructor () {
12 | Minivents(this)
13 |
14 | // Build the current data
15 | this.index = []
16 | this.mailboxes = new Map()
17 |
18 | const allRawItems = persistence.allJSONItems()
19 | Object.keys(allRawItems).forEach((id) => {
20 | if (id === MAILBOX_INDEX_KEY) {
21 | this.index = allRawItems[id]
22 | } else {
23 | this.mailboxes.set(id, new Mailbox(id, allRawItems[id]))
24 | }
25 | })
26 |
27 | // Listen for changes
28 | persistence.on('changed', (evt) => {
29 | if (evt.key === MAILBOX_INDEX_KEY) {
30 | this.index = persistence.getJSONItem(MAILBOX_INDEX_KEY)
31 | } else {
32 | if (evt.type === 'setItem') {
33 | this.mailboxes.set(evt.key, new Mailbox(evt.key, persistence.getJSONItem(evt.key)))
34 | }
35 | if (evt.type === 'removeItem') {
36 | this.mailboxes.delete(evt.key)
37 | }
38 | }
39 | this.emit('changed', {})
40 | })
41 | }
42 |
43 | /* ****************************************************************************/
44 | // Getters
45 | /* ****************************************************************************/
46 |
47 | /**
48 | * @return the mailboxes in an ordered list
49 | */
50 | orderedMailboxes () {
51 | return this.index
52 | .map(id => this.mailboxes.get(id))
53 | .filter((mailbox) => !!mailbox)
54 | }
55 |
56 | /**
57 | * @param id: the id of the mailbox
58 | * @return the mailbox record
59 | */
60 | getMailbox (id) {
61 | return this.mailboxes.get(id)
62 | }
63 | }
64 |
65 | module.exports = new MailboxStore()
66 |
--------------------------------------------------------------------------------
/src/app/src/app/stores/settingStore.js:
--------------------------------------------------------------------------------
1 | const persistence = require('../storage/settingStorage')
2 | const Minivents = require('minivents')
3 | const {
4 | Settings: {
5 | AppSettings,
6 | LanguageSettings,
7 | NewsSettings,
8 | OSSettings,
9 | ProxySettings,
10 | TraySettings,
11 | UISettings
12 | }
13 | } = require('../../shared/Models')
14 |
15 | class SettingStore {
16 | constructor () {
17 | Minivents(this)
18 |
19 | // Build the current data
20 | this.app = new AppSettings(persistence.getJSONItem('app', {}))
21 | this.language = new LanguageSettings(persistence.getJSONItem('language', {}))
22 | this.news = new NewsSettings(persistence.getJSONItem('news', {}))
23 | this.os = new OSSettings(persistence.getJSONItem('os', {}))
24 | this.proxy = new ProxySettings(persistence.getJSONItem('proxy', {}))
25 | this.tray = new TraySettings(persistence.getJSONItem('tray', {}))
26 | this.ui = new UISettings(persistence.getJSONItem('ui', {}))
27 |
28 | // Listen for changes
29 | persistence.on('changed:app', () => {
30 | const prev = this.language
31 | this.app = new AppSettings(persistence.getJSONItem('app', {}))
32 | this.emit('changed', { })
33 | this.emit('changed:app', { prev: prev, next: this.app })
34 | })
35 | persistence.on('changed:language', () => {
36 | const prev = this.language
37 | this.language = new LanguageSettings(persistence.getJSONItem('language', {}))
38 | this.emit('changed', { })
39 | this.emit('changed:language', { prev: prev, next: this.language })
40 | })
41 | persistence.on('changed:news', () => {
42 | const prev = this.news
43 | this.news = new NewsSettings(persistence.getJSONItem('news', {}))
44 | this.emit('changed', { })
45 | this.emit('changed:news', { prev: prev, next: this.news })
46 | })
47 | persistence.on('changed:os', () => {
48 | const prev = this.os
49 | this.os = new OSSettings(persistence.getJSONItem('os', {}))
50 | this.emit('changed', { })
51 | this.emit('changed:os', { prev: prev, next: this.os })
52 | })
53 | persistence.on('changed:proxy', () => {
54 | const prev = this.proxy
55 | this.proxy = new ProxySettings(persistence.getJSONItem('proxy', {}))
56 | this.emit('changed', { })
57 | this.emit('changed:proxy', { prev: prev, next: this.proxy })
58 | })
59 | persistence.on('changed:tray', () => {
60 | const prev = this.tray
61 | this.tray = new TraySettings(persistence.getJSONItem('tray', {}))
62 | this.emit('changed', { })
63 | this.emit('changed:tray', { prev: prev, next: this.tray })
64 | })
65 | persistence.on('changed:ui', () => {
66 | const prev = this.ui
67 | this.ui = new UISettings(persistence.getJSONItem('ui', {}))
68 | this.emit('changed', { })
69 | this.emit('changed:ui', { prev: prev, next: this.ui })
70 | })
71 | }
72 | }
73 |
74 | module.exports = new SettingStore()
75 |
--------------------------------------------------------------------------------
/src/app/src/app/windows/ContentWindow.js:
--------------------------------------------------------------------------------
1 | const WMailWindow = require('./WMailWindow')
2 | const {shell} = require('electron')
3 |
4 | class ContentWindow extends WMailWindow {
5 |
6 | /* ****************************************************************************/
7 | // Creation
8 | /* ****************************************************************************/
9 |
10 | /**
11 | * The default window preferences
12 | * @param partition: the partition to set the window to
13 | * @param extraPreferences = undefined: extra preferences to merge into the prefs
14 | * @return the settings
15 | */
16 | defaultWindowPreferences (partition, extraPreferences = undefined) {
17 | return Object.assign(super.defaultWindowPreferences(extraPreferences), {
18 | minWidth: 400,
19 | minHeight: 400,
20 | webPreferences: {
21 | nodeIntegration: false,
22 | partition: partition
23 | }
24 | })
25 | }
26 |
27 | /**
28 | * Starts the window
29 | * @param url: the start url
30 | * @param partition: the window partition
31 | * @param windowPreferences=undefined: additional window preferences to supply
32 | */
33 | start (url, partition, windowPreferences = undefined) {
34 | this.createWindow(this.defaultWindowPreferences(partition, windowPreferences), url)
35 | }
36 |
37 | /**
38 | * Creates and launches the window
39 | * @arguments: passed through to super()
40 | */
41 | createWindow () {
42 | super.createWindow.apply(this, Array.from(arguments))
43 | this.window.webContents.on('new-window', (evt, url) => {
44 | evt.preventDefault()
45 | shell.openExternal(url)
46 | })
47 | }
48 | }
49 |
50 | module.exports = ContentWindow
51 |
--------------------------------------------------------------------------------
/src/app/src/index.js:
--------------------------------------------------------------------------------
1 | require('./app/main.js')
2 |
--------------------------------------------------------------------------------
/src/app/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const ROOT_DIR = path.resolve(path.join(__dirname, '../../'))
3 | const BIN_DIR = path.join(ROOT_DIR, 'bin')
4 | const devRequire = (n) => require(path.join(ROOT_DIR, 'node_modules', n))
5 |
6 | const CleanWebpackPlugin = devRequire('clean-webpack-plugin')
7 | const CopyWebpackPlugin = devRequire('copy-webpack-plugin')
8 |
9 | module.exports = {
10 | output: {
11 | path: BIN_DIR,
12 | filename: '__.js'
13 | },
14 | plugins: [
15 | new CleanWebpackPlugin(['app'], {
16 | root: BIN_DIR,
17 | verbose: true,
18 | dry: false
19 | }),
20 | new CopyWebpackPlugin([
21 | { from: path.join(__dirname, 'src'), to: 'app', force: true },
22 | { from: path.join(__dirname, 'node_modules'), to: 'app/node_modules', force: true },
23 | { from: path.join(__dirname, '../shared/'), to: 'app/shared', force: true },
24 | { from: path.join(ROOT_DIR, 'package.json'), to: 'app', force: true }
25 | ], {
26 | ignore: [ '.DS_Store' ]
27 | })
28 | ]
29 | }
30 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "wmail-scenes-mailboxes",
3 | "author": "Thomas Beverley",
4 | "license": "MPL-2.0",
5 | "repository": "https://github.com/Thomas101/wmail",
6 | "description": "The mailboxes window for the WMail app",
7 | "dependencies": {
8 | "addressparser": "1.0.1",
9 | "alt": "0.18.6",
10 | "bootstrap-grid": "2.0.1",
11 | "camelcase": "4.0.0",
12 | "compare-version": "0.1.2",
13 | "fbjs": "0.8.9",
14 | "https-proxy-agent": "1.0.0",
15 | "material-ui": "0.17.0",
16 | "minivents": "2.0.2",
17 | "qs": "6.3.1",
18 | "querystring": "0.2.0",
19 | "react": "15.4.2",
20 | "react-addons-shallow-compare": "15.4.2",
21 | "react-color": "2.11.1",
22 | "react-dom": "15.4.2",
23 | "react-tap-event-plugin": "2.0.1",
24 | "react-timer-mixin": "0.13.3",
25 | "react-tooltip": "3.2.7",
26 | "urijs": "1.18.7",
27 | "uuid": "3.0.1"
28 | },
29 | "devDependencies": {
30 | "standard": "8.6.0"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/Components/ColorPickerButton.js:
--------------------------------------------------------------------------------
1 | const React = require('react')
2 | const { RaisedButton, Popover } = require('material-ui')
3 | const { ChromePicker } = require('react-color')
4 |
5 | module.exports = React.createClass({
6 | /* **************************************************************************/
7 | // Class
8 | /* **************************************************************************/
9 |
10 | displayName: 'ColorPickerButton',
11 | propTypes: {
12 | value: React.PropTypes.string,
13 | label: React.PropTypes.string.isRequired,
14 | disabled: React.PropTypes.bool.isRequired,
15 | anchorOrigin: React.PropTypes.object.isRequired,
16 | targetOrigin: React.PropTypes.object.isRequired,
17 | icon: React.PropTypes.node,
18 | onChange: React.PropTypes.func
19 | },
20 |
21 | /* **************************************************************************/
22 | // Data lifecycle
23 | /* **************************************************************************/
24 |
25 | getInitialState () {
26 | return {
27 | open: false,
28 | anchor: null
29 | }
30 | },
31 |
32 | getDefaultProps () {
33 | return {
34 | label: 'Pick Colour',
35 | disabled: false,
36 | anchorOrigin: {horizontal: 'left', vertical: 'bottom'},
37 | targetOrigin: {horizontal: 'left', vertical: 'top'}
38 | }
39 | },
40 |
41 | /* **************************************************************************/
42 | // Rendering
43 | /* **************************************************************************/
44 |
45 | render () {
46 | const { label, disabled, onChange, anchorOrigin, targetOrigin, icon, ...passProps } = this.props
47 | return (
48 |
49 |
this.setState({ open: true, anchor: evt.target })} />
54 | this.setState({open: false})}>
60 | {
63 | if (onChange) {
64 | onChange(Object.assign({}, col, {
65 | rgbaStr: `rgba(${col.rgb.r}, ${col.rgb.g}, ${col.rgb.b}, ${col.rgb.a})`
66 | }))
67 | }
68 | }} />
69 |
70 |
71 | )
72 | }
73 | })
74 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/Components/Grid/Col.js:
--------------------------------------------------------------------------------
1 | import 'bootstrap-grid'
2 |
3 | const React = require('react')
4 |
5 | module.exports = React.createClass({
6 | displayName: 'GridCol',
7 |
8 | propTypes: {
9 | xs: React.PropTypes.number,
10 | sm: React.PropTypes.number,
11 | md: React.PropTypes.number,
12 | lg: React.PropTypes.number,
13 | offset: React.PropTypes.number,
14 | className: React.PropTypes.string,
15 | children: React.PropTypes.node
16 | },
17 |
18 | render () {
19 | const {xs, sm, md, lg, offset, className, children, ...passProps} = this.props
20 |
21 | let mode = 'xs'
22 | let size = 12
23 | if (xs !== undefined) {
24 | mode = 'xs'
25 | size = xs
26 | } else if (sm !== undefined) {
27 | mode = 'sm'
28 | size = sm
29 | } else if (md !== undefined) {
30 | mode = 'md'
31 | size = md
32 | } else if (lg !== undefined) {
33 | mode = 'lg'
34 | size = lg
35 | }
36 |
37 | const classNames = [
38 | ['col', mode, size].join('-'),
39 | offset !== undefined ? ['col', mode, 'offset', size].join('-') : undefined,
40 | className
41 | ].filter((c) => !!c).join(' ')
42 |
43 | return (
44 |
45 | {children}
46 |
47 | )
48 | }
49 | })
50 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/Components/Grid/Container.js:
--------------------------------------------------------------------------------
1 | import 'bootstrap-grid'
2 |
3 | const React = require('react')
4 |
5 | module.exports = React.createClass({
6 | displayName: 'GridContainer',
7 |
8 | propTypes: {
9 | className: React.PropTypes.string,
10 | children: React.PropTypes.node,
11 | fluid: React.PropTypes.bool
12 | },
13 |
14 | render () {
15 | const {fluid, className, ...passProps} = this.props
16 |
17 | const classNames = [
18 | fluid ? 'container-fluid' : 'conainer',
19 | className
20 | ].filter((c) => !!c).join(' ')
21 |
22 | return (
23 |
24 | {this.props.children}
25 |
26 | )
27 | }
28 | })
29 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/Components/Grid/Row.js:
--------------------------------------------------------------------------------
1 | import 'bootstrap-grid'
2 |
3 | const React = require('react')
4 |
5 | module.exports = React.createClass({
6 | displayName: 'GridRow',
7 |
8 | propTypes: {
9 | className: React.PropTypes.string,
10 | children: React.PropTypes.node
11 | },
12 |
13 | render () {
14 | return (
15 | !!c).join(' ')}>
18 | {this.props.children}
19 |
20 | )
21 | }
22 | })
23 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/Components/Grid/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | Container: require('./Container'),
3 | Col: require('./Col'),
4 | Row: require('./Row')
5 | }
6 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/Components/TrayPreview.js:
--------------------------------------------------------------------------------
1 | const React = require('react')
2 | const TrayRenderer = require('./TrayRenderer')
3 | const shallowCompare = require('react-addons-shallow-compare')
4 |
5 | module.exports = React.createClass({
6 | /* **************************************************************************/
7 | // Class
8 | /* **************************************************************************/
9 |
10 | displayName: 'TrayPreview',
11 | propTypes: {
12 | config: React.PropTypes.object.isRequired,
13 | size: React.PropTypes.number.isRequired
14 | },
15 |
16 | /* **************************************************************************/
17 | // Component Lifecycle
18 | /* **************************************************************************/
19 |
20 | componentWillMount () {
21 | TrayRenderer.renderPNGDataImage(this.props.config)
22 | .then((png) => this.setState({ image: png }))
23 | },
24 |
25 | componentWillReceiveProps (nextProps) {
26 | if (shallowCompare(this, nextProps, this.state)) {
27 | TrayRenderer.renderPNGDataImage(nextProps.config)
28 | .then((png) => this.setState({ image: png }))
29 | }
30 | },
31 |
32 | /* **************************************************************************/
33 | // Data Lifecycle
34 | /* **************************************************************************/
35 |
36 | getInitialState () {
37 | return { image: null }
38 | },
39 |
40 | /* **************************************************************************/
41 | // Rendering
42 | /* **************************************************************************/
43 |
44 | render () {
45 | const { size, style, ...passProps } = this.props
46 | delete passProps.config
47 |
48 | return (
49 |
57 | {!this.state.image ? undefined : (
58 |

62 | )}
63 |
64 | )
65 | }
66 | })
67 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/Components/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | ColorPickerButton: require('./ColorPickerButton'),
3 | Grid: require('./Grid'),
4 | TrayIconEditor: require('./TrayIconEditor'),
5 | TrayPreview: require('./TrayPreview'),
6 | TrayRenderer: require('./TrayRenderer'),
7 | WebView: require('./WebView')
8 | }
9 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/Dispatch/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | mailboxDispatch: require('./mailboxDispatch'),
3 | navigationDispatch: require('./navigationDispatch')
4 | }
5 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/Dispatch/navigationDispatch.js:
--------------------------------------------------------------------------------
1 | const Minivents = require('minivents')
2 | const {ipcRenderer} = window.nativeRequire('electron')
3 |
4 | class NavigationDispatch {
5 |
6 | /* **************************************************************************/
7 | // Lifecycle
8 | /* **************************************************************************/
9 |
10 | constructor () {
11 | Minivents(this)
12 | }
13 |
14 | /**
15 | * Binds the listeners to the ipc renderer
16 | */
17 | bindIPCListeners () {
18 | ipcRenderer.on('launch-settings', () => { this.openSettings() })
19 | return this
20 | }
21 |
22 | /* **************************************************************************/
23 | // Actions
24 | /* **************************************************************************/
25 |
26 | /**
27 | * Emits an open settings command
28 | */
29 | openSettings () {
30 | this.emit('opensettings', {})
31 | }
32 |
33 | /**
34 | * Opens the settings at a mailbox
35 | * @param mailboxId: the id of the mailbox
36 | */
37 | openMailboxSettings (mailboxId) {
38 | this.emit('opensettings', {
39 | route: {
40 | tab: 'accounts',
41 | mailboxId: mailboxId
42 | }
43 | })
44 | }
45 |
46 | /**
47 | * Opens the news
48 | */
49 | openNews () {
50 | this.emit('opennews', {})
51 | }
52 | }
53 |
54 | module.exports = new NavigationDispatch()
55 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/Notifications/Notification.js:
--------------------------------------------------------------------------------
1 | const { BrowserWindow } = window.nativeRequire('electron').remote
2 | const path = require('path')
3 |
4 | class Notification {
5 | constructor (text, options) {
6 | this.__options__ = Object.assign({}, options)
7 | this.browserWindow = new BrowserWindow({
8 | x: 0,
9 | y: 0,
10 | useContentSize: true,
11 | show: false,
12 | autoHideMenuBar: true,
13 | frame: false,
14 | resizable: false,
15 | skipTaskbar: true,
16 | alwaysOnTop: true,
17 | backgroundColor: '#FFF',
18 | webPreferences: {
19 | nodeIntegration: true
20 | }
21 | })
22 | const htmlPath = 'file://' + path.join(path.dirname(window.location.href.replace('file://', '')), 'notification.html')
23 | this.browserWindow.loadURL(htmlPath)
24 | this.browserWindow.once('ready-to-show', () => {
25 | this.browserWindow.webContents.executeJavaScript(`window.renderNotification.apply(this, ${JSON.stringify([text, options])})`)
26 | this.browserWindow.show()
27 | this.browserWindow.webContents.openDevTools()
28 | })
29 |
30 | setTimeout(() => {
31 | this.close()
32 | }, 3000)
33 | }
34 |
35 | close () {
36 | if (!this.browserWindow) { return }
37 | this.browserWindow.close()
38 | this.browserWindow = null
39 | }
40 | }
41 |
42 | module.exports = Notification
43 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/ReactComponents.less:
--------------------------------------------------------------------------------
1 | .ReactComponent-MaterialUI-Dialog-Body-Scrollbars {
2 | &::-webkit-scrollbar {
3 | -webkit-appearance: none;
4 | width: 7px;
5 | }
6 | &::-webkit-scrollbar-thumb {
7 | border-radius: 4px;
8 | background-color: rgba(0,0,0,.5);
9 | -webkit-box-shadow: 0 0 1px rgba(255,255,255,.5);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/index.js:
--------------------------------------------------------------------------------
1 | import './ReactComponents.less'
2 | const React = require('react')
3 | const ReactDOM = require('react-dom')
4 | const App = require('./ui/App')
5 | const mailboxActions = require('./stores/mailbox/mailboxActions')
6 | const settingsActions = require('./stores/settings/settingsActions')
7 | const composeActions = require('./stores/compose/composeActions')
8 | const mailboxWizardActions = require('./stores/mailboxWizard/mailboxWizardActions')
9 | const { ipcRenderer } = window.nativeRequire('electron')
10 |
11 | // See if we're offline and run a re-direct
12 | if (window.navigator.onLine === false) {
13 | window.location.href = 'offline.html'
14 | }
15 |
16 | // Load what we have in the db
17 | mailboxActions.load()
18 | mailboxWizardActions.load()
19 | settingsActions.load()
20 | composeActions.load()
21 |
22 | // Remove loading
23 | ;(() => {
24 | const loading = document.getElementById('loading')
25 | loading.parentElement.removeChild(loading)
26 | })()
27 |
28 | // Render and prepare for unrender
29 | ReactDOM.render(, document.getElementById('app'))
30 | ipcRenderer.on('prepare-reload', function () {
31 | ReactDOM.unmountComponentAtNode(document.getElementById('app'))
32 | })
33 | ipcRenderer.send('mailboxes-js-loaded', {})
34 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/mailboxes.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | WMail
6 |
7 |
8 |
9 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/reporter.js:
--------------------------------------------------------------------------------
1 | const ipc = window.nativeRequire('electron').ipcRenderer
2 |
3 | class Reporter {
4 | reportError (errorStr) {
5 | ipc.send('report-error', { error: errorStr })
6 | }
7 | }
8 |
9 | module.exports = new Reporter()
10 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/stores/alt.js:
--------------------------------------------------------------------------------
1 | const Alt = require('alt')
2 | module.exports = new Alt()
3 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/stores/appWizard/appWizardActions.js:
--------------------------------------------------------------------------------
1 | const alt = require('../alt')
2 |
3 | class AppWizardActions {
4 |
5 | /* **************************************************************************/
6 | // Lifecycle
7 | /* **************************************************************************/
8 |
9 | /**
10 | * Starts the wizard
11 | */
12 | startWizard () { return {} }
13 |
14 | /**
15 | * Progresses the wizard to the next stage
16 | */
17 | progressNextStep () { return {} }
18 |
19 | /**
20 | * Cancels the wizard
21 | */
22 | cancelWizard () { return {} }
23 |
24 | /**
25 | * Cancel and discards the wizard
26 | */
27 | discardWizard () { return {} }
28 | }
29 |
30 | module.exports = alt.createActions(AppWizardActions)
31 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/stores/appWizard/appWizardStore.js:
--------------------------------------------------------------------------------
1 | const alt = require('../alt')
2 | const actions = require('./appWizardActions')
3 | const settingsActions = require('../settings/settingsActions')
4 |
5 | class AppWizardStore {
6 | /* **************************************************************************/
7 | // Lifecycle
8 | /* **************************************************************************/
9 |
10 | constructor () {
11 | this.startOpen = false
12 | this.trayConfiguratorOpen = false
13 | this.mailtoHandlerOpen = false
14 | this.completeOpen = false
15 |
16 | /**
17 | * @return true if any configuration dialogs are open
18 | */
19 | this.hasAnyItemsOpen = () => {
20 | return this.startOpen || this.mailtoHandlerOpen || this.trayConfiguratorOpen || this.completeOpen
21 | }
22 |
23 | /* ****************************************/
24 | // Listeners
25 | /* ****************************************/
26 |
27 | this.bindListeners({
28 | handleStartWizard: actions.START_WIZARD,
29 | handleProgressNextStep: actions.PROGRESS_NEXT_STEP,
30 | handleCancelWizard: actions.CANCEL_WIZARD,
31 | handleDiscardWizard: actions.DISCARD_WIZARD
32 | })
33 | }
34 |
35 | /* **************************************************************************/
36 | // Utils
37 | /* **************************************************************************/
38 |
39 | clearAll () {
40 | this.startOpen = false
41 | this.trayConfiguratorOpen = false
42 | this.mailtoHandlerOpen = false
43 | this.completeOpen = false
44 | }
45 |
46 | /* **************************************************************************/
47 | // Handlers
48 | /* **************************************************************************/
49 |
50 | handleStartWizard () {
51 | this.clearAll()
52 | this.startOpen = true
53 | }
54 |
55 | handleProgressNextStep () {
56 | if (this.startOpen) {
57 | this.clearAll()
58 | this.trayConfiguratorOpen = true
59 | } else if (this.trayConfiguratorOpen) {
60 | this.clearAll()
61 | if (process.platform === 'darwin' || process.platform === 'win32') {
62 | this.mailtoHandlerOpen = true
63 | } else {
64 | this.completeOpen = true
65 | }
66 | } else if (this.mailtoHandlerOpen) {
67 | this.clearAll()
68 | this.completeOpen = true
69 | } else if (this.completeOpen) {
70 | this.clearAll()
71 | settingsActions.setHasSeenAppWizard.defer(true)
72 | }
73 | }
74 |
75 | handleCancelWizard () {
76 | this.clearAll()
77 | }
78 |
79 | handleDiscardWizard () {
80 | this.clearAll()
81 | settingsActions.setHasSeenAppWizard.defer(true)
82 | }
83 | }
84 |
85 | module.exports = alt.createStore(AppWizardStore, 'AppWizardStore')
86 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/stores/appWizard/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | A: require('./appWizardActions'),
3 | appWizardActions: require('./appWizardActions'),
4 | S: require('./appWizardStore'),
5 | appWizardStore: require('./appWizardStore')
6 | }
7 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/stores/compose/composeActions.js:
--------------------------------------------------------------------------------
1 | const alt = require('../alt')
2 | const { ipcRenderer } = window.nativeRequire('electron')
3 | const URI = require('urijs')
4 | const addressparser = require('addressparser')
5 |
6 | class ComposeActions {
7 |
8 | /* **************************************************************************/
9 | // Lifecycle
10 | /* **************************************************************************/
11 |
12 | load () {
13 | return {}
14 | }
15 |
16 | /* **************************************************************************/
17 | // New Message
18 | /* **************************************************************************/
19 |
20 | /**
21 | * Composes a new message
22 | * @param recipient=undefined: the recipient to send to
23 | * @param subject=undefined: the subject of the message
24 | * @param body=undefined: the body of the message
25 | */
26 | composeNewMessage (recipient = undefined, subject = undefined, body = undefined) {
27 | return { recipient: recipient, subject: subject, body: body }
28 | }
29 |
30 | /**
31 | * Clears the current compose
32 | */
33 | clearCompose () {
34 | return {}
35 | }
36 |
37 | /**
38 | * Sets the target mailbox
39 | * @param mailboxId: the id of the mailbox
40 | */
41 | setTargetMailbox (mailboxId) {
42 | return { mailboxId: mailboxId }
43 | }
44 |
45 | /**
46 | * Opens a mailto link
47 | * @param mailtoLink='': the link to try to open
48 | */
49 | processMailtoLink (mailtoLink = '') {
50 | if (mailtoLink.indexOf('mailto:') === 0) {
51 | const uri = URI(mailtoLink || '')
52 | const recipients = addressparser(decodeURIComponent(uri.pathname())).map((r) => r.address)
53 | const qs = uri.search(true)
54 | return this.composeNewMessage(recipients.join(','), qs.subject || qs.Subject, qs.body || qs.Body)
55 | } else {
56 | return { valid: false }
57 | }
58 | }
59 | }
60 |
61 | const actions = alt.createActions(ComposeActions)
62 | ipcRenderer.on('open-mailto-link', (evt, req) => actions.processMailtoLink(req.mailtoLink))
63 | module.exports = actions
64 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/stores/compose/composeStore.js:
--------------------------------------------------------------------------------
1 | const alt = require('../alt')
2 | const actions = require('./composeActions')
3 | const uuid = require('uuid')
4 | const { ipcRenderer } = window.nativeRequire('electron')
5 |
6 | class ComposeStore {
7 | /* **************************************************************************/
8 | // Lifecycle
9 | /* **************************************************************************/
10 |
11 | constructor () {
12 | this.composing = false
13 | this.composeRef = uuid.v4()
14 | this.recipient = undefined
15 | this.subject = undefined
16 | this.body = undefined
17 | this.targetMailbox = undefined
18 |
19 | /* ****************************************/
20 | // Message Getters
21 | /* ****************************************/
22 |
23 | /**
24 | * @return a dictionary containing just the message info
25 | */
26 | this.getMessageInfo = () => {
27 | return {
28 | recipient: this.recipient,
29 | subject: this.subject,
30 | body: this.body
31 | }
32 | }
33 |
34 | /* ****************************************/
35 | // Listeners
36 | /* ****************************************/
37 | this.bindListeners({
38 | handleComposeNewMessage: actions.COMPOSE_NEW_MESSAGE,
39 | handleClearCompose: actions.CLEAR_COMPOSE,
40 | handleSetTargetMailbox: actions.SET_TARGET_MAILBOX
41 | })
42 | }
43 |
44 | /* **************************************************************************/
45 | // New Message
46 | /* **************************************************************************/
47 |
48 | handleComposeNewMessage ({ recipient, subject, body }) {
49 | ipcRenderer.send('focus-app', { })
50 | this.composing = true
51 | this.composeRef = uuid.v4()
52 | this.recipient = recipient
53 | this.subject = subject
54 | this.body = body
55 | this.targetMailbox = undefined
56 | }
57 |
58 | handleClearCompose () {
59 | this.composing = false
60 | this.composeRef = uuid.v4()
61 | this.recipient = undefined
62 | this.subject = undefined
63 | this.body = undefined
64 | this.targetMailbox = undefined
65 | }
66 |
67 | handleSetTargetMailbox ({ mailboxId }) {
68 | this.targetMailbox = mailboxId
69 | }
70 | }
71 |
72 | module.exports = alt.createStore(ComposeStore, 'ComposeStore')
73 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/stores/compose/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | A: require('./composeActions'),
3 | composeActions: require('./composeActions'),
4 | S: require('./composeStore'),
5 | composeStore: require('./composeStore')
6 | }
7 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/stores/dictionaries/dictionariesActions.js:
--------------------------------------------------------------------------------
1 | const alt = require('../alt')
2 | const uuid = require('uuid')
3 |
4 | class DictionariesActions {
5 |
6 | /* **************************************************************************/
7 | // Changing
8 | /* **************************************************************************/
9 |
10 | /**
11 | * Starts the dictionary process
12 | * @return { id }
13 | */
14 | startDictionaryInstall () {
15 | return { id: uuid.v4() }
16 | }
17 |
18 | /**
19 | * Finishes / cancels the dictionary change
20 | */
21 | stopDictionaryInstall () {
22 | return { }
23 | }
24 |
25 | /**
26 | * Starts the dictionary process
27 | * @param id: the change id for validation
28 | * @param lang: the lang code to change to
29 | */
30 | pickDictionaryInstallLanguage (id, lang) {
31 | return { id: id, lang: lang }
32 | }
33 |
34 | /**
35 | * Starts the dictionary install
36 | * @param id: the change id for validation
37 | */
38 | installDictionary (id) {
39 | return { id: id }
40 | }
41 | }
42 |
43 | module.exports = alt.createActions(DictionariesActions)
44 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/stores/dictionaries/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | A: require('./dictionariesActions'),
3 | dictionariesActions: require('./dictionariesActions'),
4 | S: require('./dictionariesStore'),
5 | dictionariesStore: require('./dictionariesStore')
6 | }
7 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/stores/google/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | A: require('./googleActions'),
3 | googleActions: require('./googleActions'),
4 | S: require('./googleStore'),
5 | googleStore: require('./googleStore')
6 | }
7 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/stores/http/httpActions.js:
--------------------------------------------------------------------------------
1 | const alt = require('../alt')
2 | const uuid = require('uuid')
3 |
4 | class HttpActions {
5 |
6 | /* **************************************************************************/
7 | // Requests
8 | /* **************************************************************************/
9 |
10 | /**
11 | * Fetches text from a remote endpoint
12 | * @param url: the url of the license
13 | * @param config: the config for the fetch request if required
14 | * @return { id, promise, ... } tracking id
15 | */
16 | fetchText (url, config) {
17 | const id = uuid.v4()
18 | const promise = Promise.resolve()
19 | .then(() => window.fetch(url))
20 | .then((res) => res.ok ? Promise.resolve(res) : Promise.reject(res))
21 | .then((res) => res.text())
22 | .then((res) => {
23 | this.requestSuccess(id, res)
24 | }, (err) => {
25 | this.requestFailure(id, err)
26 | })
27 |
28 | return { id: uuid.v4(), promise: promise }
29 | }
30 |
31 | /**
32 | * Indicates a request ended in success
33 | * @param id: the id of the request
34 | * @param data: the data that was received
35 | */
36 | requestSuccess (id, data) {
37 | return { id: id, data: data }
38 | }
39 |
40 | /**
41 | * Indicates a request ended in failure
42 | * @param id: the id of the request
43 | * @param err: the error that occured
44 | */
45 | requestFailure (id, err) {
46 | return { id: id, error: err }
47 | }
48 |
49 | /* **************************************************************************/
50 | // Clearup
51 | /* **************************************************************************/
52 |
53 | /**
54 | * Clears the response
55 | * @param id: the id of the task
56 | */
57 | clearResponse (id) {
58 | return { id: id }
59 | }
60 | }
61 |
62 | module.exports = alt.createActions(HttpActions)
63 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/stores/http/httpStore.js:
--------------------------------------------------------------------------------
1 | const alt = require('../alt')
2 | const actions = require('./httpActions')
3 |
4 | class HttpStore {
5 | /* **************************************************************************/
6 | // Lifecycle
7 | /* **************************************************************************/
8 |
9 | constructor () {
10 | this.requests = new Map()
11 |
12 | /* ****************************************/
13 | // Requests
14 | /* ****************************************/
15 |
16 | /**
17 | * @param id: the id of the task
18 | * @return true if this task is inflight
19 | */
20 | this.isInflight = (id) => {
21 | return this.tasks.has(id) ? this.tasks.get(id).inflight : false
22 | }
23 |
24 | /**
25 | * @param id: the id of the task
26 | * @return the completion error, or undefined if none
27 | */
28 | this.error = (id) => {
29 | return this.tasks.has(id) ? this.tasks.get(id).error : undefined
30 | }
31 |
32 | /**
33 | * @param id: the id of the task
34 | * @return the completion response, or undefined if none
35 | */
36 | this.response = (id) => {
37 | return this.tasks.has(id) ? this.tasks.get(id).response : undefined
38 | }
39 |
40 | /* ****************************************/
41 | // Listeners
42 | /* ****************************************/
43 |
44 | this.bindListeners({
45 | handleFetchText: actions.FETCH_TEXT,
46 | handleRequestSuccess: actions.REQUEST_SUCCESS,
47 | handleRequestFailure: actions.REQUEST_FAILURE,
48 | handleClearResponse: actions.CLEAR_RESPONSE
49 | })
50 | }
51 |
52 | /* **************************************************************************/
53 | // Handlers: Fetch
54 | /* **************************************************************************/
55 |
56 | handleFetchText ({ id }) {
57 | this.tasks.set(id, { inflight: true })
58 | }
59 |
60 | handleRequestSuccess ({ id, data }) {
61 | this.tasks.set(id, { inflight: false, response: data })
62 | }
63 |
64 | handleRequestFailure ({ id, error }) {
65 | this.tasks.set(id, { inflight: false, error: error })
66 | }
67 |
68 | handleClearResponse ({ id }) {
69 | this.tasks.delete(id)
70 | }
71 | }
72 |
73 | module.exports = alt.createStore(HttpStore, 'HttpStore')
74 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/stores/http/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | A: require('./httpActions'),
3 | httpActions: require('./httpActions'),
4 | S: require('./httpStore'),
5 | httpStore: require('./httpStore')
6 | }
7 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/stores/mailbox/avatarPersistence.js:
--------------------------------------------------------------------------------
1 | const StorageBucket = require('../StorageBucket')
2 | module.exports = new StorageBucket('avatar')
3 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/stores/mailbox/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | A: require('./mailboxActions'),
3 | mailboxActions: require('./mailboxActions'),
4 | S: require('./mailboxStore'),
5 | mailboxStore: require('./mailboxStore')
6 | }
7 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/stores/mailbox/mailboxPersistence.js:
--------------------------------------------------------------------------------
1 | const StorageBucket = require('../StorageBucket')
2 | module.exports = new StorageBucket('mailboxes')
3 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/stores/mailbox/migration.js:
--------------------------------------------------------------------------------
1 | const persistence = require('./mailboxPersistence')
2 | const {MAILBOX_INDEX_KEY} = require('shared/constants')
3 |
4 | module.exports = {
5 | /**
6 | * Migrates the data from version 1.3.1
7 | */
8 | from_1_3_1: function () {
9 | if (window.localStorage.getItem('migrate_mailboxes_from_1_3_1') !== 'true') {
10 | const index = JSON.parse(window.localStorage.getItem('Mailbox_index') || '[]')
11 | if (index.length) {
12 | const mailboxes = index.reduce((acc, mailboxId) => {
13 | acc[mailboxId] = JSON.parse(window.localStorage.getItem('Mailbox_' + mailboxId))
14 | delete acc[mailboxId].customAvatar // not migrating avatars. Drop these
15 | return acc
16 | }, {})
17 |
18 | // Write the new values
19 | Object.keys(mailboxes).forEach((mailboxId) => {
20 | persistence.setJSONItemSync(mailboxId, mailboxes[mailboxId])
21 | })
22 | persistence.setJSONItemSync(MAILBOX_INDEX_KEY, index)
23 |
24 | // Write the completion
25 | window.localStorage.setItem('pre_1_3_1:Mailbox_index', JSON.stringify(index))
26 | window.localStorage.removeItem('Mailbox_index')
27 | Object.keys(mailboxes).forEach((mailboxId) => {
28 | window.localStorage.setItem('pre_1_3_1:Mailbox_' + mailboxId, JSON.stringify(mailboxes[mailboxId]))
29 | window.localStorage.removeItem('Mailbox_' + mailboxId)
30 | })
31 | }
32 | }
33 |
34 | window.localStorage.setItem('migrate_mailboxes_from_1_3_1', 'true')
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/stores/mailboxWizard/Configurations.js:
--------------------------------------------------------------------------------
1 | const { Mailbox, Google } = require('shared/Models/Mailbox')
2 | const configurations = {}
3 | configurations[Mailbox.TYPE_GMAIL] = {
4 | DEFAULT_INBOX: { // Unread Messages in primary category
5 | googleConf: {
6 | takeLabelCountFromUI: false,
7 | unreadMode: Google.UNREAD_MODES.PRIMARY_INBOX_UNREAD//
8 | }
9 | },
10 | PRIORIY_INBOX: { // Unread Important Messages
11 | googleConf: {
12 | takeLabelCountFromUI: false,
13 | unreadMode: Google.UNREAD_MODES.INBOX_UNREAD_IMPORTANT
14 | }
15 | },
16 | UNREAD_INBOX: { // All Unread Messages
17 | googleConf: {
18 | takeLabelCountFromUI: false,
19 | unreadMode: Google.UNREAD_MODES.INBOX_UNREAD
20 | }
21 | }
22 | }
23 | configurations[Mailbox.TYPE_GINBOX] = {
24 | UNREAD_INBOX: {
25 | googleConf: {
26 | takeLabelCountFromUI: false,
27 | unreadMode: Google.UNREAD_MODES.INBOX_UNREAD
28 | }
29 | },
30 | DEFAULT_INBOX: {
31 | googleConf: {
32 | takeLabelCountFromUI: false,
33 | unreadMode: Google.UNREAD_MODES.GINBOX_DEFAULT
34 | }
35 | }
36 | }
37 |
38 | module.exports = configurations
39 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/stores/mailboxWizard/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | A: require('./mailboxWizardActions'),
3 | mailboxWizardActions: require('./mailboxWizardActions'),
4 | S: require('./mailboxWizardStore'),
5 | mailboxWizardStore: require('./mailboxWizardStore'),
6 | Configurations: require('./Configurations')
7 | }
8 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/stores/mailboxWizard/mailboxWizardActions.js:
--------------------------------------------------------------------------------
1 | const alt = require('../alt')
2 | const { ipcRenderer } = window.nativeRequire('electron')
3 | const { Mailbox } = require('shared/Models/Mailbox')
4 |
5 | class MailboxWizardActions {
6 |
7 | /* **************************************************************************/
8 | // Lifecycle
9 | /* **************************************************************************/
10 |
11 | /**
12 | * Loads any start off services
13 | */
14 | load () { return {} }
15 |
16 | /* **************************************************************************/
17 | // Adding
18 | /* **************************************************************************/
19 |
20 | /**
21 | * Opens the add mailbox picker
22 | */
23 | openAddMailbox () { return {} }
24 |
25 | /**
26 | * Dismisses the add mailbox picker
27 | */
28 | cancelAddMailbox () { return {} }
29 |
30 | /**
31 | * Starts the auth process for google inbox
32 | */
33 | authenticateGinboxMailbox () {
34 | return { provisionalId: Mailbox.provisionId() }
35 | }
36 |
37 | /**
38 | * Starts the auth process for gmail
39 | */
40 | authenticateGmailMailbox () {
41 | return { provisionalId: Mailbox.provisionId() }
42 | }
43 |
44 | /**
45 | * Reauthetnicates a google mailbox
46 | * @param mailboxId: the id of the mailbox
47 | */
48 | reauthenticateGoogleMailbox (mailboxId) {
49 | return { mailboxId: mailboxId }
50 | }
51 |
52 | /* **************************************************************************/
53 | // Authentication callbacks
54 | /* **************************************************************************/
55 |
56 | /**
57 | * Handles a mailbox authenticating
58 | * @param evt: the event that came over the ipc
59 | * @param data: the data that came across the ipc
60 | */
61 | authGoogleMailboxSuccess (evt, data) {
62 | return { provisionalId: data.id, type: data.type, temporaryAuth: data.temporaryAuth, mode: data.mode }
63 | }
64 |
65 | /**
66 | * Handles a mailbox authenticating error
67 | * @param evt: the ipc event that fired
68 | * @param data: the data that came across the ipc
69 | */
70 | authGoogleMailboxFailure (evt, data) {
71 | return { evt: evt, data: data }
72 | }
73 |
74 | /* **************************************************************************/
75 | // Config
76 | /* **************************************************************************/
77 |
78 | /**
79 | * Configures an account
80 | * @param configuration: the additional configuration to provide
81 | */
82 | configureMailbox (configuration) {
83 | return { configuration: configuration }
84 | }
85 |
86 | /**
87 | * Configures the enabled services
88 | * @param enabledServices: the enabled servies
89 | * @param compact: whether they should be compact or not
90 | */
91 | configureMailboxServices (enabledServices, compact) {
92 | return { enabledServices: enabledServices, compact: compact }
93 | }
94 |
95 | /**
96 | * Completes mailbox configuration
97 | */
98 | configurationComplete () { return {} }
99 | }
100 |
101 | const actions = alt.createActions(MailboxWizardActions)
102 | ipcRenderer.on('auth-google-complete', actions.authGoogleMailboxSuccess)
103 | ipcRenderer.on('auth-google-error', actions.authGoogleMailboxFailure)
104 |
105 | module.exports = actions
106 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/stores/platform/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | A: require('./platformActions'),
3 | platformActions: require('./platformActions'),
4 | S: require('./platformStore'),
5 | platformStore: require('./platformStore')
6 | }
7 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/stores/platform/platformActions.js:
--------------------------------------------------------------------------------
1 | const alt = require('../alt')
2 |
3 | class PlatformActions {
4 |
5 | /* **************************************************************************/
6 | // Login
7 | /* **************************************************************************/
8 |
9 | /**
10 | * @param openAtLogin: true to open at login
11 | * @param openAsHidden: true to open as hidden
12 | */
13 | changeLoginPref (openAtLogin, openAsHidden) {
14 | return { openAtLogin: openAtLogin, openAsHidden: openAsHidden }
15 | }
16 |
17 | /* **************************************************************************/
18 | // Mailto
19 | /* **************************************************************************/
20 |
21 | /**
22 | * Sets if the app is the default mailto link handler
23 | * @param isCurrentApp: true if this is the handler
24 | */
25 | changeMailtoLinkHandler (isCurrentApp) {
26 | return { isCurrentApp: isCurrentApp }
27 | }
28 | }
29 |
30 | module.exports = alt.createActions(PlatformActions)
31 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/stores/settings/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | S: require('./settingsStore'),
3 | settingsStore: require('./settingsStore'),
4 | A: require('./settingsActions'),
5 | settingsActions: require('./settingsActions')
6 | }
7 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/stores/settings/migration.js:
--------------------------------------------------------------------------------
1 | const persistence = require('./settingsPersistence')
2 |
3 | module.exports = {
4 | /**
5 | * Migrates the data from version 1.3.1
6 | */
7 | from_1_3_1: function () {
8 | if (window.localStorage.getItem('migrate_settings_from_1_3_1') !== 'true') {
9 | const prev = JSON.parse(window.localStorage.getItem('App_settings') || '{}')
10 | const next = {
11 | language: {},
12 | os: {},
13 | proxy: prev.proxyServer || {},
14 | tray: {},
15 | ui: {}
16 | }
17 | const transfer = function (from, bucket, to) {
18 | if (prev[from] !== undefined) {
19 | next[bucket][to] = prev[from]
20 | }
21 | }
22 |
23 | // Language
24 | transfer('spellcheckerEnabled', 'language', 'spellcheckerEnabled')
25 |
26 | // os
27 | transfer('alwaysAskDownloadLocation', 'os', 'alwaysAskDownloadLocation')
28 | transfer('defaultDownloadLocation', 'os', 'defaultDownloadLocation')
29 | transfer('notificationsEnabled', 'os', 'notificationsEnabled')
30 | transfer('notificationsSilent', 'os', 'notificationsSilent')
31 | transfer('openLinksInBackground', 'os', 'openLinksInBackground')
32 |
33 | // tray
34 | transfer('showTrayIcon', 'tray', 'show')
35 | transfer('showTrayUnreadCount', 'tray', 'showUnreadCount')
36 | transfer('trayReadColor', 'tray', 'readColor')
37 | transfer('trayUnreadColor', 'tray', 'unreadColor')
38 |
39 | // ui
40 | transfer('showTitlebar', 'ui', 'showTitlebar')
41 | transfer('showAppBadge', 'ui', 'showAppBadge')
42 | transfer('showAppMenu', 'ui', 'showAppMenu')
43 | transfer('sidebarEnabled', 'ui', 'sidebarEnabled')
44 |
45 | // Save
46 | persistence.setJSONItemSync('language', next.language)
47 | persistence.setJSONItemSync('os', next.os)
48 | persistence.setJSONItemSync('proxy', next.proxy)
49 | persistence.setJSONItemSync('tray', next.tray)
50 | persistence.setJSONItemSync('ui', next.ui)
51 |
52 | // Save
53 | window.localStorage.setItem('pre_1_3_1:App_settings', JSON.stringify(prev))
54 | window.localStorage.removeItem('App_settings')
55 | }
56 |
57 | window.localStorage.setItem('migrate_settings_from_1_3_1', 'true')
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/stores/settings/settingsPersistence.js:
--------------------------------------------------------------------------------
1 | const StorageBucket = require('../StorageBucket')
2 | module.exports = new StorageBucket('settings')
3 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/ui/AppBadge.js:
--------------------------------------------------------------------------------
1 | const React = require('react')
2 | const shallowCompare = require('react-addons-shallow-compare')
3 | const { remote } = window.nativeRequire('electron')
4 | const {nativeImage, app} = remote
5 |
6 | const AppBadge = React.createClass({
7 | displayName: 'AppBadge',
8 |
9 | propTypes: {
10 | unreadCount: React.PropTypes.number.isRequired
11 | },
12 | statics: {
13 | /**
14 | * @return true if the current platform supports app badges
15 | */
16 | supportsAppBadge () {
17 | if (process.platform === 'darwin') {
18 | return true
19 | } else if (process.platform === 'linux' && app.isUnityRunning()) {
20 | return true
21 | } else {
22 | return false
23 | }
24 | },
25 | /**
26 | * @return true if this platform supports overlay icons
27 | */
28 | supportsAppOverlayIcon () {
29 | return process.platform === 'win32'
30 | }
31 | },
32 |
33 | /* **************************************************************************/
34 | // Component Lifecycle
35 | /* **************************************************************************/
36 |
37 | componentWillUnmount () {
38 | if (AppBadge.supportsAppBadge()) {
39 | app.setBadgeCount(0)
40 | } else if (AppBadge.supportsAppOverlayIcon()) {
41 | const win = remote.getCurrentWindow()
42 | win.setOverlayIcon(null, '')
43 | }
44 | },
45 |
46 | /* **************************************************************************/
47 | // Rendering
48 | /* **************************************************************************/
49 |
50 | shouldComponentUpdate (nextProps, nextState) {
51 | return shallowCompare(this, nextProps, nextState)
52 | },
53 |
54 | render () {
55 | const { unreadCount } = this.props
56 |
57 | if (AppBadge.supportsAppBadge()) {
58 | app.setBadgeCount(unreadCount)
59 | } else if (AppBadge.supportsAppOverlayIcon()) {
60 | const win = remote.getCurrentWindow()
61 | if (unreadCount === 0) {
62 | win.setOverlayIcon(null, '')
63 | } else {
64 | const text = unreadCount.toString().length > 3 ? '+' : unreadCount.toString()
65 | const canvas = document.createElement('canvas')
66 | canvas.height = 140
67 | canvas.width = 140
68 |
69 | const ctx = canvas.getContext('2d')
70 | ctx.fillStyle = 'red'
71 | ctx.beginPath()
72 | ctx.ellipse(70, 70, 65, 65, 0, 0, 2 * Math.PI)
73 | ctx.fill()
74 | ctx.textAlign = 'center'
75 | ctx.fillStyle = 'white'
76 |
77 | if (text.length > 2) {
78 | ctx.font = '65px sans-serif'
79 | ctx.fillText(text, 70, 90)
80 | } else if (text.length > 1) {
81 | ctx.font = 'bold 80px sans-serif'
82 | ctx.fillText(text, 70, 97)
83 | } else {
84 | ctx.font = 'bold 100px sans-serif'
85 | ctx.fillText(text, 70, 106)
86 | }
87 |
88 | const badgeDataURL = canvas.toDataURL()
89 | const img = nativeImage.createFromDataURL(badgeDataURL)
90 | win.setOverlayIcon(img, text)
91 | }
92 | }
93 |
94 | return ()
95 | }
96 | })
97 |
98 | module.exports = AppBadge
99 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/ui/AppWizard/AppWizard.js:
--------------------------------------------------------------------------------
1 | const React = require('react')
2 | const { appWizardStore } = require('../../stores/appWizard')
3 | const shallowCompare = require('react-addons-shallow-compare')
4 | const AppWizardStart = require('./AppWizardStart')
5 | const AppWizardComplete = require('./AppWizardComplete')
6 | const AppWizardMailto = require('./AppWizardMailto')
7 | const AppWizardTray = require('./AppWizardTray')
8 |
9 | module.exports = React.createClass({
10 | /* **************************************************************************/
11 | // Class
12 | /* **************************************************************************/
13 |
14 | displayName: 'AppWizard',
15 |
16 | /* **************************************************************************/
17 | // Component Lifecycle
18 | /* **************************************************************************/
19 |
20 | componentDidMount () {
21 | this.renderTO = null
22 | appWizardStore.listen(this.wizardChanged)
23 | },
24 |
25 | componentWillUnmount () {
26 | clearTimeout(this.renderTO)
27 | appWizardStore.unlisten(this.wizardChanged)
28 | },
29 |
30 | /* **************************************************************************/
31 | // Data lifecycle
32 | /* **************************************************************************/
33 |
34 | getInitialState () {
35 | const wizardState = appWizardStore.getState()
36 | const itemsOpen = wizardState.hasAnyItemsOpen()
37 | return {
38 | itemsOpen: itemsOpen,
39 | render: itemsOpen,
40 | trayConfiguratorOpen: wizardState.trayConfiguratorOpen,
41 | mailtoHandlerOpen: wizardState.mailtoHandlerOpen,
42 | completeOpen: wizardState.completeOpen,
43 | startOpen: wizardState.startOpen
44 | }
45 | },
46 |
47 | wizardChanged (wizardState) {
48 | this.setState((prevState) => {
49 | const itemsOpen = wizardState.hasAnyItemsOpen()
50 | const update = {
51 | itemsOpen: itemsOpen,
52 | trayConfiguratorOpen: wizardState.trayConfiguratorOpen,
53 | mailtoHandlerOpen: wizardState.mailtoHandlerOpen,
54 | completeOpen: wizardState.completeOpen,
55 | startOpen: wizardState.startOpen
56 | }
57 |
58 | if (prevState.itemsOpen !== itemsOpen) {
59 | clearTimeout(this.renderTO)
60 | if (prevState.itemsOpen && !itemsOpen) {
61 | this.renderTO = setTimeout(() => {
62 | this.setState({ render: false })
63 | }, 1000)
64 | } else if (!prevState.itemsOpen && itemsOpen) {
65 | update.render = true
66 | }
67 | }
68 | return update
69 | })
70 | },
71 |
72 | /* **************************************************************************/
73 | // Rendering
74 | /* **************************************************************************/
75 |
76 | shouldComponentUpdate (nextProps, nextState) {
77 | return shallowCompare(this, nextProps, nextState)
78 | },
79 |
80 | render () {
81 | const { render, startOpen, trayConfiguratorOpen, mailtoHandlerOpen, completeOpen } = this.state
82 | if (render) {
83 | return (
84 |
90 | )
91 | } else {
92 | return null
93 | }
94 | }
95 | })
96 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/ui/AppWizard/AppWizardMailto.js:
--------------------------------------------------------------------------------
1 | const React = require('react')
2 | const { appWizardActions } = require('../../stores/appWizard')
3 | const { platformActions } = require('../../stores/platform')
4 | const shallowCompare = require('react-addons-shallow-compare')
5 | const { Dialog, RaisedButton } = require('material-ui')
6 |
7 | module.exports = React.createClass({
8 | /* **************************************************************************/
9 | // Class
10 | /* **************************************************************************/
11 |
12 | displayName: 'AppWizardMailto',
13 | propTypes: {
14 | isOpen: React.PropTypes.bool.isRequired
15 | },
16 |
17 | /* **************************************************************************/
18 | // Rendering
19 | /* **************************************************************************/
20 |
21 | shouldComponentUpdate (nextProps, nextState) {
22 | return shallowCompare(this, nextProps, nextState)
23 | },
24 |
25 | render () {
26 | const { isOpen } = this.props
27 | const actions = (
28 |
29 | appWizardActions.cancelWizard()} />
33 | appWizardActions.progressNextStep()} />
36 | {
41 | platformActions.changeMailtoLinkHandler(true)
42 | appWizardActions.progressNextStep()
43 | }} />
44 |
45 | )
46 |
47 | return (
48 |
63 | )
64 | }
65 | })
66 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/ui/AppWizard/AppWizardStart.js:
--------------------------------------------------------------------------------
1 | const React = require('react')
2 | const { appWizardActions } = require('../../stores/appWizard')
3 | const shallowCompare = require('react-addons-shallow-compare')
4 | const { Dialog, RaisedButton, FontIcon, Avatar } = require('material-ui')
5 | const Colors = require('material-ui/styles/colors')
6 |
7 | module.exports = React.createClass({
8 | /* **************************************************************************/
9 | // Class
10 | /* **************************************************************************/
11 |
12 | displayName: 'AppWizardStart',
13 | propTypes: {
14 | isOpen: React.PropTypes.bool.isRequired
15 | },
16 |
17 | /* **************************************************************************/
18 | // Rendering
19 | /* **************************************************************************/
20 |
21 | shouldComponentUpdate (nextProps, nextState) {
22 | return shallowCompare(this, nextProps, nextState)
23 | },
24 |
25 | render () {
26 | const { isOpen } = this.props
27 | const actions = (
28 |
29 | appWizardActions.discardWizard()} />
33 | appWizardActions.cancelWizard()} />
36 | appWizardActions.progressNextStep()} />
41 |
42 | )
43 |
44 | return (
45 |
66 | )
67 | }
68 | })
69 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/ui/AppWizard/AppWizardTray.js:
--------------------------------------------------------------------------------
1 | const React = require('react')
2 | const { appWizardActions } = require('../../stores/appWizard')
3 | const { settingsStore } = require('../../stores/settings')
4 | const shallowCompare = require('react-addons-shallow-compare')
5 | const { Dialog, RaisedButton } = require('material-ui')
6 | const { TrayIconEditor } = require('../../Components')
7 |
8 | module.exports = React.createClass({
9 | /* **************************************************************************/
10 | // Class
11 | /* **************************************************************************/
12 |
13 | displayName: 'AppWizardTray',
14 | propTypes: {
15 | isOpen: React.PropTypes.bool.isRequired
16 | },
17 |
18 | /* **************************************************************************/
19 | // Component Lifecycle
20 | /* **************************************************************************/
21 |
22 | componentDidMount () {
23 | settingsStore.listen(this.settingsUpdated)
24 | },
25 |
26 | componentWillUnmount () {
27 | settingsStore.unlisten(this.settingsUpdated)
28 | },
29 |
30 | /* **************************************************************************/
31 | // Data Lifecycle
32 | /* **************************************************************************/
33 |
34 | getInitialState () {
35 | return {
36 | tray: settingsStore.getState().tray
37 | }
38 | },
39 |
40 | settingsUpdated (settingsState) {
41 | this.setState({ tray: settingsState.tray })
42 | },
43 |
44 | /* **************************************************************************/
45 | // Rendering
46 | /* **************************************************************************/
47 |
48 | shouldComponentUpdate (nextProps, nextState) {
49 | return shallowCompare(this, nextProps, nextState)
50 | },
51 |
52 | render () {
53 | const { isOpen } = this.props
54 | const { tray } = this.state
55 |
56 | const actions = (
57 |
58 | appWizardActions.cancelWizard()} />
62 | appWizardActions.progressNextStep()} />
66 |
67 | )
68 |
69 | return (
70 |
87 | )
88 | }
89 | })
90 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/ui/AppWizard/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./AppWizard')
2 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/ui/DictionaryInstaller/DictionaryInstallHandler.js:
--------------------------------------------------------------------------------
1 | const React = require('react')
2 | const { Dialog } = require('material-ui')
3 | const dictionariesStore = require('../../stores/dictionaries/dictionariesStore')
4 | const DictionaryInstallStepper = require('./DictionaryInstallStepper')
5 |
6 | module.exports = React.createClass({
7 | /* **************************************************************************/
8 | // Class
9 | /* **************************************************************************/
10 |
11 | displayName: 'DictionaryInstallHandler',
12 |
13 | /* **************************************************************************/
14 | // Component Lifecycle
15 | /* **************************************************************************/
16 |
17 | componentWillMount () {
18 | dictionariesStore.listen(this.dictionariesChanged)
19 | },
20 |
21 | componentWillUnmount () {
22 | dictionariesStore.unlisten(this.dictionariesChanged)
23 | },
24 |
25 | /* **************************************************************************/
26 | // Data lifecycle
27 | /* **************************************************************************/
28 |
29 | getInitialState () {
30 | const store = dictionariesStore.getState()
31 | return {
32 | isInstalling: store.isInstalling(),
33 | installId: store.installId()
34 | }
35 | },
36 |
37 | dictionariesChanged (store) {
38 | this.setState({
39 | isInstalling: store.isInstalling(),
40 | installId: store.installId()
41 | })
42 | },
43 |
44 | /* **************************************************************************/
45 | // Rendering
46 | /* **************************************************************************/
47 |
48 | render () {
49 | return (
50 |
58 | )
59 | }
60 | })
61 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/ui/Mailbox/Google/GoogleMailboxCalendarTab.js:
--------------------------------------------------------------------------------
1 | const React = require('react')
2 | const MailboxTabSleepable = require('../MailboxTabSleepable')
3 | const Mailbox = require('shared/Models/Mailbox/Mailbox')
4 | const { settingsStore } = require('../../../stores/settings')
5 | const {
6 | remote: {shell}
7 | } = window.nativeRequire('electron')
8 |
9 | const REF = 'mailbox_tab'
10 |
11 | module.exports = React.createClass({
12 | /* **************************************************************************/
13 | // Class
14 | /* **************************************************************************/
15 |
16 | displayName: 'GoogleMailboxCalendarTab',
17 | propTypes: {
18 | mailboxId: React.PropTypes.string.isRequired
19 | },
20 |
21 | /* **************************************************************************/
22 | // Component lifecylce
23 | /* **************************************************************************/
24 |
25 | componentDidMount () {
26 | settingsStore.listen(this.settingsChanged)
27 | },
28 |
29 | componentWillUnmount () {
30 | settingsStore.unlisten(this.settingsChanged)
31 | },
32 |
33 | /* **************************************************************************/
34 | // Data lifecylce
35 | /* **************************************************************************/
36 |
37 | getInitialState () {
38 | const settingsState = settingsStore.getState()
39 | return {
40 | os: settingsState.os
41 | }
42 | },
43 |
44 | settingsChanged (settingsState) {
45 | this.setState({ os: settingsState.os })
46 | },
47 |
48 | /* **************************************************************************/
49 | // Browser Events
50 | /* **************************************************************************/
51 |
52 | /**
53 | * Opens a new url in the correct way
54 | * @param url: the url to open
55 | */
56 | handleOpenNewWindow (url) {
57 | shell.openExternal(url, { activate: !this.state.os.openLinksInBackground })
58 | },
59 |
60 | /* **************************************************************************/
61 | // Rendering
62 | /* **************************************************************************/
63 |
64 | render () {
65 | const { mailboxId } = this.props
66 |
67 | return (
68 | { this.handleOpenNewWindow(evt.url) }} />
74 | )
75 | }
76 | })
77 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/ui/Mailbox/Google/GoogleMailboxCommunicationTab.js:
--------------------------------------------------------------------------------
1 | const React = require('react')
2 | const MailboxTabSleepable = require('../MailboxTabSleepable')
3 | const Mailbox = require('shared/Models/Mailbox/Mailbox')
4 | const { settingsStore } = require('../../../stores/settings')
5 | const URL = window.nativeRequire('url')
6 |
7 | const REF = 'mailbox_tab'
8 |
9 | module.exports = React.createClass({
10 | /* **************************************************************************/
11 | // Class
12 | /* **************************************************************************/
13 |
14 | displayName: 'GoogleMailboxCommunicationTab',
15 | propTypes: {
16 | mailboxId: React.PropTypes.string.isRequired
17 | },
18 |
19 | /* **************************************************************************/
20 | // Component lifecylce
21 | /* **************************************************************************/
22 |
23 | componentDidMount () {
24 | settingsStore.listen(this.settingsChanged)
25 | },
26 |
27 | componentWillUnmount () {
28 | settingsStore.unlisten(this.settingsChanged)
29 | },
30 |
31 | /* **************************************************************************/
32 | // Data lifecylce
33 | /* **************************************************************************/
34 |
35 | getInitialState () {
36 | const settingsState = settingsStore.getState()
37 | return {
38 | os: settingsState.os
39 | }
40 | },
41 |
42 | settingsChanged (settingsState) {
43 | this.setState({ os: settingsState.os })
44 | },
45 |
46 | /* **************************************************************************/
47 | // Browser Events
48 | /* **************************************************************************/
49 |
50 | /**
51 | * Opens a new url in the correct way
52 | * @param url: the url to open
53 | */
54 | handleOpenNewWindow (url) {
55 | const purl = URL.parse(url, true)
56 |
57 | if (purl.host === 'hangouts.google.com') {
58 | this.setState({ browserSrc: url })
59 | }
60 | },
61 |
62 | /* **************************************************************************/
63 | // Rendering
64 | /* **************************************************************************/
65 |
66 | render () {
67 | const { mailboxId } = this.props
68 |
69 | return (
70 | { this.handleOpenNewWindow(evt.url) }} />
77 | )
78 | }
79 | })
80 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/ui/Mailbox/Google/GoogleMailboxContactsTab.js:
--------------------------------------------------------------------------------
1 | const React = require('react')
2 | const MailboxTabSleepable = require('../MailboxTabSleepable')
3 | const Mailbox = require('shared/Models/Mailbox/Mailbox')
4 | const { settingsStore } = require('../../../stores/settings')
5 | const {
6 | remote: {shell}
7 | } = window.nativeRequire('electron')
8 |
9 | const REF = 'mailbox_tab'
10 |
11 | module.exports = React.createClass({
12 | /* **************************************************************************/
13 | // Class
14 | /* **************************************************************************/
15 |
16 | displayName: 'GoogleMailboxContactsTab',
17 | propTypes: {
18 | mailboxId: React.PropTypes.string.isRequired
19 | },
20 |
21 | /* **************************************************************************/
22 | // Component lifecylce
23 | /* **************************************************************************/
24 |
25 | componentDidMount () {
26 | settingsStore.listen(this.settingsChanged)
27 | },
28 |
29 | componentWillUnmount () {
30 | settingsStore.unlisten(this.settingsChanged)
31 | },
32 |
33 | /* **************************************************************************/
34 | // Data lifecylce
35 | /* **************************************************************************/
36 |
37 | getInitialState () {
38 | const settingsState = settingsStore.getState()
39 | return {
40 | os: settingsState.os
41 | }
42 | },
43 |
44 | settingsChanged (settingsState) {
45 | this.setState({ os: settingsState.os })
46 | },
47 |
48 | /* **************************************************************************/
49 | // Browser Events
50 | /* **************************************************************************/
51 |
52 | /**
53 | * Opens a new url in the correct way
54 | * @param url: the url to open
55 | */
56 | handleOpenNewWindow (url) {
57 | shell.openExternal(url, { activate: !this.state.os.openLinksInBackground })
58 | },
59 |
60 | /* **************************************************************************/
61 | // Rendering
62 | /* **************************************************************************/
63 |
64 | render () {
65 | const { mailboxId } = this.props
66 |
67 | return (
68 | { this.handleOpenNewWindow(evt.url) }} />
74 | )
75 | }
76 | })
77 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/ui/Mailbox/Google/GoogleMailboxNotesTab.js:
--------------------------------------------------------------------------------
1 | const React = require('react')
2 | const MailboxTabSleepable = require('../MailboxTabSleepable')
3 | const Mailbox = require('shared/Models/Mailbox/Mailbox')
4 | const { settingsStore } = require('../../../stores/settings')
5 | const {
6 | remote: {shell}
7 | } = window.nativeRequire('electron')
8 |
9 | const REF = 'mailbox_tab'
10 |
11 | module.exports = React.createClass({
12 | /* **************************************************************************/
13 | // Class
14 | /* **************************************************************************/
15 |
16 | displayName: 'GoogleMailboxNotesTab',
17 | propTypes: {
18 | mailboxId: React.PropTypes.string.isRequired
19 | },
20 |
21 | /* **************************************************************************/
22 | // Component lifecylce
23 | /* **************************************************************************/
24 |
25 | componentDidMount () {
26 | settingsStore.listen(this.settingsChanged)
27 | },
28 |
29 | componentWillUnmount () {
30 | settingsStore.unlisten(this.settingsChanged)
31 | },
32 |
33 | /* **************************************************************************/
34 | // Data lifecylce
35 | /* **************************************************************************/
36 |
37 | getInitialState () {
38 | const settingsState = settingsStore.getState()
39 | return {
40 | os: settingsState.os
41 | }
42 | },
43 |
44 | settingsChanged (settingsState) {
45 | this.setState({ os: settingsState.os })
46 | },
47 |
48 | /* **************************************************************************/
49 | // Browser Events
50 | /* **************************************************************************/
51 |
52 | /**
53 | * Opens a new url in the correct way
54 | * @param url: the url to open
55 | */
56 | handleOpenNewWindow (url) {
57 | shell.openExternal(url, { activate: !this.state.os.openLinksInBackground })
58 | },
59 |
60 | /* **************************************************************************/
61 | // Rendering
62 | /* **************************************************************************/
63 |
64 | render () {
65 | const { mailboxId } = this.props
66 |
67 | return (
68 | { this.handleOpenNewWindow(evt.url) }} />
74 | )
75 | }
76 | })
77 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/ui/Mailbox/Google/GoogleMailboxStorageTab.js:
--------------------------------------------------------------------------------
1 | const React = require('react')
2 | const MailboxTabSleepable = require('../MailboxTabSleepable')
3 | const Mailbox = require('shared/Models/Mailbox/Mailbox')
4 | const { settingsStore } = require('../../../stores/settings')
5 | const URL = window.nativeRequire('url')
6 | const {
7 | remote: {shell}, ipcRenderer
8 | } = window.nativeRequire('electron')
9 |
10 | const REF = 'mailbox_tab'
11 |
12 | module.exports = React.createClass({
13 | /* **************************************************************************/
14 | // Class
15 | /* **************************************************************************/
16 |
17 | displayName: 'GoogleMailboxStorageTab',
18 | propTypes: {
19 | mailboxId: React.PropTypes.string.isRequired
20 | },
21 |
22 | /* **************************************************************************/
23 | // Component lifecylce
24 | /* **************************************************************************/
25 |
26 | componentDidMount () {
27 | settingsStore.listen(this.settingsChanged)
28 | },
29 |
30 | componentWillUnmount () {
31 | settingsStore.unlisten(this.settingsChanged)
32 | },
33 |
34 | /* **************************************************************************/
35 | // Data lifecylce
36 | /* **************************************************************************/
37 |
38 | getInitialState () {
39 | const settingsState = settingsStore.getState()
40 | return {
41 | os: settingsState.os
42 | }
43 | },
44 |
45 | settingsChanged (settingsState) {
46 | this.setState({ os: settingsState.os })
47 | },
48 |
49 | /* **************************************************************************/
50 | // Browser Events
51 | /* **************************************************************************/
52 |
53 | /**
54 | * Opens a new url in the correct way
55 | * @param url: the url to open
56 | */
57 | handleOpenNewWindow (url) {
58 | const purl = URL.parse(url)
59 | if (purl.host === 'docs.google.com') {
60 | ipcRenderer.send('new-window', { partition: 'persist:' + this.props.mailboxId, url: url })
61 | } else {
62 | shell.openExternal(url, { activate: !this.state.os.openLinksInBackground })
63 | }
64 | },
65 |
66 | /* **************************************************************************/
67 | // Rendering
68 | /* **************************************************************************/
69 |
70 | render () {
71 | const { mailboxId } = this.props
72 |
73 | return (
74 | { this.handleOpenNewWindow(evt.url) }} />
80 | )
81 | }
82 | })
83 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/ui/Mailbox/MailboxTargetUrl.js:
--------------------------------------------------------------------------------
1 | const React = require('react')
2 | const { Paper } = require('material-ui')
3 |
4 | module.exports = React.createClass({
5 | /* **************************************************************************/
6 | // Class
7 | /* **************************************************************************/
8 |
9 | displayName: 'MailboxTargetUrl',
10 | propTypes: {
11 | url: React.PropTypes.string
12 | },
13 |
14 | /* **************************************************************************/
15 | // Rendering
16 | /* **************************************************************************/
17 |
18 | render () {
19 | const { url, ...passProps } = this.props
20 |
21 | const className = [
22 | 'ReactComponent-MailboxTargetUrl',
23 | url ? 'active' : undefined
24 | ].concat(this.props.className).filter((c) => !!c).join(' ')
25 | return (
26 |
27 | {url}
28 |
29 | )
30 | }
31 | })
32 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/ui/Mailbox/mailboxWindow.less:
--------------------------------------------------------------------------------
1 | .ReactComponent-MailboxWindows {
2 | .ReactComponent-MailboxTab {
3 | position: absolute;
4 | top: 10000px;
5 | bottom: -10000px;
6 | left: 0px;
7 | right: 0px;
8 | width: 100%;
9 | height: 100%;
10 |
11 | &.active {
12 | top: 0px;
13 | bottom: 0px;
14 | }
15 |
16 | @SEARCH_HEIGHT: 48px;
17 | .ReactComponent-MailboxSearch {
18 | position: absolute;
19 | bottom: -@SEARCH_HEIGHT;
20 | left: 0px;
21 | min-width: 300px;
22 | height: @SEARCH_HEIGHT;
23 | background-color: white;
24 | transition: none !important;
25 | z-index: 10;
26 | overflow: hidden;
27 |
28 | &.active {
29 | bottom: 0px;
30 | }
31 | }
32 |
33 | @TARGET_URL_HEIGHT: 16px;
34 | .ReactComponent-MailboxTargetUrl {
35 | position: absolute;
36 | bottom: -@TARGET_URL_HEIGHT;
37 | height: @TARGET_URL_HEIGHT;
38 | max-width: 50%;
39 | right: 0px;
40 | background-color: white;
41 | z-index: 9;
42 | overflow: hidden;
43 | text-align: right;
44 | font-size: 11px;
45 | line-height: @TARGET_URL_HEIGHT;
46 | padding-left: 3px;
47 | padding-right: 3px;
48 | transition-duration: 150ms !important;
49 | white-space: nowrap;
50 | text-overflow: ellipsis;
51 |
52 | &.active {
53 | bottom: 0px;
54 | }
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/ui/MailboxWizard/ConfigureCompleteWizardDialog.js:
--------------------------------------------------------------------------------
1 | const React = require('react')
2 | const { FontIcon, Dialog, RaisedButton } = require('material-ui')
3 | const Colors = require('material-ui/styles/colors')
4 | const { mailboxWizardStore, mailboxWizardActions } = require('../../stores/mailboxWizard')
5 | const { appWizardActions } = require('../../stores/appWizard')
6 | const { settingsStore } = require('../../stores/settings')
7 |
8 | const styles = {
9 | container: {
10 | textAlign: 'center'
11 | },
12 | tick: {
13 | color: Colors.green600,
14 | fontSize: '80px'
15 | },
16 | instruction: {
17 | textAlign: 'center'
18 | }
19 | }
20 |
21 | module.exports = React.createClass({
22 | /* **************************************************************************/
23 | // Class
24 | /* **************************************************************************/
25 |
26 | displayName: 'ConfigureCompleteWizardDialog',
27 |
28 | /* **************************************************************************/
29 | // Component Lifecycle
30 | /* **************************************************************************/
31 |
32 | componentDidMount () {
33 | mailboxWizardStore.listen(this.mailboxWizardChanged)
34 | settingsStore.listen(this.settingsChanged)
35 | },
36 |
37 | componentWillUnmount () {
38 | mailboxWizardStore.unlisten(this.mailboxWizardChanged)
39 | settingsStore.unlisten(this.settingsChanged)
40 | },
41 |
42 | /* **************************************************************************/
43 | // Data lifecycle
44 | /* **************************************************************************/
45 |
46 | getInitialState () {
47 | return {
48 | isOpen: mailboxWizardStore.getState().configurationCompleteOpen,
49 | hasSeenAppWizard: settingsStore.getState().app.hasSeenAppWizard
50 | }
51 | },
52 |
53 | mailboxWizardChanged (wizardState) {
54 | this.setState({ isOpen: wizardState.configurationCompleteOpen })
55 | },
56 |
57 | settingsChanged (settingsState) {
58 | this.setState({ hasSeenAppWizard: settingsStore.getState().app.hasSeenAppWizard })
59 | },
60 |
61 | /* **************************************************************************/
62 | // Rendering
63 | /* **************************************************************************/
64 |
65 | render () {
66 | const { isOpen, hasSeenAppWizard } = this.state
67 | const actions = (
68 | {
72 | mailboxWizardActions.configurationComplete()
73 | if (!hasSeenAppWizard) {
74 | setTimeout(() => {
75 | appWizardActions.startWizard()
76 | }, 500) // Feels more natural after a delay
77 | }
78 | }} />
79 | )
80 |
81 | return (
82 |
96 | )
97 | }
98 | })
99 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/ui/MailboxWizard/MailboxWizard.js:
--------------------------------------------------------------------------------
1 | const React = require('react')
2 | const { mailboxWizardStore } = require('../../stores/mailboxWizard')
3 | const shallowCompare = require('react-addons-shallow-compare')
4 | const AddMailboxWizardDialog = require('./AddMailboxWizardDialog')
5 | const ConfigureMailboxWizardDialog = require('./ConfigureMailboxWizardDialog')
6 | const ConfigureMailboxServicesDialog = require('./ConfigureMailboxServicesDialog')
7 | const ConfigureCompleteWizardDialog = require('./ConfigureCompleteWizardDialog')
8 |
9 | module.exports = React.createClass({
10 | /* **************************************************************************/
11 | // Class
12 | /* **************************************************************************/
13 |
14 | displayName: 'MailboxWizard',
15 |
16 | /* **************************************************************************/
17 | // Component Lifecycle
18 | /* **************************************************************************/
19 |
20 | componentDidMount () {
21 | this.renderTO = null
22 | mailboxWizardStore.listen(this.wizardChanged)
23 | },
24 |
25 | componentWillUnmount () {
26 | clearTimeout(this.renderTO)
27 | mailboxWizardStore.unlisten(this.wizardChanged)
28 | },
29 |
30 | /* **************************************************************************/
31 | // Data lifecycle
32 | /* **************************************************************************/
33 |
34 | getInitialState () {
35 | const itemsOpen = mailboxWizardStore.getState().hasAnyItemsOpen()
36 | return {
37 | itemsOpen: itemsOpen,
38 | render: itemsOpen
39 | }
40 | },
41 |
42 | wizardChanged (wizardState) {
43 | this.setState((prevState) => {
44 | const itemsOpen = wizardState.hasAnyItemsOpen()
45 | const update = { itemsOpen: itemsOpen }
46 | if (prevState.itemsOpen !== itemsOpen) {
47 | clearTimeout(this.renderTO)
48 | if (prevState.itemsOpen && !itemsOpen) {
49 | this.renderTO = setTimeout(() => {
50 | this.setState({ render: false })
51 | }, 1000)
52 | } else if (!prevState.itemsOpen && itemsOpen) {
53 | update.render = true
54 | }
55 | }
56 | return update
57 | })
58 | },
59 |
60 | /* **************************************************************************/
61 | // Rendering
62 | /* **************************************************************************/
63 |
64 | shouldComponentUpdate (nextProps, nextState) {
65 | return shallowCompare(this, nextProps, nextState)
66 | },
67 |
68 | render () {
69 | if (this.state.render) {
70 | return (
71 |
77 | )
78 | } else {
79 | return null
80 | }
81 | }
82 | })
83 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/ui/MailboxWizard/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./MailboxWizard')
2 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/ui/NewsDialog.less:
--------------------------------------------------------------------------------
1 | .ReactComponent-NewsDialog-Body {
2 | padding: 0;
3 | overflow: hidden;
4 | position: relative;
5 |
6 | &:before {
7 | content: "";
8 | margin-bottom: 100%;
9 | display: inline-block;
10 | }
11 |
12 | >iframe, >webview {
13 | border: none;
14 | width: 100%;
15 | height: 100%;
16 | position: absolute;
17 | top: 0;
18 | left: 0;
19 | right: 0;
20 | bottom: 0;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/ui/Settings/Accounts/AccountAdvancedSettings.js:
--------------------------------------------------------------------------------
1 | const React = require('react')
2 | const { Paper, Toggle } = require('material-ui')
3 | const mailboxActions = require('../../../stores/mailbox/mailboxActions')
4 | const styles = require('../settingStyles')
5 | const shallowCompare = require('react-addons-shallow-compare')
6 |
7 | module.exports = React.createClass({
8 | /* **************************************************************************/
9 | // Class
10 | /* **************************************************************************/
11 |
12 | displayName: 'AccountAdvancedSettings',
13 | propTypes: {
14 | mailbox: React.PropTypes.object.isRequired,
15 | showRestart: React.PropTypes.func.isRequired
16 | },
17 |
18 | /* **************************************************************************/
19 | // Rendering
20 | /* **************************************************************************/
21 |
22 | shouldComponentUpdate (nextProps, nextState) {
23 | return shallowCompare(this, nextProps, nextState)
24 | },
25 |
26 | render () {
27 | const { mailbox, showRestart, ...passProps } = this.props
28 |
29 | return (
30 |
31 | Advanced
32 | {
37 | showRestart()
38 | mailboxActions.artificiallyPersistCookies(mailbox.id, toggled)
39 | }} />
40 |
41 | )
42 | }
43 | })
44 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/ui/Settings/Accounts/AccountManagementSettings.js:
--------------------------------------------------------------------------------
1 | const React = require('react')
2 | const Colors = require('material-ui/styles/colors')
3 | const { Paper, FlatButton, FontIcon } = require('material-ui')
4 | const mailboxActions = require('../../../stores/mailbox/mailboxActions')
5 | const styles = require('../settingStyles')
6 | const shallowCompare = require('react-addons-shallow-compare')
7 | const TimerMixin = require('react-timer-mixin')
8 |
9 | module.exports = React.createClass({
10 | /* **************************************************************************/
11 | // Class
12 | /* **************************************************************************/
13 |
14 | displayName: 'AccountManagementSettings',
15 | mixins: [TimerMixin],
16 | propTypes: {
17 | mailbox: React.PropTypes.object.isRequired
18 | },
19 |
20 | /* **************************************************************************/
21 | // Component Lifecycle
22 | /* **************************************************************************/
23 |
24 | componentWillMount () {
25 | this.confirmingDeleteTO = null
26 | },
27 |
28 | componentWillReceiveProps (nextProps) {
29 | if (this.props.mailbox.id !== nextProps.mailbox.id) {
30 | this.setState({ confirmingDelete: false })
31 | this.clearTimeout(this.confirmingDeleteTO)
32 | }
33 | },
34 |
35 | /* **************************************************************************/
36 | // Data lifecycle
37 | /* **************************************************************************/
38 |
39 | getInitialState () {
40 | return {
41 | confirmingDelete: false
42 | }
43 | },
44 |
45 | /* **************************************************************************/
46 | // UI Events
47 | /* **************************************************************************/
48 |
49 | /**
50 | * Handles the delete button being tapped
51 | */
52 | handleDeleteTapped (evt) {
53 | if (this.state.confirmingDelete) {
54 | mailboxActions.remove(this.props.mailbox.id)
55 | } else {
56 | this.setState({ confirmingDelete: true })
57 | this.confirmingDeleteTO = this.setTimeout(() => {
58 | this.setState({ confirmingDelete: false })
59 | }, 4000)
60 | }
61 | },
62 |
63 | /* **************************************************************************/
64 | // Rendering
65 | /* **************************************************************************/
66 |
67 | shouldComponentUpdate (nextProps, nextState) {
68 | return shallowCompare(this, nextProps, nextState)
69 | },
70 |
71 | render () {
72 | const passProps = Object.assign({}, this.props)
73 | delete passProps.mailbox
74 |
75 | return (
76 |
77 | delete}
80 | labelStyle={{color: Colors.red600}}
81 | onTouchTap={this.handleDeleteTapped} />
82 |
83 | )
84 | }
85 | })
86 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/ui/Settings/Accounts/CustomCodeEditingModal.js:
--------------------------------------------------------------------------------
1 | const React = require('react')
2 | const { RaisedButton, FlatButton, Dialog, TextField } = require('material-ui')
3 | const shallowCompare = require('react-addons-shallow-compare')
4 | const uuid = require('uuid')
5 |
6 | module.exports = React.createClass({
7 | /* **************************************************************************/
8 | // Class
9 | /* **************************************************************************/
10 |
11 | displayName: 'CustomCodeEditingModal',
12 | propTypes: {
13 | title: React.PropTypes.string,
14 | open: React.PropTypes.bool.isRequired,
15 | code: React.PropTypes.string,
16 | onCancel: React.PropTypes.func.isRequired,
17 | onSave: React.PropTypes.func.isRequired
18 | },
19 |
20 | /* **************************************************************************/
21 | // Component Lifecycle
22 | /* **************************************************************************/
23 |
24 | componentWillReceiveProps (nextProps) {
25 | if (this.props.open !== nextProps.open) {
26 | this.setState({ editingKey: uuid.v4() })
27 | }
28 | },
29 |
30 | /* **************************************************************************/
31 | // Data Lifecycle
32 | /* **************************************************************************/
33 |
34 | getInitialState () {
35 | return {
36 | editingKey: uuid.v4()
37 | }
38 | },
39 |
40 | /* **************************************************************************/
41 | // Rendering
42 | /* **************************************************************************/
43 |
44 | shouldComponentUpdate (nextProps, nextState) {
45 | return shallowCompare(this, nextProps, nextState)
46 | },
47 |
48 | render () {
49 | const actions = [
50 | ( this.props.onCancel(evt)} />),
55 | ( this.props.onSave(evt, this.refs.editor.getValue())} />)
60 | ]
61 |
62 | return (
63 |
87 | )
88 | }
89 | })
90 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/ui/Settings/General/DownloadSettingsSection.js:
--------------------------------------------------------------------------------
1 | const React = require('react')
2 | const ReactDOM = require('react-dom')
3 | const { Toggle, Paper, RaisedButton, FontIcon } = require('material-ui')
4 | const settingsActions = require('../../../stores/settings/settingsActions')
5 | const styles = require('../settingStyles')
6 | const shallowCompare = require('react-addons-shallow-compare')
7 |
8 | module.exports = React.createClass({
9 | /* **************************************************************************/
10 | // Class
11 | /* **************************************************************************/
12 |
13 | displayName: 'DownloadSettingsSection',
14 | propTypes: {
15 | os: React.PropTypes.object.isRequired
16 | },
17 |
18 | /* **************************************************************************/
19 | // Component Lifecycle
20 | /* **************************************************************************/
21 |
22 | componentDidMount () {
23 | ReactDOM.findDOMNode(this.refs.defaultDownloadInput).setAttribute('webkitdirectory', 'webkitdirectory')
24 | },
25 |
26 | componentDidUpdate () {
27 | ReactDOM.findDOMNode(this.refs.defaultDownloadInput).setAttribute('webkitdirectory', 'webkitdirectory')
28 | },
29 |
30 | /* **************************************************************************/
31 | // Rendering
32 | /* **************************************************************************/
33 |
34 | shouldComponentUpdate (nextProps, nextState) {
35 | return shallowCompare(this, nextProps, nextState)
36 | },
37 |
38 | render () {
39 | const {os, ...passProps} = this.props
40 |
41 | return (
42 |
43 | Downloads
44 |
45 | settingsActions.setAlwaysAskDownloadLocation(toggled)} />
50 |
51 |
52 | folder}
55 | containerElement='label'
56 | disabled={os.alwaysAskDownloadLocation}
57 | style={styles.fileInputButton}>
58 | settingsActions.setDefaultDownloadLocation(evt.target.files[0].path)} />
64 |
65 | {os.alwaysAskDownloadLocation ? undefined : {os.defaultDownloadLocation}}
66 |
67 |
68 | )
69 | }
70 | })
71 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/ui/Settings/General/NotificationSettingsSection.js:
--------------------------------------------------------------------------------
1 | const React = require('react')
2 | const { Toggle, Paper } = require('material-ui')
3 | const settingsActions = require('../../../stores/settings/settingsActions')
4 | const styles = require('../settingStyles')
5 | const shallowCompare = require('react-addons-shallow-compare')
6 |
7 | module.exports = React.createClass({
8 | /* **************************************************************************/
9 | // Rendering
10 | /* **************************************************************************/
11 |
12 | displayName: 'NotificationSettingsSection',
13 | propTypes: {
14 | os: React.PropTypes.object.isRequired
15 | },
16 |
17 | /* **************************************************************************/
18 | // Rendering
19 | /* **************************************************************************/
20 |
21 | shouldComponentUpdate (nextProps, nextState) {
22 | return shallowCompare(this, nextProps, nextState)
23 | },
24 |
25 | render () {
26 | const { os, ...passProps } = this.props
27 |
28 | return (
29 |
30 | Notifications
31 | settingsActions.setNotificationsEnabled(toggled)} />
36 | settingsActions.setNotificationsSilent(!toggled)} />
42 |
43 | )
44 | }
45 | })
46 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/ui/Settings/General/PlatformSettingsSection.js:
--------------------------------------------------------------------------------
1 | const React = require('react')
2 | const { Toggle, Paper, SelectField, MenuItem } = require('material-ui')
3 | const platformActions = require('../../../stores/platform/platformActions')
4 | const styles = require('../settingStyles')
5 | const shallowCompare = require('react-addons-shallow-compare')
6 |
7 | const LOGIN_OPEN_MODES = {
8 | OFF: 'false|false',
9 | ON: 'true|false',
10 | ON_BACKGROUND: 'true|true'
11 | }
12 |
13 | module.exports = React.createClass({
14 | /* **************************************************************************/
15 | // Rendering
16 | /* **************************************************************************/
17 |
18 | displayName: 'PlatformSettingsSection',
19 | propTypes: {
20 | mailtoLinkHandlerSupported: React.PropTypes.bool.isRequired,
21 | isMailtoLinkHandler: React.PropTypes.bool.isRequired,
22 | openAtLoginSupported: React.PropTypes.bool.isRequired,
23 | openAtLogin: React.PropTypes.bool.isRequired,
24 | openAsHiddenAtLogin: React.PropTypes.bool.isRequired
25 | },
26 |
27 | /* **************************************************************************/
28 | // UI Events
29 | /* **************************************************************************/
30 |
31 | /**
32 | * Handles the open at login state chaning
33 | */
34 | handleOpenAtLoginChanged (evt, index, value) {
35 | switch (value) {
36 | case LOGIN_OPEN_MODES.OFF:
37 | platformActions.changeLoginPref(false, false)
38 | break
39 | case LOGIN_OPEN_MODES.ON:
40 | platformActions.changeLoginPref(true, false)
41 | break
42 | case LOGIN_OPEN_MODES.ON_BACKGROUND:
43 | platformActions.changeLoginPref(true, true)
44 | break
45 | }
46 | },
47 |
48 | /* **************************************************************************/
49 | // Rendering
50 | /* **************************************************************************/
51 |
52 | shouldComponentUpdate (nextProps, nextState) {
53 | return shallowCompare(this, nextProps, nextState)
54 | },
55 |
56 | render () {
57 | const {
58 | mailtoLinkHandlerSupported,
59 | isMailtoLinkHandler,
60 | openAtLoginSupported,
61 | openAtLogin,
62 | openAsHiddenAtLogin,
63 | ...passProps
64 | } = this.props
65 |
66 | if (!mailtoLinkHandlerSupported && !openAtLoginSupported) { return null }
67 |
68 | return (
69 |
70 | Platform
71 | {mailtoLinkHandlerSupported ? (
72 | platformActions.changeMailtoLinkHandler(toggled)} />
77 | ) : undefined}
78 | {openAtLoginSupported ? (
79 |
84 |
85 |
86 |
87 |
88 | ) : undefined}
89 |
90 | )
91 | }
92 | })
93 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/ui/Settings/General/TraySettingsSection.js:
--------------------------------------------------------------------------------
1 | const React = require('react')
2 | const { Toggle, Paper, SelectField, MenuItem } = require('material-ui')
3 | const { TrayIconEditor } = require('../../../Components')
4 | const settingsActions = require('../../../stores/settings/settingsActions')
5 | const styles = require('../settingStyles')
6 | const shallowCompare = require('react-addons-shallow-compare')
7 | const Tray = require('../../Tray')
8 |
9 | module.exports = React.createClass({
10 | /* **************************************************************************/
11 | // Class
12 | /* **************************************************************************/
13 |
14 | displayName: 'TraySettingsSection',
15 | propTypes: {
16 | tray: React.PropTypes.object.isRequired
17 | },
18 |
19 | /* **************************************************************************/
20 | // Rendering
21 | /* **************************************************************************/
22 |
23 | shouldComponentUpdate (nextProps, nextState) {
24 | return shallowCompare(this, nextProps, nextState)
25 | },
26 |
27 | render () {
28 | const {tray, ...passProps} = this.props
29 |
30 | return (
31 |
32 | {process.platform === 'darwin' ? 'Menu Bar' : 'Tray'}
33 |
34 | settingsActions.setShowTrayIcon(toggled)} />
39 | settingsActions.setShowTrayUnreadCount(toggled)} />
45 | {Tray.platformSupportsDpiMultiplier() ? (
46 | settingsActions.setDpiMultiplier(value)}>
50 |
51 |
52 |
53 |
54 |
55 |
56 | ) : undefined }
57 |
58 |
59 |
60 |
61 | )
62 | }
63 | })
64 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/ui/Settings/settingStyles.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | /* **************************************************************************/
3 | // Modal
4 | /* **************************************************************************/
5 | dialog: {
6 | width: '90%',
7 | maxWidth: 1200
8 | },
9 | tabToggles: {
10 | display: 'flex',
11 | flexDirection: 'row',
12 | alignContent: 'stretch'
13 | },
14 | tabToggle: {
15 | height: 50,
16 | borderRadius: 0,
17 | flex: 1,
18 | borderBottomWidth: 2,
19 | borderBottomStyle: 'solid'
20 | },
21 |
22 | /* **************************************************************************/
23 | // General
24 | /* **************************************************************************/
25 | paper: {
26 | padding: 15,
27 | marginBottom: 5,
28 | marginTop: 5
29 | },
30 | subheading: {
31 | marginTop: 0,
32 | marginBottom: 10,
33 | color: '#CCC',
34 | fontWeight: '300',
35 | fontSize: 16
36 | },
37 | fileInputButton: {
38 | marginRight: 15,
39 | position: 'relative',
40 | overflow: 'hidden'
41 | },
42 | fileInput: {
43 | position: 'absolute',
44 | top: 0,
45 | left: 0,
46 | right: 0,
47 | bottom: 0,
48 | opacity: 0,
49 | width: '100%',
50 | cursor: 'pointer'
51 | },
52 | button: {
53 | marginTop: 5,
54 | marginBottom: 5
55 | },
56 |
57 | /* **************************************************************************/
58 | // Account
59 | /* **************************************************************************/
60 |
61 | accountPicker: {
62 | position: 'relative',
63 | height: 100
64 | },
65 | accountPickerAvatar: {
66 | position: 'absolute',
67 | top: 20,
68 | left: 20,
69 | boxShadow: 'rgba(0, 0, 0, 0.117647) 0px 1px 6px, rgba(0, 0, 0, 0.117647) 0px 1px 4px' // copied from paper
70 | },
71 | accountPickerContainer: {
72 | position: 'absolute',
73 | top: 25,
74 | left: 100,
75 | right: 0
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/ui/Sidelist/SidelistItemAddMailbox.js:
--------------------------------------------------------------------------------
1 | const React = require('react')
2 | const { IconButton } = require('material-ui')
3 | const Colors = require('material-ui/styles/colors')
4 | const styles = require('./SidelistStyles')
5 | const ReactTooltip = require('react-tooltip')
6 | const { mailboxWizardActions } = require('../../stores/mailboxWizard')
7 |
8 | module.exports = React.createClass({
9 |
10 | /* **************************************************************************/
11 | // Class
12 | /* **************************************************************************/
13 |
14 | displayName: 'SidelistItemAddMailbox',
15 |
16 | /* **************************************************************************/
17 | // Rendering
18 | /* **************************************************************************/
19 |
20 | render () {
21 | const { style, ...passProps } = this.props
22 | return (
23 |
28 | mailboxWizardActions.openAddMailbox()}
31 | iconStyle={{ color: Colors.blueGrey400 }}>
32 | add_circle
33 |
34 |
39 |
40 | )
41 | }
42 | })
43 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/ui/Sidelist/SidelistItemMailbox/SidelistItemMailboxAvatar.js:
--------------------------------------------------------------------------------
1 | const React = require('react')
2 | const { Avatar } = require('material-ui')
3 | const { mailboxStore } = require('../../../stores/mailbox')
4 | const shallowCompare = require('react-addons-shallow-compare')
5 | const styles = require('../SidelistStyles')
6 |
7 | module.exports = React.createClass({
8 | /* **************************************************************************/
9 | // Class
10 | /* **************************************************************************/
11 |
12 | displayName: 'SidelistItemMailboxAvatar',
13 | propTypes: {
14 | isActive: React.PropTypes.bool.isRequired,
15 | isHovering: React.PropTypes.bool.isRequired,
16 | mailbox: React.PropTypes.object.isRequired,
17 | index: React.PropTypes.number.isRequired,
18 | onClick: React.PropTypes.func.isRequired
19 | },
20 |
21 | /* **************************************************************************/
22 | // Rendering
23 | /* **************************************************************************/
24 |
25 | shouldComponentUpdate (nextProps, nextState) {
26 | return shallowCompare(this, nextProps, nextState)
27 | },
28 |
29 | render () {
30 | const { isActive, isHovering, mailbox, index, ...passProps } = this.props
31 |
32 | let url
33 | let children
34 | let backgroundColor
35 | const borderColor = isActive || isHovering ? mailbox.color : 'white'
36 | if (mailbox.hasCustomAvatar) {
37 | url = mailboxStore.getState().getAvatar(mailbox.customAvatarId)
38 | backgroundColor = 'white'
39 | } else if (mailbox.avatarURL) {
40 | url = mailbox.avatarURL
41 | backgroundColor = 'white'
42 | } else {
43 | children = index
44 | backgroundColor = mailbox.color
45 | }
46 |
47 | return (
48 |
56 | {children}
57 |
58 | )
59 | }
60 | })
61 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/ui/Sidelist/SidelistItemMailbox/SidelistItemMailboxServices.js:
--------------------------------------------------------------------------------
1 | const React = require('react')
2 | const shallowCompare = require('react-addons-shallow-compare')
3 | const styles = require('../SidelistStyles')
4 | const SidelistItemMailboxService = require('./SidelistItemMailboxService')
5 |
6 | module.exports = React.createClass({
7 | /* **************************************************************************/
8 | // Class
9 | /* **************************************************************************/
10 |
11 | displayName: 'SidelistItemMailboxServices',
12 | propTypes: {
13 | mailbox: React.PropTypes.object.isRequired,
14 | isActiveMailbox: React.PropTypes.bool.isRequired,
15 | activeService: React.PropTypes.string.isRequired,
16 | onOpenService: React.PropTypes.func.isRequired
17 | },
18 |
19 | /* **************************************************************************/
20 | // Rendering
21 | /* **************************************************************************/
22 |
23 | shouldComponentUpdate (nextProps, nextState) {
24 | return shallowCompare(this, nextProps, nextState)
25 | },
26 |
27 | render () {
28 | const { mailbox, isActiveMailbox, activeService, onOpenService, onContextMenu } = this.props
29 | if (!mailbox.hasEnabledServices) { return null }
30 |
31 | return (
32 |
33 | {mailbox.enabledServies.map((service) => {
34 | return (
35 |
43 | )
44 | })}
45 |
46 | )
47 | }
48 | })
49 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/ui/Sidelist/SidelistItemMailbox/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./SidelistItemMailbox')
2 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/ui/Sidelist/SidelistItemNews.js:
--------------------------------------------------------------------------------
1 | const React = require('react')
2 | const { IconButton, Badge } = require('material-ui')
3 | const Colors = require('material-ui/styles/colors')
4 | const {navigationDispatch} = require('../../Dispatch')
5 | const styles = require('./SidelistStyles')
6 | const ReactTooltip = require('react-tooltip')
7 | const { settingsStore } = require('../../stores/settings')
8 |
9 | module.exports = React.createClass({
10 |
11 | /* **************************************************************************/
12 | // Class
13 | /* **************************************************************************/
14 |
15 | displayName: 'SidelistItemNews',
16 |
17 | /* **************************************************************************/
18 | // Component Lifecycle
19 | /* **************************************************************************/
20 |
21 | componentDidMount () {
22 | settingsStore.listen(this.settingsUpdated)
23 | },
24 |
25 | componentWillUnmount () {
26 | settingsStore.unlisten(this.settingsUpdated)
27 | },
28 |
29 | /* **************************************************************************/
30 | // Data Lifecycle
31 | /* **************************************************************************/
32 |
33 | getInitialState () {
34 | return {
35 | hasUnopenedNewsId: settingsStore.getState().news.hasUnopenedNewsId,
36 | newsLevel: settingsStore.getState().news.newsLevel
37 | }
38 | },
39 |
40 | settingsUpdated (settingsState) {
41 | this.setState({
42 | hasUnopenedNewsId: settingsState.news.hasUnopenedNewsId,
43 | newsLevel: settingsState.news.newsLevel
44 | })
45 | },
46 |
47 | /* **************************************************************************/
48 | // UI Events
49 | /* **************************************************************************/
50 |
51 | handleClick () {
52 | navigationDispatch.openNews()
53 | },
54 |
55 | /* **************************************************************************/
56 | // Rendering
57 | /* **************************************************************************/
58 |
59 | render () {
60 | const { style, ...passProps } = this.props
61 | const { hasUnopenedNewsId, newsLevel } = this.state
62 | return (
63 |
68 |
72 | {hasUnopenedNewsId && newsLevel === 'notify' ? (
73 |
78 | ) : undefined}
79 |
84 |
85 | )
86 | }
87 | })
88 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/ui/Sidelist/SidelistItemSettings.js:
--------------------------------------------------------------------------------
1 | const React = require('react')
2 | const { IconButton } = require('material-ui')
3 | const Colors = require('material-ui/styles/colors')
4 | const {navigationDispatch} = require('../../Dispatch')
5 | const styles = require('./SidelistStyles')
6 | const ReactTooltip = require('react-tooltip')
7 |
8 | module.exports = React.createClass({
9 |
10 | /* **************************************************************************/
11 | // Class
12 | /* **************************************************************************/
13 |
14 | displayName: 'SidelistItemSettings',
15 |
16 | /* **************************************************************************/
17 | // Rendering
18 | /* **************************************************************************/
19 |
20 | /**
21 | * Renders the app
22 | */
23 | render () {
24 | const { style, ...passProps } = this.props
25 | return (
26 |
31 | navigationDispatch.openSettings()}
34 | iconStyle={{ color: Colors.blueGrey400 }}>
35 | settings
36 |
37 |
42 |
43 | )
44 | }
45 | })
46 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/ui/Sidelist/SidelistItemWizard.js:
--------------------------------------------------------------------------------
1 | const React = require('react')
2 | const { IconButton } = require('material-ui')
3 | const Colors = require('material-ui/styles/colors')
4 | const { appWizardActions } = require('../../stores/appWizard')
5 | const styles = require('./SidelistStyles')
6 | const ReactTooltip = require('react-tooltip')
7 |
8 | module.exports = React.createClass({
9 |
10 | /* **************************************************************************/
11 | // Class
12 | /* **************************************************************************/
13 |
14 | displayName: 'SidelistItemWizard',
15 |
16 | /* **************************************************************************/
17 | // Rendering
18 | /* **************************************************************************/
19 |
20 | /**
21 | * Renders the app
22 | */
23 | render () {
24 | const { style, ...passProps } = this.props
25 | return (
26 |
31 | appWizardActions.startWizard()}
34 | iconStyle={{ color: Colors.yellow600 }} />
35 |
40 |
41 | )
42 | }
43 | })
44 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/ui/Sidelist/SidelistMailboxes.js:
--------------------------------------------------------------------------------
1 | const React = require('react')
2 | const { mailboxStore } = require('../../stores/mailbox')
3 | const SidelistItemMailbox = require('./SidelistItemMailbox')
4 |
5 | module.exports = React.createClass({
6 |
7 | /* **************************************************************************/
8 | // Class
9 | /* **************************************************************************/
10 |
11 | displayName: 'SidelistMailboxes',
12 |
13 | /* **************************************************************************/
14 | // Lifecycle
15 | /* **************************************************************************/
16 |
17 | componentDidMount () {
18 | mailboxStore.listen(this.mailboxesChanged)
19 | },
20 |
21 | componentWillUnmount () {
22 | mailboxStore.unlisten(this.mailboxesChanged)
23 | },
24 |
25 | /* **************************************************************************/
26 | // Data lifecycle
27 | /* **************************************************************************/
28 |
29 | getInitialState () {
30 | return {
31 | mailboxIds: mailboxStore.getState().mailboxIds()
32 | }
33 | },
34 |
35 | mailboxesChanged (store) {
36 | this.setState({
37 | mailboxIds: store.mailboxIds()
38 | })
39 | },
40 |
41 | /* **************************************************************************/
42 | // Rendering
43 | /* **************************************************************************/
44 |
45 | shouldComponentUpdate (nextProps, nextState) {
46 | if (JSON.stringify(this.state.mailboxIds) !== JSON.stringify(nextState.mailboxIds)) { return true }
47 | return false
48 | },
49 |
50 | render () {
51 | const { styles, ...passProps } = this.props
52 | const { mailboxIds } = this.state
53 | return (
54 |
55 | {mailboxIds.map((mailboxId, index, arr) => {
56 | return (
57 | )
63 | })}
64 |
65 | )
66 | }
67 | })
68 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/ui/Sidelist/SidelistStyles.less:
--------------------------------------------------------------------------------
1 | .ReactComponent-Sidelist-Scroller {
2 | &::-webkit-scrollbar { display: none; }
3 | }
4 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/ui/Sidelist/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./Sidelist')
2 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/ui/Welcome/Welcome.js:
--------------------------------------------------------------------------------
1 | const React = require('react')
2 | const { mailboxWizardActions } = require('../../stores/mailboxWizard')
3 | const { RaisedButton, FontIcon } = require('material-ui')
4 | const Colors = require('material-ui/styles/colors')
5 |
6 | const styles = {
7 | icon: {
8 | height: 80,
9 | width: 80,
10 | display: 'inline-block',
11 | marginLeft: 10,
12 | marginRight: 10,
13 | backgroundSize: 'contain',
14 | backgroundPosition: 'center',
15 | backgroundRepeat: 'no-repeat',
16 | backgroundImage: 'url("../../icons/app_512.png")'
17 | },
18 | container: {
19 | textAlign: 'center',
20 | overflow: 'auto'
21 | },
22 | heading: {
23 | backgroundColor: Colors.red400,
24 | color: 'white',
25 | paddingTop: 40,
26 | paddingBottom: 20
27 | },
28 | headingTitle: {
29 | fontWeight: '200',
30 | fontSize: '30px',
31 | marginBottom: 0
32 | },
33 | headingSubtitle: {
34 | fontWeight: '200',
35 | fontSize: '18px'
36 | },
37 | setupItem: {
38 | marginTop: 32
39 | },
40 | setupItemExtended: {
41 | fontSize: '85%',
42 | color: Colors.grey600
43 | }
44 | }
45 |
46 | module.exports = React.createClass({
47 |
48 | /* **************************************************************************/
49 | // Class
50 | /* **************************************************************************/
51 |
52 | displayName: 'Welcome',
53 |
54 | /* **************************************************************************/
55 | // Rendering
56 | /* **************************************************************************/
57 |
58 | render () {
59 | return (
60 |
61 |
62 |
63 |
Welcome to WMail
64 |
...the open-source desktop client for Gmail and Google Inbox
65 |
66 |
67 |
mail_outline)}
70 | primary
71 | onClick={() => mailboxWizardActions.openAddMailbox()} />
72 |
73 | Get started by adding your first Gmail or Google Inbox accout
74 |
75 |
76 |
77 | )
78 | }
79 | })
80 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/ui/appContent.less:
--------------------------------------------------------------------------------
1 | #app {
2 | &, .master, .detail, .titlebar {
3 | position: fixed;
4 | top: 0px;
5 | left: 0px;
6 | right: 0px;
7 | bottom: 0px;
8 | }
9 | & {
10 | overflow: hidden;
11 | }
12 |
13 | .titlebar {
14 | bottom: auto;
15 | height: 16px;
16 | -webkit-app-region: drag;
17 | z-index: 100;
18 | }
19 |
20 | .master {
21 | width: 70px;
22 | right: auto;
23 | z-index: 100;
24 | -webkit-app-region: drag;
25 | }
26 |
27 | .detail {
28 | left: 70px;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/ui/appTheme.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const { Spacing, zIndex } = require('material-ui/styles')
4 | const Colors = require('material-ui/styles/colors')
5 | const colorManipulator = require('material-ui/utils/colorManipulator')
6 | const getMuiTheme = require('material-ui/styles/getMuiTheme').default
7 |
8 | module.exports = getMuiTheme({
9 | spacing: Spacing,
10 | zIndex: zIndex,
11 | fontFamily: 'Roboto, sans-serif',
12 | palette: {
13 | primary1Color: Colors.lightBlue600,
14 | primary2Color: Colors.lightBlue500,
15 | primary3Color: Colors.blueGrey100,
16 | accent1Color: Colors.redA200,
17 | accent2Color: Colors.grey100,
18 | accent3Color: Colors.grey600,
19 | textColor: Colors.darkBlack,
20 | alternateTextColor: Colors.white,
21 | canvasColor: Colors.white,
22 | borderColor: Colors.grey300,
23 | disabledColor: colorManipulator.fade(Colors.darkBlack, 0.3),
24 | pickerHeaderColor: Colors.cyan500
25 | }
26 | })
27 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/src/ui/layout.less:
--------------------------------------------------------------------------------
1 | html, body {
2 | position: fixed;
3 | top: 0px;
4 | left: 0px;
5 | right: 0px;
6 | bottom: 0px;
7 | overflow: visible;
8 | }
9 |
--------------------------------------------------------------------------------
/src/scenes/mailboxes/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const ROOT_DIR = path.resolve(path.join(__dirname, '../../../'))
3 | const BIN_DIR = path.join(ROOT_DIR, 'bin')
4 | const OUT_DIR = path.join(BIN_DIR, 'scenes/mailboxes')
5 | const devRequire = (n) => require(path.join(ROOT_DIR, 'node_modules', n))
6 |
7 | const webpack = devRequire('webpack')
8 | const CopyWebpackPlugin = devRequire('copy-webpack-plugin')
9 | const webpackTargetElectronRenderer = devRequire('webpack-target-electron-renderer')
10 | const CleanWebpackPlugin = devRequire('clean-webpack-plugin')
11 |
12 | const isProduction = process.env.NODE_ENV === 'production'
13 |
14 | const options = {
15 | devtool: isProduction ? undefined : (process.env.WEBPACK_DEVTOOL || 'source-map'),
16 | entry: {
17 | mailboxes: [
18 | path.join(__dirname, 'src')
19 | ]
20 | },
21 | output: {
22 | path: OUT_DIR,
23 | filename: 'mailboxes.js'
24 | },
25 | plugins: [
26 | !isProduction ? undefined : new webpack.DefinePlugin({
27 | 'process.env': {
28 | 'NODE_ENV': JSON.stringify('production')
29 | }
30 | }),
31 |
32 | // Clean out our bin dir
33 | new CleanWebpackPlugin([path.relative(BIN_DIR, OUT_DIR)], {
34 | root: BIN_DIR,
35 | verbose: true,
36 | dry: false
37 | }),
38 |
39 | // Ignore electron modules and other modules we don't want to compile in
40 | new webpack.IgnorePlugin(new RegExp('^(electron)$')),
41 |
42 | // Copy our static assets
43 | new CopyWebpackPlugin([
44 | { from: path.join(__dirname, 'src/mailboxes.html'), to: 'mailboxes.html', force: true },
45 | { from: path.join(__dirname, 'src/offline.html'), to: 'offline.html', force: true },
46 | { from: path.join(__dirname, 'src/notification.html'), to: 'notification.html', force: true }
47 | ], {
48 | ignore: [ '.DS_Store' ]
49 | }),
50 |
51 | // Minify in production
52 | isProduction ? new webpack.optimize.UglifyJsPlugin({
53 | compress: { warnings: false }
54 | }) : undefined
55 | ].filter((p) => !!p),
56 | resolve: {
57 | extensions: ['', '.js', '.jsx', '.less', '.css'],
58 | alias: {
59 | shared: path.resolve(path.join(__dirname, '../../shared'))
60 | },
61 | root: [
62 | __dirname,
63 | path.resolve(path.join(__dirname, 'src'))
64 | ],
65 | modulesDirectories: [path.join(__dirname, 'node_modules')]
66 | },
67 | module: {
68 | loaders: [
69 | {
70 | test: /(\.jsx|\.js)$/,
71 | loader: 'babel',
72 | exclude: /node_modules/,
73 | include: [
74 | __dirname,
75 | path.resolve(path.join(__dirname, '../../shared'))
76 | ],
77 | query: {
78 | cacheDirectory: true,
79 | presets: ['react', 'stage-0', 'es2015']
80 | }
81 | },
82 | {
83 | test: /(\.less|\.css)$/,
84 | loaders: ['style', 'css', 'less']
85 | },
86 | {
87 | test: /(\.json)$/,
88 | loader: 'json'
89 | }
90 | ]
91 | }
92 | }
93 |
94 | options.target = webpackTargetElectronRenderer(options)
95 | module.exports = options
96 |
--------------------------------------------------------------------------------
/src/scenes/platform/src/nativeRequire.js:
--------------------------------------------------------------------------------
1 | window.nativeRequire = function (name) {
2 | return require(name)
3 | }
4 |
5 | window.remoteRequire = function (name) {
6 | const path = require('path')
7 | const {remote} = require('electron')
8 | return remote.require(path.join(__dirname, '../../app/app/', name))
9 | }
10 |
11 | window.appNodeModulesRequire = function (name) {
12 | return require('../../app/node_modules/' + name)
13 | }
14 |
15 | window.appPackage = function () {
16 | return require('../../app/package.json')
17 | }
18 |
--------------------------------------------------------------------------------
/src/scenes/platform/src/webviewInjection/Browser/Browser.js:
--------------------------------------------------------------------------------
1 | const KeyboardNavigator = require('./KeyboardNavigator')
2 | const Spellchecker = require('./Spellchecker')
3 | const ContextMenu = require('./ContextMenu')
4 | const { ipcRenderer } = require('electron')
5 |
6 | class Browser {
7 |
8 | /* **************************************************************************/
9 | // Lifecycle
10 | /* **************************************************************************/
11 |
12 | constructor () {
13 | this.keyboardNavigator = new KeyboardNavigator()
14 | this.spellchecker = new Spellchecker()
15 | this.contextMenu = new ContextMenu(this.spellchecker)
16 |
17 | ipcRenderer.on('get-process-memory-info', (evt, data) => {
18 | ipcRenderer.sendToHost({
19 | data: process.getProcessMemoryInfo(),
20 | type: data.__respond__
21 | })
22 | })
23 | }
24 | }
25 |
26 | module.exports = Browser
27 |
--------------------------------------------------------------------------------
/src/scenes/platform/src/webviewInjection/Browser/DictionaryLoad.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs')
2 | const path = require('path')
3 | const LanguageSettings = require('../../../../app/shared/Models/Settings/LanguageSettings')
4 | const enUS = require('../../../../app/node_modules/dictionary-en-us')
5 | const pkg = require('../../../../app/package.json')
6 | const AppDirectory = require('../../../../app/node_modules/appdirectory')
7 |
8 | const appDirectory = new AppDirectory(pkg.name).userData()
9 | const userDictionariesPath = LanguageSettings.userDictionariesPath(appDirectory)
10 |
11 | class DictionaryLoad {
12 | /* **************************************************************************/
13 | // Loader Utils
14 | /* **************************************************************************/
15 |
16 | /**
17 | * Loads a custom dictionary from disk
18 | * @param language: the language to load
19 | * @return promise
20 | */
21 | static _loadCustomDictionary_ (language) {
22 | return new Promise((resolve, reject) => {
23 | const tasks = [
24 | { path: path.join(userDictionariesPath, language + '.aff'), type: 'aff' },
25 | { path: path.join(userDictionariesPath, language + '.dic'), type: 'dic' }
26 | ].map((desc) => {
27 | return new Promise((resolve, reject) => {
28 | fs.readFile(desc.path, (err, data) => {
29 | err ? reject(Object.assign({ error: err }, desc)) : resolve(Object.assign({ data: data }, desc))
30 | })
31 | })
32 | })
33 |
34 | Promise.all(tasks)
35 | .then((loaded) => {
36 | const loadObj = loaded.reduce((acc, load) => {
37 | acc[load.type] = load.data
38 | return acc
39 | }, {})
40 | resolve(loadObj)
41 | }, (err) => {
42 | reject(err)
43 | })
44 | })
45 | }
46 |
47 | /**
48 | * Loads an inbuilt language
49 | * @param language: the language to load
50 | * @return promise
51 | */
52 | static _loadInbuiltDictionary_ (language) {
53 | if (language === 'en_US') {
54 | return new Promise((resolve, reject) => {
55 | enUS((err, load) => {
56 | if (err) {
57 | reject(err)
58 | } else {
59 | resolve({ aff: load.aff, dic: load.dic })
60 | }
61 | })
62 | })
63 | } else {
64 | return Promise.reject(new Error('Unknown Dictionary'))
65 | }
66 | }
67 |
68 | /* **************************************************************************/
69 | // Loaders
70 | /* **************************************************************************/
71 |
72 | /**
73 | * Loads a dictionary
74 | * @param language: the language to load
75 | * @return promise
76 | */
77 | static load (language) {
78 | return new Promise((resolve, reject) => {
79 | DictionaryLoad._loadInbuiltDictionary_(language).then(
80 | (dic) => resolve(dic),
81 | (_err) => {
82 | DictionaryLoad._loadCustomDictionary_(language).then(
83 | (dic) => resolve(dic),
84 | (_err) => reject(new Error('Unknown Dictionary'))
85 | )
86 | }
87 | )
88 | })
89 | }
90 | }
91 |
92 | module.exports = DictionaryLoad
93 |
--------------------------------------------------------------------------------
/src/scenes/platform/src/webviewInjection/Browser/KeyboardNavigator.js:
--------------------------------------------------------------------------------
1 | const injector = require('../injector')
2 |
3 | class KeyboardNavigator {
4 | /* **************************************************************************/
5 | // Lifecycle
6 | /* **************************************************************************/
7 |
8 | constructor () {
9 | injector.injectBodyEvent('keydown', this._handleKeydown_)
10 | }
11 |
12 | /* **************************************************************************/
13 | // Event handlers
14 | /* **************************************************************************/
15 |
16 | _handleKeydown_ (evt) {
17 | if (evt.keyCode === 8) { // Backspace
18 | // Look for reasons to cancel
19 | if (evt.target.tagName === 'INPUT') { return }
20 | if (evt.target.tagName === 'TEXTAREA') { return }
21 | if (evt.target.tagName === 'SELECT') { return }
22 | if (evt.target.tagName === 'OPTION') { return }
23 | if (evt.path.findIndex((e) => e.getAttribute && e.getAttribute('contentEditable') === 'true') !== -1) { return }
24 |
25 | window.history.back()
26 | }
27 | }
28 | }
29 |
30 | module.exports = KeyboardNavigator
31 |
--------------------------------------------------------------------------------
/src/scenes/platform/src/webviewInjection/Google/GinboxApi.js:
--------------------------------------------------------------------------------
1 | const escapeHTML = require('../../../../app/node_modules/escape-html')
2 |
3 | class GinboxApi {
4 |
5 | /**
6 | * @return true if the API is ready
7 | */
8 | static isReady () { return document.readyState === 'complete' }
9 |
10 | /**
11 | * Gets the visible unread count. Ensures that clusters are only counted once/
12 | * May throw a dom exception if things go wrong
13 | * @return the unread count
14 | */
15 | static getVisibleUnreadCount () {
16 | const unread = Array.from(document.querySelectorAll('[data-item-id] [email]')).reduce((acc, elm) => {
17 | const isUnread = elm.tagName !== 'IMG' && window.getComputedStyle(elm).fontWeight === 'bold'
18 | if (isUnread) {
19 | const clusterElm = elm.closest('[data-item-id^="#clusters"]')
20 | if (clusterElm) {
21 | acc.openClusters.add(clusterElm)
22 | } else {
23 | acc.messages.add(elm)
24 | }
25 | }
26 | return acc
27 | }, { messages: new Set(), openClusters: new Set() })
28 | return unread.messages.size + unread.openClusters.size
29 | }
30 |
31 | /**
32 | * Checks if the inbox tab is visble
33 | * May throw a dom exception if things go wrong
34 | * @return true or false
35 | */
36 | static isInboxTabVisible () {
37 | const elm = document.querySelector('nav [role="menuitem"]') // The first item
38 | return window.getComputedStyle(elm).backgroundColor.substr(-4) !== ', 0)'
39 | }
40 |
41 | /**
42 | * Checks if the pinned setting is toggled
43 | * May throw a dom exception if things go wrong
44 | * @return true or false
45 | */
46 | static isInboxPinnedToggled () {
47 | const elm = document.querySelector('[jsaction="global.toggle_pinned"]')
48 | return elm ? elm.getAttribute('aria-pressed') === 'true' : false
49 | }
50 |
51 | /**
52 | * Handles opening the compose ui and prefills relevant items
53 | * @param data: the data that was sent with the event
54 | */
55 | static composeMessage (data) {
56 | const composeButton = document.querySelector('button.y.hC') || document.querySelector('[jsaction="jsl._"]')
57 | if (!composeButton) { return }
58 | composeButton.click()
59 |
60 | setTimeout(() => {
61 | // Grab elements
62 | const bodyEl = document.querySelector('[g_editable="true"][role="textbox"]')
63 | if (!bodyEl) { return }
64 | const dialogEl = bodyEl.closest('[role="dialog"]')
65 | if (!dialogEl) { return }
66 | const recipientEl = dialogEl.querySelector('input') // first input
67 | const subjectEl = dialogEl.querySelector('[jsaction*="subject"]')
68 | let focusableEl
69 |
70 | // Recipient
71 | if (data.recipient && recipientEl) {
72 | recipientEl.value = escapeHTML(data.recipient)
73 | focusableEl = subjectEl
74 | }
75 |
76 | // Subject
77 | if (data.subject && subjectEl) {
78 | subjectEl.value = escapeHTML(data.subject)
79 | focusableEl = bodyEl
80 | }
81 |
82 | // Body
83 | if (data.body && bodyEl) {
84 | bodyEl.innerHTML = escapeHTML(data.body) + bodyEl.innerHTML
85 | const labelEl = bodyEl.parentElement.querySelector('label')
86 | if (labelEl) { labelEl.style.display = 'none' }
87 | focusableEl = bodyEl
88 | }
89 |
90 | if (focusableEl) {
91 | setTimeout(() => focusableEl.focus(), 500)
92 | }
93 | })
94 | }
95 | }
96 |
97 | module.exports = GinboxApi
98 |
--------------------------------------------------------------------------------
/src/scenes/platform/src/webviewInjection/Google/GinboxChangeEmitter.js:
--------------------------------------------------------------------------------
1 | const {ipcRenderer} = require('electron')
2 | const injector = require('../injector')
3 | const GinboxApi = require('./GinboxApi')
4 | const MAX_MESSAGE_HASH_TIME = 1000 * 60 * 10 // 10 mins
5 |
6 | class GinboxChangeEmitter {
7 |
8 | /* **************************************************************************/
9 | // Lifecycle
10 | /* **************************************************************************/
11 |
12 | constructor () {
13 | this.state = {
14 | messageHash: this.currentMessageHash,
15 | messageHashTime: new Date().getTime()
16 | }
17 |
18 | this.latestMessageInterval = setInterval(this.recheckMessageHash.bind(this), 2000)
19 | this.clickThrottle = null
20 | injector.injectBodyEvent('click', this.handleBodyClick)
21 | }
22 |
23 | /* **************************************************************************/
24 | // Properties
25 | /* **************************************************************************/
26 |
27 | get currentMessageHash () {
28 | const topItem = document.querySelector('[data-item-id]')
29 | const topHash = topItem ? topItem.getAttribute('data-item-id') : '_'
30 | const unreadCount = GinboxApi.getVisibleUnreadCount()
31 | return unreadCount + ':' + topHash
32 | }
33 |
34 | /* **************************************************************************/
35 | // Event Handlers
36 | /* **************************************************************************/
37 |
38 | /**
39 | * Re-checks the top message id and fires an unread-count-changed event when
40 | * changed
41 | */
42 | recheckMessageHash () {
43 | const now = new Date().getTime()
44 | const nextMessageHash = this.currentMessageHash
45 | if (this.state.messageHash !== nextMessageHash || now - this.state.messageHashTime > MAX_MESSAGE_HASH_TIME) {
46 | this.state.messageHash = nextMessageHash
47 | this.state.messageHashTime = now
48 | clearTimeout(this.clickThrottle)
49 | ipcRenderer.sendToHost({
50 | type: 'unread-count-changed',
51 | data: { trigger: 'periodic-diff' }
52 | })
53 | }
54 | }
55 |
56 | /**
57 | * Adds a click event into the body which fires off an unread-count-changed
58 | * event rather lazily to catch the message hash not working correctly
59 | */
60 | handleBodyClick () {
61 | clearTimeout(this.clickThrottle)
62 | this.clickThrottle = setTimeout(() => {
63 | ipcRenderer.sendToHost({
64 | type: 'unread-count-changed',
65 | data: { trigger: 'delayed-click' }
66 | })
67 | }, 10000)
68 | }
69 | }
70 |
71 | module.exports = GinboxChangeEmitter
72 |
--------------------------------------------------------------------------------
/src/scenes/platform/src/webviewInjection/Google/GmailApiExtras.js:
--------------------------------------------------------------------------------
1 | const escapeHTML = require('../../../../app/node_modules/escape-html')
2 |
3 | class GmailApiExtras {
4 | /**
5 | * Handles opening the compose ui and prefills relevant items
6 | * @param gmailApi: the gmail api
7 | * @param data: the data that was sent with the event
8 | */
9 | static composeMessage (gmailApi, data) {
10 | if (!gmailApi) { return }
11 |
12 | gmailApi.compose.start_compose()
13 |
14 | if (data.recipient || data.subject || data.body) {
15 | setTimeout(() => {
16 | // Grab elements
17 | const subjectEl = Array.from(document.querySelectorAll('[name="subjectbox"]')).slice(-1)[0]
18 | if (!subjectEl) { return }
19 | const dialogEl = subjectEl.closest('[role="dialog"]')
20 | if (!dialogEl) { return }
21 | const bodyEl = dialogEl.querySelector('[g_editable="true"][role="textbox"]')
22 | const recipientEl = dialogEl.querySelector('[name="to"]')
23 | let focusableEl
24 |
25 | // Recipient
26 | if (data.recipient && recipientEl) {
27 | recipientEl.value = escapeHTML(data.recipient)
28 | focusableEl = subjectEl
29 | }
30 |
31 | // Subject
32 | if (data.subject && subjectEl) {
33 | subjectEl.value = escapeHTML(data.subject)
34 | focusableEl = bodyEl
35 | }
36 |
37 | // Body
38 | if (data.body && bodyEl) {
39 | bodyEl.innerHTML = escapeHTML(data.body) + bodyEl.innerHTML
40 | focusableEl = bodyEl
41 | }
42 |
43 | if (focusableEl) {
44 | setTimeout(() => focusableEl.focus(), 500)
45 | }
46 | })
47 | }
48 | }
49 | }
50 |
51 | module.exports = GmailApiExtras
52 |
--------------------------------------------------------------------------------
/src/scenes/platform/src/webviewInjection/Google/GmailChangeEmitter.js:
--------------------------------------------------------------------------------
1 | const {ipcRenderer} = require('electron')
2 | const MAX_MESSAGE_HASH_TIME = 1000 * 60 * 10 // 10 mins
3 |
4 | class GmailChangeEmitter {
5 |
6 | /* **************************************************************************/
7 | // Lifecycle
8 | /* **************************************************************************/
9 |
10 | /**
11 | * @param gmailApi: the gmail api instance
12 | */
13 | constructor (gmailApi) {
14 | this.gmailApi = gmailApi
15 | this.state = {
16 | count: this.gmailApi.get.unread_inbox_emails(),
17 | countTime: new Date().getTime()
18 | }
19 |
20 | this.gmailApi.observe.on('http_event', this.handleHTTPEvent.bind(this))
21 | }
22 |
23 | /* **************************************************************************/
24 | // Event Handlers
25 | /* **************************************************************************/
26 |
27 | /**
28 | * Handles gmail firing a http event by checking if the unread count has changed
29 | * and passing this event up across the bridge
30 | */
31 | handleHTTPEvent () {
32 | const now = new Date().getTime()
33 | const nextCount = this.gmailApi.get.unread_inbox_emails()
34 | if (this.state.count !== nextCount || now - this.state.messageHashTime > MAX_MESSAGE_HASH_TIME) {
35 | this.state.count = nextCount
36 | this.state.countTime = now
37 | ipcRenderer.sendToHost({
38 | type: 'unread-count-changed',
39 | data: { trigger: 'http-event' }
40 | })
41 | }
42 | }
43 | }
44 |
45 | module.exports = GmailChangeEmitter
46 |
--------------------------------------------------------------------------------
/src/scenes/platform/src/webviewInjection/Google/GoogleService.js:
--------------------------------------------------------------------------------
1 | const injector = require('../injector')
2 | const Browser = require('../Browser/Browser')
3 | const WMail = require('../WMail/WMail')
4 |
5 | class GoogleService {
6 |
7 | /* **************************************************************************/
8 | // Lifecycle
9 | /* **************************************************************************/
10 |
11 | constructor () {
12 | this.browser = new Browser()
13 | this.wmail = new WMail()
14 |
15 | injector.injectStyle(`
16 | a[href*="/SignOutOptions"] {
17 | visibility: hidden !important;
18 | }
19 | `)
20 | }
21 | }
22 |
23 | module.exports = GoogleService
24 |
--------------------------------------------------------------------------------
/src/scenes/platform/src/webviewInjection/Google/GoogleWindowOpen.js:
--------------------------------------------------------------------------------
1 | const ipcRenderer = require('electron').ipcRenderer
2 |
3 | class GoogleWindowOpen {
4 | /* **************************************************************************/
5 | // Lifecycle
6 | /* **************************************************************************/
7 |
8 | constructor () {
9 | this.gmailApi = undefined
10 |
11 | // Inject into the main window
12 | this.injectWindow(window)
13 | document.addEventListener('DOMContentLoaded', function () {
14 | const frames = document.querySelectorAll('iframe')
15 | for (let i = 0; i < frames.length; i++) {
16 | try {
17 | this.injectWindow(frames[i].contentWindow)
18 | } catch (ex) { }
19 | }
20 | }, false)
21 | }
22 |
23 | /* **************************************************************************/
24 | // Utils
25 | /* **************************************************************************/
26 |
27 | /**
28 | * @param url: the url to get the qs argument from
29 | * @param key: the key of the value to get
30 | * @param defaultValue=undefined: the default value to return
31 | * @return the string value
32 | */
33 | getQSArg (url, key, defaultValue = undefined) {
34 | const regex = new RegExp('[\\?&]' + key + '=([^]*)')
35 | const results = regex.exec(url)
36 | return results === null ? defaultValue : results[1]
37 | }
38 |
39 | /* **************************************************************************/
40 | // Injection
41 | /* **************************************************************************/
42 |
43 | /**
44 | * Injects the window.open polyfill. Gmail opens new windows. They do....
45 | * w = window.open("", "_blank", "")
46 | * w.document.write('')
47 | * this proxies that request and sends it up to be opened
48 | * @param w: the window to inject into
49 | */
50 | injectWindow (w) {
51 | const defaultfn = w.open
52 | const handlerInst = this
53 | w.open = function () {
54 | // Open message in new window -- old style
55 | if (arguments[0] === '' && arguments[1] === '_blank') {
56 | return {
57 | document: {
58 | write: function (value) {
59 | const parser = new window.DOMParser()
60 | const xml = parser.parseFromString(value, 'text/xml')
61 | if (xml.firstChild && xml.firstChild.getAttribute('HTTP-EQUIV') === 'refresh') {
62 | const content = xml.firstChild.getAttribute('content')
63 | const url = content.replace('0; url=', '')
64 | ipcRenderer.sendToHost({ type: 'js-new-window', url: url })
65 | }
66 | }
67 | }
68 | }
69 | } else if (handlerInst.gmailApi && arguments[0].indexOf('ui=2') !== -1 && arguments[0].indexOf('view=btop') !== -1) {
70 | const ik = handlerInst.gmailApi.tracker.ik
71 | const currentUrlMsgId = window.location.hash.split('/').pop().replace(/#/, '').split('?')[0]
72 | const th = handlerInst.getQSArg(arguments[0], 'th', currentUrlMsgId)
73 |
74 | ipcRenderer.sendToHost({
75 | type: 'js-new-window',
76 | url: 'https://mail.google.com/mail?ui=2&view=lg&ik=' + ik + '&msg=' + th
77 | })
78 | return { closed: false, focus: function () { } }
79 | } else {
80 | return defaultfn.apply(this, Array.from(arguments))
81 | }
82 | }
83 | }
84 | }
85 |
86 | module.exports = GoogleWindowOpen
87 |
--------------------------------------------------------------------------------
/src/scenes/platform/src/webviewInjection/WMail/CustomCode.js:
--------------------------------------------------------------------------------
1 | const ipcRenderer = require('electron').ipcRenderer
2 | const injector = require('../injector')
3 |
4 | class CustomCode {
5 | constructor () {
6 | ipcRenderer.on('inject-custom-content', this._handleInject_.bind(this))
7 | }
8 |
9 | _handleInject_ (evt, data) {
10 | if (data.js) {
11 | injector.injectJavaScript(data.js)
12 | }
13 | if (data.css) {
14 | injector.injectStyle(data.css)
15 | }
16 | }
17 | }
18 |
19 | module.exports = CustomCode
20 |
--------------------------------------------------------------------------------
/src/scenes/platform/src/webviewInjection/WMail/WMail.js:
--------------------------------------------------------------------------------
1 | const CustomCode = require('./CustomCode')
2 |
3 | class WMail {
4 | constructor () {
5 | this.customCode = new CustomCode()
6 | }
7 | }
8 |
9 | module.exports = WMail
10 |
--------------------------------------------------------------------------------
/src/scenes/platform/src/webviewInjection/elconsole.js:
--------------------------------------------------------------------------------
1 | const ipcRenderer = require('electron').ipcRenderer
2 |
3 | class ELConsole {
4 | /**
5 | * Logs the supplied arguments and also logs them to the parent frame
6 | */
7 | log () {
8 | ipcRenderer.sendToHost({
9 | type: 'elevated-log',
10 | messages: Array.from(arguments)
11 | })
12 | console.log.apply(this, Array.from(arguments))
13 | }
14 |
15 | /**
16 | * Logs the supplied arguments as errors and also logs them to the parent frame
17 | */
18 | error () {
19 | ipcRenderer.sendToHost({
20 | type: 'elevated-error',
21 | messages: Array.from(arguments)
22 | })
23 | console.error.apply(this, Array.from(arguments))
24 | }
25 | }
26 |
27 | module.exports = new ELConsole()
28 |
--------------------------------------------------------------------------------
/src/scenes/platform/src/webviewInjection/googleMail.js:
--------------------------------------------------------------------------------
1 | const elconsole = require('./elconsole')
2 | try {
3 | const GoogleMail = require('./Google/GoogleMail')
4 | /*eslint-disable */
5 | const googleMail = new GoogleMail()
6 | /*eslint-enable */
7 | } catch (ex) {
8 | elconsole.error('Error', ex)
9 | }
10 |
--------------------------------------------------------------------------------
/src/scenes/platform/src/webviewInjection/googleService.js:
--------------------------------------------------------------------------------
1 | const elconsole = require('./elconsole')
2 | try {
3 | const GoogleService = require('./Google/GoogleService')
4 | /*eslint-disable */
5 | const googleService = new GoogleService()
6 | /*eslint-enable */
7 | } catch (ex) {
8 | elconsole.error('Error', ex)
9 | }
10 |
--------------------------------------------------------------------------------
/src/scenes/platform/src/webviewInjection/injector.js:
--------------------------------------------------------------------------------
1 | class Injector {
2 | /* **************************************************************************/
3 | // Lifecycle
4 | /* **************************************************************************/
5 |
6 | constructor () {
7 | this.scripts = { pending: [], interval: null }
8 | this.bodyEvents = { pending: [], interval: null }
9 | }
10 |
11 | /* **************************************************************************/
12 | // Script injection
13 | /* **************************************************************************/
14 |
15 | /**
16 | * Injects an element into the dom
17 | * @param el: the element to inject
18 | * @param callback=undefined: executed when injected
19 | */
20 | injectScriptElement (el, callback) {
21 | if (document.head) {
22 | document.head.appendChild(el)
23 | if (callback) { callback() }
24 | } else {
25 | this.scripts.pending.push([el, callback])
26 | if (this.scripts.interval === null) {
27 | this.scripts.interval = setInterval(() => {
28 | if (document.head) {
29 | clearInterval(this.scripts.interval)
30 | this.scripts.interval = null
31 | this.scripts.pending.forEach((inf) => {
32 | document.head.appendChild(inf[0])
33 | if (inf[1]) { inf[1]() }
34 | })
35 | this.scripts.pending = []
36 | }
37 | }, 10)
38 | }
39 | }
40 | }
41 |
42 | /**
43 | * Injects javascript into the head
44 | * @param js: the js code to inject
45 | * @param callback=undefined: executed when injected
46 | */
47 | injectJavaScript (js, callback) {
48 | const el = document.createElement('script')
49 | el.type = 'text/javascript'
50 | el.innerHTML = js
51 | this.injectScriptElement(el, callback)
52 | }
53 |
54 | /**
55 | * Injects a stylesheet into the head
56 | * @param css: the css code to inject
57 | * @param callback=undefined: executed when injected
58 | */
59 | injectStyle (css, callback) {
60 | const el = document.createElement('style')
61 | el.innerHTML = css
62 | this.injectScriptElement(el, callback)
63 | }
64 |
65 | /* **************************************************************************/
66 | // Event injection
67 | /* **************************************************************************/
68 |
69 | /**
70 | * Injects a body event listener
71 | * @param eventName: the name of the event
72 | * @param fn: the function to call
73 | */
74 | injectBodyEvent (eventName, fn) {
75 | if (document.body) {
76 | document.body.addEventListener(eventName, fn, false)
77 | } else {
78 | this.bodyEvents.pending.push([eventName, fn])
79 | if (this.bodyEvents.interval === null) {
80 | this.bodyEvents.interval = setInterval(() => {
81 | if (document.body) {
82 | clearInterval(this.bodyEvents.interval)
83 | this.bodyEvents.interval = null
84 | this.bodyEvents.pending.forEach((inf) => {
85 | document.body.addEventListener(inf[0], inf[1], false)
86 | })
87 | this.bodyEvents.pending = []
88 | }
89 | }, 100)
90 | }
91 | }
92 | }
93 | }
94 |
95 | module.exports = new Injector()
96 |
--------------------------------------------------------------------------------
/src/scenes/platform/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const ROOT_DIR = path.resolve(path.join(__dirname, '../../../'))
3 | const BIN_DIR = path.join(ROOT_DIR, 'bin')
4 | const OUT_DIR = path.join(BIN_DIR, 'scenes/platform')
5 | const devRequire = (n) => require(path.join(ROOT_DIR, 'node_modules', n))
6 |
7 | const CleanWebpackPlugin = devRequire('clean-webpack-plugin')
8 | const CopyWebpackPlugin = devRequire('copy-webpack-plugin')
9 |
10 | module.exports = {
11 | output: {
12 | path: OUT_DIR,
13 | filename: '__.js'
14 | },
15 | plugins: [
16 | new CleanWebpackPlugin([path.relative(BIN_DIR, OUT_DIR)], {
17 | root: BIN_DIR,
18 | verbose: true,
19 | dry: false
20 | }),
21 | new CopyWebpackPlugin([
22 | { from: path.join(__dirname, 'src'), to: '', force: true }
23 | ], {
24 | ignore: [ '.DS_Store' ]
25 | })
26 | ]
27 | }
28 |
--------------------------------------------------------------------------------
/src/shared/Models/Mailbox/MailboxServices.js:
--------------------------------------------------------------------------------
1 | // Try to keep these generic to support n-types of account
2 | module.exports = Object.freeze({
3 | DEFAULT: 'mail',
4 | STORAGE: 'storage', // google drive
5 | CONTACTS: 'contacts', // google contacts
6 | NOTES: 'notes', // google keep
7 | CALENDAR: 'calendar', // google calendar
8 | COMMUNICATION: 'communication' // google hangouts
9 | })
10 |
--------------------------------------------------------------------------------
/src/shared/Models/Mailbox/MailboxTypes.js:
--------------------------------------------------------------------------------
1 | module.exports = Object.freeze({
2 | GMAIL: 'gmail',
3 | GINBOX: 'ginbox'
4 | })
5 |
--------------------------------------------------------------------------------
/src/shared/Models/Mailbox/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | Google: require('./Google'),
3 | Mailbox: require('./Mailbox')
4 | }
5 |
--------------------------------------------------------------------------------
/src/shared/Models/Model.js:
--------------------------------------------------------------------------------
1 | class Model {
2 |
3 | /* **************************************************************************/
4 | // Lifecycle
5 | /* **************************************************************************/
6 |
7 | constructor (data) {
8 | this.__data__ = Object.freeze(JSON.parse(JSON.stringify(data)))
9 | }
10 |
11 | /* **************************************************************************/
12 | // Cloning
13 | /* **************************************************************************/
14 |
15 | /**
16 | * Clones a copy of the data
17 | * @return a new copy of the data copied deeply.
18 | */
19 | cloneData () {
20 | return JSON.parse(JSON.stringify(this.__data__))
21 | }
22 |
23 | /**
24 | * Clones the data and merges the given changeset
25 | * @param changeset: { k->v } to merge. Only 1 level deep
26 | */
27 | changeData (changeset) {
28 | return Object.assign(this.cloneData(), changeset)
29 | }
30 |
31 | /* **************************************************************************/
32 | // Utils
33 | /* **************************************************************************/
34 |
35 | /**
36 | * @param the key to get
37 | * @param defaultValue: the value to return if undefined
38 | * @return the value or defaultValue
39 | */
40 | _value_ (key, defaultValue) {
41 | return this.__data__[key] === undefined ? defaultValue : this.__data__[key]
42 | }
43 | }
44 |
45 | module.exports = Model
46 |
--------------------------------------------------------------------------------
/src/shared/Models/Settings/AppSettings.js:
--------------------------------------------------------------------------------
1 | const Model = require('../Model')
2 |
3 | class AppSettings extends Model {
4 | get ignoreGPUBlacklist () { return this._value_('ignoreGPUBlacklist', true) }
5 | get disableSmoothScrolling () { return this._value_('disableSmoothScrolling', false) }
6 | get enableUseZoomForDSF () { return this._value_('enableUseZoomForDSF', true) }
7 | get checkForUpdates () { return this._value_('checkForUpdates', true) }
8 | get hasSeenAppWizard () { return this._value_('hasSeenAppWizard', false) }
9 | }
10 |
11 | module.exports = AppSettings
12 |
--------------------------------------------------------------------------------
/src/shared/Models/Settings/LanguageSettings.js:
--------------------------------------------------------------------------------
1 | const Model = require('../Model')
2 | const path = require('path')
3 |
4 | class LanguageSettings extends Model {
5 |
6 | /* ****************************************************************************/
7 | // Class
8 | /* ****************************************************************************/
9 |
10 | /**
11 | * @param root: the root directory of the app
12 | * @return the paths to add user dictionaries
13 | */
14 | static userDictionariesPath (root) { return path.join(root, 'user_dictionaries') }
15 | static get defaultSpellcheckerLanguage () { return 'en_US' }
16 |
17 | /* ****************************************************************************/
18 | // Properties: Spellchecker
19 | /* ****************************************************************************/
20 |
21 | get spellcheckerEnabled () { return this._value_('spellcheckerEnabled', true) }
22 | get spellcheckerLanguage () { return this._value_('spellcheckerLanguage', LanguageSettings.defaultSpellcheckerLanguage) }
23 | get hasSecondarySpellcheckerLanguage () { return this.secondarySpellcheckerLanguage === null }
24 | get secondarySpellcheckerLanguage () { return this._value_('secondarySpellcheckerLanguage', null) }
25 |
26 | }
27 |
28 | module.exports = LanguageSettings
29 |
--------------------------------------------------------------------------------
/src/shared/Models/Settings/NewsSettings.js:
--------------------------------------------------------------------------------
1 | const Model = require('../Model')
2 |
3 | class NewsSettings extends Model {
4 | get newsId () { return this._value_('newsId', 0) }
5 | get newsLevel () { return this._value_('newsLevel', 'none') }
6 | get newsFeed () { return this._value_('newsFeed', undefined) }
7 | get openedNewsId () { return this._value_('openedNewsId', 0) }
8 |
9 | get hasUpdateInfo () { return !!this.newsFeed }
10 | get hasUnopenedNewsId () { return this.openedNewsId < this.newsId }
11 | get showNewsInSidebar () { return this._value_('showNewsInSidebar', true) }
12 | }
13 |
14 | module.exports = NewsSettings
15 |
--------------------------------------------------------------------------------
/src/shared/Models/Settings/OSSettings.js:
--------------------------------------------------------------------------------
1 | const Model = require('../Model')
2 |
3 | class OSSettings extends Model {
4 | get alwaysAskDownloadLocation () { return this._value_('alwaysAskDownloadLocation', true) }
5 | get defaultDownloadLocation () { return this._value_('defaultDownloadLocation', undefined) }
6 | get notificationsEnabled () { return this._value_('notificationsEnabled', true) }
7 | get notificationsSilent () { return this._value_('notificationsSilent', false) }
8 | get openLinksInBackground () { return this._value_('openLinksInBackground', false) }
9 | }
10 |
11 | module.exports = OSSettings
12 |
--------------------------------------------------------------------------------
/src/shared/Models/Settings/ProxySettings.js:
--------------------------------------------------------------------------------
1 | const Model = require('../Model')
2 |
3 | class ProxySettings extends Model {
4 | get enabled () { return this._value_('enabled', false) }
5 | get host () { return this.__data__.host }
6 | get port () { return this.__data__.port }
7 | get url () { return this.host + ':' + this.port }
8 | }
9 |
10 | module.exports = ProxySettings
11 |
--------------------------------------------------------------------------------
/src/shared/Models/Settings/SettingsIdent.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | SEGMENTS: {
3 | APP: 'app',
4 | LANGUAGE: 'language',
5 | NEWS: 'news',
6 | OS: 'os',
7 | PROXY: 'proxy',
8 | TRAY: 'tray',
9 | UI: 'ui'
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/shared/Models/Settings/TraySettings.js:
--------------------------------------------------------------------------------
1 | const Model = require('../Model')
2 |
3 | class TraySettings extends Model {
4 |
5 | /* **************************************************************************/
6 | // Lifecycle
7 | /* **************************************************************************/
8 |
9 | /**
10 | * @param data: the tray data
11 | * @param themedDefaults: the default values for the tray
12 | */
13 | constructor (data, themedDefaults = {}) {
14 | super(data)
15 | this.__themedDefaults__ = Object.freeze(Object.assign({}, themedDefaults))
16 | }
17 |
18 | /* **************************************************************************/
19 | // Properties
20 | /* **************************************************************************/
21 |
22 | get show () { return this._value_('show', true) }
23 | get showUnreadCount () { return this._value_('showUnreadCount', true) }
24 | get readColor () { return this._value_('readColor', this.__themedDefaults__.readColor) }
25 | get readBackgroundColor () { return this._value_('readBackgroundColor', this.__themedDefaults__.readBackgroundColor) }
26 | get unreadColor () { return this._value_('unreadColor', this.__themedDefaults__.unreadColor) }
27 | get unreadBackgroundColor () { return this._value_('unreadBackgroundColor', this.__themedDefaults__.unreadBackgroundColor) }
28 |
29 | get dpiMultiplier () {
30 | let defaultValue = 1
31 | try {
32 | defaultValue = window.devicePixelRatio
33 | } catch (ex) { }
34 | return this._value_('dpiMultiplier', defaultValue)
35 | }
36 | }
37 |
38 | module.exports = TraySettings
39 |
--------------------------------------------------------------------------------
/src/shared/Models/Settings/UISettings.js:
--------------------------------------------------------------------------------
1 | const Model = require('../Model')
2 |
3 | class UISettings extends Model {
4 | get showTitlebar () { return this._value_('showTitlebar', false) }
5 | get showAppBadge () { return this._value_('showAppBadge', true) }
6 | get showTitlebarCount () { return this._value_('showTitlebarCount', true) }
7 | get showAppMenu () { return this._value_('showAppMenu', process.platform !== 'win32') }
8 | get sidebarEnabled () { return this._value_('sidebarEnabled', true) }
9 | get openHidden () { return this._value_('openHidden', false) }
10 | }
11 |
12 | module.exports = UISettings
13 |
--------------------------------------------------------------------------------
/src/shared/Models/Settings/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | AppSettings: require('./AppSettings'),
3 | LanguageSettings: require('./LanguageSettings'),
4 | NewsSettings: require('./NewsSettings'),
5 | OSSettings: require('./OSSettings'),
6 | ProxySettings: require('./ProxySettings'),
7 | TraySettings: require('./TraySettings'),
8 | UISettings: require('./UISettings'),
9 |
10 | SettingsIdent: require('./SettingsIdent')
11 | }
12 |
--------------------------------------------------------------------------------
/src/shared/Models/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | Mailbox: require('./Mailbox'),
3 | Settings: require('./Settings')
4 | }
5 |
--------------------------------------------------------------------------------
/src/shared/b64Assets.js:
--------------------------------------------------------------------------------
1 | module.exports = Object.freeze({
2 | BLANK_PNG: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAAtJREFUCB1jYAACAAAFAAGNu5vzAAAAAElFTkSuQmCC',
3 | MAIL_SVG: 'data:image/svg+xml;base64,PHN2ZyBmaWxsPSIjMDAwMDAwIiBoZWlnaHQ9IjI0IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSIyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4gICAgPHBhdGggZD0iTTAgMGgyNHYyNEgweiIgZmlsbD0ibm9uZSIvPiAgICA8cGF0aCBkPSJNMjAgNEg0Yy0xLjEgMC0xLjk5LjktMS45OSAyTDIgMThjMCAxLjEuOSAyIDIgMmgxNmMxLjEgMCAyLS45IDItMlY2YzAtMS4xLS45LTItMi0yem0wIDE0SDRWOGw4IDUgOC01djEwem0tOC03TDQgNmgxNmwtOCA1eiIvPjwvc3ZnPg=='
4 | })
5 |
--------------------------------------------------------------------------------
/src/shared/constants.js:
--------------------------------------------------------------------------------
1 | module.exports = Object.freeze({
2 | APP_ID: 'tombeverley.wmail',
3 |
4 | MAILBOX_INDEX_KEY: '__index__',
5 | MAILBOX_SLEEP_WAIT: 1000 * 30, // 30 seconds
6 |
7 | WEB_URL: 'https://thomas101.github.io/wmail/',
8 | GITHUB_URL: 'https://github.com/Thomas101/wmail/',
9 | GITHUB_ISSUE_URL: 'https://github.com/Thomas101/wmail/issues',
10 | UPDATE_DOWNLOAD_URL: 'http://thomas101.github.io/wmail/download',
11 | UPDATE_CHECK_URL: 'https://thomas101.github.io/wmail/version.json',
12 | PRIVACY_URL: 'https://thomas101.github.io/wmail/privacy',
13 | USER_SCRIPTS_WEB_URL: 'https://github.com/Thomas101/wmail-user-scripts',
14 | UPDATE_CHECK_INTERVAL: 1000 * 60 * 60 * 24, // 24 hours
15 |
16 | GMAIL_PROFILE_SYNC_MS: 1000 * 60 * 60, // 60 mins
17 | GMAIL_UNREAD_SYNC_MS: 1000 * 60, // 60 seconds
18 | GMAIL_NOTIFICATION_MAX_MESSAGE_AGE_MS: 1000 * 60 * 60 * 2, // 2 hours
19 | GMAIL_NOTIFICATION_FIRST_RUN_GRACE_MS: 1000 * 30, // 30 seconds
20 |
21 | REFOCUS_MAILBOX_INTERVAL_MS: 300,
22 |
23 | DB_EXTENSION: 'wmaildb',
24 | DB_WRITE_DELAY_MS: 500 // 0.5secs
25 | })
26 |
--------------------------------------------------------------------------------
/src/shared/dictionaryExcludes.js:
--------------------------------------------------------------------------------
1 | const EN = new Set([
2 | 'ain',
3 | 'can',
4 | 'couldn',
5 | 'didn',
6 | 'don',
7 | 'doesn',
8 | 'hadn',
9 | 'hasn',
10 | 'havn',
11 | 'isn',
12 | 'mightn',
13 | 'mustn',
14 | 'needn',
15 | 'oughtn',
16 | 'shan',
17 | 'shouldn',
18 | 'wasn',
19 | 'weren',
20 | 'won',
21 | 'wouldn'
22 | ])
23 |
24 | module.exports = {
25 | en_AU: EN,
26 | en_CA: EN,
27 | en_GB: EN,
28 | en_US: EN,
29 | en_ZA: EN
30 | }
31 |
--------------------------------------------------------------------------------
/src/shared/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | b64Assets: require('./b64Assets'),
3 | credentials: require('./credentials'),
4 | constants: require('./constants'),
5 | Models: require('./Models'),
6 | dictionaries: require('./dictionaries.js')
7 | }
8 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | function getArg (name, defaultValue) {
2 | const arg = process.argv.find((a) => a.indexOf(name) === 0)
3 | return arg === undefined ? defaultValue : arg
4 | }
5 |
6 | // Production
7 | if (getArg('-p') !== undefined) {
8 | console.log('[PRODUCTION BUILD]')
9 | process.env.NODE_ENV = 'production'
10 | } else {
11 | console.log('[DEVELOPMENT BUILD]')
12 | }
13 |
14 | // Cheap / expensive source maps
15 | if (getArg('--fast') !== undefined) {
16 | console.log('[CHEAP SOURCEMAPS]')
17 | process.env.WEBPACK_DEVTOOL = 'eval-cheap-module-source-map'
18 | } else {
19 | console.log('[FULL SOURCEMAPS]')
20 | }
21 |
22 | // Task export
23 | const task = getArg('--task=', '--task=all').substr(7)
24 | if (task === 'app') {
25 | console.log('[TASK=app]')
26 | module.exports = [ require('./src/app/webpack.config.js') ]
27 | } else if (task === 'mailboxes') {
28 | console.log('[TASK=mailboxes]')
29 | module.exports = [ require('./src/scenes/mailboxes/webpack.config.js') ]
30 | } else if (task === 'platform') {
31 | console.log('[TASK=platform]')
32 | module.exports = [ require('./src/scenes/platform/webpack.config.js') ]
33 | } else if (task === 'assets') {
34 | console.log('[TASK=assets]')
35 | module.exports = [ require('./assets/webpack.config.js') ]
36 | } else {
37 | console.log('[TASK=all]')
38 | module.exports = [
39 | require('./assets/webpack.config.js'),
40 | require('./src/app/webpack.config.js'),
41 | require('./src/scenes/mailboxes/webpack.config.js'),
42 | require('./src/scenes/platform/webpack.config.js')
43 | ]
44 | }
45 |
--------------------------------------------------------------------------------