diff --git a/.DS_Store b/.DS_Store index 41cd727a466643d1628b7d2eda0dc4e9b59f3225..e3c05730d82d0583b4f9f47b5de4e93b3536c6d5 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/CHANGELOG.md b/CHANGELOG.md index 94072dadef6337fa145b9fca08d288a01ecaa502..34d2fde34c20f164dd285cba2287e7501db7b8e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +# 0.1.27 + +_9th October 2020_ + +### Added + +- Basic card view of all nodes +- Leave link in Navigation + +### Changed + +- List view now you can enter basic read mode +- List view you can now discard your nodes + # 0.1.26 _6th October 2020_ @@ -10,7 +24,7 @@ _6th October 2020_ ### Added -- the toolbar now shows (in basic form) to you only your device name and the microcosm you are currently viewing +- The toolbar now shows (in basic form) to you only your device name and the microcosm you are currently viewing # 0.1.25 diff --git a/app/package-lock.json b/app/package-lock.json index 1a49c77dafb46d8f45240741c6ba5984f67a53ae..a410708be006576fd5175b45ba0c403eb070a756 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -1,6 +1,6 @@ { "name": "nodenogg.in", - "version": "0.1.24", + "version": "0.1.27", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/app/src/components/CardsLayer.vue b/app/src/components/CardsLayer.vue new file mode 100644 index 0000000000000000000000000000000000000000..dbd835d120d100b630a5968313d460f45d0529d8 --- /dev/null +++ b/app/src/components/CardsLayer.vue @@ -0,0 +1,196 @@ +<template> + <div> + <div v-for="(value, index) in configPositions" v-bind:key="index"> + <div class="nodes" v-if="nodeid == value.node_id && deleted == false"> + <form> + <!-- <div v-if="posvalue.read_mode == false"> --> + <div v-if="value.read_mode == false"> + <div v-for="value in $options.myArray" v-bind:key="value.node_id"> + <div v-if="value.deleted == false"> + <textarea + v-if="nodeid == value.node_id" + @focus="editTrue(true)" + @blur="editTrue(false)" + autofocus + v-model="value.node_text" + @input="editNode" + :id="value.node_id" + placeholder="Idea goes here!" + ref="newnode" + ></textarea> + </div> + </div> + </div> + <div v-if="value.read_mode == true"> + <p + class="read" + :id="nodeid" + :inner-html.prop="nodetext | marked" + ></p> + </div> + + <div class="allemoji"> + <div + class="eachemoji" + v-for="(emojis, index) in configEmoji" + :key="index" + > + <p v-if="nodeid == emojis.node_id"> + {{ emojis.emoji_text }} + </p> + </div> + </div> + <p class="info">*markdown supported & autosaves</p> + <div class="btn-row"> + <BaseButton buttonClass="danger" @click="deleteFlag()" + >Discard</BaseButton + > + <div v-if="value.read_mode == true"> + <BaseButton class="read" buttonClass="action" @click="readFlag()" + >Edit Mode + </BaseButton> + </div> + <div v-else> + <BaseButton class="read" buttonClass="action" @click="readFlag()" + >Read Mode</BaseButton + > + </div> + </div> + </form> + </div> + </div> + </div> +</template> + +<script> +import { mapState } from 'vuex' +import marked from 'marked' +var readmode + +export default { + name: 'CardsLayer', + + data: function () { + return {} + }, + + props: { + nodeid: String, + nodetext: String, + deleted: Boolean, + }, + + computed: mapState({ + myNodes: (state) => state.myNodes, + configPositions: (state) => state.configPositions, + configEmoji: (state) => state.configEmoji, + }), + + filters: { + marked: marked, + }, + + myArray: null, + created() { + this.$options.myArray = this.myNodes + }, + + methods: { + editNode(e) { + var nodeid = e.target.id + var nodetext = e.target.value + this.$store.dispatch('editNode', { nodeid, nodetext }) + }, + deleteFlag(e) { + e = this.nodeid + this.$store.dispatch('deleteFlag', { e }) + }, + readFlag(e) { + e = this.nodeid + + var i + for (i = 0; i < Object.keys(this.configPositions).length; i++) { + if (this.configPositions[i].node_id == this.nodeid) { + this.localreadmode = this.configPositions[i].read_mode + } + } + + if (this.localreadmode == true) { + readmode = false + this.$store.dispatch('readFlag', { e, readmode }) + this.mode = 'Read' + } else { + readmode = true + this.$store.dispatch('readFlag', { e, readmode }) + this.mode = 'Edit' + } + }, + + editTrue(e) { + this.$emit('editTrue', e) + }, + }, +} +</script> + +<style lang="css" scoped> +h2 { + color: red; +} + +.nodes { + /* width: 95%; */ + width: 285px; + height: 355px; + border: 2px dashed black; + background-color: rgb(155, 194, 216); + margin-top: 1em; + margin-right: 1em; +} +.read { + /* min-width: 100px; */ + min-height: 175px; + padding: 0 1em 0 1em; +} + +textarea { + width: 90%; + height: 175px; + resize: none; + box-sizing: border-box; + font-family: 'Inter var', Helvetica, sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + margin: 1em; + border: none; + outline: none; + background-color: rgb(187, 227, 255); + scrollbar-color: yellow rgb(187, 227, 255); +} + +.btn-row { + position: relative; + margin-bottom: 5px; + display: flex; + justify-content: center; + flex-wrap: wrap; + padding: 0 15px; + border-radius: 4px; +} + +.allemoji { + font-size: 2em; + display: grid; + grid-template-columns: repeat(auto-fill, minmax(0, auto)); + + /* float: left; */ +} + +.eachemoji p { + margin: 0em; +} + +img { + width: 100%; +} +</style> diff --git a/app/src/components/ListLayer.vue b/app/src/components/ListLayer.vue index 63ae6cef6e97e830c8911f0ca06e5530dac1dac4..5be9562cbfd15797a299e45234fa3f6a1044f97f 100644 --- a/app/src/components/ListLayer.vue +++ b/app/src/components/ListLayer.vue @@ -3,20 +3,56 @@ <div v-for="(value, index) in configPositions" v-bind:key="index"> <div class="nodes" v-if="nodeid == value.node_id && deleted == false"> <form> - <!-- <div v-if="posvalue.read_mode == false"> --> - <div v-for="value in $options.myArray" v-bind:key="value.node_id"> - <div v-if="value.deleted == false"> - <textarea - v-if="nodeid == value.node_id" - @focus="editTrue(true)" - @blur="editTrue(false)" - autofocus - v-model="value.node_text" - @input="editNode" - :id="value.node_id" - placeholder="Idea goes here!" - ref="newnode" - ></textarea> + <div v-if="value.read_mode == false"> + <!-- <div v-if="posvalue.read_mode == false"> --> + <div v-for="value in $options.myArray" v-bind:key="value.node_id"> + <div v-if="value.deleted == false"> + <textarea + v-if="nodeid == value.node_id" + @focus="editTrue(true)" + @blur="editTrue(false)" + autofocus + ref="newnode" + v-model="value.node_text" + @input="editNode" + :id="value.node_id" + placeholder="Idea goes here!" + ></textarea> + </div> + </div> + </div> + <div v-if="value.read_mode == true"> + <p + class="read" + :id="nodeid" + :inner-html.prop="nodetext | marked" + ></p> + </div> + <div class="allemoji"> + <div + class="eachemoji" + v-for="(emojis, index) in configEmoji" + :key="index" + > + <p v-if="nodeid == emojis.node_id"> + {{ emojis.emoji_text }} + </p> + </div> + </div> + <p class="info">*markdown supported & autosaves</p> + <div class="btn-row"> + <BaseButton buttonClass="danger" @click="deleteFlag()" + >Discard</BaseButton + > + <div v-if="value.read_mode == true"> + <BaseButton class="read" buttonClass="action" @click="readFlag()" + >Edit Mode + </BaseButton> + </div> + <div v-else> + <BaseButton class="read" buttonClass="action" @click="readFlag()" + >Read Mode</BaseButton + > </div> </div> </form> @@ -27,7 +63,8 @@ <script> import { mapState } from 'vuex' - +import marked from 'marked' +var readmode export default { name: 'ListLayer', @@ -41,9 +78,14 @@ export default { deleted: Boolean, }, + filters: { + marked: marked, + }, + computed: mapState({ myNodes: (state) => state.myNodes, configPositions: (state) => state.configPositions, + configEmoji: (state) => state.configEmoji, }), myArray: null, @@ -61,6 +103,34 @@ export default { editTrue(e) { this.$emit('editTrue', e) }, + + deleteFlag(e) { + e = this.nodeid + this.$store.dispatch('deleteFlag', { e }) + }, + readFlag(e) { + e = this.nodeid + + var i + for (i = 0; i < Object.keys(this.configPositions).length; i++) { + if (this.configPositions[i].node_id == this.nodeid) { + this.localreadmode = this.configPositions[i].read_mode + } + } + + if (this.localreadmode == true) { + readmode = false + this.$store.dispatch('readFlag', { e, readmode }) + this.mode = 'Read' + } else { + readmode = true + this.$store.dispatch('readFlag', { e, readmode }) + this.mode = 'Edit' + } + }, + focusInput() { + this.$refs.newnode.focus() + }, }, } </script> @@ -91,4 +161,29 @@ textarea { background-color: rgb(187, 227, 255); scrollbar-color: yellow rgb(187, 227, 255); } +.btn-row { + position: relative; + margin-bottom: 5px; + display: flex; + justify-content: center; + flex-wrap: wrap; + padding: 0 15px; + border-radius: 4px; +} + +.allemoji { + font-size: 2em; + display: grid; + grid-template-columns: repeat(auto-fill, minmax(0, auto)); + + /* float: left; */ +} + +.eachemoji p { + margin: 0em; +} + +img { + width: 100%; +} </style> diff --git a/app/src/components/OtherCardslayer.vue b/app/src/components/OtherCardslayer.vue new file mode 100644 index 0000000000000000000000000000000000000000..c6894f420ed6a791b7745590743536f428be0dd0 --- /dev/null +++ b/app/src/components/OtherCardslayer.vue @@ -0,0 +1,257 @@ +<template> + <div> + <div v-for="(value, index) in configPositions" v-bind:key="index"> + <div class="nodes" v-if="nodeid == value.node_id && deleted == false"> + <form> + <div v-for="value in $options.myArray" v-bind:key="value.node_id"> + <div v-if="nodeid == value.node_id && deleted == false"> + <p + class="read" + :id="nodeid" + :inner-html.prop="nodetext | marked" + ></p> + <!-- <h3>Reactions</h3> --> + + <div class="react" v-if="value.node_id != undefined"> + <!-- <h2>React</h2> --> + <div class="eeee"> + <input :value="value.node_id" name="id" readonly hidden /> + <input + id="emojifield" + class="regular-input" + v-model="input" + readonly + /> + <div class="allemoji"> + <div + class="eachemoji" + v-for="(emojis, index) in configEmoji" + :key="index" + > + <p v-if="value.node_id == emojis.node_id"> + {{ emojis.emoji_text }} + </p> + </div> + </div> + <emoji-picker @emoji="append" :search="search"> + <div + class="emoji-invoker" + slot="emoji-invoker" + slot-scope="{ events: { click: clickEvent } }" + @click.stop="clickEvent" + > + <svg + height="24" + viewBox="0 0 24 24" + width="24" + xmlns="http://www.w3.org/2000/svg" + > + <path d="M0 0h24v24H0z" fill="none" /> + <path + d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm3.5-9c.83 0 1.5-.67 1.5-1.5S16.33 8 15.5 8 14 8.67 14 9.5s.67 1.5 1.5 1.5zm-7 0c.83 0 1.5-.67 1.5-1.5S9.33 8 8.5 8 7 8.67 7 9.5 7.67 11 8.5 11zm3.5 6.5c2.33 0 4.31-1.46 5.11-3.5H6.89c.8 2.04 2.78 3.5 5.11 3.5z" + /> + </svg> + </div> + <div + slot="emoji-picker" + slot-scope="{ emojis, insert, display }" + > + <div + class="emoji-picker" + :style="{ + top: display.y + 'px', + left: display.x + 'px', + }" + > + <div class="emoji-picker__search"> + <input type="text" v-model="search" v-focus /> + </div> + <div> + <div + v-for="(emojiGroup, category) in emojis" + :key="category" + > + <h5>{{ category }}</h5> + <div class="emojis"> + <span + v-for="(emoji, emojiName) in emojiGroup" + :key="emojiName" + @click="insert(emoji), sentReact()" + :title="emojiName" + >{{ emoji }}</span + > + </div> + </div> + </div> + </div> + </div> + </emoji-picker> + </div> + </div> + </div> + </div> + </form> + </div> + </div> + </div> +</template> + +<script> +import { mapState } from 'vuex' +import EmojiPicker from 'vue-emoji-picker' +import marked from 'marked' + +export default { + name: 'OtherCardslayer', + + components: { + EmojiPicker, + }, + data() { + return { + input: '', + search: '', + } + }, + + props: { + nodeid: String, + nodetext: String, + deleted: Boolean, + }, + + filters: { + marked: marked, + }, + + computed: mapState({ + otherNodes: (state) => state.otherNodes, + configPositions: (state) => state.configPositions, + configEmoji: (state) => state.configEmoji, + }), + + myArray: null, + created() { + this.$options.myArray = this.otherNodes + }, + + methods: { + append(emoji) { + this.input += emoji + }, + sentReact(nodeid, emojitext) { + emojitext = this.input + nodeid = this.nodeid + this.$store.dispatch('addEmoji', { + nodeid, + emojitext, + }) + + this.input = '' + }, + }, + directives: { + focus: { + inserted(el) { + el.focus() + }, + }, + }, +} +</script> + +<style lang="css" scoped> +h2 { + color: red; +} + +.allemoji { + font-size: 2em; + display: grid; + grid-template-columns: repeat(auto-fill, minmax(0, auto)); +} + +.eachemoji p { + margin: 0em; +} +.nodes { + width: 285px; + border: 2px solid black; + background-color: rgb(205, 234, 255); + margin-top: 1em; + margin-right: 1em; + padding-left: 1em; + padding-bottom: 1em; +} + +input { + display: none; +} + +.emoji-invoker { + cursor: pointer; + transition: all 0.8s; +} +.emoji-invoker:hover > svg { + fill: #000000; +} +.emoji-invoker > svg { + fill: #767b7e; + margin-top: 10px; + margin-left: 0.2em; + transform: scale(1.5); +} + +.emoji-picker { + transform: scale(1.2); + z-index: 1; + font-family: Montserrat; + border: 1px solid #ccc; + width: 18rem; + height: 20rem; + overflow: scroll; + padding: 1rem; + box-sizing: border-box; + border-radius: 0.5rem; + background: #fff; + box-shadow: 1px 1px 8px #c7dbe6; + margin-top: 3em; + margin-left: 3em; +} +.emoji-picker__search { + display: flex; +} +.emoji-picker__search > input { + flex: 1; + border-radius: 10rem; + border: 1px solid #ccc; + padding: 0.5rem 1rem; + outline: none; +} +.emoji-picker h5 { + margin-top: 0; + margin-bottom: 0; + color: #b1b1b1; + text-transform: uppercase; + font-size: 0.8rem; + cursor: default; +} +.emoji-picker .emojis { + display: flex; + flex-wrap: wrap; + justify-content: space-between; +} +.emoji-picker .emojis:after { + content: ''; + flex: auto; +} +.emoji-picker .emojis span { + padding: 0.2rem; + cursor: pointer; + border-radius: 5px; +} +.emoji-picker .emojis span:hover { + background: #ececec; + cursor: pointer; +} +</style> diff --git a/app/src/components/OtherListlayer.vue b/app/src/components/OtherListlayer.vue index c2c267ff8131a96e79f15bfd22b60d634b38673b..348f2af0d0410021eaae61a452c4bc821a3bc2a1 100644 --- a/app/src/components/OtherListlayer.vue +++ b/app/src/components/OtherListlayer.vue @@ -20,17 +20,17 @@ export default { name: 'OtherListlayer', props: { nodeid: String, - nodetext: String + nodetext: String, }, computed: mapState({ - otherNodes: state => state.otherNodes + otherNodes: (state) => state.otherNodes, }), filters: { // need to write a reverse data filter I suspect here so new data is at the top of list - marked: marked - } + marked: marked, + }, } </script> diff --git a/app/src/router/index.js b/app/src/router/index.js index d188aec0007ccc33a90444b769fa864689b3354b..bc336a7142a9df6455e8319bfd007847b7ab615a 100644 --- a/app/src/router/index.js +++ b/app/src/router/index.js @@ -1,8 +1,10 @@ import Vue from 'vue' import VueRouter from 'vue-router' import Home from '../views/Home.vue' +import Cards from '../views/Cards.vue' import List from '../views/List.vue' import Discarded from '../views/Discarded.vue' +import Leave from '../views/Leave.vue' // import Oldhome from '../views/Oldhome' //import Test from '../views/Test' @@ -14,9 +16,14 @@ export const routes = [ name: 'Home', component: Home, }, + { + path: '/cards', + name: 'Cards', + component: Cards, + }, { path: '/list', - name: 'List', + name: 'My List', component: List, }, { @@ -34,6 +41,12 @@ export const routes = [ import(/* webpackChunkName: "about" */ '../views/About.vue'), }, + { + path: '/leave', + name: 'Leave', + component: Leave, + }, + // { // path: '/test', // name: 'IPFS Test', diff --git a/app/src/views/Cards.vue b/app/src/views/Cards.vue new file mode 100644 index 0000000000000000000000000000000000000000..9db21482501a3cf3c8f00aa1810e36bf1347e42e --- /dev/null +++ b/app/src/views/Cards.vue @@ -0,0 +1,128 @@ +<template> + <div> + <div v-if="clientset"> + <div id="listwrapper"> + <h1 class="mobile">nodes - card view</h1> + <div class="btn-row"> + <BaseButton class="new" buttonClass="action" @click="addNode()" + >Create Node</BaseButton + > + </div> + <div class="grid"> + <CardsLayer + @editTrue="(e) => editTrue(e)" + v-for="value in myNodes" + v-bind:key="value.node_id" + v-bind:nodeid="value.node_id" + v-bind:nodetext="value.node_text" + v-bind:deleted="value.deleted" + /> + + <OtherCardslayer + v-for="value in otherNodes" + v-bind:key="value.node_id" + v-bind:nodeid="value.node_id" + v-bind:nodetext="value.node_text" + v-bind:deleted="value.deleted" + /> + </div> + </div> + </div> + <div v-else> + <OnBoard @clientAdded="clientAdded()" @editTrue="(e) => editTrue(e)" /> + </div> + </div> +</template> + +<script> +import Router from '@/router' +import CardsLayer from '@/components/CardsLayer' +import OtherCardslayer from '@/components/OtherCardslayer' +import OnBoard from '@/components/OnBoard' +import { mapState } from 'vuex' +import marked from 'marked' +import { shortcutsMixin } from '@/components/mixins/shortcutsMixin.js' + +export default { + name: 'List', + + mixins: [shortcutsMixin], + data: function () { + return { + localmicrocosm: Router.currentRoute.params.microcosm, + clientid: '', + clientset: false, + offline: false, + name: false, + microcosm: false, + } + }, + + props: { + nodeid: String, + nodetext: String, + deleted: Boolean, + }, + + computed: { + ...mapState({ + myNodes: (state) => state.myNodes, + otherNodes: (state) => state.otherNodes, + }), + }, + + created() { + if (typeof window !== 'undefined') { + document.addEventListener('keydown', this.handleKeyPress) + } + }, + + beforeDestroy() { + if (typeof window !== 'undefined') { + document.removeEventListener('keydown', this.handleKeyPress) + } + }, + + mounted() {}, + + methods: { + clientAdded() { + this.clientset = !this.clientset + }, + + addNode() { + this.$store.dispatch('addNode') + }, + + editTrue(e) { + this.$store.dispatch('shortcutState', e) + }, + }, + components: { + CardsLayer, + OtherCardslayer, + OnBoard, + }, + filters: { + marked: marked, + }, +} +</script> + +<style lang="css" scoped> +.grid { + display: flex; + flex-wrap: wrap; +} + +#listwrapper { + margin-left: 1em; + margin-bottom: 1em; +} +.mobile { + font-size: 1em; +} +.new { + margin-bottom: 1em; +} +</style> diff --git a/app/src/views/Discarded.vue b/app/src/views/Discarded.vue index 4ae754b52680ade4b63f6c13eba97ca829ba606b..867dbcf6bb31eebb1b1a78a3aa18f9d9aa1af6ef 100644 --- a/app/src/views/Discarded.vue +++ b/app/src/views/Discarded.vue @@ -58,6 +58,9 @@ export default { clientAdded() { this.clientset = !this.clientset }, + editTrue(e) { + this.$store.dispatch('shortcutState', e) + }, }, } </script> diff --git a/app/src/views/Leave.vue b/app/src/views/Leave.vue new file mode 100644 index 0000000000000000000000000000000000000000..2a0432d2a45a2f95f2e99729e31596669958fdf9 --- /dev/null +++ b/app/src/views/Leave.vue @@ -0,0 +1,25 @@ +<template> + <div class="leave"></div> +</template> +<script> +export default { + name: 'Leave', + + created() { + this.removeLocal() + }, + + methods: { + removeLocal: function () { + localStorage.removeItem('myNNClient') + localStorage.removeItem('mylastMicrocosm') + + location.assign( + process.env.VUE_APP_HTTP + '://' + process.env.VUE_APP_URL + '/' + ) + }, + }, +} +</script> + +<style lang="css" scoped></style> diff --git a/app/src/views/List.vue b/app/src/views/List.vue index 7db164ed208d23bbec5b6dbee60c86befcc9316f..b6631db6c696b93bd3c74c62d604c81339bcaab6 100644 --- a/app/src/views/List.vue +++ b/app/src/views/List.vue @@ -57,6 +57,8 @@ export default { computed: { ...mapState({ myNodes: (state) => state.myNodes, + shortcutstate: (state) => state.shortcutstate, + toolmode: (state) => state.ui.mode, }), },