diff --git a/.all-contributorsrc b/.all-contributorsrc index 863a9182..49ed6583 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -127,6 +127,24 @@ "contributions": [ "code" ] + }, + { + "login": "jonathanurias96", + "name": "jonathanurias96", + "avatar_url": "https://avatars2.githubusercontent.com/u/57416786?v=4", + "profile": "https://github.com/jonathanurias96", + "contributions": [ + "code" + ] + }, + { + "login": "falecci", + "name": "Federico Alecci", + "avatar_url": "https://avatars2.githubusercontent.com/u/17703824?v=4", + "profile": "https://falecci.dev", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 7, diff --git a/README.md b/README.md index c416b592..e3e9b3c1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Responsively App [![Twitter Follow](https://img.shields.io/twitter/follow/ResponsivelyApp?style=social)](https://twitter.com/ResponsivelyApp) -[![All Contributors](https://img.shields.io/badge/all_contributors-13-orange.svg?style=flat-square)](#contributors-) +[![All Contributors](https://img.shields.io/badge/all_contributors-15-orange.svg?style=flat-square)](#contributors-) [![Gitter chat](https://img.shields.io/gitter/room/badges/shields.svg)](https://gitter.im/responsively-app) [![xscode](https://img.shields.io/badge/Available%20on-xs%3Acode-blue?style=?style=plastic&logo=appveyor&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAZQTFRF////////VXz1bAAAAAJ0Uk5T/wDltzBKAAAAlUlEQVR42uzXSwqAMAwE0Mn9L+3Ggtgkk35QwcnSJo9S+yGwM9DCooCbgn4YrJ4CIPUcQF7/XSBbx2TEz4sAZ2q1RAECBAiYBlCtvwN+KiYAlG7UDGj59MViT9hOwEqAhYCtAsUZvL6I6W8c2wcbd+LIWSCHSTeSAAECngN4xxIDSK9f4B9t377Wd7H5Nt7/Xz8eAgwAvesLRjYYPuUAAAAASUVORK5CYII=)](https://xscode.com/manojvivek/responsively-app) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](https://github.com/manojVivek/responsively-app/issues) @@ -82,6 +82,10 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
Ryan Pais

💻 ⚠️
Jonathan

💻
Gema Anggada ✌︎

💻 +
jonathanurias96

💻 + + +
Federico Alecci

💻 diff --git a/desktop-app/app/Routes.js b/desktop-app/app/Routes.js index 2ea80091..39426d8f 100644 --- a/desktop-app/app/Routes.js +++ b/desktop-app/app/Routes.js @@ -5,7 +5,7 @@ import App from './containers/App'; import Browser from './containers/Browser'; import LeftIconsPaneContainer from './containers/LeftIconsPaneContainer'; import styles from './layout.css'; -import StatusBar from './components/StatusBar'; +import StatusBarContainer from './containers/StatusBarContainer'; import DevToolResizerContainer from './containers/DevToolResizerContainer'; export default () => ( @@ -20,7 +20,7 @@ export default () => ( - + ); diff --git a/desktop-app/app/actions/bookmarks.js b/desktop-app/app/actions/bookmarks.js index 87cc53aa..a3513a3b 100644 --- a/desktop-app/app/actions/bookmarks.js +++ b/desktop-app/app/actions/bookmarks.js @@ -2,11 +2,11 @@ export const TOGGLE_BOOKMARK = 'TOGGLE_BOOKMARK'; export const EDIT_BOOKMARK = 'EDIT_BOOKMARK'; // Add or Remove an URL from the bookmark list -export function toggleBookmarkUrl(url, title = null) { +export function toggleBookmarkUrl(url, pageMeta = {}) { return { type: TOGGLE_BOOKMARK, url, - title + title: pageMeta.title, }; } @@ -16,6 +16,6 @@ export function editBookmark(bookmark, {title, url}) { type: EDIT_BOOKMARK, title, url, - bookmark - } + bookmark, + }; } diff --git a/desktop-app/app/actions/browser.js b/desktop-app/app/actions/browser.js index 9253419f..217df926 100644 --- a/desktop-app/app/actions/browser.js +++ b/desktop-app/app/actions/browser.js @@ -22,6 +22,7 @@ import {DEVTOOLS_MODES} from '../constants/previewerLayouts'; import console from 'electron-timber'; export const NEW_ADDRESS = 'NEW_ADDRESS'; +export const NEW_PAGE_META_FIELD = 'NEW_PAGE_META_FIELD'; export const NEW_DEV_TOOLS_CONFIG = 'NEW_DEV_TOOLS_CONFIG'; export const NEW_HOMEPAGE = 'NEW_HOMEPAGE'; export const NEW_ZOOM_LEVEL = 'NEW_ZOOM_LEVEL'; @@ -47,6 +48,14 @@ export function newAddress(address) { }; } +export function newPageMetaField(name, value) { + return { + type: NEW_PAGE_META_FIELD, + name, + value, + }; +} + export function newWindowSize(size) { return { type: NEW_WINDOW_SIZE, @@ -183,6 +192,12 @@ export function onAddressChange(newURL, force) { }; } +export function onPageMetaFieldUpdate(name, value) { + return (dispatch: Dispatch, getState: RootStateType) => { + dispatch(newPageMetaField(name, value)); + }; +} + function isHashOnlyChange(newURL, oldURL) { if (!newURL || !oldURL) { return false; diff --git a/desktop-app/app/actions/statusBar.js b/desktop-app/app/actions/statusBar.js new file mode 100644 index 00000000..dee862dc --- /dev/null +++ b/desktop-app/app/actions/statusBar.js @@ -0,0 +1,14 @@ +// @flow +import {statusBarSettings} from '../settings/statusBarSettings'; + +export const TOGGLE_STATUS_BAR_VISIBILITY = 'TOGGLE_STATUS_BAR_VISIBILITY'; + +export function toggleStatusBarVisibility() { + const newVisibility = !statusBarSettings.getVisibility(); + statusBarSettings.setVisibility(newVisibility); + + return { + type: TOGGLE_STATUS_BAR_VISIBILITY, + visible: newVisibility, + }; +} diff --git a/desktop-app/app/app-updater.js b/desktop-app/app/app-updater.js index a5baf107..58063752 100644 --- a/desktop-app/app/app-updater.js +++ b/desktop-app/app/app-updater.js @@ -1,10 +1,9 @@ import {autoUpdater} from 'electron-updater'; import log from 'electron-log'; -import {pkg} from './utils/generalUtils'; -const EventEmitter = require('events').EventEmitter; +const {EventEmitter} = require('events'); -const AppUpdaterState = { +const AppUpdaterStatus = { Idle: 'idle', Checking: 'checking', NoUpdate: 'noUpdate', @@ -12,46 +11,52 @@ const AppUpdaterState = { Downloaded: 'downloaded', } -Object.freeze(AppUpdaterState); +Object.freeze(AppUpdaterStatus); class AppUpdater extends EventEmitter { - state: AppUpdaterState; + status: string; - readyStates: AppUpdaterState[]; + timerId = null; constructor() { super(); log.transports.file.level = 'info'; autoUpdater.logger = log; - this.state = AppUpdaterState.Idle; - this.readyStates = [AppUpdaterState.Idle, AppUpdaterState.NoUpdate, AppUpdaterState.Downloaded]; + this.status = AppUpdaterStatus.Idle; - autoUpdater.on('checking-for-update', () => this.handleStatusChange(AppUpdaterState.Checking)); - autoUpdater.on('update-not-available', () => this.handleStatusChange(AppUpdaterState.NoUpdate)); - autoUpdater.on('download-progress', () => this.handleStatusChange(AppUpdaterState.Downloading)); - autoUpdater.on('update-downloaded', () => this.handleStatusChange(AppUpdaterState.Downloaded)); + autoUpdater.on('checking-for-update', () => this.handleStatusChange(AppUpdaterStatus.Checking, false)); + autoUpdater.on('update-not-available', () => this.handleStatusChange(AppUpdaterStatus.NoUpdate, true)); + autoUpdater.on('download-progress', () => this.handleStatusChange(AppUpdaterStatus.Downloading, false)); + autoUpdater.on('update-downloaded', () => this.handleStatusChange(AppUpdaterStatus.Downloaded, true)); } - getCurrentState() { - return this.state; - } - - readyToCheck() { - return this.readyStates.includes(this.state); + getCurrentStatus() { + return this.status; } checkForUpdatesAndNotify() { - if (this.readyToCheck()) + if (this.status === AppUpdaterStatus.Idle) return autoUpdater.checkForUpdatesAndNotify(); } - handleStatusChange(nextStatus: AppUpdaterState) { - const changed = this.state !== nextStatus; - this.state = nextStatus; - if (changed) + handleStatusChange(nextStatus: string, backToIdle: boolean) { + clearTimeout(this.timerId); + + if (this.status !== nextStatus) { + this.status = nextStatus; this.emit('status-changed', nextStatus); + + if (backToIdle) { + this.timerId = setTimeout(() => { + if (this.status !== AppUpdaterStatus.Idle) { + this.status = AppUpdaterStatus.Idle; + this.emit('status-changed', AppUpdaterStatus.Idle); + } + }, 2000); + } + } } - } +} const appUpdaterInstance = new AppUpdater(); -export { AppUpdaterState, appUpdaterInstance as appUpdater }; \ No newline at end of file +export { AppUpdaterStatus, appUpdaterInstance as appUpdater }; \ No newline at end of file diff --git a/desktop-app/app/app.global.css b/desktop-app/app/app.global.css index 383490ba..0d5feea8 100644 --- a/desktop-app/app/app.global.css +++ b/desktop-app/app/app.global.css @@ -47,6 +47,10 @@ body { overflow: hidden; } +svg { + pointer-events: none; +} + /*h2 { margin: 0; font-size: 2.25rem; diff --git a/desktop-app/app/components/Addressinput/index.js b/desktop-app/app/components/Addressinput/index.js index aa895208..bd92f691 100644 --- a/desktop-app/app/components/Addressinput/index.js +++ b/desktop-app/app/components/Addressinput/index.js @@ -6,11 +6,12 @@ import DeleteCookieIcon from '../icons/DeleteCookie'; import DeleteStorageIcon from '../icons/DeleteStorage'; import FavIconOff from '@material-ui/icons/StarBorder'; import FavIconOn from '@material-ui/icons/Star'; -import {iconsColor} from '../../constants/colors'; +import {iconsColor, lightIconsColor} from '../../constants/colors'; import commonStyles from '../common.styles.css'; import styles from './style.css'; import {Tooltip} from '@material-ui/core'; +import {Icon} from 'flwww'; type Props = { address: string, @@ -45,7 +46,6 @@ class AddressBar extends React.Component { } render() { - const FavIcon = this.props.isBookmarked ? FavIconOn : FavIconOff; return (
{ [commonStyles.enabled]: true, })} > - +
this.props.toggleBookmark(this.state.userTypedAddress)} + onClick={() => + this.props.toggleBookmark(this.state.userTypedAddress) + } > - +
diff --git a/desktop-app/app/components/BookmarksBar/index.js b/desktop-app/app/components/BookmarksBar/index.js index b55c436b..0b121599 100644 --- a/desktop-app/app/components/BookmarksBar/index.js +++ b/desktop-app/app/components/BookmarksBar/index.js @@ -1,74 +1,112 @@ // @flow -import React, { useState } from 'react'; +import React, {useState} from 'react'; import Grid from '@material-ui/core/Grid'; import Button from '@material-ui/core/Button'; import Menu from '@material-ui/core/Menu'; import MenuItem from '@material-ui/core/MenuItem'; -import BookmarkEditDialog from './BookmarkEditDialog' -import styles from './style.css'; +import BookmarkEditDialog from './BookmarkEditDialog'; +import styles from './style.module.css'; +import Globe from '../icons/Globe'; -export const BookmarksBar = function ({bookmarks, onBookmarkClick, onBookmarkDelete, onBookmarkEdit}) { - return - {bookmarks.map((bookmark, k) => ( - - ))} - +export const BookmarksBar = function({ + bookmarks, + onBookmarkClick, + onBookmarkDelete, + onBookmarkEdit, +}) { + return ( +
+ {bookmarks.map((bookmark, k) => ( + + ))} +
+ ); }; -const useToggle = function () { - const [value, setValue] = useState(false) +const useToggle = function() { + const [value, setValue] = useState(false); return [ value, - function () { setValue(true) }, - function () { setValue(false) } - ] -} + function() { + setValue(true); + }, + function() { + setValue(false); + }, + ]; +}; -function BookmarkItem ({bookmark, onClick, onDelete, onEdit}) { +function BookmarkItem({bookmark, onClick, onDelete, onEdit}) { const [anchorEl, setAnchorEl] = useState(null); - const [renameDialog, openRenameDialog, closeRenameDialog] = useToggle(null) + const [renameDialog, openRenameDialog, closeRenameDialog] = useToggle(null); - const handleContextMenu = function (event) { + const handleContextMenu = function(event) { event.preventDefault(); setAnchorEl(event.currentTarget); }; - const handleClose = function () { + const handleClose = function() { setAnchorEl(null); }; - const handleClick = function () { - onClick(bookmark) + const handleClick = function() { + onClick(bookmark); }; - const handleDelete = function () { - onDelete(bookmark) + const handleDelete = function() { + setAnchorEl(null); + onDelete(bookmark); }; - const handleRename = function (title, url) { - onEdit(bookmark, {title, url}) - setAnchorEl(null) - } + const handleRename = function(title, url) { + onEdit(bookmark, {title, url}); + setAnchorEl(null); + }; - const closeDialog = function () { - closeRenameDialog() - setAnchorEl(null) - } + const closeDialog = function() { + closeRenameDialog(); + setAnchorEl(null); + }; - return - - - Rename - Delete - - - + return ( + <> +
+ + {bookmark.title} +
+ + Edit + Delete + + + + ); } diff --git a/desktop-app/app/components/BookmarksBar/style.css b/desktop-app/app/components/BookmarksBar/style.css deleted file mode 100644 index c88d5055..00000000 --- a/desktop-app/app/components/BookmarksBar/style.css +++ /dev/null @@ -1,3 +0,0 @@ -.bookmarks { - padding: 0 10px; -} diff --git a/desktop-app/app/components/BookmarksBar/style.module.css b/desktop-app/app/components/BookmarksBar/style.module.css new file mode 100644 index 00000000..976246fc --- /dev/null +++ b/desktop-app/app/components/BookmarksBar/style.module.css @@ -0,0 +1,28 @@ +.bookmarks { + padding: 0 10px; + display: flex; + margin-top: 4px; +} + +.bookmarkItem { + color: #ffffffcc; + fill: #ffffffcc; + cursor: default; + padding: 2px 10px; + border-radius: 15px; + font-size: 13px; + min-width: 100px; + max-width: 150px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.bookmarkItem:hover { + background-color: #6075ef; + fill: #fff; +} + +.icon { + margin-right: 5px; +} diff --git a/desktop-app/app/components/Header/style.module.css b/desktop-app/app/components/Header/style.module.css index dc7f3325..d33eb7d0 100644 --- a/desktop-app/app/components/Header/style.module.css +++ b/desktop-app/app/components/Header/style.module.css @@ -1,6 +1,6 @@ .header { width: calc(100vw - 50px); - padding: 20px 0 10px; + padding: 20px 0 5px; background: #252526; box-shadow: 0 3px 5px rgba(0, 0, 0, 0.35); z-index: 2; diff --git a/desktop-app/app/components/NavigationControls/index.js b/desktop-app/app/components/NavigationControls/index.js index fa59d9d6..b4df5724 100644 --- a/desktop-app/app/components/NavigationControls/index.js +++ b/desktop-app/app/components/NavigationControls/index.js @@ -18,6 +18,13 @@ class NavigationControls extends Component { ipcRenderer.on('reload-url', this.props.triggerNavigationReload); ipcRenderer.on('reload-css', this.props.reloadCSS); } + componentWillUnmount() { + ipcRenderer.removeListener( + 'reload-url', + this.props.triggerNavigationReload + ); + ipcRenderer.removeListener('reload-css', this.props.reloadCSS); + } render() { const iconProps = { diff --git a/desktop-app/app/components/StatusBar/index.js b/desktop-app/app/components/StatusBar/index.js index 119c1fad..80ef7a68 100644 --- a/desktop-app/app/components/StatusBar/index.js +++ b/desktop-app/app/components/StatusBar/index.js @@ -1,16 +1,64 @@ -import React from 'react'; +import React, {useState, useEffect} from 'react'; import cx from 'classnames'; +import {shell, ipcRenderer} from 'electron'; +import PropTypes from 'prop-types'; + import styles from './styles.module.css'; import Github from '../icons/Github'; -import {shell} from 'electron'; import Twitter from '../icons/Twitter'; import RoadMap from '../icons/RoadMap'; const Spacer = ({width = 10}) => ( -
+
); -const StatusBar = () => { +const AppUpdaterStatusInfoSection = () => { + const [status, setAppUpdaterStatus] = useState('idle'); + useEffect(() => { + const handler = (event, args) => { + setAppUpdaterStatus(args.nextStatus); + }; + ipcRenderer.on('updater-status-changed', handler); + return () => { + ipcRenderer.removeListener('updater-status-changed', handler); + }; + }, []); + + let label = ''; + switch (status) { + case 'checking': + label = 'Update Info: Checking for Updates...'; + break; + case 'noUpdate': + label = 'Update Info: The App is up to date!'; + break; + case 'downloading': + label = 'Update Info: Downloading Update...'; + break; + case 'downloaded': + label = 'Update Info: Update Downloaded'; + break; + default: + label = null; + break; + } + if (label == null) return null; + return ( +
+
+ + {label} + +
+
+ ); +}; + +const StatusBar = ({visible}) => { + if (!visible) { + return null; + } + return (
@@ -56,6 +104,7 @@ const StatusBar = () => {
+
{ ); }; +StatusBar.propTypes = { + visible: PropTypes.bool.isRequired, +}; + export default StatusBar; diff --git a/desktop-app/app/components/WebView/index.js b/desktop-app/app/components/WebView/index.js index 5aa1cdd3..7bd76991 100644 --- a/desktop-app/app/components/WebView/index.js +++ b/desktop-app/app/components/WebView/index.js @@ -131,6 +131,18 @@ class WebView extends Component { this.initEventTriggers(this.webviewRef.current); }); + if (this.props.transmitNavigatorStatus) { + this.webviewRef.current.addEventListener( + 'page-favicon-updated', + ({favicons}) => this.props.onPageMetaFieldUpdate('favicons', favicons) + ); + + this.webviewRef.current.addEventListener( + 'page-title-updated', + ({title}) => this.props.onPageMetaFieldUpdate('title', title) + ); + } + this.webviewRef.current.addEventListener('did-start-loading', () => { this.setState({errorCode: null, errorDesc: null}); this.props.onLoadingStateChange(true); @@ -400,20 +412,20 @@ class WebView extends Component { initEventTriggers = webview => { this.getWebContentForId(webview.getWebContentsId()).executeJavaScript(` responsivelyApp.deviceId = '${this.props.device.id}'; - document.body.addEventListener('mouseleave', () => { + document.addEventListener('mouseleave', () => { window.responsivelyApp.mouseOn = false; if (responsivelyApp.domInspectorEnabled) { responsivelyApp.domInspector.disable(); } }); - document.body.addEventListener('mouseenter', () => { + document.addEventListener('mouseenter', () => { responsivelyApp.mouseOn = true; if (responsivelyApp.domInspectorEnabled) { responsivelyApp.domInspector.enable(); } }); - window.addEventListener('scroll', (e) => { + document.addEventListener('scroll', (e) => { if (!responsivelyApp.mouseOn) { return; } diff --git a/desktop-app/app/components/icons/Globe.js b/desktop-app/app/components/icons/Globe.js new file mode 100644 index 00000000..a6281702 --- /dev/null +++ b/desktop-app/app/components/icons/Globe.js @@ -0,0 +1,121 @@ +import React, {Fragment} from 'react'; + +export default function Globe({ + width, + height, + color, + padding, + margin, + className, +}) { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/desktop-app/app/constants/pubsubEvents.js b/desktop-app/app/constants/pubsubEvents.js index cb5d53ac..05e94ff5 100644 --- a/desktop-app/app/constants/pubsubEvents.js +++ b/desktop-app/app/constants/pubsubEvents.js @@ -11,3 +11,6 @@ export const FLIP_ORIENTATION_ALL_DEVICES = 'FLIP_ORIENTATION_ALL_DEVICES'; export const ENABLE_INSPECTOR_ALL_DEVICES = 'ENABLE_INSPECTOR_ALL_DEVICES'; export const DISABLE_INSPECTOR_ALL_DEVICES = 'DISABLE_INSPECTOR_ALL_DEVICES'; export const STOP_LOADING = 'STOP_LOADING'; + +// status bar events +export const STATUS_BAR_VISIBILITY_CHANGE = 'status-bar-visibility-change'; diff --git a/desktop-app/app/constants/settingKeys.js b/desktop-app/app/constants/settingKeys.js index 6485262e..c585d71e 100644 --- a/desktop-app/app/constants/settingKeys.js +++ b/desktop-app/app/constants/settingKeys.js @@ -2,3 +2,4 @@ export const ACTIVE_DEVICES = 'activeDevices'; export const CUSTOM_DEVICES = 'customDevices'; export const USER_PREFERENCES = 'userPreferences'; export const BOOKMARKS = 'bookmarks'; +export const STATUS_BAR_VISIBILITY = 'statusBarVisibility'; diff --git a/desktop-app/app/containers/AddressBar/index.js b/desktop-app/app/containers/AddressBar/index.js index bf059bb5..d5045074 100644 --- a/desktop-app/app/containers/AddressBar/index.js +++ b/desktop-app/app/containers/AddressBar/index.js @@ -6,13 +6,15 @@ import {bindActionCreators} from 'redux'; import AddressInput from '../../components/Addressinput'; import * as BrowserActions from '../../actions/browser'; -import {toggleBookmarkUrl} from '../../actions/bookmarks' +import {toggleBookmarkUrl} from '../../actions/bookmarks'; const AddressBar = function(props) { + const handler = (_, url) => { + props.onAddressChange(url); + }; useEffect(() => { - ipcRenderer.on('address-change', (_, url) => { - props.onAddressChange(url); - }); + ipcRenderer.on('address-change', handler); + return () => ipcRenderer.removeListener('address-change', handler); }, []); return ( + props.toggleBookmarkUrl(url, props.browser.currentPageMeta) + } deleteCookies={props.deleteCookies} deleteStorage={props.deleteStorage} /> @@ -31,7 +35,9 @@ const AddressBar = function(props) { function mapStateToProps(state) { return { browser: state.browser, - isBookmarked: state.bookmarks.bookmarks.some(b => b.url === state.browser.address) + isBookmarked: state.bookmarks.bookmarks.some( + b => b.url === state.browser.address + ), }; } diff --git a/desktop-app/app/containers/StatusBarContainer/index.js b/desktop-app/app/containers/StatusBarContainer/index.js new file mode 100644 index 00000000..dfa2ab47 --- /dev/null +++ b/desktop-app/app/containers/StatusBarContainer/index.js @@ -0,0 +1,36 @@ +// @flow +import React, {useEffect} from 'react'; +import {ipcRenderer} from 'electron'; +import {connect} from 'react-redux'; +import {bindActionCreators} from 'redux'; + +import StatusBar from '../../components/StatusBar'; +import {toggleStatusBarVisibility} from '../../actions/statusBar'; +import {STATUS_BAR_VISIBILITY_CHANGE} from '../../constants/pubsubEvents'; + +const StatusBarContainer = props => { + useEffect(() => { + const handler = () => { + props.toggleStatusBarVisibility(); + }; + + ipcRenderer.on(STATUS_BAR_VISIBILITY_CHANGE, handler); + + return () => + ipcRenderer.removeListener(STATUS_BAR_VISIBILITY_CHANGE, handler); + }, []); + + return ; +}; + +function mapStateToProps(state) { + return { + visible: state.statusBar.visible, + }; +} + +function mapDispatchToProps(dispatch) { + return bindActionCreators({toggleStatusBarVisibility}, dispatch); +} + +export default connect(mapStateToProps, mapDispatchToProps)(StatusBarContainer); diff --git a/desktop-app/app/main.dev.js b/desktop-app/app/main.dev.js index 6f5ac14c..29659368 100644 --- a/desktop-app/app/main.dev.js +++ b/desktop-app/app/main.dev.js @@ -297,7 +297,7 @@ const createWindow = async () => { appUpdater.on('status-changed', nextStatus => { menuBuilder.buildMenu(true); - // update status bar info + mainWindow.webContents.send('updater-status-changed', {nextStatus}); }); // Remove this if your app does not use auto updates appUpdater.checkForUpdatesAndNotify(); diff --git a/desktop-app/app/menu.js b/desktop-app/app/menu.js index 8fdf0bf7..e813f877 100644 --- a/desktop-app/app/menu.js +++ b/desktop-app/app/menu.js @@ -14,10 +14,9 @@ import { getAllShortcuts, registerShortcut, } from './shortcut-manager/main-shortcut-manager'; -import { - appUpdater, - AppUpdaterState -} from './app-updater'; +import {appUpdater, AppUpdaterStatus} from './app-updater'; +import {statusBarSettings} from './settings/statusBarSettings'; +import {STATUS_BAR_VISIBILITY_CHANGE} from './constants/pubsubEvents'; const path = require('path'); @@ -105,13 +104,16 @@ export default class MenuBuilder { id: 'CHECK_FOR_UPDATES', click() { appUpdater.checkForUpdatesAndNotify().then(r => { - if (r == null || r.updateInfo == null || r.updateInfo.version === pkg.version) { - dialog - .showMessageBox(BrowserWindow.getAllWindows()[0], { - type: 'info', - title: 'Responsively', - message: 'There are currently no updates available' - }); + if ( + r == null || + r.updateInfo == null || + r.updateInfo.version === pkg.version + ) { + dialog.showMessageBox(BrowserWindow.getAllWindows()[0], { + type: 'info', + title: 'Responsively', + message: 'There are currently no updates available', + }); } }); }, @@ -122,12 +124,12 @@ export default class MenuBuilder { click() { const iconPath = path.join(__dirname, '../resources/icons/64x64.png'); const title = 'Responsively'; - const description = pkg.description; + const {description} = pkg; const version = pkg.version || 'Unknown'; - const electron = process.versions['electron'] || 'Unknown'; - const chrome = process.versions['chrome'] || 'Unknown'; - const node = process.versions['node'] || 'Unknown'; - const v8 = process.versions['v8'] || 'Unknown'; + const electron = process.versions.electron || 'Unknown'; + const chrome = process.versions.chrome || 'Unknown'; + const node = process.versions.node || 'Unknown'; + const v8 = process.versions.v8 || 'Unknown'; const osText = `${os.type()} ${os.arch()} ${os.release()}`.trim() || 'Unknown'; const usefulInfo = `Version: ${version}\nElectron: ${electron}\nChrome: ${chrome}\nNode.js: ${node}\nV8: ${v8}\nOS: ${osText}`; @@ -179,7 +181,7 @@ export default class MenuBuilder { } let filePath = selected[0]; if (!filePath.startsWith('file://')) { - filePath = 'file://' + filePath; + filePath = `file://${filePath}`; } this.mainWindow.webContents.send('address-change', filePath); }, @@ -188,27 +190,42 @@ export default class MenuBuilder { }; getCheckForUpdatesMenuState() { - const updaterState = appUpdater.getCurrentState(); + const updaterStatus = appUpdater.getCurrentStatus(); let label = 'Check for Updates...'; let enabled = true; - - if (updaterState === AppUpdaterState.Checking) { - enabled = false; - label = 'Checking for Updates...'; - } - else if (updaterState === AppUpdaterState.Downloading) { - enabled = false; - label = 'Downloading Update...'; - } + switch(updaterStatus) { + case AppUpdaterStatus.Idle: + label = 'Check for Updates...'; + enabled = true; + break; + case AppUpdaterStatus.Checking: + label = 'Checking for Updates...'; + enabled = false; + break; + case AppUpdaterStatus.NoUpdate: + label = 'No Updates'; + enabled = false; + break; + case AppUpdaterStatus.Downloading: + label = 'Downloading Update...'; + enabled = false; + break; + case AppUpdaterStatus.Downloaded: + label = 'Update Downloaded'; + enabled = false; + break; + } + return {label, enabled}; } buildMenu(isUpdate: boolean = false) { - if (isUpdate) { - const chkUpdtMenu = this.subMenuHelp.submenu.find(x => x.id === 'CHECK_FOR_UPDATES'); - const {label, enabled} = this.getCheckForUpdatesMenuState(); + const chkUpdtMenu = this.subMenuHelp.submenu.find( + x => x.id === 'CHECK_FOR_UPDATES' + ); + const {label, enabled} = this.getCheckForUpdatesMenuState(); chkUpdtMenu.label = label; chkUpdtMenu.enabled = enabled; } @@ -340,6 +357,14 @@ export default class MenuBuilder { this.mainWindow.toggleDevTools(); }, }, + { + label: 'Show Status Bar', + type: 'checkbox', + checked: statusBarSettings.getVisibility(), + click: () => { + this.mainWindow.webContents.send(STATUS_BAR_VISIBILITY_CHANGE); + }, + }, ], }; const subMenuViewProd = { @@ -373,6 +398,14 @@ export default class MenuBuilder { this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen()); }, }, + { + label: 'Show Status Bar', + type: 'checkbox', + checked: statusBarSettings.getVisibility(), + click: () => { + this.mainWindow.webContents.send(STATUS_BAR_VISIBILITY_CHANGE); + }, + }, ], }; const subMenuWindow = { @@ -446,6 +479,16 @@ export default class MenuBuilder { this.mainWindow.toggleDevTools(); }, }, + { + label: 'Show Status Bar', + type: 'checkbox', + checked: statusBarSettings.getVisibility(), + click: () => { + this.mainWindow.webContents.send( + STATUS_BAR_VISIBILITY_CHANGE + ); + }, + }, ] : [ { @@ -480,6 +523,16 @@ export default class MenuBuilder { ); }, }, + { + label: 'Show Status Bar', + type: 'checkbox', + checked: statusBarSettings.getVisibility(), + click: () => { + this.mainWindow.webContents.send( + STATUS_BAR_VISIBILITY_CHANGE + ); + }, + }, ], }, this.subMenuHelp, diff --git a/desktop-app/app/reducers/bookmarks.js b/desktop-app/app/reducers/bookmarks.js index ebf88a42..2f30b37f 100644 --- a/desktop-app/app/reducers/bookmarks.js +++ b/desktop-app/app/reducers/bookmarks.js @@ -1,12 +1,13 @@ import settings from 'electron-settings'; -import {TOGGLE_BOOKMARK, EDIT_BOOKMARK} from '../actions/bookmarks' -import {BOOKMARKS} from '../constants/settingKeys' -import { getWebsiteName } from '../components/WebView/screenshotUtil'; +import {TOGGLE_BOOKMARK, EDIT_BOOKMARK} from '../actions/bookmarks'; +import {BOOKMARKS} from '../constants/settingKeys'; +import {getWebsiteName} from '../components/WebView/screenshotUtil'; +import console from 'electron-timber'; type BookmarksType = { title: string, - url: string -} + url: string, +}; function fetchBookmarks(): BookmarksType { return settings.get(BOOKMARKS) || []; @@ -24,22 +25,24 @@ export default function browser( ) { switch (action.type) { case TOGGLE_BOOKMARK: - let bookmarks = state.bookmarks + let bookmarks = state.bookmarks; const bookmark = { - title: getWebsiteName(action.url), - url: action.url - } + title: action.title || getWebsiteName(action.url), + url: action.url, + }; if (bookmarks.find(b => b.url === action.url)) { - bookmarks = bookmarks.filter(b => b.url !== action.url) + bookmarks = bookmarks.filter(b => b.url !== action.url); } else { - bookmarks = [...bookmarks, bookmark] + bookmarks = [...bookmarks, bookmark]; } - persistBookmarks(bookmarks) - return {...state, bookmarks} + persistBookmarks(bookmarks); + return {...state, bookmarks}; case EDIT_BOOKMARK: - const updatedBookmarks = state.bookmarks.map(b => b === action.bookmark ? {...b, title: action.title, url: action.url} : b) - persistBookmarks(updatedBookmarks) - return {...state, bookmarks: updatedBookmarks} + const updatedBookmarks = state.bookmarks.map(b => + b === action.bookmark ? {...b, title: action.title, url: action.url} : b + ); + persistBookmarks(updatedBookmarks); + return {...state, bookmarks: updatedBookmarks}; default: return state; } diff --git a/desktop-app/app/reducers/browser.js b/desktop-app/app/reducers/browser.js index 21c7085c..9bdc902d 100644 --- a/desktop-app/app/reducers/browser.js +++ b/desktop-app/app/reducers/browser.js @@ -17,9 +17,8 @@ import { NEW_INSPECTOR_STATUS, NEW_WINDOW_SIZE, DEVICE_LOADING, - DEVICE_FOCUS, - DEVICE_UNFOCUS, NEW_FOCUSED_DEVICE, + NEW_PAGE_META_FIELD, } from '../actions/browser'; import type {Action} from './types'; import getAllDevices from '../constants/devices'; @@ -99,6 +98,11 @@ type PreviewerType = { focusedDeviceId: string, }; +type PageMetaType = { + title: String, + favicons: Array, +}; + type UserPreferenceType = { disableSSLValidation: boolean, reopenLastAddress: boolean, @@ -114,6 +118,7 @@ export type BrowserStateType = { devices: Array, homepage: string, address: string, + currentPageMeta: PageMetaType, zoomLevel: number, scrollPosition: ScrollPositionType, navigatorStatus: NavigatorStatusType, @@ -214,6 +219,7 @@ export default function browser( address: _getUserPreferences().reopenLastAddress ? getLastOpenedAddress() : getHomepage(), + currentPageMeta: {}, zoomLevel: 0.6, previousZoomLevel: null, scrollPosition: {x: 0, y: 0}, @@ -251,7 +257,15 @@ export default function browser( switch (action.type) { case NEW_ADDRESS: saveLastOpenedAddress(action.address); - return {...state, address: action.address}; + return {...state, address: action.address, currentPageMeta: {}}; + case NEW_PAGE_META_FIELD: + return { + ...state, + currentPageMeta: { + ...state.currentPageMeta, + [action.name]: action.value, + }, + }; case NEW_HOMEPAGE: const {homepage} = action; saveHomepage(homepage); diff --git a/desktop-app/app/reducers/index.js b/desktop-app/app/reducers/index.js index ec43acae..327fef04 100644 --- a/desktop-app/app/reducers/index.js +++ b/desktop-app/app/reducers/index.js @@ -2,12 +2,14 @@ import {combineReducers} from 'redux'; import {connectRouter} from 'connected-react-router'; import browser from './browser'; -import bookmarks from './bookmarks' +import bookmarks from './bookmarks'; +import statusBar from './statusBar'; export default function createRootReducer(history: History) { return combineReducers({ router: connectRouter(history), browser, bookmarks, + statusBar, }); } diff --git a/desktop-app/app/reducers/statusBar.js b/desktop-app/app/reducers/statusBar.js new file mode 100644 index 00000000..016ab1e6 --- /dev/null +++ b/desktop-app/app/reducers/statusBar.js @@ -0,0 +1,20 @@ +// @flow +import {TOGGLE_STATUS_BAR_VISIBILITY} from '../actions/statusBar'; +import {statusBarSettings} from '../settings/statusBarSettings'; + +export type StatusBarStateType = {visible: boolean}; + +export default function app( + state: StatusBarStateType = {visible: statusBarSettings.getVisibility()}, + action: Action +) { + switch (action.type) { + case TOGGLE_STATUS_BAR_VISIBILITY: + return { + ...state, + visible: action.visible, + }; + default: + return state; + } +} diff --git a/desktop-app/app/settings/statusBarSettings.js b/desktop-app/app/settings/statusBarSettings.js new file mode 100644 index 00000000..aec1afd6 --- /dev/null +++ b/desktop-app/app/settings/statusBarSettings.js @@ -0,0 +1,24 @@ +import settings from 'electron-settings'; +import {STATUS_BAR_VISIBILITY} from '../constants/settingKeys'; + +class StatusBarSettings { + constructor() { + const visibility = settings.get(STATUS_BAR_VISIBILITY); + + if (visibility === undefined) { + settings.set(STATUS_BAR_VISIBILITY, true); + } + } + + getVisibility() { + return settings.get(STATUS_BAR_VISIBILITY); + } + + setVisibility(visible) { + settings.set(STATUS_BAR_VISIBILITY, visible); + } +} + +const statusBarSettingsInstance = new StatusBarSettings(); + +export {statusBarSettingsInstance as statusBarSettings}; diff --git a/desktop-app/package.json b/desktop-app/package.json index 700d02ef..9983d5e2 100644 --- a/desktop-app/package.json +++ b/desktop-app/package.json @@ -1,7 +1,7 @@ { "name": "Responsively-App", "productName": "ResponsivelyApp", - "version": "0.3.0", + "version": "0.4.0", "description": "A developer-friendly browser for developing responsive web apps", "scripts": { "build": "concurrently \"yarn build-main\" \"yarn build-renderer\"", diff --git a/desktop-app/resources/globe.svg b/desktop-app/resources/globe.svg new file mode 100644 index 00000000..8b9de905 --- /dev/null +++ b/desktop-app/resources/globe.svg @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +