Allow selecting data to load in V3 cred explorer (#460)

Summary:
Text input boxes for repository owner and name now appear. “Loading the
data” consists of logging the attempt to the console.

Test Plan:
Run `yarn start`, and note that the inputs are keyed against the same
local store key as their V1 equivalents. Note that clicking “Load data”
prints a message to the console.

Paired with @decentralion.

wchargin-branch: v3-load-data-ui
This commit is contained in:
William Chargin 2018-06-29 18:04:20 -07:00 committed by GitHub
parent 0b3c91a7bd
commit 95c206b346
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 191 additions and 0 deletions

View File

@ -3,9 +3,12 @@
import React from "react";
import {Route, NavLink, type Match} from "react-router-dom";
import CredExplorer from "./credExplorer/App";
export default class App extends React.Component<{match: Match}> {
render() {
const {match} = this.props;
const CRED_EXPLORER_ROUTE = match.url + "/explorer";
return (
<div>
<nav>
@ -13,11 +16,15 @@ export default class App extends React.Component<{match: Match}> {
<li>
<NavLink to={match.url}>Home</NavLink>
</li>
<li>
<NavLink to={CRED_EXPLORER_ROUTE}>Cred Explorer</NavLink>
</li>
</ul>
</nav>
<hr />
<Route exact path={match.url} component={Home} />
<Route path={CRED_EXPLORER_ROUTE} component={CredExplorer} />
</div>
);
}

87
src/v3/app/LocalStore.js Normal file
View File

@ -0,0 +1,87 @@
// @flow
/*
* A simple abstraction over 'localStorage' to provide transparent JSON
* serialization and deserialization.
*
* The implementation is borrowed heavily from Khan Academy's LocalStore
* module, and also KaVideoPlayer's SafeLocalStore module.
*/
export default class LocalStore {
version: string;
keyPrefix: string;
constructor({version, keyPrefix}: {|+version: string, +keyPrefix: string|}) {
this.version = version;
this.keyPrefix = keyPrefix;
}
cacheKey(key: string): string {
if (!key) {
throw new Error("Falsy key provided to cacheKey: " + key);
}
return [this.keyPrefix, this.version, key].join(":");
}
get(key: string, whenUnavailable: any): any {
if (!this.isEnabled()) {
return whenUnavailable;
}
try {
const data = window.localStorage[this.cacheKey(key)];
if (data) {
return JSON.parse(data);
} else {
return whenUnavailable;
}
} catch (e) {
// If we had trouble retrieving, like FF's NS_FILE_CORRUPTED:
// http://stackoverflow.com/q/18877643/
return whenUnavailable;
}
}
set(key: string, data: any): void {
if (!this.isEnabled()) {
return;
}
const stringified = JSON.stringify(data);
try {
window.localStorage[this.cacheKey(key)] = stringified;
} catch (e) {
// Probably went over the storage limit... that's not good.
throw e;
}
}
/*
* Delete whatever data was associated with the given key.
*/
del(key: string): void {
if (!this.isEnabled()) {
return;
}
const cacheKey = this.cacheKey(key);
if (cacheKey in window.localStorage) {
// (IE throws when deleting a non-existent entry.)
delete window.localStorage[cacheKey];
}
}
/*
* Local storage might be disabled in old browsers or in Safari's
* private browsing mode. Don't die.
*/
isEnabled(): boolean {
const uid = String(+new Date());
try {
window.sessionStorage[uid] = uid;
const enabled = window.sessionStorage[uid] === uid;
window.sessionStorage.removeItem(uid);
return enabled;
} catch (e) {
return false;
}
}
}

View File

@ -0,0 +1,92 @@
// @flow
import React from "react";
import {StyleSheet, css} from "aphrodite/no-important";
import LocalStore from "./LocalStore";
type Props = {};
type State = {
repoOwner: string,
repoName: string,
};
const REPO_OWNER_KEY = "repoOwner";
const REPO_NAME_KEY = "repoName";
export default class App extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
repoOwner: "",
repoName: "",
};
}
componentDidMount() {
this.setState((state) => ({
repoOwner: LocalStore.get(REPO_OWNER_KEY, state.repoOwner),
repoName: LocalStore.get(REPO_NAME_KEY, state.repoName),
}));
}
render() {
return (
<div>
<header className={css(styles.header)}>
<h1>Cred Explorer</h1>
</header>
<p>Welcome to the SourceCred Explorer!</p>
<div>
<label>
Repository owner:
<input
value={this.state.repoOwner}
onChange={(e) => {
const value = e.target.value;
this.setState({repoOwner: value}, () => {
LocalStore.set(REPO_OWNER_KEY, this.state.repoOwner);
});
}}
/>
</label>
<br />
<label>
Repository name:
<input
value={this.state.repoName}
onChange={(e) => {
const value = e.target.value;
this.setState({repoName: value}, () => {
LocalStore.set(REPO_NAME_KEY, this.state.repoName);
});
}}
/>
</label>
<br />
<button onClick={() => this.loadData()}>Load data</button>
</div>
</div>
);
}
loadData() {
const validRe = /^[A-Za-z0-9_-]+$/;
const {repoOwner, repoName} = this.state;
if (!repoOwner.match(validRe)) {
console.error(`Invalid repository owner: ${JSON.stringify(repoOwner)}`);
return;
}
if (!repoName.match(validRe)) {
console.error(`Invalid repository name: ${JSON.stringify(repoName)}`);
return;
}
console.log(`Would load data for: ${repoOwner}/${repoName}.`);
}
}
const styles = StyleSheet.create({
header: {
color: "#090",
},
});

View File

@ -0,0 +1,5 @@
// @flow
import LocalStore from "../LocalStore";
export default new LocalStore({version: "1", keyPrefix: "cred-explorer"});