Коммит
28d578a65b
|
@ -18,6 +18,7 @@ const am = new ActionManager([
|
|||
"NOTIFY_BLOCK_URL",
|
||||
"NOTIFY_UNBLOCK_URL",
|
||||
"NOTIFY_UNBLOCK_ALL",
|
||||
"NOTIFY_BOOKMARK_ADD",
|
||||
"NOTIFY_BOOKMARK_DELETE",
|
||||
"NOTIFY_HISTORY_DELETE",
|
||||
"NOTIFY_HISTORY_DELETE_CANCELLED",
|
||||
|
@ -27,7 +28,8 @@ const am = new ActionManager([
|
|||
"SEARCH_STATE_RESPONSE",
|
||||
"NOTIFY_ROUTE_CHANGE",
|
||||
"NOTIFY_PERFORMANCE",
|
||||
"NOTIFY_USER_EVENT"
|
||||
"NOTIFY_USER_EVENT",
|
||||
"NOTIFY_OPEN_WINDOW"
|
||||
]);
|
||||
|
||||
// This is a a set of actions that have sites in them,
|
||||
|
@ -118,8 +120,12 @@ function RequestExperiments() {
|
|||
return RequestExpect("EXPERIMENTS_REQUEST", "EXPERIMENTS_RESPONSE");
|
||||
}
|
||||
|
||||
function NotifyBookmarkDelete(data) {
|
||||
return Notify("NOTIFY_BOOKMARK_DELETE", data);
|
||||
function NotifyBookmarkAdd(url) {
|
||||
return Notify("NOTIFY_BOOKMARK_ADD", url);
|
||||
}
|
||||
|
||||
function NotifyBookmarkDelete(bookmarkGuid) {
|
||||
return Notify("NOTIFY_BOOKMARK_DELETE", bookmarkGuid);
|
||||
}
|
||||
|
||||
function NotifyHistoryDelete(data) {
|
||||
|
@ -167,6 +173,10 @@ function NotifyEvent(data) {
|
|||
return Notify("NOTIFY_USER_EVENT", data);
|
||||
}
|
||||
|
||||
function NotifyOpenWindow(data) {
|
||||
return Notify("NOTIFY_OPEN_WINDOW", data);
|
||||
}
|
||||
|
||||
am.defineActions({
|
||||
Notify,
|
||||
Response,
|
||||
|
@ -182,12 +192,14 @@ am.defineActions({
|
|||
NotifyBlockURL,
|
||||
NotifyUnblockURL,
|
||||
NotifyUnblockAll,
|
||||
NotifyBookmarkAdd,
|
||||
NotifyBookmarkDelete,
|
||||
NotifyHistoryDelete,
|
||||
NotifyPerformSearch,
|
||||
NotifyRouteChange,
|
||||
NotifyPerf,
|
||||
NotifyEvent
|
||||
NotifyEvent,
|
||||
NotifyOpenWindow
|
||||
});
|
||||
|
||||
module.exports = am;
|
||||
|
|
|
@ -11,16 +11,19 @@ const constants = {
|
|||
defaultPage: DEFAULT_PAGE,
|
||||
pages: new Set(Array.from(urlPatternToPageMap.values()).concat(DEFAULT_PAGE)),
|
||||
events: new Set([
|
||||
"BLOCK",
|
||||
"BOOKMARK_ADD",
|
||||
"BOOKMARK_DELETE",
|
||||
"CLICK",
|
||||
"DELETE",
|
||||
"BOOKMARK_DELETE",
|
||||
"BLOCK",
|
||||
"UNBLOCK",
|
||||
"UNBLOCK_ALL",
|
||||
"SHARE",
|
||||
"LOAD_MORE",
|
||||
"LOAD_MORE_SCROLL",
|
||||
"SEARCH"
|
||||
"OPEN_NEW_WINDOW",
|
||||
"OPEN_PRIVATE_WINDOW",
|
||||
"SEARCH",
|
||||
"SHARE",
|
||||
"UNBLOCK",
|
||||
"UNBLOCK_ALL"
|
||||
]),
|
||||
sources: new Set([
|
||||
"TOP_SITES",
|
||||
|
|
|
@ -3,7 +3,8 @@ const {connect} = require("react-redux");
|
|||
const {justDispatch} = require("selectors/selectors");
|
||||
const {actions} = require("common/action-manager");
|
||||
const SiteIcon = require("components/SiteIcon/SiteIcon");
|
||||
const DeleteMenu = require("components/DeleteMenu/DeleteMenu");
|
||||
const LinkMenu = require("components/LinkMenu/LinkMenu");
|
||||
const LinkMenuButton = require("components/LinkMenuButton/LinkMenuButton");
|
||||
const {prettyUrl, getRandomFromTimestamp} = require("lib/utils");
|
||||
const moment = require("moment");
|
||||
const classNames = require("classnames");
|
||||
|
@ -33,9 +34,6 @@ const ActivityFeedItem = React.createClass({
|
|||
showDate: false
|
||||
};
|
||||
},
|
||||
onDeleteClick() {
|
||||
this.setState({showContextMenu: true});
|
||||
},
|
||||
render() {
|
||||
const site = this.props;
|
||||
const title = site.title || site.provider_display || (site.parsedUrl && site.parsedUrl.hostname);
|
||||
|
@ -63,7 +61,7 @@ const ActivityFeedItem = React.createClass({
|
|||
dateLabel = moment(date).format("h:mm A");
|
||||
}
|
||||
|
||||
return (<li className={classNames("feed-item", {bookmark: site.bookmarkGuid, fixed: this.state.showContextMenu})}>
|
||||
return (<li className={classNames("feed-item", {bookmark: site.bookmarkGuid, active: this.state.showContextMenu})}>
|
||||
<a onClick={this.props.onClick} href={site.url} ref="link">
|
||||
<span className="star" hidden={!site.bookmarkGuid} />
|
||||
{icon}
|
||||
|
@ -77,19 +75,15 @@ const ActivityFeedItem = React.createClass({
|
|||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<div className="action-items-container">
|
||||
<div className="action-item icon-delete" ref="delete" onClick={this.onDeleteClick}></div>
|
||||
<div className="action-item icon-share" ref="share" onClick={() => this.props.onShare(site.url)}></div>
|
||||
<div className="action-item icon-more" onClick={() => alert("Sorry. We are still working on this feature.")}></div>
|
||||
</div>
|
||||
<DeleteMenu
|
||||
<LinkMenuButton onClick={() => this.setState({showContextMenu: true})} />
|
||||
<LinkMenu
|
||||
visible={this.state.showContextMenu}
|
||||
onUpdate={val => this.setState({showContextMenu: val})}
|
||||
url={site.url}
|
||||
bookmarkGuid={site.bookmarkGuid}
|
||||
allowBlock={this.props.page === "NEW_TAB"}
|
||||
site={site}
|
||||
page={this.props.page}
|
||||
index={this.props.index}
|
||||
source={this.props.source}
|
||||
index={this.props.index}
|
||||
/>
|
||||
</li>);
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
}
|
||||
|
||||
.feed-item {
|
||||
@include link-menu-button;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
|
@ -132,7 +133,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
&.fixed,
|
||||
&.active,
|
||||
&:hover {
|
||||
background: $feed-hover-color;
|
||||
border: 1px solid $faintest-black;
|
||||
|
|
|
@ -21,10 +21,21 @@ const ContextMenu = React.createClass({
|
|||
return (<span hidden={!this.props.visible} className="context-menu">
|
||||
<ul>
|
||||
{this.props.options.map((option, i) => {
|
||||
return (<li key={i}><a className="context-menu-link" onClick={() => {
|
||||
this.props.onUpdate(false);
|
||||
option.onClick();
|
||||
}}>{option.label}</a></li>);
|
||||
if (option.type === "separator") {
|
||||
return (<li key={i} className="separator" />);
|
||||
}
|
||||
return (<li key={i}><a
|
||||
className="context-menu-link"
|
||||
ref={option.ref}
|
||||
onClick={() => {
|
||||
this.props.onUpdate(false);
|
||||
option.onClick();
|
||||
if (option.userEvent) {
|
||||
this.props.onUserEvent(option.userEvent);
|
||||
}
|
||||
}}>
|
||||
{option.label}
|
||||
</a></li>);
|
||||
})}
|
||||
</ul>
|
||||
</span>);
|
||||
|
@ -34,9 +45,13 @@ const ContextMenu = React.createClass({
|
|||
ContextMenu.propTypes = {
|
||||
visible: React.PropTypes.bool,
|
||||
onUpdate: React.PropTypes.func.isRequired,
|
||||
onUserEvent: React.PropTypes.func,
|
||||
options: React.PropTypes.arrayOf(React.PropTypes.shape({
|
||||
label: React.PropTypes.string.isRequired,
|
||||
onClick: React.PropTypes.func.isRequired
|
||||
type: React.PropTypes.string,
|
||||
label: React.PropTypes.string,
|
||||
onClick: React.PropTypes.func,
|
||||
userEvent: React.PropTypes.string,
|
||||
ref: React.PropTypes.string
|
||||
})).isRequired
|
||||
};
|
||||
|
||||
|
|
|
@ -3,8 +3,9 @@
|
|||
position: absolute;
|
||||
font-size: $context-menu-font-size;
|
||||
box-shadow: $context-menu-shadow;
|
||||
top: 100%;
|
||||
right: 0;
|
||||
top: ($link-menu-button-size / 4);
|
||||
left: 100%;
|
||||
margin-left: 5px;
|
||||
z-index: 10000;
|
||||
background: $bg-grey;
|
||||
border-radius: $context-menu-border-radius;
|
||||
|
@ -15,6 +16,11 @@
|
|||
list-style: none;
|
||||
|
||||
> li {
|
||||
&.separator {
|
||||
margin: $context-menu-outer-padding 0;
|
||||
border-bottom: 1px solid $context-menu-border-color;
|
||||
}
|
||||
|
||||
> a {
|
||||
cursor: pointer;
|
||||
color: inherit;
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
const React = require("react");
|
||||
const {connect} = require("react-redux");
|
||||
const ContextMenu = require("components/ContextMenu/ContextMenu");
|
||||
const {actions} = require("common/action-manager");
|
||||
const {FIRST_RUN_TYPE} = require("lib/first-run-data");
|
||||
|
||||
const LinkMenu = React.createClass({
|
||||
getDefaultProps() {
|
||||
return {
|
||||
visible: false,
|
||||
allowBlock: true
|
||||
};
|
||||
},
|
||||
userEvent(event) {
|
||||
const {page, source, index, Experiments, dispatch} = this.props;
|
||||
if (page && source) {
|
||||
let payload = {
|
||||
event,
|
||||
page: page,
|
||||
source: source,
|
||||
action_position: index
|
||||
};
|
||||
if (["BLOCK", "DELETE"].includes(event) && Experiments.data.reverseMenuOptions) {
|
||||
payload.experiment_id = Experiments.data.id;
|
||||
}
|
||||
dispatch(actions.NotifyEvent(payload));
|
||||
}
|
||||
},
|
||||
getOptions() {
|
||||
const {site, allowBlock, Experiments, dispatch} = this.props;
|
||||
const isNotDefault = site.type !== FIRST_RUN_TYPE;
|
||||
|
||||
// Don't add delete options for default links
|
||||
// that show up if your history is empty
|
||||
const deleteOptions = isNotDefault ? [
|
||||
{type: "separator"},
|
||||
allowBlock && {
|
||||
ref: "dismiss",
|
||||
label: "Dismiss",
|
||||
userEvent: "BLOCK",
|
||||
onClick: () => dispatch(actions.NotifyBlockURL(site.url))
|
||||
},
|
||||
{
|
||||
ref: "delete",
|
||||
label: "Delete from History",
|
||||
userEvent: "DELETE",
|
||||
onClick: () => dispatch(actions.NotifyHistoryDelete(site.url))
|
||||
}
|
||||
] : [];
|
||||
|
||||
if (Experiments.data.reverseMenuOptions) {
|
||||
deleteOptions.reverse();
|
||||
}
|
||||
|
||||
return [
|
||||
(site.bookmarkGuid ? {
|
||||
ref: "removeBookmark",
|
||||
label: "Remove Bookmark",
|
||||
userEvent: "BOOKMARK_DELETE",
|
||||
onClick: () => dispatch(actions.NotifyBookmarkDelete(site.bookmarkGuid))
|
||||
} : {
|
||||
ref: "addBookmark",
|
||||
label: "Bookmark",
|
||||
userEvent: "BOOKMARK_ADD",
|
||||
onClick: () => dispatch(actions.NotifyBookmarkAdd(site.url))
|
||||
}),
|
||||
{type: "separator"},
|
||||
{
|
||||
ref: "openWindow",
|
||||
label: "Open in a New Window",
|
||||
userEvent: "OPEN_NEW_WINDOW",
|
||||
onClick: () => dispatch(actions.NotifyOpenWindow({url: site.url}))
|
||||
},
|
||||
{
|
||||
ref: "openPrivate",
|
||||
label: "Open in a Private Window",
|
||||
userEvent: "OPEN_PRIVATE_WINDOW",
|
||||
onClick: () => dispatch(actions.NotifyOpenWindow({url: site.url, isPrivate: true}))
|
||||
}]
|
||||
.concat(deleteOptions).filter(o => o);
|
||||
},
|
||||
render() {
|
||||
return (<ContextMenu
|
||||
visible={this.props.visible}
|
||||
onUpdate={this.props.onUpdate}
|
||||
onUserEvent={this.userEvent}
|
||||
options={this.getOptions()} />);
|
||||
}
|
||||
});
|
||||
|
||||
LinkMenu.propTypes = {
|
||||
visible: React.PropTypes.bool,
|
||||
onUpdate: React.PropTypes.func.isRequired,
|
||||
allowBlock: React.PropTypes.bool,
|
||||
site: React.PropTypes.shape({
|
||||
url: React.PropTypes.string.isRequired,
|
||||
bookmarkGuid: React.PropTypes.string
|
||||
}).isRequired,
|
||||
|
||||
// This is for events
|
||||
page: React.PropTypes.string,
|
||||
source: React.PropTypes.string,
|
||||
index: React.PropTypes.number
|
||||
};
|
||||
|
||||
module.exports = connect(({Experiments}) => ({Experiments}))(LinkMenu);
|
|
@ -0,0 +1,15 @@
|
|||
const React = require("react");
|
||||
|
||||
const LinkMenuButton = React.createClass({
|
||||
render() {
|
||||
return (<button className="link-menu-button" onClick={e => {e.preventDefault(); this.props.onClick(e);}}>
|
||||
<span className="sr-only">Open context menu</span>
|
||||
</button>);
|
||||
}
|
||||
});
|
||||
|
||||
LinkMenuButton.propTypes = {
|
||||
onClick: React.PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
module.exports = LinkMenuButton;
|
|
@ -0,0 +1,21 @@
|
|||
.link-menu-button {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
top: -($link-menu-button-size / 2);
|
||||
right: -($link-menu-button-size / 2);
|
||||
width: $link-menu-button-size;
|
||||
height: $link-menu-button-size;
|
||||
background-color: $white;
|
||||
background-image: url('img/glyph-more-16.svg');
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
background-clip: padding-box;
|
||||
border: $link-menu-button-border;
|
||||
border-radius: 100%;
|
||||
box-shadow: $link-menu-button-boxshadow;
|
||||
transform: scale(0.25);
|
||||
opacity: 0;
|
||||
transition-property: transform, opacity;
|
||||
transition-duration: 200ms;
|
||||
z-index: 399;
|
||||
}
|
|
@ -4,9 +4,9 @@ const {justDispatch} = require("selectors/selectors");
|
|||
const {actions} = require("common/action-manager");
|
||||
const moment = require("moment");
|
||||
const SiteIcon = require("components/SiteIcon/SiteIcon");
|
||||
const DeleteMenu = require("components/DeleteMenu/DeleteMenu");
|
||||
const LinkMenu = require("components/LinkMenu/LinkMenu");
|
||||
const LinkMenuButton = require("components/LinkMenuButton/LinkMenuButton");
|
||||
const classNames = require("classnames");
|
||||
const {FIRST_RUN_TYPE} = require("lib/first-run-data");
|
||||
|
||||
const DEFAULT_LENGTH = 3;
|
||||
|
||||
|
@ -66,15 +66,11 @@ const SpotlightItem = React.createClass({
|
|||
</div>
|
||||
<div className="inner-border" />
|
||||
</a>
|
||||
<div
|
||||
hidden={site.type === FIRST_RUN_TYPE}
|
||||
className="tile-close-icon" ref="delete"
|
||||
onClick={() => this.setState({showContextMenu: true})} />
|
||||
<DeleteMenu
|
||||
<LinkMenuButton onClick={() => this.setState({showContextMenu: true})} />
|
||||
<LinkMenu
|
||||
visible={this.state.showContextMenu}
|
||||
onUpdate={val => this.setState({showContextMenu: val})}
|
||||
url={site.url}
|
||||
bookmarkGuid={site.bookmarkGuid}
|
||||
site={site}
|
||||
page={this.props.page}
|
||||
index={this.props.index}
|
||||
source={this.props.source}
|
||||
|
|
|
@ -9,16 +9,11 @@
|
|||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.context-menu {
|
||||
top: 14px;
|
||||
right: -12px;
|
||||
}
|
||||
}
|
||||
|
||||
.spotlight-item {
|
||||
@include item-shadow;
|
||||
@include tile-close;
|
||||
@include link-menu-button;
|
||||
background: $white;
|
||||
display: inline-block;
|
||||
margin-right: $base-gutter;
|
||||
|
|
|
@ -3,10 +3,10 @@ const {connect} = require("react-redux");
|
|||
const {justDispatch} = require("selectors/selectors");
|
||||
const {actions} = require("common/action-manager");
|
||||
const classNames = require("classnames");
|
||||
const DeleteMenu = require("components/DeleteMenu/DeleteMenu");
|
||||
const LinkMenu = require("components/LinkMenu/LinkMenu");
|
||||
const LinkMenuButton = require("components/LinkMenuButton/LinkMenuButton");
|
||||
const SiteIcon = require("components/SiteIcon/SiteIcon");
|
||||
const DEFAULT_LENGTH = 6;
|
||||
const {FIRST_RUN_TYPE} = require("lib/first-run-data");
|
||||
|
||||
const TopSites = React.createClass({
|
||||
getInitialState() {
|
||||
|
@ -44,21 +44,20 @@ const TopSites = React.createClass({
|
|||
return (<div className="tile-outer" key={site.guid || site.cacheKey || i}>
|
||||
<a onClick={() => this.onClick(i)} className={classNames("tile", {active: isActive})} href={site.url}>
|
||||
<SiteIcon className="tile-img-container" site={site} faviconSize={32} showTitle />
|
||||
<div hidden={site.type === FIRST_RUN_TYPE} className="tile-close-icon" onClick={(ev) => {
|
||||
<LinkMenuButton onClick={(ev) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
this.setState({showContextMenu: true, activeTile: i});
|
||||
}}></div>
|
||||
}} />
|
||||
<div className="inner-border" />
|
||||
</a>
|
||||
<DeleteMenu
|
||||
<LinkMenu
|
||||
visible={isActive}
|
||||
onUpdate={val => this.setState({showContextMenu: val})}
|
||||
url={site.url}
|
||||
bookmarkGuid={site.bookmarkGuid}
|
||||
site={site}
|
||||
page={this.props.page}
|
||||
index={i}
|
||||
source="TOP_SITES"
|
||||
index={i}
|
||||
/>
|
||||
</div>);
|
||||
})}
|
||||
|
|
|
@ -7,15 +7,11 @@
|
|||
}
|
||||
}
|
||||
|
||||
.context-menu {
|
||||
top: 14px;
|
||||
right: 12px;
|
||||
}
|
||||
|
||||
// This is a container for the delete menu
|
||||
.tile-outer {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
margin: 0 $tile-gutter 0 0;
|
||||
@media (min-width: $break-point) {
|
||||
display: block;
|
||||
}
|
||||
|
@ -23,14 +19,13 @@
|
|||
|
||||
.tile {
|
||||
@include item-shadow;
|
||||
@include tile-close;
|
||||
@include link-menu-button;
|
||||
display: inline-flex;
|
||||
flex-shrink: 0;
|
||||
border-radius: $border-radius;
|
||||
flex-direction: column;
|
||||
font-size: $tile-font-size;
|
||||
height: $tile-height;
|
||||
margin: 0 $tile-gutter 0 0;
|
||||
position: relative;
|
||||
text-decoration: none;
|
||||
width: $tile-width;
|
||||
|
|
|
@ -76,3 +76,4 @@ a {
|
|||
@import './components/Loader/Loader';
|
||||
@import './components/LoadMore/LoadMore';
|
||||
@import './components/ContextMenu/ContextMenu';
|
||||
@import './components/LinkMenuButton/LinkMenuButton';
|
||||
|
|
|
@ -36,11 +36,22 @@ module.exports = function setRowsOrError(requestType, responseType, querySize) {
|
|||
}
|
||||
}
|
||||
break;
|
||||
case am.type("RECEIVE_BOOKMARKS_CHANGES"):
|
||||
state.rows = prevState.rows.map(site => {
|
||||
if (site.type === "history" && site.url === action.data.url) {
|
||||
const {bookmarkGuid, bookmarkTitle, lastModified} = action.data;
|
||||
const frecency = typeof action.data.frecency !== "undefined" ? action.data.frecency : site.frecency;
|
||||
return Object.assign({}, site, {bookmarkGuid, bookmarkTitle, frecency, bookmarkDateCreated: lastModified});
|
||||
} else {
|
||||
return site;
|
||||
}
|
||||
});
|
||||
break;
|
||||
case am.type("NOTIFY_BLOCK_URL"):
|
||||
case am.type("NOTIFY_HISTORY_DELETE"):
|
||||
state.rows = prevState.rows.filter(val => val.url !== action.data);
|
||||
break;
|
||||
case am.type("NOTIFY_BOOKMARK_DELETE"):
|
||||
case requestType === am.type("RECENT_BOOKMARKS_REQUEST") && am.type("NOTIFY_BOOKMARK_DELETE"):
|
||||
state.rows = prevState.rows.filter(val => val.bookmarkGuid !== action.data);
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -41,8 +41,10 @@ $tile-border: 1px $white;
|
|||
$tile-title-bg-color: rgba($white, 0.6);
|
||||
$tile-title-hover-color: $white;
|
||||
$tile-title-font-size: 11px;
|
||||
$tile-close-border: 1px solid rgba($black, 0.2);
|
||||
$tile-close-boxshadow: 0 2px 0 rgba($black, 0.1);
|
||||
|
||||
$link-menu-button-size: 27px;
|
||||
$link-menu-button-border: 1px solid rgba($black, 0.2);
|
||||
$link-menu-button-boxshadow: 0 2px 0 rgba($black, 0.1);
|
||||
|
||||
$feed-font-size: 12px;
|
||||
$feed-date-font-color: #BFBFBF;
|
||||
|
@ -93,7 +95,8 @@ $item-shadow-hover: 0 1px 0 0 $faintest-black, 0 0 0 5px $faintest-black;
|
|||
|
||||
$loader-size: 16px;
|
||||
|
||||
$context-menu-shadow: 0 5px 10px rgba(0, 0, 0, 0.3), 0 0 0 1px rgba(0, 0, 0, 0.2);
|
||||
$context-menu-border-color: rgba(0, 0, 0, 0.2);
|
||||
$context-menu-shadow: 0 5px 10px rgba(0, 0, 0, 0.3), 0 0 0 1px $context-menu-border-color;
|
||||
$context-menu-font-size: 14px;
|
||||
$context-menu-border-radius: 5px;
|
||||
$context-menu-outer-padding: 5px;
|
||||
|
@ -109,35 +112,12 @@ $context-menu-item-padding: 3px 20px;
|
|||
}
|
||||
}
|
||||
|
||||
@mixin tile-close {
|
||||
.tile-close-icon {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
top: -14px;
|
||||
right: -14px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
background-color: $white;
|
||||
background-image: url('img/glyph-delete-16.svg');
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
background-clip: padding-box;
|
||||
border: $tile-close-border;
|
||||
border-radius: 100%;
|
||||
box-shadow: $tile-close-boxshadow;
|
||||
transform: scale(0.25);
|
||||
opacity: 0;
|
||||
transition-property: transform, opacity;
|
||||
transition-duration: 200ms;
|
||||
z-index: 399;
|
||||
}
|
||||
|
||||
@mixin link-menu-button {
|
||||
&:hover,
|
||||
&.active {
|
||||
.tile-close-icon {
|
||||
.link-menu-button {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
transition-delay: 500ms;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
const {assert} = require("chai");
|
||||
const ConnectedActivityFeed = require("components/ActivityFeed/ActivityFeed");
|
||||
const {ActivityFeedItem, GroupedActivityFeed, groupSitesBySession} = ConnectedActivityFeed;
|
||||
const DeleteMenu = require("components/DeleteMenu/DeleteMenu");
|
||||
const LinkMenu = require("components/LinkMenu/LinkMenu");
|
||||
const LinkMenuButton = require("components/LinkMenuButton/LinkMenuButton");
|
||||
const SiteIcon = require("components/SiteIcon/SiteIcon");
|
||||
const React = require("react");
|
||||
const ReactDOM = require("react-dom");
|
||||
|
@ -77,12 +78,12 @@ describe("ActivityFeedItem", function() {
|
|||
instance = renderWithProvider(<ActivityFeedItem {...fakeSiteWithBookmark} />);
|
||||
assert.include(ReactDOM.findDOMNode(instance).className, "bookmark");
|
||||
});
|
||||
it("should show the delete menu when the delete button is clicked", () => {
|
||||
it("should show the link menu when the link button is clicked", () => {
|
||||
const item = renderWithProvider(<ActivityFeedItem {...fakeSite} />);
|
||||
const button = item.refs.delete;
|
||||
const button = ReactDOM.findDOMNode(TestUtils.findRenderedComponentWithType(item, LinkMenuButton));
|
||||
TestUtils.Simulate.click(button);
|
||||
const deleteMenu = TestUtils.findRenderedComponentWithType(item, DeleteMenu);
|
||||
assert.equal(deleteMenu.props.visible, true);
|
||||
const menu = TestUtils.findRenderedComponentWithType(item, LinkMenu);
|
||||
assert.equal(menu.props.visible, true);
|
||||
});
|
||||
it("should render date if showDate=true", () => {
|
||||
const item = renderWithProvider(<ActivityFeedItem showDate={true} {...fakeSite} />);
|
||||
|
@ -171,20 +172,6 @@ describe("GroupedActivityFeed", function() {
|
|||
const link = TestUtils.scryRenderedComponentsWithType(instance, ActivityFeedItem)[0].refs.link;
|
||||
TestUtils.Simulate.click(link);
|
||||
});
|
||||
it("should send an event onShare", done => {
|
||||
function dispatch(a) {
|
||||
if (a.type === "NOTIFY_USER_EVENT") {
|
||||
assert.equal(a.data.event, "SHARE");
|
||||
assert.equal(a.data.page, "NEW_TAB");
|
||||
assert.equal(a.data.source, "ACTIVITY_FEED");
|
||||
assert.equal(a.data.action_position, 2);
|
||||
done();
|
||||
}
|
||||
}
|
||||
instance = renderWithProvider(<GroupedActivityFeed dispatch={dispatch} page={"NEW_TAB"} sites={sites} />);
|
||||
const link = TestUtils.scryRenderedComponentsWithType(instance, ActivityFeedItem)[2].refs.share;
|
||||
TestUtils.Simulate.click(link);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -44,17 +44,34 @@ describe("ContextMenu", () => {
|
|||
setup({options});
|
||||
assert.equal(links.length, options.length);
|
||||
});
|
||||
it("should add a ref for options if provided", () => {
|
||||
const options = [
|
||||
{label: "Test", onClick: () => {}, ref: "foo"}
|
||||
];
|
||||
setup({options});
|
||||
assert.ok(instance.refs.foo);
|
||||
});
|
||||
it("should call the onClick function when an option button is clicked", done => {
|
||||
setup({options: [{label: "Foo", onClick() {
|
||||
done();
|
||||
}}]});
|
||||
TestUtils.Simulate.click(links[0]);
|
||||
});
|
||||
it("should call the onUserEvent function when an option button is clicked and has a userEvent", done => {
|
||||
setup({
|
||||
onUserEvent: type => {
|
||||
assert.equal(type, "FOO");
|
||||
done();
|
||||
},
|
||||
options: [{label: "Foo", userEvent: "FOO", onClick() {}}]
|
||||
});
|
||||
TestUtils.Simulate.click(links[0]);
|
||||
});
|
||||
it("should call onUpdate with false when an option is clicked", done => {
|
||||
setup({
|
||||
visible: true,
|
||||
options: [{label: "Test", onClick: () => {}}],
|
||||
onUpdate: value => {
|
||||
onUpdate: (value) => {
|
||||
assert.isFalse(value);
|
||||
done();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,173 @@
|
|||
const {assert} = require("chai");
|
||||
const React = require("react");
|
||||
const TestUtils = require("react-addons-test-utils");
|
||||
const {renderWithProvider} = require("test/test-utils");
|
||||
const LinkMenu = require("components/LinkMenu/LinkMenu");
|
||||
const ContextMenu = require("components/ContextMenu/ContextMenu");
|
||||
const {FIRST_RUN_TYPE} = require("lib/first-run-data");
|
||||
|
||||
const DEFAULT_PROPS = {
|
||||
onUpdate: () => {},
|
||||
site: {
|
||||
url: "https://foo.com"
|
||||
},
|
||||
page: "NEW_TAB",
|
||||
source: "ACTIVITY_FEED",
|
||||
index: 3
|
||||
};
|
||||
const EXPERIMENT_DATA = {Experiments: {data: {id: "exp-01", reverseMenuOptions: true}}};
|
||||
|
||||
describe("LinkMenu", () => {
|
||||
let instance;
|
||||
let contextMenu;
|
||||
|
||||
function setup(custom = {}, customProvider = {}) {
|
||||
const props = Object.assign({}, DEFAULT_PROPS, custom);
|
||||
instance = renderWithProvider(<LinkMenu {...props} />, customProvider);
|
||||
contextMenu = TestUtils.findRenderedComponentWithType(instance, ContextMenu);
|
||||
}
|
||||
|
||||
beforeEach(setup);
|
||||
|
||||
it("should render a ContextMenu", () => {
|
||||
assert.ok(contextMenu);
|
||||
});
|
||||
|
||||
it("should pass visible, onUpdate props to ContextMenu", () => {
|
||||
const onUpdate = () => {};
|
||||
setup({visible: true, onUpdate});
|
||||
assert.isTrue(contextMenu.props.visible, "visible");
|
||||
assert.equal(contextMenu.props.onUpdate, onUpdate, "onUpdate");
|
||||
});
|
||||
|
||||
it("should show 'Add Bookmark' and hide 'Remove Bookmark' for a non-bookmark", () => {
|
||||
assert.ok(contextMenu.refs.addBookmark, "show addBoomark");
|
||||
assert.isUndefined(contextMenu.refs.removeBookmark, "hide removeBookmark");
|
||||
});
|
||||
|
||||
it("should hide 'Add Bookmark' and show 'Remove Bookmark' for a bookmark", () => {
|
||||
setup({site: {url: "https://foo.com", bookmarkGuid: "asdasd23123"}});
|
||||
assert.isUndefined(contextMenu.refs.addBookmark, "hide addBoomark");
|
||||
assert.ok(contextMenu.refs.removeBookmark, "show removeBookmark");
|
||||
});
|
||||
|
||||
it("should hide delete options for FIRST_RUN_TYPE", () => {
|
||||
setup({site: {url: "https://foo.com", type: FIRST_RUN_TYPE}});
|
||||
assert.isUndefined(contextMenu.refs.dismiss, "hide dismiss");
|
||||
assert.isUndefined(contextMenu.refs.delete, "hide delete");
|
||||
});
|
||||
|
||||
it("should hide dismiss option if allowBlock is false", () => {
|
||||
setup({allowBlock: false});
|
||||
assert.isUndefined(contextMenu.refs.dismiss, "hide dismiss");
|
||||
});
|
||||
|
||||
describe("individual options", () => {
|
||||
// Checks to make sure each action
|
||||
// 1. Fires a custom action (options.event)
|
||||
// 2. Has the right event data (options.eventData)
|
||||
// 3. Fires a NOTIFY_USER_EVENT with type options.userEvent
|
||||
// When options.ref is clicked
|
||||
function checkOption(options) {
|
||||
it(`should ${options.ref}`, done => {
|
||||
let count = 0;
|
||||
setup(options.props || {}, {dispatch(action) {
|
||||
if (action.type === options.event) {
|
||||
assert.deepEqual(action.data, options.eventData, "event data");
|
||||
count++;
|
||||
}
|
||||
if (action.type === "NOTIFY_USER_EVENT") {
|
||||
assert.equal(action.data.event, options.userEvent);
|
||||
assert.equal(action.data.page, DEFAULT_PROPS.page);
|
||||
assert.equal(action.data.source, DEFAULT_PROPS.source);
|
||||
assert.equal(action.data.action_position, DEFAULT_PROPS.index);
|
||||
count++;
|
||||
}
|
||||
if (count === 2) {
|
||||
done();
|
||||
}
|
||||
}});
|
||||
TestUtils.Simulate.click(contextMenu.refs[options.ref]);
|
||||
});
|
||||
}
|
||||
checkOption({
|
||||
ref: "removeBookmark",
|
||||
props: {site: {url: "https://foo.com", bookmarkGuid: "foo123"}},
|
||||
event: "NOTIFY_BOOKMARK_DELETE",
|
||||
eventData: "foo123",
|
||||
userEvent: "BOOKMARK_DELETE"
|
||||
});
|
||||
checkOption({
|
||||
ref: "addBookmark",
|
||||
event: "NOTIFY_BOOKMARK_ADD",
|
||||
eventData: DEFAULT_PROPS.site.url,
|
||||
userEvent: "BOOKMARK_ADD"
|
||||
});
|
||||
checkOption({
|
||||
ref: "openWindow",
|
||||
event: "NOTIFY_OPEN_WINDOW",
|
||||
eventData: {url: DEFAULT_PROPS.site.url},
|
||||
userEvent: "OPEN_NEW_WINDOW"
|
||||
});
|
||||
checkOption({
|
||||
ref: "openPrivate",
|
||||
event: "NOTIFY_OPEN_WINDOW",
|
||||
eventData: {url: DEFAULT_PROPS.site.url, isPrivate: true},
|
||||
userEvent: "OPEN_PRIVATE_WINDOW"
|
||||
});
|
||||
checkOption({
|
||||
ref: "dismiss",
|
||||
event: "NOTIFY_BLOCK_URL",
|
||||
eventData: DEFAULT_PROPS.site.url,
|
||||
userEvent: "BLOCK"
|
||||
});
|
||||
checkOption({
|
||||
ref: "delete",
|
||||
event: "NOTIFY_HISTORY_DELETE",
|
||||
eventData: DEFAULT_PROPS.site.url,
|
||||
userEvent: "DELETE"
|
||||
});
|
||||
});
|
||||
|
||||
describe("experiment", () => {
|
||||
it("should reverse delete options", () => {
|
||||
setup({}, {getState() {
|
||||
return {Experiments: {data: {reverseMenuOptions: true}}};
|
||||
}});
|
||||
let deleteIndex;
|
||||
let dismissIndex;
|
||||
contextMenu.props.options.forEach((o, i) => {
|
||||
if (o.ref === "dismiss") {
|
||||
dismissIndex = i;
|
||||
} else if (o.ref === "delete") {
|
||||
deleteIndex = i;
|
||||
}
|
||||
});
|
||||
assert.isBelow(deleteIndex, dismissIndex);
|
||||
});
|
||||
it("should not send the experiment id with non-delete user events", done => {
|
||||
setup({}, {
|
||||
getState: () => EXPERIMENT_DATA,
|
||||
dispatch(action) {
|
||||
if (action.type === "NOTIFY_USER_EVENT") {
|
||||
assert.isUndefined(action.data.experiment_id);
|
||||
done();
|
||||
}
|
||||
}
|
||||
});
|
||||
TestUtils.Simulate.click(contextMenu.refs.openWindow);
|
||||
});
|
||||
it("should send the experiment id with user events", done => {
|
||||
setup({}, {
|
||||
getState: () => EXPERIMENT_DATA,
|
||||
dispatch(action) {
|
||||
if (action.type === "NOTIFY_USER_EVENT") {
|
||||
assert.equal(action.data.experiment_id, "exp-01");
|
||||
done();
|
||||
}
|
||||
}
|
||||
});
|
||||
TestUtils.Simulate.click(contextMenu.refs.delete);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,22 @@
|
|||
const {assert} = require("chai");
|
||||
const React = require("react");
|
||||
const ReactDOM = require("react-dom");
|
||||
const TestUtils = require("react-addons-test-utils");
|
||||
const LinkMenuButton = require("components/LinkMenuButton/LinkMenuButton");
|
||||
|
||||
describe("LinkMenuButton", () => {
|
||||
it("should render", () => {
|
||||
let instance = TestUtils.renderIntoDocument(<LinkMenuButton onClick={() => {}} />);
|
||||
assert.ok(instance);
|
||||
});
|
||||
it("should preventDefault and call onClick when clicked", done => {
|
||||
let preventDefaultCalled = false;
|
||||
let instance = TestUtils.renderIntoDocument(<LinkMenuButton onClick={e => {
|
||||
assert.isTrue(preventDefaultCalled, "preventDefault was called");
|
||||
done();
|
||||
}} />);
|
||||
TestUtils.Simulate.click(ReactDOM.findDOMNode(instance), {preventDefault: () => {
|
||||
preventDefaultCalled = true;
|
||||
}});
|
||||
});
|
||||
});
|
|
@ -2,13 +2,13 @@ const {assert} = require("chai");
|
|||
const moment = require("moment");
|
||||
const ConnectedSpotlight = require("components/Spotlight/Spotlight");
|
||||
const {Spotlight, SpotlightItem} = ConnectedSpotlight;
|
||||
const DeleteMenu = require("components/DeleteMenu/DeleteMenu");
|
||||
const LinkMenu = require("components/LinkMenu/LinkMenu");
|
||||
const LinkMenuButton = require("components/LinkMenuButton/LinkMenuButton");
|
||||
const React = require("react");
|
||||
const ReactDOM = require("react-dom");
|
||||
const TestUtils = require("react-addons-test-utils");
|
||||
const SiteIcon = require("components/SiteIcon/SiteIcon");
|
||||
const {mockData, faker, renderWithProvider} = require("test/test-utils");
|
||||
const firstRunData = require("lib/first-run-data");
|
||||
const fakeSpotlightItems = mockData.Spotlight.rows;
|
||||
const fakeSiteWithImage = faker.createSite();
|
||||
fakeSiteWithImage.bestImage = fakeSiteWithImage.images[0];
|
||||
|
@ -102,15 +102,11 @@ describe("SpotlightItem", function() {
|
|||
instance = renderWithProvider(<SpotlightItem {...props} />);
|
||||
assert.equal(instance.refs.contextMessage.textContent, "Visited recently");
|
||||
});
|
||||
it("should not show delete icon for first run items", () => {
|
||||
instance = renderWithProvider(<SpotlightItem {...firstRunData.Highlights[0]} />);
|
||||
assert.equal(instance.refs.delete.hidden, true);
|
||||
});
|
||||
it("should show delete menu when delete icon is pressed", () => {
|
||||
const button = instance.refs.delete;
|
||||
it("should show link menu when link button is pressed", () => {
|
||||
const button = ReactDOM.findDOMNode(TestUtils.findRenderedComponentWithType(instance, LinkMenuButton));
|
||||
TestUtils.Simulate.click(button);
|
||||
const deleteMenu = TestUtils.findRenderedComponentWithType(instance, DeleteMenu);
|
||||
assert.equal(deleteMenu.props.visible, true);
|
||||
const menu = TestUtils.findRenderedComponentWithType(instance, LinkMenu);
|
||||
assert.equal(menu.props.visible, true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -3,10 +3,10 @@ const TestUtils = require("react-addons-test-utils");
|
|||
const React = require("react");
|
||||
const ReactDOM = require("react-dom");
|
||||
const {overrideConsoleError, renderWithProvider} = require("test/test-utils");
|
||||
const firstRunData = require("lib/first-run-data");
|
||||
const ConnectedTopSites = require("components/TopSites/TopSites");
|
||||
const {TopSites} = ConnectedTopSites;
|
||||
const DeleteMenu = require("components/DeleteMenu/DeleteMenu");
|
||||
const LinkMenu = require("components/LinkMenu/LinkMenu");
|
||||
const LinkMenuButton = require("components/LinkMenuButton/LinkMenuButton");
|
||||
const SiteIcon = require("components/SiteIcon/SiteIcon");
|
||||
|
||||
const fakeProps = {
|
||||
|
@ -58,17 +58,11 @@ describe("TopSites", () => {
|
|||
assert.include(linkEls[1].href, fakeProps.sites[1].url);
|
||||
});
|
||||
|
||||
it("should hide the delete button for first run items", () => {
|
||||
topSites = renderWithProvider(<TopSites sites={firstRunData.TopSites} />);
|
||||
const button = TestUtils.scryRenderedDOMComponentsWithClass(topSites, "tile-close-icon")[0];
|
||||
assert.equal(button.hidden, true);
|
||||
});
|
||||
|
||||
it("should show delete menu when delete button is clicked", () => {
|
||||
const button = TestUtils.scryRenderedDOMComponentsWithClass(topSites, "tile-close-icon")[0];
|
||||
const button = ReactDOM.findDOMNode(TestUtils.scryRenderedComponentsWithType(topSites, LinkMenuButton)[0]);
|
||||
TestUtils.Simulate.click(button);
|
||||
const deleteMenu = TestUtils.scryRenderedComponentsWithType(topSites, DeleteMenu)[0];
|
||||
assert.equal(deleteMenu.props.visible, true);
|
||||
const menu = TestUtils.scryRenderedComponentsWithType(topSites, LinkMenu)[0];
|
||||
assert.equal(menu.props.visible, true);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ const RESPONSE_TYPE = "RECENT_LINKS_RESPONSE";
|
|||
|
||||
describe("setRowsOrError", () => {
|
||||
let reducer;
|
||||
|
||||
beforeEach(() => {
|
||||
reducer = setRowsOrError(REQUEST_TYPE, RESPONSE_TYPE);
|
||||
});
|
||||
|
@ -103,6 +104,49 @@ describe("setRowsOrError", () => {
|
|||
assert.isTrue(state.canLoadMore);
|
||||
});
|
||||
|
||||
it("should set bookmark status of history items on RECEIVE_BOOKMARKS_CHANGES", () => {
|
||||
const action = {type: "RECEIVE_BOOKMARKS_CHANGES", data: {
|
||||
bookmarkGuid: "bookmark123",
|
||||
lastModified: 1234124,
|
||||
frecency: 200,
|
||||
bookmarkTitle: "foo",
|
||||
url: "https://foo.com"
|
||||
}};
|
||||
const prevRows = [
|
||||
{type: "history", url: "blah.com"},
|
||||
{type: "history", url: "https://foo.com", frecency: 1}
|
||||
];
|
||||
const result = reducer(Object.assign({}, setRowsOrError.DEFAULTS, {rows: prevRows}), action);
|
||||
const newRow = result.rows[1];
|
||||
assert.equal(newRow.bookmarkGuid, action.data.bookmarkGuid, "should have the right bookmarkGuid");
|
||||
assert.equal(newRow.bookmarkDateCreated, action.data.lastModified, "should have the right bookmarkDateCreated");
|
||||
assert.equal(newRow.frecency, action.data.frecency, "should have the right frecency");
|
||||
assert.equal(newRow.bookmarkTitle, action.data.bookmarkTitle, "should have the right bookmarkTitle");
|
||||
});
|
||||
|
||||
it("should remove bookmark status of history items on RECEIVE_BOOKMARKS_CHANGES", () => {
|
||||
const action = {type: "RECEIVE_BOOKMARKS_CHANGES", data: {
|
||||
url: "https://foo.com"
|
||||
}};
|
||||
const prevRows = [
|
||||
{type: "history", url: "blah.com"},
|
||||
{
|
||||
type: "history",
|
||||
bookmarkGuid: "bookmark123",
|
||||
bookmarkDateCreated: 1234124,
|
||||
frecency: 200,
|
||||
bookmarkTitle: "foo",
|
||||
url: "https://foo.com"
|
||||
}
|
||||
];
|
||||
const result = reducer(Object.assign({}, setRowsOrError.DEFAULTS, {rows: prevRows}), action);
|
||||
const newRow = result.rows[1];
|
||||
assert.isUndefined(newRow.bookmarkGuid, "should remove bookmarkGuid");
|
||||
assert.isUndefined(newRow.bookmarkDateCreated, "should remove bookmarkDateCreated");
|
||||
assert.equal(newRow.frecency, prevRows[1].frecency, "should not change the frecency");
|
||||
assert.isUndefined(newRow.bookmarkTitle, "should remove bookmarkTitle");
|
||||
});
|
||||
|
||||
((event) => {
|
||||
it(`should remove a row removed via ${event}`, () => {
|
||||
const action = {type: event, data: "http://foo.com"};
|
||||
|
@ -112,13 +156,12 @@ describe("setRowsOrError", () => {
|
|||
});
|
||||
})("NOTIFY_HISTORY_DELETE", "NOTIFY_BLOCK_URL");
|
||||
|
||||
((event) => {
|
||||
it(`should remove a row removed via ${event}`, () => {
|
||||
const action = {type: event, data: "boorkmarkFOO"};
|
||||
const prevRows = [{bookmarkGuid: "boorkmarkFOO"}, {bookmarkGuid: "boorkmarkBAR"}];
|
||||
const state = reducer(Object.assign({}, setRowsOrError.DEFAULTS, {rows: prevRows}), action);
|
||||
assert.deepEqual(state.rows, [{bookmarkGuid: "boorkmarkBAR"}]);
|
||||
});
|
||||
})("NOTIFY_BOOKMARK_DELETE", "NOTIFY_BLOCK_URL");
|
||||
it("should remove \"NOTIFY_BOOKMARK_DELETE\" if request type is \"RECENT_BOOKMARKS_REQUEST\"", () => {
|
||||
reducer = setRowsOrError("RECENT_BOOKMARKS_REQUEST", "RECENT_LINKS_RESPONSE");
|
||||
const action = {type: "NOTIFY_BOOKMARK_DELETE", data: "boorkmarkFOO"};
|
||||
const prevRows = [{bookmarkGuid: "boorkmarkFOO"}, {bookmarkGuid: "boorkmarkBAR"}];
|
||||
const state = reducer(Object.assign({}, setRowsOrError.DEFAULTS, {rows: prevRows}), action);
|
||||
assert.deepEqual(state.rows, [{bookmarkGuid: "boorkmarkBAR"}]);
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -10,6 +10,7 @@ const {ActionButton} = require("sdk/ui/button/action");
|
|||
const tabs = require("sdk/tabs");
|
||||
const simplePrefs = require("sdk/simple-prefs");
|
||||
const privateBrowsing = require("sdk/private-browsing");
|
||||
const windows = require("sdk/windows").browserWindows;
|
||||
const {Memoizer} = require("lib/Memoizer");
|
||||
const {PlacesProvider} = require("lib/PlacesProvider");
|
||||
const {SearchProvider} = require("lib/SearchProvider");
|
||||
|
@ -128,6 +129,15 @@ ActivityStreams.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
_respondOpenWindow({msg}) {
|
||||
if (msg.type === am.type("NOTIFY_OPEN_WINDOW")) {
|
||||
windows.open({
|
||||
url: msg.data.url,
|
||||
isPrivate: msg.data.isPrivate
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Responds to places requests
|
||||
*/
|
||||
|
@ -153,6 +163,9 @@ ActivityStreams.prototype = {
|
|||
this._processAndSendLinks(links, "HIGHLIGHTS_LINKS_RESPONSE", worker, msg.meta);
|
||||
});
|
||||
break;
|
||||
case am.type("NOTIFY_BOOKMARK_ADD"):
|
||||
PlacesProvider.links.asyncAddBookmark(msg.data);
|
||||
break;
|
||||
case am.type("NOTIFY_BOOKMARK_DELETE"):
|
||||
PlacesProvider.links.asyncDeleteBookmark(msg.data);
|
||||
break;
|
||||
|
@ -288,6 +301,7 @@ ActivityStreams.prototype = {
|
|||
this._respondToPlacesRequests(args);
|
||||
this._respondToSearchRequests(args);
|
||||
this._logPerfMeter(args);
|
||||
this._respondOpenWindow(args);
|
||||
};
|
||||
this.on(CONTENT_TO_ADDON, this._contentToAddonHandlers);
|
||||
},
|
||||
|
|
|
@ -277,6 +277,16 @@ Links.prototype = {
|
|||
return Bookmarks.remove(bookmarkGuid);
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds a bookmark
|
||||
*
|
||||
* @param {String} url the url to bookmark
|
||||
* @returns {Promise} Returns a promise set to an object representing the bookmark
|
||||
*/
|
||||
asyncAddBookmark: function PlacesProvider_asyncAddBookmark(url) {
|
||||
return Bookmarks.insert({url, type: Bookmarks.TYPE_BOOKMARK, parentGuid: Bookmarks.unfiledGuid});
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes a history link
|
||||
*
|
||||
|
|
|
@ -238,6 +238,29 @@ exports.test_Links_getFrecentLinks = function*(assert) {
|
|||
assert.equal(links[3].url, "https://mozilla4.com/3", "Expected 4th link");
|
||||
};
|
||||
|
||||
exports.test_Links_asyncAddBookmark = function*(assert) {
|
||||
let provider = PlacesProvider.links;
|
||||
|
||||
let bookmarks = [
|
||||
"https://mozilla1.com/0",
|
||||
"https://mozilla1.com/1"
|
||||
];
|
||||
|
||||
let links = yield provider.getRecentBookmarks();
|
||||
assert.equal(links.length, 0, "empty bookmarks yields empty links");
|
||||
let bookmarksSize = yield provider.getBookmarksSize();
|
||||
assert.equal(bookmarksSize, 0, "empty bookmarks yields 0 size");
|
||||
|
||||
for (let url of bookmarks) {
|
||||
yield provider.asyncAddBookmark(url);
|
||||
}
|
||||
|
||||
links = yield provider.getRecentBookmarks();
|
||||
assert.equal(links.length, 2, "2 bookmarks on bookmark list");
|
||||
bookmarksSize = yield provider.getBookmarksSize();
|
||||
assert.equal(bookmarksSize, 2, "size 2 for 2 bookmarks added");
|
||||
};
|
||||
|
||||
exports.test_Links_asyncDeleteBookmark = function*(assert) {
|
||||
let provider = PlacesProvider.links;
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче