added reduxy stuff

master
Adam Veldhousen 2020-01-19 15:06:38 -06:00
parent 5f8da6179f
commit 1972f9eebf
Signed by: adam
GPG Key ID: 6DB29003C6DD1E4B
25 changed files with 752 additions and 14448 deletions

25
main.js
View File

@ -1,25 +0,0 @@
const electron = require("electron");
const BrowserWindow = electron.BrowserWindow;
const app = electron.app;
function createWindow() {
// Create the browser window.
let win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true
}
});
if (process.env.NODE_ENV !== "prod") {
win.loadURL("http://localhost:3000");
} else {
// and load the index.html of the app.
win.loadFile("./build/index.html");
}
}
console.log(Object.keys(electron));
app.on("ready", createWindow);

14281
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,41 +1,51 @@
{
"name": "frontend",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^7.1.2",
"date-fns": "^2.9.0",
"lodash.debounce": "^4.0.8",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"react-markdown": "^4.3.1",
"react-scripts": "3.3.0"
"name": "frontend",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^7.1.2",
"date-fns": "^2.9.0",
"lodash.debounce": "^4.0.8",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"react-markdown": "^4.3.1",
"react-scripts": "3.3.0",
"weedux": "^3.5.3-beta"
},
"scripts": {
"start": "electron ./src/server",
"dev": "BROWSER=none react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"build": {
"appId": "com.electron.electron-with-create-react-app",
"win": {
"iconUrl": "https://cdn2.iconfinder.com/data/icons/designer-skills/128/react-256.png"
},
"scripts": {
"start": "electron ./main.js",
"dev": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"electron": "^7.1.9"
"directories": {
"buildResources": "public"
}
},
"devDependencies": {
"electron": "^7.1.9"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}

View File

@ -5,6 +5,7 @@
background-color: #282c34;
color: white;
width: 100%;
height: 100vh;
}
.SearchArea {

View File

@ -1,74 +1,60 @@
import React, { PureComponent } from "react";
import { connect } from "weedux";
import { store, actions } from "./state/index.js";
import NoteList from "./components/NoteList/NoteList";
import NoteContent from "./components/NoteContent";
import Omnibar from "./components/Omnibar";
import { createNote, getNotes, upsertNote } from "./state";
import NoteContent from "./components/NoteContent/NoteContent";
import Omnibar from "./components/Omnibar/Omnibar";
import "./App.css";
import { bindActionCreators } from "weedux/lib";
class App extends PureComponent {
state = { ...getNotes(), selectedNote: null };
async componentDidMount() {
const { getNotes } = this.props;
getNotes({ filter: "" });
}
handleSearch = text => {
this.setState({
notes: getNotes().notes.filter(
n =>
n.title.includes(text) ||
n.tags.find(x => x === text) ||
n.content.includes(text)
)
});
const normalizedText = text.toLocaleLowerCase();
const { getNotes } = this.props;
getNotes({ filter: normalizedText });
// this.setState({
// notes: getNotes().notes.filter(
// n =>
// n.title.toLocaleLowerCase().includes(normalizedText) ||
// n.tags.find(
// x => x.toLocaleLowerCase() === normalizedText
// ) ||
// n.content.toLocaleLowerCase().includes(normalizedText)
// )
// });
};
handleCreate = title => {
const newNote = upsertNote(title);
this.setState({ ...getNotes(), selectedNote: newNote });
const { createNote } = this.props;
createNote({ title, content: "", tags: [] });
};
handleSelectNote = noteTitle => {
const { notes } = this.state;
const sn = notes.filter(({ title }) => title === noteTitle);
if (sn.length > 0) {
this.setState({ selectedNote: sn[0] });
}
handleSelectNote = title => {
const { selectNote } = this.props;
selectNote({ title });
};
handleSaveNoteContent = content => {
const { notes, selectedNote } = this.state;
const idx = notes.findIndex(
({ title }) => title === selectedNote.title
);
if (idx >= 0) {
notes[idx].content = content;
this.setState({
notes: [...notes],
selectedNote: { ...notes[idx] }
});
console.log("updated note", selectedNote.title, "content");
}
const { updateNote, selectedNote } = this.props;
updateNote({ ...selectedNote, content });
};
handleTagUpdate = newTags => {
const { notes, selectedNote } = this.state;
const idx = notes.findIndex(
({ title }) => title === selectedNote.title
);
if (idx >= 0) {
notes[idx].tags = newTags;
this.setState({
notes: [...notes],
selectedNote: { ...notes[idx] }
});
console.log("updated note tags", selectedNote.tags);
}
handleTagUpdate = ({ title, newTags }) => {
// const { updateTags } = this.props;
// updateTags({ title, tags: newTags });
};
render() {
const { notes, selectedNote } = this.state;
const { notes, selectedNote } = this.props;
console.log(this.props);
return (
<div className="App">
@ -96,4 +82,11 @@ class App extends PureComponent {
}
}
export default App;
const ms2p = s => {
console.log(s);
return s;
};
const md2p = dispatch => bindActionCreators({ ...actions }, dispatch);
export default connect(ms2p, md2p, store)(App);

View File

@ -1,5 +1,5 @@
.EditableNoteArea {
height: 90%;
height: 100%;
border: gray solid 1px;
padding: 10px;
display: flex;
@ -21,8 +21,9 @@
}
.EditableNoteArea .markdownPreview {
flex-basis: 1 1 min-content;
overflow: scroll;
flex-grow: 1;
flex-shrink: 1;
overflow-y: scroll;
}
.EditableNoteArea .markdownPreview h1,
@ -33,3 +34,11 @@
.EditableNoteArea .markdownPreview p {
margin-top: 0;
}
.EditableNoteArea .markdownPreview a:visited {
color: darkgoldenrod;
}
.EditableNoteArea .markdownPreview a:link {
color: orange;
}

View File

@ -1,6 +1,6 @@
import React from "react";
import ReactMarkdown from "react-markdown";
import { noop } from "../../state";
import { noop } from "../../utils";
import "./EditableNoteArea.css";
@ -9,14 +9,16 @@ export default function EditableNoteArea({
onSave = noop,
preview = false
}) {
const updateContent = ({ target: { value } }) => {
onSave(value);
};
const updateContent = ({ target: { value } }) => onSave(value);
return (
<section className="EditableNoteArea">
{!preview ? (
<textarea value={content} onChange={updateContent} />
<textarea
value={content || ""}
placeholder="This note feels cold and empty inside"
onChange={updateContent}
/>
) : (
<div className="markdownPreview">
<ReactMarkdown source={content} />

View File

@ -1,10 +1,9 @@
.NoteContent {
min-height: 70%;
max-height: 70vh;
background-color: black;
display: flex;
flex-direction: column;
flex-grow: 1;
flex-grow: 2;
text-align: left;
padding: 0 10px 15px 10px;
}

View File

@ -1,12 +1,12 @@
import React, { useState } from "react";
import { noop } from "../state";
import EditableNoteArea from "./EditableNoteArea/EditableNoteArea";
import { noop } from "../../utils";
import EditableNoteArea from "../EditableNoteArea/EditableNoteArea";
import "./NoteContent.css";
export default function NoteContent({ note = null, onSave = noop }) {
const [renderMarkdown, setRenderMarkdown] = useState(false);
const { content, title } = note || {};
const { content } = note || {};
return (
<div className="NoteContent">

View File

@ -1,8 +1,6 @@
.NoteList {
min-height: 80px;
height: 25%;
/* flex-basis: 1 1 content; */
flex-grow: 1;
height: 20%;
min-height: 20%;
flex-shrink: 1;
list-style-type: none;
border-color: black;
@ -13,7 +11,7 @@
margin: 0;
padding: 0;
text-align: left;
overflow-y: scroll;
overflow-y: auto;
}
.NoteList li:nth-child(even) {
@ -29,10 +27,16 @@
cursor: pointer;
}
.NoteList li.empty {
text-align: center;
padding: 25px;
}
.NoteList .NoteListItem {
padding: 0 10px;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 9pt;
}

View File

@ -1,6 +1,6 @@
import React from "react";
import { formatRelative } from "date-fns";
import { noop } from "../../state";
import { noop } from "../../utils";
import TagList from "../TagList/TagList";
import "./NoteList.css";
@ -29,7 +29,9 @@ export default function NoteList({
))}
{notes.length === 0 && (
<li>Click the text box above to create a note.</li>
<li className="empty">
Click the text box above to create a note.
</li>
)}
</ul>
);

View File

@ -1,5 +1,5 @@
import React, { useState } from "react";
import { noop, enterKeyPressed } from "../state";
import { noop, enterKeyPressed } from "../../utils";
import "./Omnibar.css";
@ -7,6 +7,10 @@ export default function Omnibar({ onSearch = noop, onEnter = noop }) {
const [text, setText] = useState("");
const onTextChange = text => setText(text) || onSearch(text);
const onEnterKey = () => {
onEnter(text);
onTextChange("");
};
return (
<div className="Omnibar">
@ -14,7 +18,8 @@ export default function Omnibar({ onSearch = noop, onEnter = noop }) {
type="text"
placeholder='Type here to search. Type here and press "enter" to create a note.'
onChange={({ target }) => onTextChange(target.value)}
onKeyPress={enterKeyPressed(() => onEnter(text))}
value={text}
onKeyPress={enterKeyPressed(onEnterKey)}
/>
</div>
);

View File

@ -1,7 +1,7 @@
.TagList {
display: flex;
align-items: baseline;
overflow-x: scroll;
align-items: center;
overflow-x: auto;
}
.TagList input {
@ -11,6 +11,8 @@
.TagList span {
color: #aaa;
white-space: nowrap;
padding: 0 10px;
}
.TagList ul {

View File

@ -1,5 +1,5 @@
import React, { Fragment, useState } from "react";
import { noop, enterKeyPressed } from "../../state";
import { noop, enterKeyPressed } from "../../utils";
import "./TagList.css";
export default function TagList({ tags = [], onTagUpdate = noop }) {
@ -29,7 +29,7 @@ export default function TagList({ tags = [], onTagUpdate = noop }) {
/>
) : (
<Fragment>
{tags.length <= 0 && <span>tags</span>}
{tags.length <= 0 && <span>Add a Tag</span>}
<ul>
{tags.map(t => (
<li key={t}>{t}</li>

View File

@ -1,5 +1,8 @@
body {
margin: 0;
height: 100vh;
width: 100vw;
overflow-x: hidden;
display: flex;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto",
"Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans",

View File

@ -1,12 +1,29 @@
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import * as serviceWorker from "./serviceWorker";
ReactDOM.render(<App />, document.getElementById('root'));
ReactDOM.render(<App />, document.getElementById("root"));
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
// handles opening anchor tags in the browser outside of the app
document.addEventListener("DOMContentLoaded", function() {
document.addEventListener(
"click",
function(e) {
// only handle for a tags
if (e.target.href) {
e.preventDefault();
e.stopPropagation();
window.api.sendLinkNav(e.target.href);
return false;
}
},
true
);
});

45
src/server/index.js Normal file
View File

@ -0,0 +1,45 @@
const path = require("path");
const { app, shell, BrowserWindow, ipcMain } = require("electron");
const { createBackend } = require("./notes");
let win;
function createWindow() {
// Create the browser window.
win = new BrowserWindow({
width: 600,
height: 800,
minHeight: 800,
minWidth: 600,
title: "XNV",
autoHideMenuBar: true,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
enableRemoteModule: false,
preload: path.join(__dirname, "preload.js")
}
});
const backend = createBackend({
directory: "C:/Users/AdamM/Documents/notes"
});
ipcMain.on("note_command", async (event, args) => {
const response = await backend.onNoteCommand(JSON.parse(args));
win.webContents.send("note_response", JSON.stringify(response));
});
ipcMain.on("element-clicked", async (event, args) => {
console.log("opening browser window to ", args);
shell.openExternal(args);
});
if (process.env.NODE_ENV !== "prod") {
win.loadURL("http://localhost:3000");
} else {
// and load the index.html of the app.
win.loadFile("./build/index.html");
}
}
app.on("ready", createWindow);

175
src/server/notes.js Normal file
View File

@ -0,0 +1,175 @@
const path = require("path");
const fs = require("fs");
const defaultSettings = {
directory: "./",
defaultExtension: "txt"
};
function listNotesInDirectory(directoryPath = "") {
return new Promise(function(resolve, reject) {
fs.readdir(directoryPath, function(err, files) {
if (err) return reject(err);
resolve(
files
.map(fPath => ({
ext: path.extname(fPath),
name: fPath,
friendlyName: path.basename(fPath, path.extname(fPath)),
fullPath: path.join(directoryPath, fPath)
}))
.filter(
({ ext }) =>
ext === ".md" || ext === ".txt" || ext === ".utf8"
)
);
});
});
}
function getNoteTime(fullPath) {
return new Promise((a, r) => {
fs.stat(fullPath, (err, stats) => {
if (err) return r(err);
a({
lastModified: stats.mtime,
created: stats.ctime,
size: stats.size
});
});
});
}
function getNoteContent(fullPath) {
return new Promise((a, r) =>
fs.readFile(fullPath, "utf8", (err, data) => {
if (err) return r(err);
a({ content: data });
})
);
}
function loadTags(directoryPath) {
return new Promise((a, r) =>
fs.readFile(
path.join(directoryPath, "meta.json"),
"utf8",
(err, data) => {
if (err) {
if (!err.message.includes("ENOENT")) return r(err);
return a(saveTags(directoryPath, {}));
}
a(JSON.parse(data));
}
)
);
}
function saveTags(directoryPath, tags = {}) {
return new Promise((a, r) =>
fs.writeFile(
path.join(directoryPath, "meta.json"),
JSON.stringify(tags),
"utf8",
err => {
if (err) return r(err);
a({});
}
)
);
}
let tagsCache = {};
function getNoteTags(directoryPath, friendlyName) {
return Promise.resolve(tagsCache[friendlyName] || []);
}
async function loadNotesInDirectory(directoryPath = "") {
const tagCache = await loadTags(directoryPath);
const files = await listNotesInDirectory(directoryPath);
return await Promise.all(
files.map(async ({ fullPath, friendlyName, ext }) => {
const [noteTime, noteContent] = await Promise.all([
getNoteTime(fullPath),
getNoteContent(fullPath),
getNoteTags(directoryPath, friendlyName)
]);
return {
path: fullPath,
title: friendlyName,
tags: tagsCache[friendlyName] || [],
...noteTime,
...noteContent
};
})
);
}
async function saveNoteInDirectory(
{ directory, defaultExtension },
{ title, content, tags }
) {
let fullPath = path.join(directory, `${title}.${defaultExtension}`);
const match = await listNotesInDirectory(directory).filter(
f => f.friendlyName === title
);
if (match || match.length > 0) fullPath = match[0].fullPath;
tagsCache[title] = tags;
return Promise.all([
new Promise((a, r) => {
fs.writeFile(fullPath, content, "utf8", err => {
if (err) return r(err);
a();
});
}),
saveTags(directory, tagsCache)
]);
}
function newNoteBackend(initSettings) {
let settings = { ...defaultSettings, ...initSettings };
async function onNoteCommand({ type, payload }) {
console.log(`got command: ${type}`);
const { directory } = settings;
switch (type) {
case "getNotes":
const files = await loadNotesInDirectory(directory);
return {
type: "getNotes",
payload: {
notes: files.map(f => ({ ...f, tags: [] }))
}
};
case "saveNote":
const { title, content, tags } = payload;
const result = await saveNoteInDirectory(directory, {
title,
content,
tags
});
return { type: "saveNote", payload: result };
default:
return { type: "noop" };
}
}
function updateSettings({ directory }) {
settings.directory = directory;
}
return {
onNoteCommand,
updateSettings
};
}
module.exports = { createBackend: newNoteBackend };

50
src/server/preload.js Normal file
View File

@ -0,0 +1,50 @@
const { contextBridge, ipcRenderer } = require("electron");
const validSendChannels = ["note_command", "element-clicked"];
const validReceiveChannels = ["note_response"];
const send = (channel, data) => {
if (validSendChannels.includes(channel)) {
ipcRenderer.send(channel, JSON.stringify(data));
return Promise.resolve();
}
return Promise.reject();
};
const receive = channel => {
if (validReceiveChannels.includes(channel)) {
return new Promise(resolve => {
ipcRenderer.on(channel, (_, ...args) => resolve(...args));
});
}
return Promise.reject();
};
const receiveOnce = channel => {
if (validReceiveChannels.includes(channel)) {
return new Promise(resolve => {
ipcRenderer.once(channel, (_, ...args) => resolve(...args));
});
}
return Promise.reject();
};
contextBridge.exposeInMainWorld("api", {
send,
receive,
receiveOnce,
sendLinkNav: target => ipcRenderer.send("element-clicked", target),
getNotes: async ({ filter }) => {
send("note_command", { type: "getNotes", payload: { filter } });
const response = await receiveOnce("note_response");
return JSON.parse(response);
},
saveNote: async ({ title, content, tags }) => {
send("note_command", {
type: "saveNote",
payload: { title, content, tags }
});
const response = await receiveOnce("note_response");
return JSON.parse(response);
}
});

79
src/state/actions.js Normal file
View File

@ -0,0 +1,79 @@
import {
getNotes as getNotesBackend,
upsertNote as upsertNoteBackend
} from "./api";
export const reducer = (cs, { type, payload }) => {
switch (type) {
case "SELECT_NOTE":
return { ...cs, selectedNote: payload.selectedNote };
case "SAVE_NOTE_START":
case "SAVE_NOTE_SUCCESS":
case "GET_NOTES_START":
return { ...cs, notesLoading: true };
case "GET_NOTES_SUCCESS":
return {
...cs,
notesLoading: false,
error: null,
notes: [...payload.notes]
};
case "SAVE_NOTE_FAIL":
case "GET_NOTES_FAIL":
return { ...cs, notesLoading: false, error: payload.error };
default:
return { ...cs };
}
};
const selectNote = ({ title }) => (dispatch, getState) => {
const { selectedNote, notes } = getState();
let foundNote = selectedNote;
const notesFilter = notes.filter(n => n.title === title);
if (notesFilter.length > 0) {
foundNote = notesFilter[0];
}
dispatch({ type: "SELECT_NOTE", payload: { ...foundNote } });
};
const deleteNote = ({ title }) => (dispatch, getState) => {};
const createNote = ({ title, content, tags = [] }) => async (
dispatch,
getState
) => {
dispatch({ type: "SAVE_NOTE_START" });
try {
const result = await upsertNoteBackend({ title, content, tags });
dispatch({ type: "SAVE_NOTE_SUCCESS", payload: { ...result } });
} catch (error) {
dispatch({ type: "SAVE_NOTE_FAIL", payload: { error } });
}
};
const getNotes = ({ filter }) => async (dispatch, getState) => {
const { notesLoading } = getState();
if (notesLoading) {
console.warn(
"trying to load notes when the operation has already begun."
);
return;
}
dispatch({ type: "GET_NOTES_START" });
try {
const result = await getNotesBackend({ filter });
console.log(result);
dispatch({ type: "GET_NOTES_SUCCESS", payload: result });
} catch (error) {
dispatch({ type: "GET_NOTES_FAIL", payload: { error } });
}
};
export const actions = {
getNotes,
deleteNote,
createNote,
selectNote
};

168
src/state/api.js Normal file
View File

@ -0,0 +1,168 @@
const createNote = ({ title = null, content = "", tags = [] }) => {
if (title === null) {
throw new Error("cannot create a note with a null title");
}
const now = new Date().toUTCString();
return {
id: now,
created: now,
lastModified: now,
title,
content,
tags
};
};
export const getNotes = async filter => {
const { api } = window;
if (!api) return { ...notesStore };
const { getNotes } = api;
const { payload } = await getNotes({ filter });
console.log(payload);
return {
error: null,
notes: payload.notes
};
};
export const upsertNote = async ({ title, content, tags }) => {
const newNote = createNote({ title });
const { api } = window;
if (!api) {
notesStore.notes = notesStore.notes.concat(newNote);
}
const { saveNote } = api;
await saveNote({ ...newNote });
return { note: newNote };
};
const notesStore = {
error: null,
notes: [
createNote({
title: "butt 1",
content: "this is an butt note",
tags: [
"first",
"second",
"third",
"fourth",
"fifth",
"sixth",
"seventh"
]
}),
createNote({
title: "butt 2",
content: "# butt2\nthis is a dope butt note",
tags: ["butt"]
}),
createNote({
title: "too many buttes",
content: "this is an butt note",
tags: ["butt"]
}),
createNote({
title: "this note doesn't have the word butt in the content",
content: "this is an butt note",
tags: ["butt"]
}),
createNote({
title: "this is not an butt 5",
content: `
# Secret info about database
## auctioneers_auct_catalogs
- ncatalog_status
- null when the catalog isnt published/proofed
- online when the catalog is publically visible
- live during live sales
- ncatalog_type
- 1 Timed Sales
- 10 White Label
- 1/11 Gold
- 2/12 Platinum
- 3/13 Platinum Plus
- 1 Timed
- 10+ White label
For ncatalog_type 1 and 11 were "Gold," 2 and 12 were "Platinum," 3 and 13 were "Platinum Plus," but now 1 will typically be timed sales and 3/13 will be traditional. 10 is white label _only_ which means that the auction is not displayed on our site, except by direct URL. 10 + the level (1,2,3) means the catalog can be accessed as white label _and_ is displayed as a normal catalog on our site. ncatalog_type = 10 is really the only important functionality of that variable now, but there are still some gotchas lingering.
- auction_type
- j = jewelry only
- c = Business and Industrial or Real Estate or Automotive categories
- t = Dolls and Toys category
- a = timed (?)
## auctioneers_auct2_lots_sold
- Lots sold; 1 = sold, 2 = not sold
## auctioneers_auct2_np_bid_history
- The bidder_id in np_bid_history is 0 for competing/floor bids sourceid = 1 is also for floor bids.
- sourceid: 1=floor bids, 2=live, 3=absentee bid executed, 4=ios, 5=android, 6=automated bids to clear reserve price
- Competing/floor bids are entered by the clerk and are supposed to represent anyone bidding live outside of our system (at the auction house, over the phone, on another internet platform, etc), and those will always have a bidder_id of 0.
Sub catalogs; they don't exist in the system formally, they're just lots grouped by listing_agent_id
## auctioneers_auct2_np_bid_history
absforcebidderid set to 20; 20 would be the SFS (smart fox server) internal userId for the live bidder that placed a bid, thus forcing the existing absentee bid (which preempts the live bid) to be sent to the clerk.
All you really need to know is that there was a live bid that forced the absentee bid to be placed.
Only one bid is sent
sourceid 1= floor, 2=internet, 3=absentee, 4=iPhone, 5=Android
## auctioneers_auct2_npb
npb_type 1 = active dispute, 0 = resolved dispute
auctioneers_users
role known possible values: admin, auctioneer, bidder
admin_group possible values: exec, support, development, admin_group, <NULL>
## auctioneers_auct2_approval
\`SELECT distinct approval_approved FROM liveauct_liveauctioneers.auctioneers_auct2_approval;\`
approval_approved = 0 - not reviewed, 1 - approved, 2 - declined, 4 - blocked by auctioneer, 5 - suspended
## auctioneers_auct_bids
## auctioneers_auct_houses
## auctioneers_auct2_lots_sold
has ~29 million lots, we only index 21 million since some houses do high volume listings and we ignore any
plaintext pbuttwords for many of our clients just hanging out in here
usernames and pbuttwords are generally the same
FAQ
Q. Can someone tell me where we are keeping the stats that we are reporting on for auction houses? I think they have stats like number of views of their items and such.
A. They are stored in the statistics database, which is a separate machine from our main database; however, they are aggregated and stored in auctioneers_auct_catalogs_admin_data and auctioneers_auct2_lots_admin_data for catalog and lot-specific data respectively.
Q. Whats with all the ncatalog stuff?
A. In the mainhost codebase the letter "n" is often used to mean new (unfortunately) to distinguish from similarly named things from the eBay era.
ncatalog_status is the status of the catalog once it is proofed. It is null until the catalog is proofed (i.e. published/made public on our site), at which point it is "online". "Live" indicates that the auction is taking place right now (or soon), and "done" means done.
ncatalog_type is not as important as it used to be. We used to have tiered service levels that had access to different features. For ncatalog_type 1 and 11 were "Gold," 2 and 12 were "Platinum," 3 and 13 were "Platinum Plus," but now 1 will typically be timed sales and 3/13 will be traditional. 10 is white label _only_ which means that the auction is not displayed on our site, except by direct URL. 10 + the level (1,2,3) means the catalog can be accessed as white label _and_ is displayed as a normal catalog on our site. ncatalog_type = 10 is really the only important functionality of that variable now, but there are still some gotchas lingering.
`,
tags: ["not butt"]
})
]
};

8
src/state/index.js Normal file
View File

@ -0,0 +1,8 @@
import weedux, { middleware } from "weedux";
import { reducer as noteReducer, actions as acts } from "./actions";
const { thunk } = middleware;
// note CRUD
export const store = new weedux({}, noteReducer, [thunk]);
export const actions = acts;

View File

@ -1,8 +1,9 @@
import debounce from "lodash.debounce";
//import debounce from "lodash.debounce";
export const noop = () => {};
export const enterKeyPressed = (cb = noop) => ({ charCode }) =>
charCode === 13 && cb();
export const createNote = ({ title = null, content = "", tags = [] }) => {
if (title === null) {
throw new Error("cannot create a note with a null title");
@ -19,12 +20,30 @@ export const createNote = ({ title = null, content = "", tags = [] }) => {
};
};
export const getNotes = window.LoadNotes || (() => ({ ...notesStore }));
export const upsertNote = newTitle => {
const newNote = createNote({
title: newTitle
});
notesStore.notes = notesStore.notes.concat(newNote);
export const getNotes = async filter => {
const { api } = window;
if (!api) return { ...notesStore };
const { getNotes } = api;
const { payload } = await getNotes({ filter });
console.log(payload);
return {
error: null,
notes: payload.notes
};
};
export const upsertNote = async ({ title, content, tags }) => {
const newNote = createNote({ title });
const { api } = window;
if (!api) {
notesStore.notes = notesStore.notes.concat(newNote);
}
const { saveNote } = api;
await saveNote({ ...newNote });
return newNote;
};
@ -33,8 +52,8 @@ const notesStore = {
error: null,
notes: [
createNote({
title: "ass 1",
content: "this is an ass note",
title: "butt 1",
content: "this is an butt note",
tags: [
"first",
"second",
@ -46,22 +65,22 @@ const notesStore = {
]
}),
createNote({
title: "ass 2",
content: "# ass2\nthis is a dope ass note",
tags: ["ass"]
title: "butt 2",
content: "# butt2\nthis is a dope butt note",
tags: ["butt"]
}),
createNote({
title: "too many asses",
content: "this is an ass note",
tags: ["ass"]
title: "too many buttes",
content: "this is an butt note",
tags: ["butt"]
}),
createNote({
title: "this note doesn't have the word ass in the content",
content: "this is an ass note",
tags: ["ass"]
title: "this note doesn't have the word butt in the content",
content: "this is an butt note",
tags: ["butt"]
}),
createNote({
title: "this is not an ass 5",
title: "this is not an butt 5",
content: `
# Secret info about database
@ -128,8 +147,8 @@ approval_approved = 0 - not reviewed, 1 - approved, 2 - declined, 4 - blocked by
has ~29 million lots, we only index 21 million since some houses do high volume listings and we ignore any
plaintext passwords for many of our clients just hanging out in here
usernames and passwords are generally the same
plaintext pbuttwords for many of our clients just hanging out in here
usernames and pbuttwords are generally the same
FAQ
@ -149,7 +168,7 @@ ncatalog_type is not as important as it used to be. We used to have tiered servi
`,
tags: ["not ass"]
tags: ["not butt"]
})
]
};

View File

@ -4203,6 +4203,11 @@ eventemitter3@^4.0.0:
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.0.tgz#d65176163887ee59f386d64c82610b696a4a74eb"
integrity sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==
events@3.x:
version "3.1.0"
resolved "https://registry.yarnpkg.com/events/-/events-3.1.0.tgz#84279af1b34cb75aa88bf5ff291f6d0bd9b31a59"
integrity sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg==
events@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/events/-/events-3.0.0.tgz#9a0a0dfaf62893d92b875b8f2698ca4114973e88"
@ -5299,6 +5304,11 @@ immer@1.10.0:
resolved "https://registry.yarnpkg.com/immer/-/immer-1.10.0.tgz#bad67605ba9c810275d91e1c2a47d4582e98286d"
integrity sha512-O3sR1/opvCDGLEVcvrGTMtLac8GJ5IwZC4puPrLuRj3l7ICKvkmA0vGuU9OW8mV9WIBRnaxp5GJh9IEAaNOoYg==
immutable@^3.8.1:
version "3.8.2"
resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.2.tgz#c2439951455bb39913daf281376f1530e104adf3"
integrity sha1-wkOZUUVbs5kT2vKBN28VMOEErfM=
import-cwd@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9"
@ -8655,7 +8665,7 @@ prompts@^2.0.1:
kleur "^3.0.3"
sisteransi "^1.0.3"
prop-types@^15.6.2, prop-types@^15.7.2:
"prop-types@>= 15.5.10", prop-types@^15.6.2, prop-types@^15.7.2:
version "15.7.2"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
@ -11001,6 +11011,15 @@ websocket-extensions@>=0.1.1:
resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29"
integrity sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==
weedux@^3.5.3-beta:
version "3.5.3-beta"
resolved "https://registry.yarnpkg.com/weedux/-/weedux-3.5.3-beta.tgz#8e68df8f43fd1fdaeb10f6a002d1b722d757ae80"
integrity sha512-1BN0ZTxSAzhLHh9ujxYKA4afR30O5BeurueqUuZg/MA5MkdozmsGYE7hD7FvIrvNhFCzYb9DkNlZjGWhyShuTg==
dependencies:
events "3.x"
immutable "^3.8.1"
prop-types ">= 15.5.10"
whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3, whatwg-encoding@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0"