Skip to content
This repository has been archived by the owner on Apr 1, 2019. It is now read-only.

Commit

Permalink
Merge fbf81f9 into 1e9ba7b
Browse files Browse the repository at this point in the history
  • Loading branch information
Marina Samuel committed Feb 1, 2016
2 parents 1e9ba7b + fbf81f9 commit 33dceb3
Show file tree
Hide file tree
Showing 21 changed files with 688 additions and 5 deletions.
23 changes: 23 additions & 0 deletions src/actions/BlockActions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
const c = require("lib/constants");
const {request, receive} = require("lib/utils");
const userDatabase = require("lib/userDatabase");
const blockedLinks = require("lib/blockedLinks");
const async = require("lib/async");

module.exports = {
initUserDatabase() {
return async(function* (dispatch) {
dispatch(request(c.REQUEST_INIT_USER_DATABASE));
yield userDatabase.init();
yield blockedLinks.init(userDatabase);
dispatch(receive(c.REQUEST_INIT_USER_DATABASE_COMPLETE));
}, this);
},

setUndoDialogVisibility(isVisible, blockedURL) {
return receive(c.REQUEST_UNDO_DIALOG_VISIBILITY, {
visible: isVisible,
blockedURL: blockedURL
});
}
};
4 changes: 4 additions & 0 deletions src/actions/SitesActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,9 @@ module.exports = {
sites.forEach(site => dispatch(this.getSiteThumbnail(site.url)));
dispatch(receive(c.RECEIVE_FRECENT, {sites}));
}, this);
},

updateSites() {
return receive(c.RECEIVE_UPDATE_SITES);
}
};
3 changes: 3 additions & 0 deletions src/components/Base/Base.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ const TileUtils = require("lib/TileUtils");
const actions = require("actions/index");

const Tile = require("components/Tile/Tile");
const Block = require("components/Block/Block");
const Search = require("components/Search/Search");
const Settings = require("components/Settings/Settings");

