From b51e5ac0d0aa6f9ae0f92fd3ed707414718e7ae1 Mon Sep 17 00:00:00 2001 From: Max G Date: Sun, 25 Sep 2022 17:17:41 +0000 Subject: [PATCH] Adding everything to get started on cycle tracking and maybe avid habit clone --- client/certs/192.168.1.164+4-key.pem | 28 + client/certs/192.168.1.164+4.pem | 26 + client/src/assets/semantic-helper.css | 42 +- .../GlobalNotificationComponent.vue | 37 +- client/src/components/GlobalSiteMenu.vue | 2 + client/src/components/NoteInputPanel.vue | 427 +++++----- .../src/components/NoteTitleDisplayCard.vue | 13 +- client/src/components/PasteButton.vue | 116 +++ .../src/components/SideSlideMenuComponent.vue | 8 +- client/src/components/SvgDisplayer.vue | 44 + client/src/mixins/SquireButtonFunctions.js | 20 + client/src/pages/AttachmentsPage.vue | 15 + client/src/pages/NotesPage.vue | 337 ++++++-- client/src/pages/OverviewPage.vue | 761 ++++++++++++++++++ client/src/stores/mainStore.js | 2 +- configs/dev nginx sites available default.cfg | 158 ++++ server/models/Attachment.js | 16 +- server/models/User.js | 6 +- server/routes/attachmentController.js | 2 +- 19 files changed, 1727 insertions(+), 333 deletions(-) create mode 100644 client/certs/192.168.1.164+4-key.pem create mode 100644 client/certs/192.168.1.164+4.pem create mode 100644 client/src/components/PasteButton.vue create mode 100644 client/src/components/SvgDisplayer.vue create mode 100644 client/src/pages/OverviewPage.vue create mode 100644 configs/dev nginx sites available default.cfg diff --git a/client/certs/192.168.1.164+4-key.pem b/client/certs/192.168.1.164+4-key.pem new file mode 100644 index 0000000..12703eb --- /dev/null +++ b/client/certs/192.168.1.164+4-key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC9vBYgeFm2nDnj +HJ+Cq4H96adNLrynoFPs0lSxk3YMXG/mP2sXUpqT3P8S7E6QK55IyU0jtiOoYiV1 +bjcTuDrQyZXrtt+Nz0A7vQxtRu8CatXSEG8Vc3y8QrcDT0HfTezHdNkuJXE8cnYv +XSgrZH+cHF996ytOA0lLJWBCHCJH5WIHj5Jziw5dHaLc4mjxSg51xjjqfzLFWQgZ +rOPF6lviAWBFux8RIXXg7nClNvEeyikdraZvVuWFgt89KhN/ePU1Xik/o6++evhT +HxBZqMQHnTp/4h5T25lPGcs0CPPX0afwNSUdPc8yspuSYNcLsWix3oROLMX6cHBa +CTBrtlLzAgMBAAECggEAIBhG7est0dQPfrmCygnVDWyO3mF/jCN0zuStavR0zZZ9 +X0dvCBzzBPwnMb5Dc+PM/KcAo3/V/E/N4lVof690U4kmER94JXbfeLt79KhBGfmU +fdpF0C0e9oGaj7bCf9GgsgS0EDhJNV5vW4e4mc6AP5oVFSnIw4OOzGVgKQ61Rc/e +vaalAEz7xGbsYYh2Y43tFnNA6g/qOzs+H8e2Uv+G9mxx7EID1MG3txJJCBpeUeN5 +JbHQe254IBy4Ko3+i5Tq3ziYL3WyyElCwK5PiRtAP7WNL5AqFT6Fn4L2fULwkTKL +sjPSgGxt7QHomeW+12n9bst1mOEmsz0hTGrAtIf1UQKBgQDu1u7BLdTt9H+zaOFl +XfhOR3GDZnrIs+F6VuEjdjwNEQLKvOYzGhbgDNRArSETK6f03Jvl7ZC9XYwNC836 +J29fHsXVcsMJm8fq69PBSdmMXmOpRfjgALu6DYxK1vBqNQ2ToulCwiWoskKEuPUN +GwVCc/0HcvwZElWZ1UhsB5Xf9wKBgQDLXfMCtsVpfI15w8qEqRCdrCzprHSitcHJ +dCXO72+i9ygMuFtcxo7kgivT0oFUCYmt7Ex+krOlq/xbVLu4sJXWd1FDZ1IeTsIh +cuh4IhSOGJR70S4Q/DzbGUQ08Hu+4hrudaw1Y8Fod0wTERCyOIQiWBfKn3Tab3mk +X29RdGod5QKBgBig5UHaXgijm79+Yy+2vvIjf9sS6DpmAixBZTno6UxXorgRPpOq +bw1vhTueHrkBWXJwhUrycmh0iwqVWwoeoudmHvRhvybwf28EHnPiD6Lf4NsFsiI3 +MSSAXSUigOwSyHGe7PrLVmLM7vsMr4hIbwRpPYBVJRXYxCb2zV8GcTgFAoGBALiV +gWhJNE1We6K1jy9xtF8oU2uU2BiHGGkdPuPgd1dXNca13lcK8c9+RwFv42q/bXOr +MpL/3IbW36qV8fzkalvK2LtxIBvaKGHrxgykAjwnGz520nUgPKww9rOGQwsydndR +3whmjrme7jGwH5NjsKrrgkyrBojs/V+wL32jSF3xAoGAPLmI5gamjEo9K25ojNxO +XjA0fIOxUz/rTPS/qYrxcluXibz8eGXDLq8/D3Q4uWDh18ZQYzWVGoN/x2Qv0srz +SHU4AyJo6+asZAe+viOhAtI81B7uM5V4oyEkPaEASPg6+do/to7SFmdcw/XM/p2v +KYVXalAeFhW0wJ4I4z6DkuU= +-----END PRIVATE KEY----- diff --git a/client/certs/192.168.1.164+4.pem b/client/certs/192.168.1.164+4.pem new file mode 100644 index 0000000..1edf569 --- /dev/null +++ b/client/certs/192.168.1.164+4.pem @@ -0,0 +1,26 @@ +-----BEGIN CERTIFICATE----- +MIIEWTCCAsGgAwIBAgIRAPWg+zwqGDC6qPGon1qGS0cwDQYJKoZIhvcNAQELBQAw +czEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMSQwIgYDVQQLDBttYWJA +bWFydmluIChNYXggR2lhbGFuZWxsYSkxKzApBgNVBAMMIm1rY2VydCBtYWJAbWFy +dmluIChNYXggR2lhbGFuZWxsYSkwHhcNMjIwNjI2MTgxMDE5WhcNMjQwOTI2MTgx +MDE5WjBPMScwJQYDVQQKEx5ta2NlcnQgZGV2ZWxvcG1lbnQgY2VydGlmaWNhdGUx +JDAiBgNVBAsMG21hYkBtYXJ2aW4gKE1heCBHaWFsYW5lbGxhKTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAL28FiB4WbacOeMcn4Krgf3pp00uvKegU+zS +VLGTdgxcb+Y/axdSmpPc/xLsTpArnkjJTSO2I6hiJXVuNxO4OtDJleu2343PQDu9 +DG1G7wJq1dIQbxVzfLxCtwNPQd9N7Md02S4lcTxydi9dKCtkf5wcX33rK04DSUsl +YEIcIkflYgePknOLDl0dotziaPFKDnXGOOp/MsVZCBms48XqW+IBYEW7HxEhdeDu +cKU28R7KKR2tpm9W5YWC3z0qE3949TVeKT+jr756+FMfEFmoxAedOn/iHlPbmU8Z +yzQI89fRp/A1JR09zzKym5Jg1wuxaLHehE4sxfpwcFoJMGu2UvMCAwEAAaOBizCB +iDAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwHwYDVR0jBBgw +FoAUzspSlchxoKIVJLKOhPGrcTFjRyowQAYDVR0RBDkwN4IMbWFydmluLmxvY2Fs +gglsb2NhbGhvc3SHBMCoAaSHBH8AAAGHEAAAAAAAAAAAAAAAAAAAAAEwDQYJKoZI +hvcNAQELBQADggGBAAaTOkCKiVwsapOAuiEt8kG7HS/r4HG+drLzhb2dUFYfqxpz +mfRlRfFA88JN8nyFtOPcpoeOEaTPMi0hxq0rOw9zHPga5kz6LRAUJeADPgA4pw2S +fYT1CEbPMknmHQyhVODKNZN05l3vWC2CL2SDs9lirGVrzmfg7kZ0im8hc81GQgo+ +MsfnC3AT1r1rzMqGLWiBHM8BjeGGwgqjjFZmxoHuGw+0CuV2TZfkZlNoJRtlRtyV +xlkUuRkVDYbLmHLMz7n9+ItOy8epKLToFpIXyhGR+ehAzYyyJeh2SCaboJ71lU+h ++90GQPl20ajWzLtwTsZHEAehHu4l/JLWleNaQh3nVdllHyzvU3IR/C7hhVv6im+q +/KiwDR3W8LqIOGJsemca5iu73EXd1d5UU49alIPm1Ko+Z22X/WMPj74+9CNW65DV +7ebM17NNQgr4tEJdXF3IYwaZ0Epv1/Y7v6bAXT8V2mdjtXfRBwu3HyySl22a9Y4R +h7svqj31cb0ubXEfrA== +-----END CERTIFICATE----- diff --git a/client/src/assets/semantic-helper.css b/client/src/assets/semantic-helper.css index 9f6c823..cebde8e 100644 --- a/client/src/assets/semantic-helper.css +++ b/client/src/assets/semantic-helper.css @@ -290,7 +290,7 @@ i.green.icon.icon.icon.icon { border: none; /*height: calc(100% - 69px);*/ - min-height: 500px; + min-height: 300px; background-color: var(--small_element_bg_color); /*margin-bottom: 15px;*/ @@ -309,6 +309,9 @@ i.green.icon.icon.icon.icon { margin-left: auto; margin-right: auto; max-width: 1100px; + + box-shadow: 0 8px 24px rgba(0,0,0,0.1); + } .squire-box::selection, .squire-box::-moz-selection { @@ -372,8 +375,37 @@ i.green.icon.icon.icon.icon { .squire-box ol, .note-card-text ul, .squire-box ul { - margin: 8px 0 0 0; + margin: 3px 0; + display: block; } + /* Add border 1 indent level */ + .note-card-text > ol > ol, + .squire-box > ol > ol, + .note-card-text > ul > ul, + .squire-box > ul > ul + { + border-left: 1px solid var(--border_color); + } + .note-card-text ol > ol, + .squire-box ol > ol, + .note-card-text ul > ul, + .squire-box ul > ul { + list-style-type: upper-alpha; + } + + +ol { + counter-reset: item; +} +ol li { + display: block; +} +ol li:before { +content: counters(item, ".") "."; +counter-increment: item; +padding-right: 10px; +} + .note-card-text ul > li, .squire-box ul > li { position: relative; @@ -500,10 +532,6 @@ i.green.icon.icon.icon.icon { /* adjust checkboxes for mobile. Make them a little bigger, easier to click */ @media only screen and (max-width: 740px) { - .squire-box { - min-height: calc(100vh - 122px); - } - .ui.button.shrinking { font-size: 0.85714286rem; margin: 0 3px; @@ -940,4 +968,4 @@ i.green.icon.icon.icon.icon { right: 0; background-color: rgba(0,0,0,0.7); z-index: 1000; -} \ No newline at end of file +} diff --git a/client/src/components/GlobalNotificationComponent.vue b/client/src/components/GlobalNotificationComponent.vue index 19f831f..5bc8bd7 100644 --- a/client/src/components/GlobalNotificationComponent.vue +++ b/client/src/components/GlobalNotificationComponent.vue @@ -19,7 +19,7 @@ padding: 1em 5px; cursor: pointer; } - .popup-row > span { + .popup-row > p { /*width: calc(100% - 50px);*/ display: inline-block; text-align: left; @@ -85,6 +85,18 @@ animation: progressBar 3s linear; animation-fill-mode: both; } + .time-display { + display: inline-block; + width: calc(100% - 25px); + /*text-align: right;*/ + color: white; + font-size: 0.7em; + margin: 0 0 0 25px; + } + .text-display { + display: inline-block; + width: 100%; + } @keyframes progressBar { 0% { width: 0; } @@ -101,7 +113,11 @@
- {{ item }} +

+ + {{ item.text }} + {{ item.time }} +

@@ -119,8 +135,8 @@ } }, beforeMount(){ - this.$bus.$on('notification', info => { - this.displayNotification(info) + this.$bus.$on('notification', notificationText => { + this.displayNotification(notificationText) }) }, mounted(){ @@ -131,8 +147,17 @@ }, methods: { - displayNotification(newNotification){ - this.notifications.push(newNotification) + displayNotification(notificationText){ + + const date = new Date() + const time = date.toLocaleTimeString() + + const notification = { + text: notificationText, + time: time + } + + this.notifications.unshift(notification) clearTimeout(this.totalTimeout) this.totalTimeout = setTimeout(() => { this.dismiss() diff --git a/client/src/components/GlobalSiteMenu.vue b/client/src/components/GlobalSiteMenu.vue index 54c1937..43e2018 100644 --- a/client/src/components/GlobalSiteMenu.vue +++ b/client/src/components/GlobalSiteMenu.vue @@ -4,6 +4,7 @@ width: 180px; display: block; float: left; + overflow: hidden; } .global-menu { width: 180px; @@ -106,6 +107,7 @@ text-align: center; color: #8c80ae; cursor: pointer; + background-color: var(--menu-background); } .mobile-button { diff --git a/client/src/components/NoteInputPanel.vue b/client/src/components/NoteInputPanel.vue index 81cd542..575f5e2 100644 --- a/client/src/components/NoteInputPanel.vue +++ b/client/src/components/NoteInputPanel.vue @@ -1,133 +1,134 @@ @@ -360,8 +361,8 @@ import SquireButtonFunctions from '@/mixins/SquireButtonFunctions.js' export default { - name: 'InputNotes', - props: [ 'noteid', 'position', 'openMenu', 'urlData' ], + name: 'NoteInputPanel', + props: [ 'noteid', 'position', 'openMenu', 'urlData', 'openNotes'], components:{ 'note-tag-edit': () => import('@/components/NoteTagEdit.vue'), 'color-picker': () => import('@/components/ColorPicker.vue'), @@ -436,12 +437,10 @@ watch: { urlData(newVal, oldVal){ - // return - //Handle changes in URL to if(newVal.id == undefined || newVal.id != this.noteid){ - this.closeButtonAction() + // this.closeButtonAction() } //Reset all note menus on URL change @@ -453,7 +452,7 @@ this.table = false //If a menu value is set, open it - if(newVal.openMenu){ + if(newVal.openMenu && newVal.id == this.noteid){ //Only modify menu boolean if its defined if(typeof this[newVal.openMenu] == 'boolean'){ this.sideMenuOpen = true @@ -471,6 +470,12 @@ this.save() } }) + + this.$bus.$on('close_note_by_id', (noteId) => { + if(noteId == this.noteid){ + this.closeButtonAction() + } + }) }, beforeDestroy(){ @@ -485,6 +490,7 @@ //Obliterate squire instance this.editor.destroy() + // trigger save actions and reindex this.close() }, @@ -643,9 +649,9 @@ if(keyCode == 'Tab'){ if(event.shiftKey){ - this.editor.decreaseQuoteLevel() + this.outdentText() } else { - this.editor.increaseQuoteLevel() + this.indentText() } event.preventDefault() @@ -989,12 +995,14 @@ return hash; }, - closeButtonAction(){ - this.sizeDown = true + closeButtonAction(playAnimation = false){ + this.sizeDown = playAnimation + const animationTimeout = (playAnimation ? 300 : 0) //This timeout allows animation to play before closing setTimeout(() => { - this.$router.push('/notes') - }, 300) + // this.$router.push('/notes') + this.close() + }, animationTimeout) }, close(){ @@ -1022,9 +1030,9 @@ }) }, destroyWebSockets(){ - this.$io.removeListener('past_diffs') - this.$io.removeListener('update_user_count') - this.$io.removeListener('incoming_diff') + // this.$io.removeListener('past_diffs') + // this.$io.removeListener('update_user_count') + // this.$io.removeListener('incoming_diff') }, setupWebSockets(){ @@ -1134,42 +1142,41 @@ left: 0; right: 0; bottom: 0; - background-color: var(--menu-accent); + background-color: var(--small_element_bg_color); z-index: 999; cursor: pointer; - opacity: 0.9; + opacity: 0.6; } /* squire styles */ .input-container-wrapper { - position: fixed; + position: absolute; top: 0; bottom: 0; - left: 15%; - right: 15%; - z-index: 1005; + left: 0; + right: 0; overflow-y: scroll; overflow-x: hidden; scrollbar-width: none; scrollbar-color: transparent transparent; overscroll-behavior: contain; + + background-color: var(--border_color); } .note-wrapper { - background-color: var(--small_element_bg_color); - border: 1px solid var(--menu-accent); - margin: 45px 0 45px 0; + background-color: var(--border_color); position: relative; + margin: 50px auto; + + max-width: 1100px; } .note-mini-tag-area { width: 100%; padding: 5px 15px 0 15px; cursor: pointer; - - margin-left: auto; - margin-right: auto; - max-width: 1100px; position: relative; + background: var(--small_element_bg_color); } .add-mini-tag { color: var(--border_color); @@ -1195,22 +1202,29 @@ Edit Menu Styles START */ - .edit-menu { - position: fixed; - top: 0; + .menu-top-half, .menu-bottom-half { + display: flex; + justify-content: center; + position: absolute; + z-index: 1001; + background-color: green; + border-radius: 3px; + padding: 5px 5px; + background-color: var(--menu-background); + left: 0; right: 0; - width: 100%; - display: block; - background-color: var(--small_element_bg_color); - z-index: 1019; - padding: 5px 0; - border: none; - border-bottom: 1px solid var(--menu-accent); - text-align: center; } - .menu-top-half, .menu-bottom-half { - display: inline-block; + .menu-top-half{ + top: 0; + } + .menu-bottom-half { + bottom: 0; + } + .menu-top-half.hide-text .edit-button > span:not(.ui), + .menu-bottom-half.hide-text .edit-button > span:not(.ui) + { + display: none; } .edit-button { background-color: var(--small_element_bg_color); @@ -1218,14 +1232,15 @@ display: inline-block; border-radius: 3px; cursor: pointer; - font-size: 1.4em; + font-size: 1em; box-shadow: 0 0 1px 0 #c4c4c4; margin: 0 3px 0; - padding: 6px 0 0 0; + padding: 6px 12px 0; text-align: center; - min-width: 32px; - min-height: 32px; - + min-width: 25px; + min-height: 30px; + /*flex-basis: 100%;*/ + white-space: nowrap; } .edit-button > i { font-size: 1em; @@ -1244,19 +1259,29 @@ } .edit-divide { display: inline-block; - background-color: var(--menu-accent); height: 15px; - width: 1px; - margin: 0 8px; + width: 7px; padding: 0; } @media only screen and (max-width: 740px) { - .edit-spacer { - display: none; - } .edit-button { font-size: 1.2em; } + .menu-top-half, .menu-bottom-half { + padding: 3px 2px; + left: 0; + right: 0; + transform: none; + border-radius: 0; + } + .menu-bottom-half { + position: fixed; + z-index: 100000; + } + .note-wrapper { + margin-bottom: 100px; + margin-top: 38px; + } } /* @@ -1331,68 +1356,22 @@ left: calc(50% + 10px) !important; right: calc(0% + 10px) !important; } - @media only screen and (max-width: 740px) { + /*weird inbetween size for tables*/ + @media only screen and (max-width: 875px) { - .master-note-edit { - left: 0; - right: 0; - } - - - .input-container-wrapper { - left: 0; - right: 0; - top: 35px; - bottom: 37px; - background-color: var(--small_element_bg_color); - } - .note-wrapper { - margin: 0; - border: none; - } + } + @media only screen and (max-width: 830px) { .shade1, .shade2 { right: 150%; } - .edit-menu { - background-color: var(--dark_border_color); - } - /*menu overwrites */ - .bottom-edit-menu { - position: fixed; - bottom: 0; - left: 0; - right: 0; - width: 100%; - display: block; - /*background-color: var(--small_element_bg_color);*/ - background-color: var(--dark_border_color); - z-index: 1012; - border: none; - border-top: 1px solid var(--menu-accent); - min-height: 37px; - } - .menu-top-half, .menu-bottom-half { - display: flex; - justify-content: space-around; - } .edit-divide { display: none; } - .menu-bottom-half { - z-index: 1005; - position: fixed; - bottom: 4px; - left: 0; - right: 0; - text-align: center; - } .edit-button { - flex-basis: 100%; - margin: 0 2px; + padding: 6px 0px 0; } - .done-button { - flex-basis: 150%; - font-size: 13px; + .edit-button > span:not(.ui) { + display: none; } } diff --git a/client/src/components/NoteTitleDisplayCard.vue b/client/src/components/NoteTitleDisplayCard.vue index b73dc0a..738806f 100644 --- a/client/src/components/NoteTitleDisplayCard.vue +++ b/client/src/components/NoteTitleDisplayCard.vue @@ -9,7 +9,7 @@ -
+
Empty Note @@ -24,7 +24,7 @@ class="big-text">

{{ note.title }}

- #{{ tag.split(':')[0] }} + #{{ tag.split(':')[0] }}
@@ -444,10 +444,10 @@ .note-title-display-card { position: relative; background-color: var(--small_element_bg_color); + /*The subtle shadow*/ - /*box-shadow: 0px 1px 2px 1px rgba(210, 211, 211, 0.46);*/ box-shadow: 2px 2px 6px 0 rgba(0,0,0,.15); - transition: box-shadow ease 0.5s, transform linear 0.1s; + transition: box-shadow, border-color ease 0.5s, transform linear 0.5s; margin: 5px; /*padding: 0.7em 1em;*/ border-radius: .28571429rem; @@ -472,9 +472,8 @@ max-height: 450px; } .note-title-display-card:hover { - /*box-shadow: 0px 2px 2px 1px rgba(210, 211, 211, 0.8);*/ - /*transform: translateY(-2px);*/ - box-shadow: 0 8px 24px rgba(0,0,0,0.1); + box-shadow: 0 8px 15px rgba(0,0,0,0.3); + border-color: var(--main-accent); } .note-title-display-card.title-view { width: 100%; diff --git a/client/src/components/PasteButton.vue b/client/src/components/PasteButton.vue new file mode 100644 index 0000000..701c7d8 --- /dev/null +++ b/client/src/components/PasteButton.vue @@ -0,0 +1,116 @@ + + + + + diff --git a/client/src/components/SideSlideMenuComponent.vue b/client/src/components/SideSlideMenuComponent.vue index 3210e1d..a33bff1 100644 --- a/client/src/components/SideSlideMenuComponent.vue +++ b/client/src/components/SideSlideMenuComponent.vue @@ -1,9 +1,9 @@ \ No newline at end of file diff --git a/client/src/mixins/SquireButtonFunctions.js b/client/src/mixins/SquireButtonFunctions.js index 2264208..0f9bde5 100644 --- a/client/src/mixins/SquireButtonFunctions.js +++ b/client/src/mixins/SquireButtonFunctions.js @@ -404,6 +404,26 @@ const SquireButtonFunctions = { this.$router.go(-1) }, + indentText(){ + + // Lists use increase list level, increase quote breaks numbering + if(this.activeList || this.activeToDo){ + + this.editor.increaseListLevel() + return + } + this.editor.increaseQuoteLevel() + }, + outdentText(){ + + // Lists use increase list level, increase quote breaks numbering + if(this.activeList || this.activeToDo){ + + this.editor.decreaseListLevel() + return + } + this.editor.decreaseQuoteLevel() + }, }, } diff --git a/client/src/pages/AttachmentsPage.vue b/client/src/pages/AttachmentsPage.vue index 2a4de13..96b49a7 100644 --- a/client/src/pages/AttachmentsPage.vue +++ b/client/src/pages/AttachmentsPage.vue @@ -35,6 +35,15 @@ Other Files + + + + Show Shared +
@@ -165,6 +174,12 @@ this.searchParams.attachmentType = this.$route.params.type } + // include files from shared notes or selected notes + this.searchParams.includeShared = false + if(this.$route.params.type == 'shared'){ + this.searchParams.includeShared = true + } + //Set noteId in if in URL if(this.$route.params.id){ this.searchParams.noteId = this.$route.params.id diff --git a/client/src/pages/NotesPage.vue b/client/src/pages/NotesPage.vue index f4e14ea..a79dcd6 100644 --- a/client/src/pages/NotesPage.vue +++ b/client/src/pages/NotesPage.vue @@ -1,10 +1,10 @@ @@ -165,7 +176,7 @@ import axios from 'axios' export default { - name: 'SearchBar', + name: 'NotesPage', components: { 'note-input-panel': () => import(/* webpackChunkName: "NoteInputPanel" */ '@/components/NoteInputPanel.vue'), @@ -186,6 +197,8 @@ searchResultsCount: 0, searchTags: [], notes: [], + openNotes: [], + collapseFloatingList: false, highlights: [], searchDebounce: null, fastFilters: {}, @@ -247,35 +260,34 @@ this.$io.on('new_note_created', noteId => { - //Do not update note if its open - if(this.activeNoteId1 != noteId){ - this.$store.dispatch('fetchAndUpdateUserTotals') - this.updateSingleNote(noteId, false) - } + // Push new note to top of list and animate + this.updateSingleNote(noteId) + this.$store.dispatch('fetchAndUpdateUserTotals') }) this.$io.on('note_attribute_modified', noteId => { + + const drawFocus = !this.openNotes.includes(parseInt(noteId)) + this.updateSingleNote(noteId, drawFocus) + //Do not update note if its open - if(this.activeNoteId1 != noteId){ + if(this.openNotes.includes(parseInt(noteId))){ this.$store.dispatch('fetchAndUpdateUserTotals') - this.updateSingleNote(noteId, false) } }) //Update title cards when new note text is saved this.$io.on('new_note_text_saved', ({noteId, hash}) => { - //Do not update note if its open - if(this.activeNoteId1 != noteId){ - this.updateSingleNote(noteId, true) - } + const drawFocus = !this.openNotes.includes(parseInt(noteId)) + this.updateSingleNote(noteId, drawFocus) }) this.$bus.$on('update_single_note', (noteId) => { - //Do not update note if its open - if(this.activeNoteId1 != noteId){ - this.updateSingleNote(noteId) - } + + const drawFocus = !this.openNotes.includes(parseInt(noteId)) + this.updateSingleNote(noteId, drawFocus) + }) //Update totals for app @@ -283,19 +295,7 @@ //Close note event this.$bus.$on('close_active_note', ({noteId, modified}) => { - - if(modified){ - console.log('Just closed Note -> ' + noteId + ', modified -> ', modified) - } - - //A note has been closed - if(this.$route.fullPath != '/notes'){ - this.$router.push('/notes') - } - - this.$store.dispatch('fetchAndUpdateUserTotals') - //Focus and animate if modified - this.updateSingleNote(noteId, modified) + this.closeNote(noteId, modified) }) this.$bus.$on('note_deleted', (noteId) => { @@ -347,6 +347,8 @@ } }) + // Window scroll needed when scrolling full page. + // second scroll event added on note-list for floating view scroll detection window.addEventListener('scroll', this.onScroll) //Close notes when back button is pressed @@ -355,8 +357,6 @@ //update note on visibility change // document.addEventListener('visibilitychange', this.visibiltyChangeAction); - //Find previously stored notes, cache for 20 hours, load them and compare - }, beforeDestroy(){ window.removeEventListener('scroll', this.onScroll) @@ -375,9 +375,9 @@ }, mounted() { - //Open note on load if ID is set + //Open note on PAGE LOAD if ID is set if(this.$route.params.id > 1){ - this.activeNoteId1 = this.$route.params.id + this.openNote(this.$route.params.id) } //Loads initial batch and tags @@ -386,18 +386,21 @@ }, watch: { '$route.params.id': function(id){ - //Open note on ID, null id will close note - this.activeNoteId1 = id + this.openNote(id) }, '$route' (to, from) { // Reload the notes if returning to this page if(to.fullPath == '/notes' && !from.fullPath.includes('/notes/open/')){ - this.reset() } + // Close all notes if returning to /notes page + if(to.fullPath == '/notes' && from.fullPath.includes('/notes/open/')){ + this.closeAllNotes() + } + //Lookup tags set in URL if(to.params.tag && this.$store.getters.totals && this.$store.getters.totals['tags'][to.params.tag]){ @@ -410,30 +413,96 @@ } } }, - methods: { - toggleTitleView(){ - this.titleView = !this.titleView + computed: { + isFloatingList(){ + + //If note 1 or 2 is open, show floating column + return (this.openNotes.length > 0) + }, showOneColumn(){ return this.$store.getters.getIsUserOnMobile - //If note 1 or 2 is open, show one column. Or if the user is on mobile - return (this.activeNoteId1 != null || this.activeNoteId2 != null) && - !this.$store.getters.getIsUserOnMobile - }, + } + }, + methods: { openNote(id, event = null){ + // + + const intId = parseInt(id) + if(this.openNotes.includes(intId)){ + + console.log('Open already open note?') + + // const openIndex = this.openNotes.indexOf(intId) + // if(openIndex != -1){ + // console.log('Open note and remove it ', intId + ' on index ' + openIndex) + // this.openNotes.splice(openIndex, 1) + // } + // this.$bus.$emit('close_note_by_id', intId) + return + } + //Don't open note if a link is clicked in display card if(event && event.target && event.target.nodeName){ const nodeClick = event.target.nodeName if(nodeClick == 'A'){ return } } - //Open note if a link was not clicked - this.$router.push('/notes/open/'+id) + // Push note to stack if not open + if(Number.isInteger(intId) && !this.openNotes.includes(intId)){ + this.openNotes.push(intId) + } + + this.$nextTick(() => { + // change route if open ID is not the same as current ID + if(this.$route.params.id != id){ + console.log('Open note, change route -> route id ' + this.$route.params.id + ' note id ->' + id + ', ' +(this.$route.params.id == id)) + this.$router.push('/notes/open/'+id) + } + }) + + + return }, + closeNote(noteId, modified){ + + console.log('close note', this.$route.fullPath) + + const openIndex = this.openNotes.indexOf(noteId) + if(openIndex != -1){ + console.log('Removing note id ', noteId + ' on index ' + openIndex) + this.openNotes.splice(openIndex, 1) + } + + // //A note has been closed + // if(this.$route.fullPath != '/notes'){ + // this.$router.push('/notes') + // } + if(this.openNotes.length == 0 && this.$route.fullPath != '/notes'){ + this.$router.push('/notes') + } + + if(modified){ + console.log('Just closed Note -> ' + noteId + ', modified -> ', modified) + this.$store.dispatch('fetchAndUpdateUserTotals') + //Focus and animate if modified + this.updateSingleNote(noteId, modified) + } + + console.log('closeNote(): Open notes length ', this.openNotes.length) + }, + closeAllNotes(){ + console.log('Close all notes ------------') + for (let i = this.openNotes.length - 1; i >= 0; i--) { + console.log('Close all notes -> ' + this.openNotes[i]) + this.closeNote(this.openNotes[i]) + } + console.log('----------------') + }, toggleTagFilter(tagId){ this.searchTags = [tagId] @@ -489,7 +558,6 @@ } this.lastVisibilityState = document.visibilityState - }, // @TODO Don't even trigger this if the note wasn't changed updateSingleNote(noteId, focuseAndAnimate = true){ @@ -525,6 +593,7 @@ return } + // if old note data and new note data exists if(note && newNote){ //go through each prop and update it with new values @@ -533,7 +602,7 @@ }) //Push new note to front if its modified or we want it to - if( focuseAndAnimate || note.updated != newNote.updated ){ + if( note.updated != newNote.updated ){ // Find note, in section, move to front Object.keys(this.noteSections).forEach( key => { @@ -547,6 +616,9 @@ }) }) + } + + if( focuseAndAnimate ){ this.$nextTick( () => { //Trigger close animation on note this.$refs['note-'+noteId][0].justClosed() @@ -833,18 +905,133 @@ padding: 15px 0 0; } .loading-section { - position: fixed; - bottom: 40px; - padding: 0 10px; - right: 5px; - box-shadow: 0 1px 3px 0 #656565; + color: var(--main-accent); + box-shadow: 0 1px 3px 0 var(--main-accent); border-radius: 6px; background-color: var(--small_element_bg_color); - opacity: 0.9; - font-size: 0.7em; + display: inline-block; + width: 100%; + margin: 15px 0; + } + .floating-list { + z-index: 1000; + position: fixed; + left: 0; + top: 0; + bottom: 0; + width: 25%; + height: 100vh; + background-color: var(--small_element_bg_color); + padding: 15px 5px 0px 10px; + overflow-y: scroll; + overflow-x: hidden; + -ms-overflow-style: none; /* Internet Explorer 10+ */ + scrollbar-width: none; /* Firefox */ + background-color: var(--border_color); + } + .floating-list::-webkit-scrollbar { + display: none; /* Safari and Chrome */ + } + .note-panel-container { + position: fixed; + width: 75%; + height: 100vh; + background: gray; + top: 0; + right: 0; + bottom: 0; + z-index: 1000; + + display: flex; + flex-direction: row; + flex-wrap: nowrap; + justify-content: center; + align-items: stretch; + align-content: stretch; + + z-index: 1000; + } + .note-panel-fullwidth { + width: 100% !important; } + .note-panel-container > div { + flex: 1; + position: relative; + } + .hidden-floating-list { + left: -1000px !important; + } + .show-hidden-note-list-button { + position: fixed; + top: 25px; + left: 0; + min-width: 45px; + background-color: var(--main-accent); + color: var(--text_color); + display: block; + z-index: 1100; + cursor: pointer; + border-bottom-right-radius: 5px; + border-top-right-radius: 5px; + padding: 8px 0px 8px 13px; + text-align: left; + font-size: 1.4em; + } + @media (min-width:320px) { /* smartphones, iPhone, portrait 480x320 phones */ + .floating-list { + left: -1000px; + } + .note-panel-container { + width: 100%; + } + } + @media (min-width:481px) { /* portrait e-readers (Nook/Kindle), smaller tablets @ 600 or @ 640 wide. */ + .floating-list { + left: 0px; + } + .note-panel-container { + width: 75%; + } + } + @media (min-width:641px) { /* portrait tablets, portrait iPad, landscape e-readers, landscape 800x480 or 854x480 phones */ + + } + @media (min-width:961px) { /* tablet, landscape iPad, lo-res laptops ands desktops */ + + } + @media (min-width:1025px) { /* big landscape tablets, laptops, and desktops */ + + } + @media (min-width:1281px) { /* hi-res laptops and desktops */ + + } + @media (min-width:2000px) { /* BIG hi-res laptops and desktops */ + .floating-list { + left: 180px; + width: calc(30% - 180px); + } + .note-panel-container { + width: 70%; + } + } + + .master-note-edit { + position: absolute; + width: 100%; + background: var(--small_element_bg_color); + left: 0; + top: 0; + bottom: 0; + overflow: hidden; + } + .master-note-edit + .master-note-edit { + border-left: 2px solid var(--main-accent); + border-left: 5px solid var(--border_color); + } + + diff --git a/client/src/pages/OverviewPage.vue b/client/src/pages/OverviewPage.vue new file mode 100644 index 0000000..5f5bd90 --- /dev/null +++ b/client/src/pages/OverviewPage.vue @@ -0,0 +1,761 @@ + + + + \ No newline at end of file diff --git a/client/src/stores/mainStore.js b/client/src/stores/mainStore.js index 650d1c5..c24dec8 100644 --- a/client/src/stores/mainStore.js +++ b/client/src/stores/mainStore.js @@ -48,7 +48,7 @@ export default new Vuex.Store({ 'small_element_bg_color': '#000', 'text_color': '#FFF', 'dark_border_color': '#555',//'#ACACAC', //Lighter color to accent elemnts user can interact with - 'border_color': '#555', + 'border_color': '#0b0110', 'menu-accent': '#626262', 'menu-text': '#d9d9d9', }, diff --git a/configs/dev nginx sites available default.cfg b/configs/dev nginx sites available default.cfg new file mode 100644 index 0000000..623dad0 --- /dev/null +++ b/configs/dev nginx sites available default.cfg @@ -0,0 +1,158 @@ +# +# Working dev server config +# + +server { + + listen 443 ssl; + + ssl_certificate /home/mab/ss/client/certs/192.168.1.164+4.pem; + ssl_certificate_key /home/mab/ss/client/certs/192.168.1.164+4-key.pem; + ssl_session_cache shared:SSL:1m; + ssl_session_timeout 5m; + ssl_protocols TLSV1.1 TLSV1.2 TLSV1.3; + + ssl_ciphers HIGH:!aNULL:!MD5; + ssl_prefer_server_ciphers on; + + access_log /var/log/nginx/httpslocalhost.access.log; + error_log /var/log/nginx/httpslocalhost.error.log; + + client_max_body_size 20M; + + location / { + proxy_pass https://127.0.0.1:8081; + proxy_set_header Host localhost; + proxy_set_header X-Forwarded-Host localhost; + proxy_set_header X-Forwarded-Server localhost; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_redirect off; + proxy_connect_timeout 90s; + proxy_read_timeout 90s; + proxy_send_timeout 90s; + proxy_ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + } + + location /sockjs-node { + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $http_host; + proxy_set_header X-NginX-Proxy true; + + proxy_pass https://127.0.0.1:8081; + proxy_redirect off; + proxy_ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + } + + location /api { + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $http_host; + proxy_set_header X-NginX-Proxy true; + + proxy_pass http://127.0.0.1:3000; + proxy_redirect off; + proxy_ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + } + + location /socket { + proxy_pass http://127.0.0.1:3001; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_set_header Host $host; + } + +} + +## +## Working Copy below -------------------------------------------- +## + +server { + + listen 443 ssl; + + ssl_certificate /home/mab/ss/client/certs/192.168.1.164+4.pem; + ssl_certificate_key /home/mab/ss/client/certs/192.168.1.164+4-key.pem; + ssl_session_cache shared:SSL:1m; + ssl_session_timeout 5m; + ssl_protocols TLSV1.1 TLSV1.2 TLSV1.3; + + ssl_ciphers HIGH:!aNULL:!MD5; + ssl_prefer_server_ciphers on; + + access_log /var/log/nginx/httpslocalhost.access.log; + error_log /var/log/nginx/httpslocalhost.error.log; + + client_max_body_size 20M; + + location / { + proxy_pass https://127.0.0.1:8081; + proxy_set_header Host localhost; + proxy_set_header X-Forwarded-Host localhost; + proxy_set_header X-Forwarded-Server localhost; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_redirect off; + proxy_connect_timeout 90s; + proxy_read_timeout 90s; + proxy_send_timeout 90s; + proxy_ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + } + + location /sockjs-node { + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $http_host; + proxy_set_header X-NginX-Proxy true; + + proxy_pass https://127.0.0.1:8081; + proxy_redirect off; + proxy_ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + } + + location /api { + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $http_host; + proxy_set_header X-NginX-Proxy true; + + proxy_pass http://127.0.0.1:3000; + proxy_redirect off; + proxy_ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + } + + location /socket { + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $http_host; + proxy_set_header X-NginX-Proxy true; + + proxy_pass http://127.0.0.1:3001; + proxy_redirect off; + proxy_ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + } + +} + + +# Prod settings to serve static index +# location / { +# autoindex on; +# #try_files $uri $uri/ /index.html; +# } + +# location / { +# #autoindex on +# +# proxy_pass http://127.0.0.1:8444; +# proxy_http_version 1.1; +# proxy_set_header Upgrade $http_upgrade; +# proxy_set_header Connection 'upgrade'; +# proxy_set_header Host $host; +# proxy_cache_bypass $http_upgrade; +# } \ No newline at end of file diff --git a/server/models/Attachment.js b/server/models/Attachment.js index e7b1134..d268c3d 100644 --- a/server/models/Attachment.js +++ b/server/models/Attachment.js @@ -46,14 +46,17 @@ Attachment.textSearch = (userId, searchTerm) => { }) } -Attachment.search = (userId, noteId, attachmentType, offset, setSize) => { +Attachment.search = (userId, noteId, attachmentType, offset, setSize, includeShared) => { return new Promise((resolve, reject) => { let params = [userId] - let query = 'SELECT * FROM attachment WHERE user_id = ? AND visible = 1 ' + let query = ` + SELECT attachment.*, note.share_user_id FROM attachment + JOIN note ON (attachment.note_id = note.id) + WHERE attachment.user_id = ? AND visible = 1 ` if(noteId && noteId > 0){ - query += 'AND note_id = ? ' + query += 'AND attachment.note_id = ? ' params.push(noteId) } @@ -64,11 +67,16 @@ Attachment.search = (userId, noteId, attachmentType, offset, setSize) => { query += 'AND attachment_type > 1 ' } + if(!noteId){ + const sharedOrNot = includeShared ? ' NOT ':' ' + query += `AND note.share_user_id IS${sharedOrNot}NULL ` + } + query += 'ORDER BY last_indexed DESC ' const limitOffset = parseInt(offset, 10) || 0 //Either parse int, or use zero - const parsedSetSize = parseInt(setSize, 10) || 20 //Either parse int, or use zero + const parsedSetSize = parseInt(setSize, 10) || 20 query += ` LIMIT ${limitOffset}, ${parsedSetSize}` db.promise() diff --git a/server/models/User.js b/server/models/User.js index e7aab97..326dc7b 100644 --- a/server/models/User.js +++ b/server/models/User.js @@ -9,7 +9,7 @@ const speakeasy = require('speakeasy') let User = module.exports = {} -const version = '3.4.3' +const version = '3.5.0' //Login a user, if that user does not exist create them //Issues login token @@ -35,7 +35,7 @@ User.login = (username, password, authToken = null) => { // if(rows[0].length == 1){ - //Pull out user data from database results + //Pull out user data from database results const lookedUpUser = rows[0][0] //Verify Token if set @@ -255,7 +255,7 @@ User.getCounts = (userId) => { WHERE user_id = ? GROUP BY tag_id ORDER BY uses DESC - LIMIT 5 + LIMIT 16 `, [userId]) }).then( (rows, fields) => { diff --git a/server/routes/attachmentController.js b/server/routes/attachmentController.js index 4230460..d1ad68f 100644 --- a/server/routes/attachmentController.js +++ b/server/routes/attachmentController.js @@ -26,7 +26,7 @@ router.use(function setUserId (req, res, next) { }) router.post('/search', function (req, res) { - Attachment.search(userId, req.body.noteId, req.body.attachmentType, req.body.offset, req.body.setSize) + Attachment.search(userId, req.body.noteId, req.body.attachmentType, req.body.offset, req.body.setSize, req.body.includeShared) .then( data => res.send(data) ) })