Added ability to save bookmarks in Nav Plugin
Summary: This is a glue commit that glues all the components I've added in the past together. Favouriting a page (i.e. clicking on the star) adds it as a bookmark. There's four main parts to make your rreview easier: 1. Add bookmarks and favouriting to all the components that support it, including their parents. (NavigationInfoBox, SearchBar, Timeline) 2. Persist bookmarks using the indexedDB. (index.js) 3. Add saving to db through the SaveBookmarksDialog 4. Various other changes due to a changed architecture. i.e. moving bookmarks from persistedState to state. Still to come. 1. Removing bookmarks. 2. Pressing enter to save the bookmarks when the SaveBookmarksDialog pops up. 3. Alphabetizing bookmarks? Order seems to jump around. Reviewed By: jknoxville Differential Revision: D16518013 fbshipit-source-id: 2e0cef14123c611db43cca360bc66dc4c05b11ed
This commit is contained in:
committed by
Facebook Github Bot
parent
e4601a89f3
commit
84f9a1d8b5
@@ -12,14 +12,12 @@ import type {PersistedState} from '../';
|
||||
|
||||
function constructPersistedStateMock(): PersistedState {
|
||||
return {
|
||||
bookmarks: [],
|
||||
navigationEvents: [],
|
||||
};
|
||||
}
|
||||
|
||||
function constructPersistedStateMockWithEvents(): PersistedState {
|
||||
return {
|
||||
bookmarks: [],
|
||||
navigationEvents: [
|
||||
{
|
||||
uri: 'mock://this_is_a_mock_uri/mock/1',
|
||||
|
||||
@@ -11,7 +11,7 @@ import {DetailSidebar, FlexCenter, styled, colors} from 'flipper';
|
||||
import type {Bookmark} from '../';
|
||||
|
||||
type Props = {|
|
||||
bookmarks: Array<Bookmark>,
|
||||
bookmarks: Map<string, Bookmark>,
|
||||
onNavigate: string => void,
|
||||
|};
|
||||
|
||||
@@ -57,11 +57,11 @@ export default (props: Props) => {
|
||||
const {bookmarks, onNavigate} = props;
|
||||
return (
|
||||
<DetailSidebar>
|
||||
{bookmarks.length === 0 ? (
|
||||
{bookmarks.size === 0 ? (
|
||||
<NoData grow>No Bookmarks</NoData>
|
||||
) : (
|
||||
<BookmarksList>
|
||||
{bookmarks.map(bookmark => (
|
||||
{[...bookmarks.values()].map(bookmark => (
|
||||
<div
|
||||
className="bookmark-container"
|
||||
role="button"
|
||||
|
||||
@@ -11,6 +11,7 @@ import {parseURIParameters} from '../util/uri';
|
||||
import {IconButton, FavoriteButton} from './';
|
||||
|
||||
type Props = {|
|
||||
isBookmarked: boolean,
|
||||
uri: ?string,
|
||||
onNavigate: (query: string) => void,
|
||||
onFavorite: (query: string) => void,
|
||||
@@ -52,7 +53,7 @@ const NavigationInfoBoxContainer = styled('div')({
|
||||
});
|
||||
|
||||
export default (props: Props) => {
|
||||
const {uri} = props;
|
||||
const {uri, isBookmarked} = props;
|
||||
if (uri == null) {
|
||||
return (
|
||||
<NavigationInfoBoxContainer>
|
||||
@@ -65,7 +66,7 @@ export default (props: Props) => {
|
||||
<NavigationInfoBoxContainer>
|
||||
<div className="icon-container">
|
||||
<FavoriteButton
|
||||
highlighted={false}
|
||||
highlighted={isBookmarked}
|
||||
size={16}
|
||||
onClick={() => props.onFavorite(uri)}
|
||||
/>
|
||||
|
||||
@@ -7,12 +7,15 @@
|
||||
*/
|
||||
|
||||
import {Button, FlexColumn, Input, Sheet, styled} from 'flipper';
|
||||
import {useState} from 'react';
|
||||
|
||||
import type {Bookmark} from '../';
|
||||
|
||||
type Props = {|
|
||||
uri: ?string,
|
||||
shouldShow: boolean,
|
||||
onHide: ?() => void,
|
||||
onSubmit: ?(uri: string) => void,
|
||||
onSubmit: Bookmark => void,
|
||||
|};
|
||||
|
||||
const Container = styled(FlexColumn)({
|
||||
@@ -45,6 +48,7 @@ const NameInput = styled(Input)({
|
||||
|
||||
export default (props: Props) => {
|
||||
const {shouldShow, onHide, onSubmit, uri} = props;
|
||||
const [commonName, setCommonName] = useState('');
|
||||
if (uri == null || !shouldShow) {
|
||||
return null;
|
||||
} else {
|
||||
@@ -54,19 +58,30 @@ export default (props: Props) => {
|
||||
return (
|
||||
<Container>
|
||||
<Title>Save to bookmarks...</Title>
|
||||
<NameInput placeholder="Name..." />
|
||||
<NameInput
|
||||
placeholder="Name..."
|
||||
value={commonName}
|
||||
onChange={event => setCommonName(event.target.value)}
|
||||
/>
|
||||
<URIContainer>{uri}</URIContainer>
|
||||
<ButtonContainer>
|
||||
<Button onClick={() => hide()} compact padded>
|
||||
<Button
|
||||
onClick={() => {
|
||||
hide();
|
||||
setCommonName('');
|
||||
}}
|
||||
compact
|
||||
padded>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
hide();
|
||||
if (onSubmit != null) {
|
||||
onSubmit(uri);
|
||||
}
|
||||
onSubmit({uri, commonName});
|
||||
// The component state is remembered even after unmounting.
|
||||
// Thus it is necessary to reset the commonName here.
|
||||
setCommonName('');
|
||||
}}
|
||||
compact
|
||||
padded>
|
||||
|
||||
@@ -16,9 +16,12 @@ import {
|
||||
} from 'flipper';
|
||||
import {IconButton, FavoriteButton} from './';
|
||||
|
||||
import type {Bookmark} from '../';
|
||||
|
||||
type Props = {|
|
||||
onFavorite: (query: string) => void,
|
||||
onNavigate: (query: string) => void,
|
||||
bookmarks: Map<string, Bookmark>,
|
||||
|};
|
||||
|
||||
type State = {|
|
||||
@@ -63,6 +66,8 @@ class SearchBar extends Component<Props, State> {
|
||||
};
|
||||
|
||||
render = () => {
|
||||
const {bookmarks} = this.props;
|
||||
const {query} = this.state;
|
||||
return (
|
||||
<Toolbar>
|
||||
<SearchBox>
|
||||
@@ -79,19 +84,21 @@ class SearchBar extends Component<Props, State> {
|
||||
<Glyph name="chevron-down" size={12} />
|
||||
</SearchChevronContainer>
|
||||
</SearchBox>
|
||||
<IconContainer>
|
||||
<IconButton
|
||||
icon="send"
|
||||
size={16}
|
||||
outline={true}
|
||||
onClick={() => this.navigateTo(this.state.query)}
|
||||
/>
|
||||
<FavoriteButton
|
||||
size={16}
|
||||
highlighted={false}
|
||||
onClick={() => this.favorite(this.state.query)}
|
||||
/>
|
||||
</IconContainer>
|
||||
{query.length > 0 ? (
|
||||
<IconContainer>
|
||||
<IconButton
|
||||
icon="send"
|
||||
size={16}
|
||||
outline={true}
|
||||
onClick={() => this.navigateTo(this.state.query)}
|
||||
/>
|
||||
<FavoriteButton
|
||||
size={16}
|
||||
highlighted={bookmarks.has(query)}
|
||||
onClick={() => this.favorite(this.state.query)}
|
||||
/>
|
||||
</IconContainer>
|
||||
) : null}
|
||||
</Toolbar>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -9,11 +9,13 @@
|
||||
import {styled} from 'flipper';
|
||||
import {NavigationInfoBox} from './';
|
||||
|
||||
import type {NavigationEvent} from '../';
|
||||
import type {Bookmark, NavigationEvent} from '../';
|
||||
|
||||
type Props = {|
|
||||
bookmarks: Map<string, Bookmark>,
|
||||
events: Array<NavigationEvent>,
|
||||
onNavigate: string => void,
|
||||
onFavorite: string => void,
|
||||
|};
|
||||
|
||||
const TimelineContainer = styled('div')({
|
||||
@@ -22,14 +24,16 @@ const TimelineContainer = styled('div')({
|
||||
});
|
||||
|
||||
export default (props: Props) => {
|
||||
const {bookmarks, events, onNavigate, onFavorite} = props;
|
||||
return (
|
||||
<TimelineContainer>
|
||||
{props.events.map((event: NavigationEvent) => {
|
||||
{events.map((event: NavigationEvent) => {
|
||||
return (
|
||||
<NavigationInfoBox
|
||||
isBookmarked={event.uri != null ? bookmarks.has(event.uri) : false}
|
||||
uri={event.uri}
|
||||
onNavigate={props.onNavigate}
|
||||
onFavorite={() => {}} // stubbed for now
|
||||
onNavigate={onNavigate}
|
||||
onFavorite={onFavorite}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -9,12 +9,18 @@
|
||||
import {FlipperPlugin} from 'flipper';
|
||||
import {
|
||||
BookmarksSidebar,
|
||||
SaveBookmarkDialog,
|
||||
SearchBar,
|
||||
Timeline,
|
||||
ScrollableFlexColumn,
|
||||
} from './components';
|
||||
import {readBookmarksFromDB, writeBookmarkToDB} from './util/indexedDB';
|
||||
|
||||
type State = {||};
|
||||
type State = {|
|
||||
bookmarks: Map<string, Bookmark>,
|
||||
shouldShowSaveBookmarkDialog: boolean,
|
||||
saveBookmarkURI: ?string,
|
||||
|};
|
||||
|
||||
export type NavigationEvent = {|
|
||||
date: ?Date,
|
||||
@@ -23,12 +29,11 @@ export type NavigationEvent = {|
|
||||
|
||||
export type Bookmark = {|
|
||||
uri: string,
|
||||
commonName: ?string,
|
||||
commonName: string,
|
||||
|};
|
||||
|
||||
export type PersistedState = {|
|
||||
navigationEvents: Array<NavigationEvent>,
|
||||
bookmarks: Array<Bookmark>,
|
||||
|};
|
||||
|
||||
export default class extends FlipperPlugin<State, {}, PersistedState> {
|
||||
@@ -39,7 +44,12 @@ export default class extends FlipperPlugin<State, {}, PersistedState> {
|
||||
|
||||
static defaultPersistedState: PersistedState = {
|
||||
navigationEvents: [],
|
||||
bookmarks: [],
|
||||
};
|
||||
|
||||
state = {
|
||||
bookmarks: new Map<string, Bookmark>(),
|
||||
shouldShowSaveBookmarkDialog: false,
|
||||
saveBookmarkURI: null,
|
||||
};
|
||||
|
||||
static persistedStateReducer = (
|
||||
@@ -66,6 +76,12 @@ export default class extends FlipperPlugin<State, {}, PersistedState> {
|
||||
}
|
||||
};
|
||||
|
||||
componentDidMount = () => {
|
||||
readBookmarksFromDB().then(bookmarks => {
|
||||
this.setState({bookmarks});
|
||||
});
|
||||
};
|
||||
|
||||
onKeyboardAction = (action: string) => {
|
||||
if (action === 'clear') {
|
||||
this.props.setPersistedState({navigationEvents: []});
|
||||
@@ -78,16 +94,45 @@ export default class extends FlipperPlugin<State, {}, PersistedState> {
|
||||
});
|
||||
};
|
||||
|
||||
onFavorite = (uri: string) => {
|
||||
this.setState({shouldShowSaveBookmarkDialog: true, saveBookmarkURI: uri});
|
||||
};
|
||||
|
||||
addBookmark = (bookmark: Bookmark) => {
|
||||
const newBookmark = {
|
||||
uri: bookmark.uri,
|
||||
commonName:
|
||||
bookmark.commonName.length > 0 ? bookmark.commonName : bookmark.uri,
|
||||
};
|
||||
writeBookmarkToDB(newBookmark);
|
||||
const newMapRef = this.state.bookmarks;
|
||||
newMapRef.set(newBookmark.uri, newBookmark);
|
||||
this.setState({bookmarks: newMapRef});
|
||||
};
|
||||
|
||||
render() {
|
||||
const {navigationEvents, bookmarks} = this.props.persistedState;
|
||||
const {bookmarks, shouldShowSaveBookmarkDialog} = this.state;
|
||||
const {navigationEvents} = this.props.persistedState;
|
||||
return (
|
||||
<ScrollableFlexColumn>
|
||||
<SearchBar
|
||||
bookmarks={bookmarks}
|
||||
onNavigate={this.navigateTo}
|
||||
onFavorite={(query: string) => {}}
|
||||
onFavorite={this.onFavorite}
|
||||
/>
|
||||
<Timeline
|
||||
bookmarks={bookmarks}
|
||||
events={navigationEvents}
|
||||
onNavigate={this.navigateTo}
|
||||
onFavorite={this.onFavorite}
|
||||
/>
|
||||
<Timeline events={navigationEvents} onNavigate={this.navigateTo} />
|
||||
<BookmarksSidebar bookmarks={bookmarks} onNavigate={this.navigateTo} />
|
||||
<SaveBookmarkDialog
|
||||
shouldShow={shouldShowSaveBookmarkDialog}
|
||||
uri={this.state.saveBookmarkURI}
|
||||
onHide={() => this.setState({shouldShowSaveBookmarkDialog: false})}
|
||||
onSubmit={this.addBookmark}
|
||||
/>
|
||||
</ScrollableFlexColumn>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user