const Base = React.createClass({
componentWillMount: function () {
// this.props.dispatch(actions.getPrefs());
this.props.dispatch(actions.getSuggestedDirectory(this.props.intl.locale));
this.props.dispatch(actions.initUserDatabase());
this.props.dispatch(actions.getCurrentEngine());
this.props.dispatch(actions.getVisibleEngines());
// this.props.dispatch(actions.getFrecentSites());
Expand All @@ -40,6 +42,7 @@ const Base = React.createClass({
blankTiles.push(<div className="tile tile-placeholder" />);
}
return (<div>
<Block {...this.props.Block} />
<Search foo={10} />
<div className="grid" hidden={!prefs.enabled}>
{tiles.map((tile, index) => <Tile key={index} {...tile} />)}
Expand Down
61 changes: 61 additions & 0 deletions src/components/Block/Block.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
const React = require("react");
const {connect} = require("react-redux");
const actions = require("actions/index");
const blockedLinks = require("lib/blockedLinks");

const Block = React.createClass({
/**
* The undo dialog's timeout in miliseconds.
*/
HIDE_TIMEOUT_MS: 15000,
_timeout: 0,

/**
* Hides the undo dialog.
*/
hide() {
this.props.dispatch(actions.setUndoDialogVisibility(false));
clearTimeout(this._timeout);
this._timeout = 0;
},

undo() {
blockedLinks.unblock(this.props.blockedURL);
this.props.dispatch(actions.setUndoDialogVisibility(false));
this.props.dispatch(actions.updateSites(this.props.url));
},

undoAll() {
blockedLinks.reset();
this.props.dispatch(actions.setUndoDialogVisibility(false));
this.props.dispatch(actions.updateSites(this.props.url));
},

render: function () {
if (this.timeout) {
clearTimeout(this._timeout);
this._timeout = 0;
}
this._timeout = setTimeout(this.hide, this.HIDE_TIMEOUT_MS);
return (<div className="undo" hidden={!this.props.visible}>
<label id="undo-label">Thumbnail removed.</label>
<button id="undo-button" className="undo-button" onClick={e => this.undo(e)}>Undo.</button>
<button id="restore-button" className="undo-button" onClick={e => this.undoAll(e)}>Restore All.</button>
<div id="undo-close-button" className="close-icon tabbable"
data-tooltiptext="CLOSE" onClick={e => this.hide(e)}></div>
</div>);
}
});

Block.propTypes = {
visible: React.PropTypes.bool,
blockedURL: React.PropTypes.string
};

function select(state) {
return {
Block: state.Block
};
}

module.exports = connect(select)(Block);
60 changes: 60 additions & 0 deletions src/components/Block/Block.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
.undo {
align-items: center;
justify-content: center;
display: flex;
left: 6px;
position: absolute;
top: 6px;
z-index: 1;
padding: 4px 3px;
border: 1px solid;
border-color: rgba(8,22,37,.12) rgba(8,22,37,.14) rgba(8,22,37,.16);
background-color: rgba(255,255,255,.4);
color: #525e69;
font-size: 11px;

#undo-label {
margin-top: 0;
margin-bottom: 0;
}

.undo-button {
appearance: none;
cursor: pointer;
padding: 0;
margin: 0 4px;
border: 0;
background: transparent;
text-decoration: none;
min-width: 0;
color: rgb(20,79,174);
}

.undo-button:hover {
text-decoration: underline;
}

.undo-button:-moz-focusring {
outline: 1px dotted;
}

#undo-close-button {
appearance: none;
padding: 0;
border: none;
height: 16px;
width: 16px;
float: right;
right: 0;
background-image: -moz-image-rect(url(../img/close.png), 0, 16, 16, 0);
background-color: transparent;
}

#undo-close-button:hover {
background-image: -moz-image-rect(url(../img/close.png), 0, 32, 16, 16);
}

#undo-close-button:hover:active {
background-image: -moz-image-rect(url(../img/close.png), 0, 48, 16, 32);
}
}
40 changes: 39 additions & 1 deletion src/components/Tile/Tile.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,40 @@
const React = require("react");
const blockedLinks = require("lib/blockedLinks");
const {connect} = require("react-redux");
const actions = require("actions/index");

const Tile = React.createClass({
/**
* Blocks the site (removes it from the grid) and calls the given callback
* when done.
*/
block: function (e) {
e.preventDefault();
if (!this.isBlocked()) {
blockedLinks.block(this.props.url);
if (this.props.dispatch) {
// Dispatch doesn't exist in a test environment.
this.props.dispatch(actions.setUndoDialogVisibility(true, this.props.url));
this.props.dispatch(actions.updateSites());
}
}
},


/**
* Checks whether this site is blocked.
*
* @return {Boolean} Whether this site is blocked.
*/
isBlocked: function () {
return blockedLinks.isBlocked(this.props.url);
},

render: function () {
return (<a className="tile" href={this.props.url}>
<div className="tile-img-container">
<button className="control control-block" title="Remove this site"
ref="blockButton" onClick={e => this.block(e)}></button>
{this.props.imageURI && <div className="tile-img"
style={{backgroundImage: `url(${this.props.imageURI})`}} />}
{this.props.enhancedImageURI && <div className="tile-img-rollover"
Expand All @@ -23,4 +54,11 @@ Tile.propTypes = {
url: React.PropTypes.string.isRequired
};

module.exports = Tile;
function select(state) {
return {
Tile: state.Tile
};
}

module.exports = connect(select)(Tile);
module.exports.Tile = Tile;
32 changes: 32 additions & 0 deletions src/components/Tile/Tile.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,38 @@
color: $tile-text-color;
font-size: $tile-font-size;

.control {
background-color: transparent;
background-size: 24px;
border: none;
height: 24px;
width: 24px;

position: absolute;
top: 4px;
opacity: 0;
z-index: 1;
transition: opacity 100ms ease-out;
cursor: pointer;
}

.tile-img-container:hover > .control {
opacity: 1;
}

.control-block {
right: 4px;
background: url("../img/controls.svg#svgView(viewBox(320, 0, 40, 40))");
}

.control-block:hover {
background: url("../img/controls.svg#svgView(viewBox(352, 0, 40, 40))");
}

.control-block:hover:active {
background: url("../img/controls.svg#svgView(viewBox(384, 0, 40, 40))");
}

&.tile-placeholder {
border: 2px dashed #c1c1c1;
width: $tile-width;
Expand Down
3 changes: 3 additions & 0 deletions src/lib/TileUtils.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
const blockedLinks = require("lib/blockedLinks");

const TileUtils = {
DIRECTORY_FRECENCY: 1000,
MAX_NUM_LINKS: 100,
Expand All @@ -10,6 +12,7 @@ const TileUtils = {
getMergedLinks: function (allLinks) {
return allLinks
.reduce((newLinks, currLinks) => newLinks.concat(currLinks), [])
.filter(link => !blockedLinks.isBlocked(link.url))
.sort(this.compareLinks)
.slice(0, this.MAX_NUM_LINKS);
},
Expand Down
83 changes: 83 additions & 0 deletions src/lib/blockedLinks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
const async = require("lib/async");
const userDatabase = require("lib/userDatabase");

/**
* Singleton that keeps track of all blocked links in the grid.
*/
const blockedLinks = {
/**
* The cached list of blocked links.
*/
_links: new Set(),

/**
* Load the blocked links from userDatabase and cache them.
*/
init: async(function*() {
const result = yield userDatabase.load("prefs", "blockedLinks");
const loadedBlockedLinks = JSON.parse(result);
if (loadedBlockedLinks && loadedBlockedLinks.length) {
blockedLinks._links.clear();
loadedBlockedLinks.forEach(item => blockedLinks._links.add(item));
}
return blockedLinks;
}),

/**
* Blocks a given link. Adjusts siteMap accordingly, and notifies listeners.
*
* @param {String} aURL The link to block.
*/
block(aURL) {
this._links.add(aURL);
return this.save();
},

/**
* Unblocks a given link. Adjusts siteMap accordingly, and notifies listeners.
*
* @param {String} aURL The link to unblock.
*/
unblock(aURL) {
const isBlocked = this.isBlocked(aURL);
if (isBlocked) {
this._links.delete(aURL);
return this.save();
}
return Promise.resolve();
},

/**
* Saves the current list of blocked links.
*/
save() {
return userDatabase.save("prefs", "blockedLinks", JSON.stringify([...this._links]));
},

/**
* Returns whether a given link is blocked.
*
* @param {String} aURL The link to check.
*/
isBlocked(aURL) {
return this._links.has(aURL);
},

/**
* Checks whether the list of blocked links is empty.
*
* @return {Boolean} Whether the list is empty.
*/
isEmpty() {
return this._links.size === 0;
},

/**
* Resets the links cache and IDB object.
*/
reset() {
this._links.clear();
return this.save();
}
};
module.exports = blockedLinks;
6 changes: 5 additions & 1 deletion src/lib/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,16 @@ const constants = {
"REQUEST_SEARCH_SUGGESTIONS",
"RECEIVE_SEARCH_SUGGESTIONS",
"UPDATE_SEARCH_STRING",
"RECEIVE_UPDATE_SITES",
"REQUEST_CURRENT_SEARCH_ENGINE",
"RECEIVE_CURRENT_SEARCH_ENGINE",
"REQUEST_VISIBLE_SEARCH_ENGINES",
"RECEIVE_VISIBLE_SEARCH_ENGINES",
"REQUEST_FRECENT",
"RECEIVE_FRECENT"
"RECEIVE_FRECENT",
"REQUEST_INIT_USER_DATABASE",
"REQUEST_INIT_USER_DATABASE_COMPLETE",
"REQUEST_UNDO_DIALOG_VISIBILITY"
].forEach(action => constants[action] = action);

module.exports = constants;
3 changes: 3 additions & 0 deletions src/lib/log.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,8 @@ module.exports = {
},
warn() {
console.warn.apply(console, arguments); // eslint-disable-line no-console
},
error() {
console.error.apply(console, arguments); // eslint-disable-line no-console
}
};
Loading

0 comments on commit 33dceb3

Please sign in to comment.