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:
parent
0b3c91a7bd
commit
95c206b346
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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",
|
||||
},
|
||||
});
|
|
@ -0,0 +1,5 @@
|
|||
// @flow
|
||||
|
||||
import LocalStore from "../LocalStore";
|
||||
|
||||
export default new LocalStore({version: "1", keyPrefix: "cred-explorer"});
|
Loading…
Reference in New Issue