diff --git a/CHANGELOG.md b/CHANGELOG.md index f69e641ed626f5f88c98a6bd16931951fa74c0a7..ef071c4cde349dbd154a09e48cb51d239d4bd3b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,16 @@ +# 0.1.45 + +_17th December 2020_ + +### Changed + +- Card view can now be sorted however this is not saved yet. + +### Fixed + +- You can now colour nodes in Collect and Card views +- initial node creation failed on new microcosm without switching views + # 0.1.44 _14th December 2020_ @@ -16,7 +29,7 @@ _14th December 2020_ _12th December 2020_ -## Fixed +### Fixed - Partial fix so that nodes will resize to fit content (after dragging into position) diff --git a/app/package-lock.json b/app/package-lock.json index 170b90b4b80fae5da28869792850d74918233e57..22e0072e5a1aa94318be94bae7bdfda572fa60e7 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -16099,6 +16099,11 @@ "is-plain-obj": "^1.0.0" } }, + "sortablejs": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.10.2.tgz", + "integrity": "sha512-YkPGufevysvfwn5rfdlGyrGjt7/CRHwvRPogD/lC+TnvcN29jDpCifKP+rBqf+LRldfXSTh+0CGLcSg0VIxq3A==" + }, "source-list-map": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", @@ -17651,6 +17656,14 @@ "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==", "dev": true }, + "vuedraggable": { + "version": "2.24.3", + "resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-2.24.3.tgz", + "integrity": "sha512-6/HDXi92GzB+Hcs9fC6PAAozK1RLt1ewPTLjK0anTYguXLAeySDmcnqE8IC0xa7shvSzRjQXq3/+dsZ7ETGF3g==", + "requires": { + "sortablejs": "1.10.2" + } + }, "vuex": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/vuex/-/vuex-3.6.0.tgz", diff --git a/app/package.json b/app/package.json index 50e714c401d58aa5c0e535d99c7dfe1149d3a597..1ecf70d6a706a681d8dd51a888d1f07dddd0721e 100644 --- a/app/package.json +++ b/app/package.json @@ -1,6 +1,6 @@ { "name": "nodenogg.in", - "version": "0.1.44", + "version": "0.1.45", "private": true, "scripts": { "serve": "vue-cli-service serve", @@ -19,6 +19,7 @@ "vue-emoji-picker": "^1.0.1", "vue-router": "^3.4.9", "vue-swatches": "^2.1.0", + "vuedraggable": "^2.24.3", "vuex": "^3.6.0" }, "devDependencies": { diff --git a/app/src/components/CardsLayer.vue b/app/src/components/CardsLayer.vue index 9deae187648a98f10679f002b46c8b80614a2448..c228e75b9e4092259658f2889de0787ae09bd37c 100644 --- a/app/src/components/CardsLayer.vue +++ b/app/src/components/CardsLayer.vue @@ -1,60 +1,181 @@ <template> - <div class="grid"> - <div v-for="(nodes, index) in $options.myArray" v-bind:key="index"> - <form class="nodes"> - <template v-if="nodes.read_mode == false"> - <textarea - @focus="editTrue(true)" - @blur="editTrue(false)" - autofocus - v-model="nodes.node_text" - @input="editNode" - :id="nodes.node_id" - ref="nodetext" - placeholder="Type your thoughts and ideas here! (auto saved every keystroke)" - ></textarea> - </template> - <template v-else> - <p - :id="nodes.node_id" - :inner-html.prop="nodes.node_text | marked" - ></p> - </template> - <div class="btn-row"> - <SvgButton - buttonClass="nodes" - @click.prevent="deleteFlag(nodes.node_id), updateNodes()" - /> - <SvgButton2 - buttonClass="nodes" - @click.prevent=" - readFlag(nodes.node_id, nodes.read_mode), updateNodes() - " - /> + <div> + <draggable + id="dragger" + class="grid" + name="grid" + @start="drag = true" + @end="drag = false" + @update="nodePositionIndex" + > + <div v-for="(value, index) in configPositions" v-bind:key="index"> + <div v-for="(nodes, index) in $options.myArray" v-bind:key="index"> + <form + class="nodes cell" + :style="{ + backgroundColor: nodes.color, + }" + v-if="nodes.node_id == value.node_id" + > + <template v-if="nodes.read_mode == false"> + <textarea + @focus="editTrue(true)" + @blur="editTrue(false)" + autofocus + v-model="nodes.node_text" + @input="editNode" + :id="nodes.node_id" + ref="nodetext" + placeholder="Type your thoughts and ideas here! (auto saved every keystroke)" + ></textarea> + </template> + <template v-else> + <p + :id="nodes.node_id" + :inner-html.prop="nodes.node_text | marked" + ></p> + </template> + <div class="btn-row"> + <SvgButton + buttonClass="nodes" + @click.prevent="deleteFlag(nodes.node_id), updateNodes()" + /> + <SvgButton2 + buttonClass="nodes" + @click.prevent=" + readFlag(nodes.node_id, nodes.read_mode), updateNodes() + " + /> + <v-swatches + v-model="color" + @input="chooseColor(color, nodes.node_id)" + :swatches="swatches" + :shapes="shapes" + show-border + show-fallback + fallback-input-type="color" + > + <SvgButton3 buttonClass="nodes" @click.prevent slot="trigger" /> + </v-swatches> + </div> + + <div class="allemoji"> + <div + class="eachemoji" + v-for="(emojis, index) in configEmoji" + :key="index" + > + <template v-if="emojis.node_id == nodes.node_id">{{ + emojis.emoji_text + }}</template> + </div> + </div> + </form> </div> + </div> - <div class="allemoji"> - <div - class="eachemoji" - v-for="(emojis, index) in configEmoji" - :key="index" - > - <template v-if="emojis.node_id == nodes.node_id">{{ - emojis.emoji_text - }}</template> + <div + v-for="(othernodes, index) in othernodes_filtered" + v-bind:key="index.nodeid" + > + <div + class="othernodes cell" + :style="{ + backgroundColor: othernodes.color, + }" + > + <p :inner-html.prop="othernodes.node_text | marked"></p> + + <div class="eeee"> + <input :value="othernodes.node_id" name="id" readonly hidden /> + <input + id="emojifield" + class="regular-input" + v-model="input" + readonly + /> + + <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" /> + </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(othernodes.node_id)" + :title="emojiName" + >{{ emoji }}</span + > + </div> + </div> + </div> + </div> + </div> + </emoji-picker> + + <div class="allemoji"> + <div + class="eachemoji" + v-for="(emojis, index) in configEmoji" + :key="index" + > + <p v-if="othernodes.node_id == emojis.node_id"> + {{ emojis.emoji_text }} + </p> + </div> + </div> </div> </div> - </form> - </div> + </div> + </draggable> </div> </template> <script> import { mapState } from 'vuex' import marked from 'marked' +import draggable from 'vuedraggable' +import EmojiPicker from 'vue-emoji-picker' import SvgButton from '@/components/SvgButton' import SvgButton2 from '@/components/SvgButton2' +import SvgButton3 from '@/components/SvgButton3' +import VSwatches from 'vue-swatches' +import 'vue-swatches/dist/vue-swatches.css' + var readmode +var nodeid +//var nodesort +var emojitext export default { name: 'CardsLayer', @@ -64,9 +185,21 @@ export default { }, data: function () { return { + color: '#9bc2d8', + shapes: 'circles', + // swatches: [{ color: '#F493A7', showBorder: true }], + swatches: [ + ['#EB5757', '#F2994A', '#F2C94C'], + ['#219653', '#27AE60', '#6FCF97'], + ['#2F80ED', '#2D9CDB', '#56CCF2'], + ['#9B51E0', '#BB6BD9', '#E9B7FC'], + ], localreadmode: false, myArray: null, update: false, + input: '', + search: '', + orders: this.configPositions, } }, @@ -75,6 +208,7 @@ export default { }, computed: { ...mapState({ + otherNodes: (state) => state.otherNodes, myNodes: (state) => state.myNodes, configPositions: (state) => state.configPositions, configEmoji: (state) => state.configEmoji, @@ -85,6 +219,11 @@ export default { return nodes.deleted == false }) }, + othernodes_filtered: function () { + return this.otherNodes.filter((othernodes) => { + return othernodes.deleted == false + }) + }, }, // this is to stop sync chasing bug @@ -123,6 +262,47 @@ export default { }, methods: { + nodePositionIndex() { + // var i + // var j + // var dragger = document.getElementById('dragger') + // // console.log(dragger.childNodes.length) + // for (i = 0; i < dragger.childNodes.length; i++) { + // var count = i + // for (j = 0; j < Object.keys(this.configPositions).length; j++) { + // if (dragger.childNodes[i].firstChild != 'undefined') { + // if ( + // dragger.childNodes[i].firstChild[0].id == + // this.configPositions[j].node_id + // ) { + // nodeid = this.configPositions[j].node_id + // nodesort = count + // this.$store.dispatch('sortNode', { nodeid, nodesort }) + // } + // } + // // console.log(nodesort) + // } + // } + }, + chooseColor(color, nodeid) { + this.$store.dispatch('colorNode', { nodeid, color }) + this.$options.myArray = this.nodes_filtered + }, + + append(emoji) { + this.input += emoji + }, + sentReact(e) { + emojitext = this.input + nodeid = e + + this.$store.dispatch('addEmoji', { + nodeid, + emojitext, + }) + + this.input = '' + }, updateNodes() { this.update = !this.update }, @@ -162,19 +342,47 @@ export default { components: { SvgButton, SvgButton2, + SvgButton3, + VSwatches, + EmojiPicker, + draggable, }, } </script> <style lang="css" scoped> -.grid { +/* .grid { display: flex; flex-wrap: wrap; +} */ + +.grid { + display: grid; + /* grid-template-columns: repeat(3, 350px); */ + /* grid-template-rows: repeat(3, 350px); */ + grid-gap: 0.2em; } + +.grid-move { + transition: all 0.3s; +} + .nodes { + padding: 0.5em 0.5em; min-width: 343px; - max-width: 343px; + /* max-width: 343px; */ border: 2px dashed black; + /* background-color: rgb(155, 194, 216); */ + margin-top: 1em; + margin-left: 0.5em; + margin-right: 0.5em; +} + +.othernodes { + padding: 0.5em 0.5em; + min-width: 343px; + /* max-width: 343px; */ + border: 2px solid black; background-color: rgb(155, 194, 216); margin-top: 1em; margin-left: 0.5em; @@ -208,8 +416,23 @@ textarea { display: flex; justify-content: center; flex-wrap: wrap; - padding: 0 15px; - border-radius: 4px; + padding: 0 25px; +} + +.allemoji { + font-size: 2em; + display: grid; + grid-template-columns: repeat(auto-fill, minmax(0, auto)); + + /* float: left; */ +} + +.eachemoji p { + margin: 0em; +} + +input { + display: none; } .allemoji { @@ -224,6 +447,81 @@ textarea { margin: 0em; } +.emoji-invoker { + top: -0.5rem; + right: 0.5rem; + width: 1.5rem; + height: 2.5rem; + /* transform: scale(1.6); */ + /* margin: 0em 0em 1em 0em; */ + /* border-radius: 50%; */ + cursor: pointer; + transition: all 0.8s; +} +.emoji-invoker:hover > svg { + fill: #84949b; + + /* transform: scale(1.5); */ +} +.emoji-invoker > svg { + fill: #b1c6d0; + 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; +} +.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; +} + @media only screen and (max-width: 600px) { .readmode >>> a { font-size: 2em; @@ -231,4 +529,18 @@ textarea { padding-right: 0.5em; } } + +@media (min-width: 700px) { + .grid { + grid-template-columns: repeat(2, 1fr); + } +} + +@media (min-width: 1100px) { + .grid { + grid-template-columns: repeat(3, 1fr); + margin: 0 auto; + max-width: 1100px; + } +} </style> diff --git a/app/src/components/CardsLayerOld.vue b/app/src/components/CardsLayerOld.vue new file mode 100644 index 0000000000000000000000000000000000000000..df9bd254de869c92ea3822146a9ccf0cbc134de4 --- /dev/null +++ b/app/src/components/CardsLayerOld.vue @@ -0,0 +1,235 @@ +<template> + <div class="grid"> + <div v-for="(nodes, index) in $options.myArray" v-bind:key="index"> + <form class="nodes"> + <template v-if="nodes.read_mode == false"> + <textarea + @focus="editTrue(true)" + @blur="editTrue(false)" + autofocus + v-model="nodes.node_text" + @input="editNode" + :id="nodes.node_id" + ref="nodetext" + placeholder="Type your thoughts and ideas here! (auto saved every keystroke)" + ></textarea> + </template> + <template v-else> + <p + :id="nodes.node_id" + :inner-html.prop="nodes.node_text | marked" + ></p> + </template> + <div class="btn-row"> + <SvgButton + buttonClass="nodes" + @click.prevent="deleteFlag(nodes.node_id), updateNodes()" + /> + <SvgButton2 + buttonClass="nodes" + @click.prevent=" + readFlag(nodes.node_id, nodes.read_mode), updateNodes() + " + /> + </div> + + <div class="allemoji"> + <div + class="eachemoji" + v-for="(emojis, index) in configEmoji" + :key="index" + > + <template v-if="emojis.node_id == nodes.node_id">{{ + emojis.emoji_text + }}</template> + </div> + </div> + </form> + </div> + </div> +</template> + +<script> +import { mapState } from 'vuex' +import marked from 'marked' + +import SvgButton from '@/components/SvgButton' +import SvgButton2 from '@/components/SvgButton2' +var readmode + +export default { + name: 'CardsLayer', + + props: { + added: Boolean, + }, + data: function () { + return { + localreadmode: false, + myArray: null, + update: false, + } + }, + + filters: { + marked: marked, + }, + computed: { + ...mapState({ + myNodes: (state) => state.myNodes, + configPositions: (state) => state.configPositions, + configEmoji: (state) => state.configEmoji, + }), + + nodes_filtered: function () { + return this.myNodes.filter((nodes) => { + return nodes.deleted == false + }) + }, + }, + + // this is to stop sync chasing bug + + mounted() { + //access the custom option using $options + const unwatch = this.$watch('nodes_filtered', (value) => { + this.$options.myArray = this.nodes_filtered + + this.$forceUpdate() + // ignore falsy values + if (!value) return + + // stop watching when nodes_filtered[] is not empty + if (value && value.length) unwatch() + + // process value here + }) + }, + + watch: { + added: { + deep: true, + + handler() { + setTimeout(this.loadData, 200) + }, + }, + update: { + deep: true, + + handler() { + setTimeout(this.loadData, 200) + }, + }, + }, + + methods: { + updateNodes() { + this.update = !this.update + }, + loadData() { + this.$options.myArray = this.nodes_filtered + this.$forceUpdate() + }, + editNode(e) { + var nodeid = e.target.id + var nodetext = e.target.value + this.$store.dispatch('editNode', { nodeid, nodetext }) + }, + + editTrue(e) { + this.$emit('edit-true', e) + }, + + deleteFlag(e) { + if (confirm('Confirm discard?')) { + this.$store.dispatch('deleteFlag', { e }) + } else { + // nothing happens + } + }, + readFlag(e, f) { + readmode = f + readmode = !readmode + this.$store.dispatch('readFlag', { e, readmode }) + + if (readmode == true) { + this.mode = 'Read' + } else { + this.mode = 'Edit' + } + }, + }, + components: { + SvgButton, + SvgButton2, + }, +} +</script> + +<style lang="css" scoped> +.grid { + display: flex; + flex-wrap: wrap; +} +.nodes { + min-width: 343px; + max-width: 343px; + border: 2px dashed black; + background-color: rgb(155, 194, 216); + margin-top: 1em; + margin-left: 0.5em; + margin-right: 0.5em; +} + +.readmode { + margin-top: 1em; + margin-left: 1em; + padding-right: 1em; +} + +textarea { + width: 100%; + height: 175px; + resize: none; + font-size: 18px; + box-sizing: border-box; + font-family: 'Inter var', Helvetica, sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + + 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; +} + +@media only screen and (max-width: 600px) { + .readmode >>> a { + font-size: 2em; + word-break: break-all; + padding-right: 0.5em; + } +} +</style> diff --git a/app/src/components/ListLayer.vue b/app/src/components/ListLayer.vue index 6ee0540ac666b8b3ffa3d8a407993a5251c1d6e1..342d24856ce91c30dc6da9fa0746059655772742 100644 --- a/app/src/components/ListLayer.vue +++ b/app/src/components/ListLayer.vue @@ -1,7 +1,12 @@ <template> <div> <div v-for="(nodes, index) in $options.myArray" v-bind:key="index"> - <form class="nodes"> + <form + class="nodes" + :style="{ + backgroundColor: nodes.color, + }" + > <template v-if="nodes.read_mode == false"> <textarea @focus="editTrue(true)" @@ -32,6 +37,17 @@ readFlag(nodes.node_id, nodes.read_mode), updateNodes() " /> + <v-swatches + v-model="color" + @input="chooseColor(color, nodes.node_id)" + :swatches="swatches" + :shapes="shapes" + show-border + show-fallback + fallback-input-type="color" + > + <SvgButton3 buttonClass="nodes" @click.prevent slot="trigger" /> + </v-swatches> </div> <div class="allemoji"> @@ -55,6 +71,9 @@ import { mapState } from 'vuex' import marked from 'marked' import SvgButton from '@/components/SvgButton' import SvgButton2 from '@/components/SvgButton2' +import SvgButton3 from '@/components/SvgButton3' +import VSwatches from 'vue-swatches' +import 'vue-swatches/dist/vue-swatches.css' var readmode export default { @@ -66,6 +85,15 @@ export default { data: function () { return { + color: '#9bc2d8', + shapes: 'circles', + // swatches: [{ color: '#F493A7', showBorder: true }], + swatches: [ + ['#EB5757', '#F2994A', '#F2C94C'], + ['#219653', '#27AE60', '#6FCF97'], + ['#2F80ED', '#2D9CDB', '#56CCF2'], + ['#9B51E0', '#BB6BD9', '#E9B7FC'], + ], localreadmode: false, myArray: null, update: false, @@ -125,6 +153,10 @@ export default { }, methods: { + chooseColor(color, nodeid) { + this.$store.dispatch('colorNode', { nodeid, color }) + this.$options.myArray = this.nodes_filtered + }, updateNodes() { this.update = !this.update }, @@ -164,6 +196,8 @@ export default { components: { SvgButton, SvgButton2, + SvgButton3, + VSwatches, }, } </script> diff --git a/app/src/components/Navigation.vue b/app/src/components/Navigation.vue index e14f17d7b5d25f2b42fe0143f50867f313aec6ed..971eada1d98b985af98d3e2171d88290dbabf915 100644 --- a/app/src/components/Navigation.vue +++ b/app/src/components/Navigation.vue @@ -1,8 +1,10 @@ <template> <nav class="navigation"> - <router-link v-for="route in routes" :key="route.name" :to="route.path">{{ - route.name - }}</router-link> + <router-link v-for="route in routes" :key="route.name" :to="route.path"> + <div v-if="route.path != '/nitpicky'"> + {{ route.name }} + </div></router-link + > </nav> </template> diff --git a/app/src/components/NodesLayer.vue b/app/src/components/NodesLayer.vue index ff13bf6ee5a2304c756174991f1e9a55fbf1999b..4b3abc3d45bea7306d07d2189d0d7d125ec866f8 100644 --- a/app/src/components/NodesLayer.vue +++ b/app/src/components/NodesLayer.vue @@ -230,7 +230,6 @@ export default { ...mapState({ scale: (state) => state.ui.scale, myNodes: (state) => state.myNodes, - configPositions: (state) => state.configPositions, configConnections: (state) => state.configConnections, configEmoji: (state) => state.configEmoji, diff --git a/app/src/components/OtherCardslayer.vue b/app/src/components/OtherCardslayer.vue index 711d1bc4904b785231bac1c526cdee79b43f83c7..36b2859c02c4985c700dfcbb86e5e8a2769cc7fc 100644 --- a/app/src/components/OtherCardslayer.vue +++ b/app/src/components/OtherCardslayer.vue @@ -1,82 +1,88 @@ <template> <div class="grid"> - <div v-for="(nodes, index) in othernodes_filtered" v-bind:key="index"> - <div class="nodes"> - <p :inner-html.prop="nodes.node_text | marked"></p> + + <div v-for="(nodes, index) in othernodes_filtered" v-bind:key="index"> + <div class="nodes"> + <p :inner-html.prop="nodes.node_text | marked"></p> - <div class="eeee"> - <input :value="nodes.node_id" name="id" readonly hidden /> - <input - id="emojifield" - class="regular-input" - v-model="input" - readonly - /> + <div class="eeee"> + <input :value="nodes.node_id" name="id" readonly hidden /> + <input + id="emojifield" + class="regular-input" + v-model="input" + readonly + /> - <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 }"> + <emoji-picker @emoji="append" :search="search"> <div - class="emoji-picker" - :style="{ top: display.y + 'px', left: display.x + 'px' }" + class="emoji-invoker" + slot="emoji-invoker" + slot-scope="{ events: { click: clickEvent } }" + @click.stop="clickEvent" > - <div class="emoji-picker__search"> - <input type="text" v-model="search" /> - </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(nodes.node_id)" - :title="emojiName" - >{{ emoji }}</span - > + <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" /> + </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(nodes.node_id)" + :title="emojiName" + >{{ emoji }}</span + > + </div> </div> </div> </div> </div> - </div> - </emoji-picker> + </emoji-picker> - <div class="allemoji"> - <div - class="eachemoji" - v-for="(emojis, index) in configEmoji" - :key="index" - > - <p v-if="nodes.node_id == emojis.node_id"> - {{ emojis.emoji_text }} - </p> + <div class="allemoji"> + <div + class="eachemoji" + v-for="(emojis, index) in configEmoji" + :key="index" + > + <p v-if="nodes.node_id == emojis.node_id"> + {{ emojis.emoji_text }} + </p> + </div> </div> </div> </div> </div> - </div> + </div> </template> <script> import { mapState } from 'vuex' + import EmojiPicker from 'vue-emoji-picker' import marked from 'marked' @@ -88,6 +94,7 @@ export default { components: { EmojiPicker, + }, data: function () { diff --git a/app/src/router/index.js b/app/src/router/index.js index d715684b206d916237f104df50553d7f6757f227..069ce11e292c7e6808f833316d99681546d9d2ed 100644 --- a/app/src/router/index.js +++ b/app/src/router/index.js @@ -7,6 +7,7 @@ import Discarded from '../views/Discarded.vue' import Leave from '../views/Leave.vue' import About from '../views/About.vue' import NotFound from '../views/NotFound.vue' +import NitPicky from '../views/NitPicky.vue' //import store from '../store' // import Oldhome from '../views/Oldhome' //import Test from '../views/Test' @@ -46,6 +47,11 @@ export const routes = [ name: 'About', component: About, }, + { + path: '/nitpicky', + name: 'NitPicky', + component: NitPicky, + }, { // catches 404 errors diff --git a/app/src/store/index.js b/app/src/store/index.js index 36e349c6f00e99d0789ae5f83366c033c8bddee4..e1cc91630f5d940f69a7a119bf0d32a8628ec34e 100644 --- a/app/src/store/index.js +++ b/app/src/store/index.js @@ -69,6 +69,18 @@ const store = new Vuex.Store({ configEmoji: [ //{} ], + configRemote: [ + // { + // protocol: 'https://', + // couchusername: 'something', + // couchpassword: 'passcouch', + // couchurl: 'domain.com', + // }, + //{ https://, + // couchusername + // couchpassword + // couchURL} + ], }, mutations: { CREATE_MICROCOSM(state, doc) { @@ -82,16 +94,31 @@ const store = new Vuex.Store({ } else { microcosm = doc } + pouchdb = new PouchDB(microcosm) - remote = - process.env.VUE_APP_COUCH_HTTP + - '://' + - process.env.VUE_APP_COUCH_USER + - ':' + - process.env.VUE_APP_COUCH_PASS + - process.env.VUE_APP_COUCH_URL + - microcosm + - '/' + if (state.configRemote && state.configRemote.length > 0) { + remote = + state.configRemote[0].protocol + + state.configRemote[0].couchusername + + ':' + + state.configRemote[0].couchpassword + + '@' + + state.configRemote[0].couchurl + + '/' + + microcosm + + '/' + } else { + remote = + process.env.VUE_APP_COUCH_HTTP + + '://' + + process.env.VUE_APP_COUCH_USER + + ':' + + process.env.VUE_APP_COUCH_PASS + + process.env.VUE_APP_COUCH_URL + + microcosm + + '/' + } + store.dispatch('syncDB') }) }, @@ -387,14 +414,21 @@ const store = new Vuex.Store({ state.shortcutstate = e }, - // CONNECTION_STATE(state, e) { - // state.connectionstate = e - // }, + UPDATE_REMOTE(state, e) { + state.configRemote = [ + { + protocol: e.protocol, + couchusername: e.couchusername, + couchpassword: e.couchpassword, + couchurl: e.couchurl, + }, + ] + }, ADD_NODE(state) { var i + var zindex var totalNodes = [] - const reducer = (accumulator, currentValue) => accumulator + currentValue for (i = 0; i < Object.keys(state.allNodes).length; i++) { if ( state.allNodes[i].id != state.global_pos_name && @@ -406,8 +440,13 @@ const store = new Vuex.Store({ totalNodes.push(state.allNodes[i].doc.nodes.length) } } + const reducer = (accumulator, currentValue) => accumulator + currentValue + if (totalNodes && totalNodes.length > 0) { + zindex = totalNodes.reduce(reducer) + } else { + zindex = 1 + } - var zindex = totalNodes.reduce(reducer) var uniqueid = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15) @@ -481,6 +520,7 @@ const store = new Vuex.Store({ height: 370, z_index: zindex, read_mode: false, + node_sort: 0, }) return pouchdb .put({ @@ -659,6 +699,38 @@ const store = new Vuex.Store({ }) }, + SORT_NODE(state, e) { + var i + for (i = 0; i < Object.keys(state.configPositions).length; i++) { + if (e.nodeid == state.configPositions[i].node_id) { + state.configPositions[i].node_sort = e.nodesort + } + } + pouchdb + .get(state.global_pos_name) + .then(function (doc) { + // // console.log(doc) + // put the store into pouchdb + return pouchdb.bulkDocs([ + { + _id: state.global_pos_name, + _rev: doc._rev, + positions: state.configPositions, + }, + ]) + }) + .then(function () { + return pouchdb.get(state.global_pos_name).then(function (doc) { + state.configPositions = doc.positions + }) + }) + .catch(function (err) { + if (err.status == 404) { + // pouchdb.put({ }) + } + }) + }, + LEGACY_READ_FLAG(state, e) { var i // console.log(e.e) @@ -795,6 +867,9 @@ const store = new Vuex.Store({ commit('MOVE_POS', nodeid, xpos, ypos, width, height, zindex) }, + sortNode: ({ commit }, e) => { + commit('SORT_NODE', e) + }, updateConnect: ({ commit }, fromnode, xposstart, yposstart) => { commit('UPDATE_CONNECT', fromnode, xposstart, yposstart) }, @@ -822,6 +897,18 @@ const store = new Vuex.Store({ commit('COLOR_NODE', { nodeid, color }) }, + updateRemote: ( + { commit }, + { protocol, couchusername, couchpassword, couchurl } + ) => { + commit('UPDATE_REMOTE', { + protocol, + couchusername, + couchpassword, + couchurl, + }) + }, + makeConnect: ( { commit }, { fromnode, tonode, xposstart, yposstart, yposend, xposend } diff --git a/app/src/views/About.vue b/app/src/views/About.vue index dd6f338d8ad17b9559e50f865bf9da514df37f57..4c958dafd77a962ff1a9ae75eca0228f976937e8 100644 --- a/app/src/views/About.vue +++ b/app/src/views/About.vue @@ -7,9 +7,10 @@ concepts </p> <p> - Made by Adam Procter and Toby Milner-Gulland. + Made by Adam Procter. <em> - (+ many helpers (Mathew Parker, im looking at you) & + (+ many helpers (Toby Milner-Gulland & Mathew Parker, I'm looking at + you) & <a href="https://patreon.com/procterbot">supporters</a>) </em> </p> diff --git a/app/src/views/Cards.vue b/app/src/views/Cards.vue index f4b478f3cc384f5f0d4b929f493aa4c5e9689a68..74de959beade158c721dd477b1e5a3f73a6dee55 100644 --- a/app/src/views/Cards.vue +++ b/app/src/views/Cards.vue @@ -2,10 +2,10 @@ <div id="listwrapper"> <div v-if="clientset"> <h1 class="mobile">All nodes - card view</h1> - + <h2 class="danger">Positions not saved</h2> <CardsLayer @edit-true="(e) => editTrue(e)" :added="added" /> - <OtherCardslayer /> + <!-- <OtherCardslayer /> --> <!-- <BaseButton class="new" buttonClass="action" @click="addNode()" >Create Node</BaseButton @@ -55,9 +55,10 @@ <script> import CardsLayer from '@/components/CardsLayer' -import OtherCardslayer from '@/components/OtherCardslayer' +// import OtherCardslayer from '@/components/OtherCardslayer' import OnBoard from '@/components/OnBoard' import UploadLayer from '@/components/UploadLayer' + import { mapState } from 'vuex' import { shortcutsMixin } from '@/components/mixins/shortcutsMixin.js' @@ -128,7 +129,7 @@ export default { }, components: { CardsLayer, - OtherCardslayer, + // OtherCardslayer, UploadLayer, OnBoard, }, @@ -136,6 +137,9 @@ export default { </script> <style lang="css" scoped> +.danger { + color: red; +} .mobile { margin-left: 1em; font-size: 1em; diff --git a/app/src/views/Collect.vue b/app/src/views/Collect.vue index 08ba13c9732fbf39b3ad29f3fc53a52878923b8d..b4cb5503ee3b239f100b16fed143c1c2252c4183 100644 --- a/app/src/views/Collect.vue +++ b/app/src/views/Collect.vue @@ -116,6 +116,7 @@ export default { addNode() { this.$store.dispatch('addNode') this.added = !this.added + this.$forceUpdate() }, editTrue(e) { diff --git a/app/src/views/NitPicky.vue b/app/src/views/NitPicky.vue new file mode 100644 index 0000000000000000000000000000000000000000..ae544c35462ae4cc6aa74e83d2d7278daff82592 --- /dev/null +++ b/app/src/views/NitPicky.vue @@ -0,0 +1,84 @@ +<template> + <div> + <h1>Admin</h1> + <p>Access to some objects to test redirect of CouchDB and credentails</p> + + <div v-for="(value, index) in configRemote" v-bind:key="index"> + {{ value.protocol }}{{ value.couchusername }}:{{ value.couchpassword }}@{{ + value.couchurl + }} + </div> + <form> + <input + type="text" + v-model="protocol" + placeholder="https://" + autocorrect="off" + autocapitalize="none" + /> + <input + type="text" + v-model="couchusername" + placeholder="couch admin username" + autocorrect="off" + autocapitalize="none" + /> + <input + type="text" + v-model="couchpassword" + placeholder="couch admin password" + autocorrect="off" + autocapitalize="none" + /> + <input + type="text" + v-model="couchurl" + placeholder="youcouch.com" + autocorrect="off" + autocapitalize="none" + /> + + <button + @click.prevent=" + updateRemote(protocol, couchusername, couchpassword, couchurl) + " + > + Change Credentials + </button> + </form> + </div> +</template> + +<script> +import { mapState } from 'vuex' + +export default { + name: 'NitPicky', + + data: function () { + return { + protocol: '', + couchusername: '', + couchpassword: '', + couchurl: '', + } + }, + computed: mapState({ + configRemote: (state) => state.configRemote, + }), + + methods: { + updateRemote(protocol, couchusername, couchpassword, couchurl) { + // console.log(protocol, couchusername, couchpassword, couchurl) + this.$store.dispatch('updateRemote', { + protocol, + couchusername, + couchpassword, + couchurl, + }) + }, + }, +} +</script> + +<style lang="css" scoped></style>