Skip to content

Commit

Permalink
Resolve #52.
Browse files Browse the repository at this point in the history
  • Loading branch information
muety committed Jan 22, 2020
1 parent fec4737 commit 589b8a2
Show file tree
Hide file tree
Showing 9 changed files with 125 additions and 11 deletions.
6 changes: 6 additions & 0 deletions controllers/notebook.js
Expand Up @@ -25,6 +25,12 @@ exports.exist = (req, res) => {
return res.status(200).end()
}

exports.list = (req, res) => {
let list = notebooks.find().map(n => Object.assign({}, { id: n.id, count: n.notes.length }))
list.sort((a, b) => a.id - b.id)
return res.status(200).send(list)
}

exports.create = (req, res) => {
if (!req.body || !req.body.id || !req.body.password) return res.status(400).end()
if (notebooks.findOne({ id: req.body.id })) return res.status(409).end()
Expand Down
2 changes: 1 addition & 1 deletion mininote-frontend/package.json
@@ -1,6 +1,6 @@
{
"name": "mininote-frontend",
"version": "0.3.1",
"version": "0.4.0",
"description": "A simple Mardown note-taking editor.",
"author": "Ferdinand M眉tsch <mail@ferdinand-muetsch.de>",
"private": true,
Expand Down
38 changes: 32 additions & 6 deletions mininote-frontend/src/App.vue
Expand Up @@ -16,9 +16,15 @@
<notes-editor :content="currentContent" :id="selectedNoteId" :dirty="dirty" @alert="showAlert" @contentUpdate="onNoteUpdated"></notes-editor>
</div>
</div>
<div v-if="!loaded">
<div v-if="!loaded && !notebooks.length">
<div class="placeholder">
<span>Please open an existing notebook or create a new one.</span>
<span>Please create a new notebook.</span>
</div>
</div>
<div v-if="!loaded && notebooks.length">
<div class="notebooks-picker-container">
<h3>Your Notebooks</h3>
<notebooks-picker :notebooks="notebooks" @notebookSelected="onNotebookSelected"></notebooks-picker>
</div>
</div>
</div>
Expand All @@ -31,10 +37,11 @@
</template>

<script>
import { mapState, mapGetters } from 'vuex'
import { mapState, mapGetters, mapActions } from 'vuex'
import NotesEditor from './components/NotesEditor'
import NotesPicker from './components/NotesPicker'
import NotebooksPicker from './components/NotebooksPicker'
import ControlBar from './components/ControlBar'
export default {
Expand All @@ -47,7 +54,8 @@ export default {
computed: {
...mapState([
'selectedNoteId',
'version'
'version',
'notebooks'
]),
...mapGetters([
'loaded',
Expand All @@ -63,16 +71,22 @@ export default {
components: {
NotesEditor,
NotesPicker,
NotebooksPicker,
ControlBar
},
created() {
window.addEventListener("beforeunload", this.didIntentToLeave);
window.addEventListener("beforeunload", this.didIntentToLeave)
this.listNotebooks()
},
methods: {
...mapActions([
'listNotebooks'
]),
onNoteSelected: function(noteId) {
this.$store.commit('selectNote', noteId)
},
onNotesLoaded: function(notes) {
onNotebookSelected: function(notebookId) {
this.$store.commit('setLoadNotebook', notebookId)
},
addNote: function(note) {
this.notes.push(note)
Expand Down Expand Up @@ -159,6 +173,13 @@ button {
left: calc(50% - 300px);
}
#app .notebooks-picker-container {
position: absolute;
width: 500px;
top: 20vh;
left: calc(50% - 250px);
}
.alert {
width: 500px;
position: fixed;
Expand All @@ -167,4 +188,9 @@ button {
z-index: 999;
text-align: center;
}
#app h3 {
font-size: 20px;
color: #42B983;
}
</style>
8 changes: 8 additions & 0 deletions mininote-frontend/src/api/index.js
Expand Up @@ -11,6 +11,14 @@ const api = {
let req = new Request(`${apiBaseUrl}/notebook/${notebookId}`, { method: 'HEAD' })
return fetch(req).then(res => res.status === 200)
},
list() {
let req = new Request(`${apiBaseUrl}/notebook`, { method: 'GET' })
return fetch(req)
.then(res => {
if (res.status === 200) return res.json()
throw generateError(res)
})
},
create(notebookId, passwordHash) {
let headers = new Headers()
headers.append('Content-Type', 'application/json')
Expand Down
16 changes: 13 additions & 3 deletions mininote-frontend/src/components/ControlBar.vue
Expand Up @@ -53,7 +53,7 @@
<script>
import api from "../api";
import { md5 } from "../lib/md5";
import { mapState, mapGetters } from "vuex";
import { mapState, mapGetters, mapActions } from "vuex";
export default {
name: "control-bar",
Expand All @@ -74,11 +74,13 @@ export default {
},
computed: {
...mapState({
notebook: state => state.notebook
notebook: state => state.notebook,
loadNotebookId: state => state.loadNotebookId
}),
...mapGetters(["dirty"])
},
methods: {
...mapActions(['listNotebooks']),
handleError: function(err) {
this.$emit('alert', err.message)
this.reset();
Expand All @@ -102,7 +104,8 @@ export default {
creating: false,
loaded: false
};
this.$store.commit('reset');
this.$store.commit('reset')
this.listNotebooks()
setTimeout(() => this.$refs.refNotebookInput.focus(), 0);
},
tryNotebook: function() {
Expand Down Expand Up @@ -176,6 +179,13 @@ export default {
})
.catch(vm.handleError)
}
},
watch: {
loadNotebookId: function(newId, oldId) {
if (newId !== '' && newId !== oldId) {
this.inputs.name = newId
}
}
}
};
</script>
Expand Down
49 changes: 49 additions & 0 deletions mininote-frontend/src/components/NotebooksPicker.vue
@@ -0,0 +1,49 @@
<template>
<div class="picker-container">
<b-list-group>
<b-list-group-item button v-for="n in notebooks" :key="n.id" @click="chooseNotebook(n.id)" class="d-flex justify-content-between align-items-center">
{{ n.id }}
<b-badge variant="primary" pill>{{ n.count }}</b-badge>
</b-list-group-item>
</b-list-group>
</div>
</template>

<script>
import { md5 } from '../lib/md5'
import { mapState, mapGetters, mapMutations } from 'vuex'
export default {
name: 'notes-picker',
props: ['notebooks'],
computed: {},
methods: {
chooseNotebook: function(id) {
this.$emit('notebookSelected', id)
}
},
created () {
}
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.picker-container {
margin-top: 20px;
}
.picker-container ul {
overflow-y: auto;
max-height: calc(100vh - 240px)
}
.picker-container .badge-primary {
background-color: #42B983;
}
.picker-container .list-group-item {
padding: 0.35rem 0.75rem;
cursor: pointer;
}
</style>
12 changes: 12 additions & 0 deletions mininote-frontend/src/store/index.js
Expand Up @@ -7,12 +7,14 @@ Vue.use(Vuex)

const emptyState = {
notebook: {},
notebooks: [],
changes: {
add: [],
delete: [],
update: {}
},
selectedNoteId: -1,
loadNotebookId: '',
version: require('../../package.json').version
}

Expand Down Expand Up @@ -83,6 +85,9 @@ const store = new Vuex.Store({
noteById
},
mutations: {
setNotebooks(state, notebookList) {
Vue.set(state, 'notebooks', notebookList)
},
setNotebook(state, { id, password }) {
Vue.set(state.notebook, 'id', id)
Vue.set(state.notebook, 'password', password)
Expand All @@ -109,6 +114,9 @@ const store = new Vuex.Store({
},
selectNote,
selectFirst,
setLoadNotebook(state, id) {
Vue.set(state, 'loadNotebookId', id)
},
addChange(state, { type, payload }) {
switch (type) {
case 'add':
Expand Down Expand Up @@ -159,6 +167,10 @@ const store = new Vuex.Store({
}
},
actions: {
listNotebooks({ commit }) {
return api.list()
.then(data => commit('setNotebooks', data))
},
loadNotebook({ commit }, { id, password }) {
return api.getNotes(id, password)
.then(data => commit('setNotes', data))
Expand Down
3 changes: 2 additions & 1 deletion package.json
@@ -1,10 +1,11 @@
{
"name": "mininote",
"version": "0.3.1",
"version": "0.4.0",
"description": "A simple Mardown note-taking editor",
"main": "index.js",
"scripts": {
"start": "node index.js",
"dev": "NODE_ENV=dev node index.js",
"test": "TEST=true mocha --exit"
},
"repository": {
Expand Down
2 changes: 2 additions & 0 deletions routers/notebook.js
Expand Up @@ -4,6 +4,8 @@ const express = require('express'),
router = express.Router(),
notebookController = require('../controllers/notebook')

router.get('/notebook', notebookController.list)

router.post('/notebook', notebookController.create)

router.head('/notebook/:id', notebookController.exist)
Expand Down

0 comments on commit 589b8a2

Please sign in to comment.