1
0
mirror of https://github.com/BoostIo/Boostnote synced 2025-12-14 10:16:26 +00:00

Compare commits

..

811 Commits

Author SHA1 Message Date
Kohei TAKATA
a7a5b789fa Merge pull request #1230 from BoostIO/feature-v0-8-18
v0.8.18
2017-12-03 11:51:11 +09:00
Kohei TAKATA
10b7d58dc6 v0.8.18 2017-12-03 11:08:06 +09:00
Kazz Yokomizo
16f0e95e32 Merge pull request #1226 from BoostIO/add-newsletter
Add newsletter link to preference modal
2017-12-02 15:45:36 +09:00
Kazu Yokomizo
55395d3a2d Add newsletter link to preference modal 2017-12-02 15:41:43 +09:00
Kazz Yokomizo
4e0fa63fad Merge pull request #1225 from BoostIO/fix-finder-layout
Fix finder layout
2017-12-02 15:33:55 +09:00
Kazu Yokomizo
b9ea7696d8 Fix finder layout 2017-12-02 15:20:16 +09:00
Kazz Yokomizo
7de3308f52 Merge pull request #1223 from BoostIO/fixbug-cannot-open-finder
fix bug cannot open finder.
2017-12-02 15:07:19 +09:00
Kazz Yokomizo
b2b2373c7b Merge pull request #1218 from PaulRosset/fixbug-react-code-mirror
Fixbug react code mirror
2017-12-02 15:03:19 +09:00
Sosuke Suzuki
1c54f40a28 require ipcClient 2017-12-02 14:07:11 +09:00
Paul Rosset
4735992835 Fix 2017-12-01 17:24:28 +00:00
Paul Rosset
cba3519458 Fix 2017-12-01 17:19:21 +00:00
Paul Rosset
c64a5e1cca Correction eslint purpose 2017-12-01 17:10:04 +00:00
Paul Rosset
47ee8b8ce7 Fix bug on UI Tab in relation to React CodeMirror 2017-12-01 16:56:43 +00:00
Kazz Yokomizo
455b424429 Merge pull request #1211 from yosmoc/tagconfirm_onblur
confirm tag at onBlur event
2017-12-01 15:11:47 +09:00
Kazz Yokomizo
7d67ac3f12 Merge pull request #1212 from PaulRosset/correct-notification-area
Correction Notification top-left
2017-12-01 15:04:40 +09:00
Paul Rosset
34f377eb5c Correction Notification top-left 2017-11-30 21:32:28 +00:00
yosmoc
b7f4af8c78 confirm tag at onBlur event
When user inputs the tag and leave the tag input box without fixing(enter or tab key), tag string is still there, but it is not stored as a tag.

This changes solved this problem. When the cursol is out of the tag input, it registers the input as a tag.
2017-11-30 22:04:56 +01:00
Kazz Yokomizo
a6c7dde194 Merge pull request #1202 from BoostIO/fix-folded-layout
Fix folded layout
2017-11-29 16:51:28 +09:00
Kazu Yokomizo
43ebe4ecfd FIx folded layout 2017-11-29 16:43:26 +09:00
Kazz Yokomizo
1d38f1abb4 Merge pull request #1197 from BoostIO/fix-initialmodal-layout
Fix layout at initial modal
2017-11-28 16:44:07 +09:00
Kazu Yokomizo
061a0cd219 Fix layout at initial modal 2017-11-28 16:27:12 +09:00
Kazz Yokomizo
f0ed20ee2c Merge pull request #1184 from cormoran/fix/SideNavFoldEmoji
Fix surrogate pairs garbling on folded SideNav
2017-11-28 12:42:41 +09:00
Kazz Yokomizo
edfc8d95c8 Merge pull request #1070 from voidsatisfaction/feature/add_multiselect_notes_delete
Feature multiselect notes delete and move to another folder
2017-11-28 12:39:12 +09:00
voidSatisfaction
c33f9d8307 fix: from let to const 2017-11-27 14:31:11 +09:00
voidSatisfaction
eee212f5b8 Merge with master resolve conflict 2017-11-27 14:05:26 +09:00
Kazz Yokomizo
b690147b0b Merge pull request #1187 from BoostIO/fix-note-list
Fix note list layout
2017-11-27 11:26:27 +09:00
Kazu Yokomizo
10879d0f67 Fix note list layout 2017-11-27 11:22:21 +09:00
Kazz Yokomizo
b48b8f39fc Merge pull request #1144 from whizark/store-window-size-on-linux
store correct window size on Linux
2017-11-27 11:06:56 +09:00
cormoran
3d0b3e759b Fix surrogate pairs garbling on folded SideNav 2017-11-27 06:04:24 +09:00
Kazz Yokomizo
3e7f4a41e2 Merge pull request #1183 from BoostIO/update-backers
Update Backers
2017-11-27 03:03:57 +09:00
Kazz Yokomizo
b89b888129 Update Backers 2017-11-27 02:57:17 +09:00
Kazz Yokomizo
cba9afc9ba Merge pull request #1180 from romainwn/fix/SideNavFilter-Hover
SideNavItem: add background when hover
2017-11-26 19:33:16 +09:00
Unknown
a66a11b81e SideNavItem: add background when hover 2017-11-26 11:21:49 +01:00
Kazz Yokomizo
22cc2791b6 Merge pull request #1179 from BoostIO/fix-margin-notedetail
Fix Margin at Note Detail
2017-11-26 16:24:36 +09:00
Kazu Yokomizo
369cf16b68 Fix Margin at Note Detail 2017-11-26 16:14:52 +09:00
Kazz Yokomizo
0d38baf194 Merge pull request #1176 from yosmoc/initmodal_exit_fix
fix anykey can close the initmodal window issue
2017-11-26 16:05:38 +09:00
Kazz Yokomizo
5b63c95f40 Merge pull request #1178 from BoostIO/fix-typo
Fix typo
2017-11-26 15:57:36 +09:00
Kazu Yokomizo
52497999a0 Fix typo 2017-11-26 15:50:45 +09:00
Kohei TAKATA
6bab108a35 Merge pull request #1113 from yosmoc/react_proptypes_deprecated
React.PropTypes is deprecated from React 15.5
2017-11-26 09:22:38 +09:00
yosmoc
eec22e6b7d fix anykey can close the initmodal issue
this change makes only ESC key can close the initmodal window.
2017-11-25 23:39:59 +01:00
yosmoc
edaa0713e8 React.PropTypes is deprecated from React 15.5
Migrated by react-codemod + minor fix by hand.
2017-11-25 22:27:04 +01:00
Kohei TAKATA
6b6a415dd5 Merge pull request #1170 from mslourens/reduce-lint-errors
fixed eslint warnings
2017-11-25 17:43:13 +09:00
Kohei TAKATA
414e0dbc05 Merge pull request #1172 from BoostIO/feature-v0-8-17
v0.8.17
2017-11-25 14:41:20 +09:00
Kohei TAKATA
081a3da9e1 v0.8.17 2017-11-25 11:22:52 +09:00
Maurits Lourens
3fbc749395 fixed eslint warnings 2017-11-24 17:00:03 +01:00
Kazz Yokomizo
626175b2b8 Merge pull request #1165 from BoostIO/fix-infopanel-position
Fix infopanel in note detail position
2017-11-24 17:41:59 +09:00
Kazu Yokomizo
54db0c718a Fix infopanel in note detail position 2017-11-24 17:38:03 +09:00
Kazz Yokomizo
ae9175d1b1 Merge pull request #1164 from BoostIO/fix-ci-error
Fix ci error
2017-11-24 12:53:10 +09:00
Kazu Yokomizo
16e1f0f882 Fix zoom icon color 2017-11-24 12:43:04 +09:00
Kazu Yokomizo
179b830d14 Fix layout at statusbar in snippet note detail 2017-11-24 12:31:45 +09:00
Kazu Yokomizo
aeb27f7bff Fix font color in note detail 2017-11-24 12:26:39 +09:00
Kazu Yokomizo
0b2b89da0f Fix CI error 2017-11-24 12:26:28 +09:00
Kazz Yokomizo
804991b300 Merge pull request #1161 from ytk141/update-note-design
Update note design
2017-11-24 12:17:32 +09:00
Yutaka Ishii
529f271e07 style update 2017-11-24 12:04:11 +09:00
Yutaka Ishii
402d577ce0 update status bar style 2017-11-23 21:52:49 +09:00
Yutaka Ishii
4bb28a358e improve search folder 2017-11-23 21:52:49 +09:00
Yutaka Ishii
200a9275d2 add font Black 2017-11-23 21:52:49 +09:00
Yutaka Ishii
9cb086cbd9 update tag and folder 2017-11-23 21:52:49 +09:00
Kazz Yokomizo
3a836aaf34 Merge pull request #1160 from BoostIO/update-readme
Update readme
2017-11-23 19:10:20 +09:00
Kazz Yokomizo
e14a6f2374 Update readme 2017-11-23 19:01:13 +09:00
Kazz Yokomizo
28794bff79 Merge pull request #1159 from BoostIO/update-top-image
Update top image
2017-11-23 17:57:18 +09:00
Kazu Yokomizo
ecc9443c9a Update top image 2017-11-23 17:32:55 +09:00
Kazz Yokomizo
a1c499c026 Merge pull request #1156 from yamash723/update-markdown-it-kbd
Updated markdown-it-kbd to 1.1.1 from 1.1.0
2017-11-22 16:31:27 +09:00
yamash723
4e8321268c Updated markdown-it-kbd to 1.1.1 from 1.1.0 2017-11-22 16:13:09 +09:00
Kazz Yokomizo
8510325732 Merge pull request #1154 from romainwn/master
Add fr /docs translations
2017-11-21 21:39:56 +09:00
Unknown
49102be894 Add fr /docs translations 2017-11-21 12:43:51 +01:00
Kazz Yokomizo
1a97488dd7 Merge pull request #1153 from BoostIO/improve-intial-content
Improve the initial content
2017-11-21 20:15:46 +09:00
Kazu Yokomizo
b90c7e5318 Improve the initial content 2017-11-21 19:54:07 +09:00
Kazz Yokomizo
ece1e37c16 Merge pull request #1152 from BoostIO/fix-layout-at-noteitem
Fix layout at note item
2017-11-21 18:06:13 +09:00
Kazu Yokomizo
3a6e3f5cae Fix layout at note item 2017-11-21 17:55:43 +09:00
Kazz Yokomizo
8e502eec80 Merge pull request #1151 from BoostIO/fix-finder-ui
Fix finder UI
2017-11-21 15:04:16 +09:00
Kazu Yokomizo
042ebe8316 Fix finder UI 2017-11-21 13:37:25 +09:00
Kazz Yokomizo
eee47b1f76 Merge pull request #1150 from BoostIO/fix-statusbar-layout
Fix status bar layout
2017-11-21 13:26:54 +09:00
Kazu Yokomizo
7978704f1b Fix status bar layout 2017-11-21 13:22:16 +09:00
Kazz Yokomizo
9392ac438c Merge pull request #1149 from BoostIO/fix-windows-scrolling-bug
Fix windows scrolling bug
2017-11-21 12:40:10 +09:00
Kazu Yokomizo
cbcfb57e35 Fix windows scrolling bug 2017-11-21 12:20:53 +09:00
Whizark
460437397f store window size on Linux 2017-11-19 21:10:53 +09:00
Kazz Yokomizo
29fa512a90 Merge pull request #1143 from BoostIO/fix-star-position
Fix star position at note list
2017-11-18 18:11:13 +09:00
Kazu Yokomizo
059d1fb4f2 Fix star position at note list 2017-11-18 17:36:08 +09:00
Kazz Yokomizo
4d3a538213 Merge pull request #1142 from BoostIO/fix-button-ux-when-toggle
Fix button layout when hover and active
2017-11-18 16:52:33 +09:00
Kazu Yokomizo
108a0db799 Fix button layout when hover and active 2017-11-18 16:21:31 +09:00
Kazz Yokomizo
04f632570d Merge pull request #1141 from BoostIO/fix-button-toggle-ux
Fix toggle info button ux at note detail
2017-11-18 16:13:47 +09:00
Kazz Yokomizo
92b919da6c Merge pull request #1140 from BoostIO/six-button-in-trash
Fix button layout in Trash
2017-11-18 16:09:10 +09:00
Kazu Yokomizo
4f0f611e52 Fix toggle info button ux at note detail 2017-11-18 16:02:45 +09:00
Kazu Yokomizo
90f7cf0996 Fix button layout in Trash 2017-11-18 15:39:00 +09:00
Kazz Yokomizo
ce43e80bdf Merge pull request #1139 from BoostIO/fix-ci
Fix ci
2017-11-18 14:50:35 +09:00
Kazu Yokomizo
c397752b7a Fix at InfoButton 2017-11-18 14:47:39 +09:00
Kazu Yokomizo
2470364571 Fix at StatusBar 2017-11-18 14:44:06 +09:00
Kazu Yokomizo
cab122ba8b Fix at StorageItem 2017-11-18 14:43:38 +09:00
Kazu Yokomizo
56b01b5a85 Fix at SideNav 2017-11-18 14:42:57 +09:00
Kazu Yokomizo
cbcc9fb411 Fix at NoteList 2017-11-18 14:42:31 +09:00
Kazu Yokomizo
5bbc60d48a Fix at NewNoteButton 2017-11-18 14:41:51 +09:00
Kazu Yokomizo
8fb8aab7b8 Fix at TrashButton 2017-11-18 14:41:15 +09:00
Kazu Yokomizo
5b76216a64 Fix at SnippetNoteDetail 2017-11-18 14:40:41 +09:00
Kazu Yokomizo
7f5e372bdc Fix at markdown note detail 2017-11-18 14:40:11 +09:00
Kazu Yokomizo
01913a9a40 Fix at Infobutton 2017-11-18 14:39:08 +09:00
Kazu Yokomizo
62f9fcf171 Fix CI error at SideNavFilter 2017-11-18 14:35:37 +09:00
Kazz Yokomizo
34c667f6b2 Merge pull request #1138 from ytk141/design-icon
Icon Design Update
2017-11-18 14:24:38 +09:00
Yutaka Ishii
7a6de387e3 delete boostnote json 2017-11-18 13:55:24 +09:00
Yutaka Ishii
b6efbcedef design update 2 2017-11-18 13:46:46 +09:00
Kazz Yokomizo
3c70b2e5a0 Merge pull request #1136 from kazuhisya/doc-for-dist-pkgs
[doc] Add how to creating distribution packages(deb, rpm)
2017-11-17 18:45:09 +09:00
Kazuhisa Hara
62e108d460 [doc] Add how to creating distribution packages (jp)
Signed-off-by: Kazuhisa Hara <kazuhisya@gmail.com>
2017-11-17 17:28:11 +09:00
Kazuhisa Hara
b7f426b03f [doc] Add how to creating distribution packages
Signed-off-by: Kazuhisa Hara <kazuhisya@gmail.com>
2017-11-17 17:22:37 +09:00
Kazz Yokomizo
5ca13b70aa Merge pull request #1135 from BoostIO/add-information-tab
Add facebook group url to infotab
2017-11-17 13:30:37 +09:00
Kazu Yokomizo
4644d107c6 Add facebook group url to infotab 2017-11-17 13:25:41 +09:00
Kazz Yokomizo
06013b2a6a Merge pull request #1126 from yamash723/add-simple-addon
Add simple addon for CodeMirror
2017-11-17 01:25:22 +09:00
Kazz Yokomizo
7380fc8500 Merge pull request #1133 from BoostIO/add-Community-on-tab
Add community on tab in preferences
2017-11-16 22:41:52 +09:00
Kazu Yokomizo
81b550e66c Add community on tab in preferences 2017-11-16 21:14:07 +09:00
Kazz Yokomizo
f5972c273b Merge pull request #266 from kazuhisya/add-rpm-support
add rpm support
2017-11-16 11:23:53 +09:00
Kohei TAKATA
c259ec2bed Merge pull request #1119 from mslourens/paste-image
replace selection with pasted image
2017-11-15 23:57:38 +09:00
voidSatisfaction
a36e779980 refactor: add new function and fix problems from feedbacks 2017-11-15 23:19:47 +09:00
Kazz Yokomizo
86a04b0dcb Merge pull request #1127 from yamash723/modify-indent-adjustment
Add task item to indent adjustment target for list
2017-11-15 15:00:17 +09:00
Kazz Yokomizo
8b7c0c957c Merge pull request #1125 from BoostIO/fix-ci-error
Fix CI error
2017-11-15 14:18:56 +09:00
Kazu Yokomizo
0cfe971a48 Fix CI error 2017-11-15 13:48:04 +09:00
Kazz Yokomizo
8884db858c Merge pull request #1041 from machellerogden/master
make ZZ work in vim mode
2017-11-15 13:46:50 +09:00
yamash723
0fe9dd4fbb Add simple addon for CodeMirror 2017-11-15 13:10:55 +09:00
yamash723
ae57e99173 Add task item to indent adjustment target for list 2017-11-15 11:31:40 +09:00
Kazz Yokomizo
c238e1b5cb Merge pull request #1121 from BoostIO/fix-note-detail-layout
Fix note detail layout
2017-11-14 21:36:13 +09:00
Kazu Yokomizo
55ef998c55 Fix create sub snippet button color 2017-11-14 21:27:02 +09:00
Maurits Lourens
166815ccbf removed unused code 2017-11-14 13:18:36 +01:00
Kazu Yokomizo
34a16298d4 Change markdown lock icon 2017-11-14 21:15:00 +09:00
Kohei TAKATA
1613e72d47 Merge pull request #1105 from yosmoc/latest_electron
electron-prebuilt is obsolete. use latest electron instead
2017-11-14 21:02:48 +09:00
Kazu Yokomizo
e933ad89dc Fix star button at note detail 2017-11-14 21:00:10 +09:00
Kazu Yokomizo
148fb25a15 Fix note list layout 2017-11-14 20:54:31 +09:00
Kohei TAKATA
fe0544dbc9 Merge pull request #1114 from levrik/remove-mixpanel
Removed mixpanel
2017-11-14 20:46:10 +09:00
Kazu Yokomizo
0aaea28e74 Fix note detail layout 2017-11-14 20:46:07 +09:00
Kazu Yokomizo
f4764afbf4 Appear the todo percentage bar 2017-11-14 20:45:53 +09:00
Kazu Yokomizo
2a2b662a6f Fix right buttons layout in note detail 2017-11-14 20:28:51 +09:00
Kazu Yokomizo
deade1f9f8 Add trash button in trash 2017-11-14 20:26:36 +09:00
Kazu Yokomizo
2b73c17cca Fix CI 2017-11-14 18:51:50 +09:00
Kazu Yokomizo
369f8b6093 Fix CI 2017-11-14 18:40:38 +09:00
Kazu Yokomizo
5391ca161d Fix top part of note detail 2017-11-14 18:38:05 +09:00
Kazu Yokomizo
5b224d3b54 Fix note detail layout 2017-11-14 18:13:37 +09:00
Maurits Lourens
dad6a93944 replace selection with pasted image 2017-11-13 22:44:18 +01:00
Kazz Yokomizo
10ec5c1342 Merge pull request #1108 from BoostIO/change-default-color-and-add-white-theme
Change default theme and add white
2017-11-14 00:51:17 +09:00
Kazz Yokomizo
8edfc15f45 Merge pull request #1081 from yosmoc/solarized_bug_fix
solarized light/dark bug fix
2017-11-13 01:05:59 +09:00
Kazu Yokomizo
792e41f161 Fix tag list on note list 2017-11-12 12:11:47 +09:00
Levin Rickert
9fbdf895af Removed mixpanel 2017-11-12 03:44:51 +01:00
yosmoc
dbc0fedf43 improve readability 2017-11-11 23:17:00 +01:00
yosmoc
cb298b8cad check solarized theme or not beofre setting css
solarized dark/light shares the solarized.css
2017-11-11 22:55:17 +01:00
yosmoc
2459a80e15 solarized light/dark bug fix
fixup for 0bf7e8b705
ConfigManager is also needed to change.
2017-11-11 22:55:17 +01:00
Kazu Yokomizo
0a973c4db3 Fix at folded UI 2017-11-12 02:11:26 +09:00
Kazu Yokomizo
a84fddd5fa Fix CI error 2017-11-11 20:34:31 +09:00
Kazu Yokomizo
1cd26d2e71 Fix layout at note detail 2017-11-11 20:25:34 +09:00
Kazu Yokomizo
f88ac891ff Fix layout at note list 2017-11-11 20:10:16 +09:00
Kazu Yokomizo
17aa9ae85f Fix small layout bug 2017-11-11 19:10:00 +09:00
Kazu Yokomizo
bb46a9ba4c Change default color and add white theme 2017-11-11 19:06:27 +09:00
Kazu Yokomizo
4a6c16df8d Add base of white theme 2017-11-11 19:05:08 +09:00
Kazz Yokomizo
95787fafc6 Merge pull request #1106 from BoostIO/chage-initial-items
Chage initial items
2017-11-11 13:25:38 +09:00
Kazu Yokomizo
c51d79e4d7 Change the information at the Infotab 2017-11-11 12:26:57 +09:00
Kazu Yokomizo
d5232a1b0e Change default markdown content 2017-11-11 12:21:27 +09:00
Kazz Yokomizo
e093c7f982 Merge pull request #1104 from yosmoc/fix_grunt_pre-build_error
Fix grunt pre-build error
2017-11-11 10:44:12 +09:00
yosmoc
3f53bddf7e electron-prebuilt is obsolete. use latest electron instead 2017-11-11 00:54:44 +01:00
yosmoc
e03d8175e3 fix grunt pre-build error 2017-11-11 00:33:41 +01:00
Kazz Yokomizo
7ab2e9f6ca Merge pull request #1096 from BoostIO/fix-layout
Fix layout
2017-11-09 23:37:25 +09:00
Kazu Yokomizo
6316ddc6a6 Fix layout 2017-11-09 21:51:43 +09:00
Kazz Yokomizo
11c46ede5d Merge pull request #398 from asmsuechan/iss-397
Fix note title
2017-11-09 21:05:14 +09:00
Kazz Yokomizo
2ec5238d97 Merge pull request #1093 from clone1612/openPreferences
[Improvement] - Option for preferences in menus (916 & 1074)
2017-11-09 19:41:53 +09:00
Jannick Hemelhof
b7f359f6cf Option for preferences in menus 2017-11-09 09:40:59 +01:00
Kazz Yokomizo
8ef485221c Merge pull request #1052 from PaulRosset/trash-counter-notes
Add counter on deleted notes
2017-11-09 15:33:36 +09:00
Kazu Yokomizo
afddb6fc33 Merge pull request #1087 from BoostIO/udpate-readme
Update Readme
2017-11-08 19:25:05 +09:00
Kazu Yokomizo
f28ee1bc4b Merge pull request #1088 from BoostIO/update-backers
Update Backers.md
2017-11-08 19:24:48 +09:00
Kazu Yokomizo
b997f0a5a7 Update Backers.md 2017-11-08 19:20:28 +09:00
Kazu Yokomizo
48dc47ce57 Update Readme 2017-11-08 19:06:47 +09:00
voidSatisfaction
84f18ced47 fix: pintotop error 2017-11-07 23:07:39 +09:00
voidSatisfaction
037ff2e749 Merge branch 'master' into feature/add_multiselect_notes_delete 2017-11-07 22:53:14 +09:00
voidSatisfaction
f0f23ede3d Merge branch 'master' into feature/add_multiselect_notes_delete 2017-11-07 22:48:50 +09:00
voidSatisfaction
c8763063c0 resolve conflict with master 2017-11-07 22:47:08 +09:00
voidSatisfaction
e57fef2413 refactor: add utils 2017-11-07 22:38:28 +09:00
asmsuechan
f1a90f4a11 Remove an unused function 2017-11-07 14:48:32 +09:00
asmsuechan
990d7edba4 Fix some pointed by eslint 2017-11-07 14:44:50 +09:00
asmsuechan
13a1da91be [Lint] Remove an unexpected trailing comma 2017-11-07 14:44:20 +09:00
asmsuechan
930b58d2a8 [Lint] Remove semicolons 2017-11-07 14:44:20 +09:00
asmsuechan
b88b1065ee [Lint] Fix double quatations to single quatations 2017-11-07 14:44:20 +09:00
sota1235
5fbb802b32 modify: add test case 2017-11-07 14:44:20 +09:00
sota1235
a40f8d25ef modify: change to modern js 2017-11-07 14:44:20 +09:00
sota1235
0a28798d54 modify: add test case for underscore 2017-11-07 14:44:20 +09:00
asmsuechan
6047987c25 refactor: fix to use cut off file 2017-11-07 14:44:15 +09:00
sota1235
2ce96186f2 add: cut out trimming text logic for md note title and add unit test 2017-11-07 14:43:31 +09:00
asmsuechan
26357bd4bc Fix note title 2017-11-07 14:43:31 +09:00
SuenagaRyota
e94b45a7e4 Merge pull request #1064 from yosmoc/importfiletoemptyfolder
enable importing file to empty folder
2017-11-07 14:42:35 +09:00
Kazu Yokomizo
4be1eb7a28 Merge pull request #1084 from BoostIO/add-backer
Add backer
2017-11-07 12:54:43 +09:00
Kazu Yokomizo
f4bbbb640d Merge pull request #1083 from BoostIO/update-issue-template
Update issue template
2017-11-07 12:54:27 +09:00
Kazu Yokomizo
94b4e70b0f Add backer 2017-11-07 10:05:23 +09:00
Kazu Yokomizo
b31e3f7783 Update issue template 2017-11-07 10:03:07 +09:00
voidSatisfaction
9095fe934d add multi selection with arrow short cut 2017-11-07 00:01:50 +09:00
voidSatisfaction
9139495f02 feat: add multiple pin to top and rename activateNote to focusNote 2017-11-06 21:53:31 +09:00
Kazu Yokomizo
6375ac857a Merge pull request #1076 from BoostIO/update-backers
Update Backers.md
2017-11-06 19:06:34 +09:00
Kazu Yokomizo
5eac08430f Update Backers.md 2017-11-06 18:56:18 +09:00
Paul Rosset
ef0b109ad4 Add 0 by default 2017-11-06 09:19:12 +00:00
Kazu Yokomizo
7fbe6c0955 Merge pull request #1067 from BoostIO/fix-layout
Fix layout
2017-11-06 16:30:32 +09:00
Kazu Yokomizo
c86e43597c Fix CI error 2017-11-06 16:11:00 +09:00
Kazu Yokomizo
123a73a5e6 Fix at InfoPanel in trash 2017-11-06 15:57:00 +09:00
Kazu Yokomizo
706b5d253f Fix updated date at note list 2017-11-06 15:49:04 +09:00
yosmoc
8fb8c7a40b importing from file uses the same storage/folder resolve logic as creating new note
It makes less confusing for user.
2017-11-06 00:22:43 +01:00
yosmoc
21220f93b1 enable importing file to empty folder
Current implementation is fetching the import destination storageKey and folderkey from a file in the folder. If no files are in the folder, it cannot fetch these keys. Use prop.params.storageKey / folderKey instead.
2017-11-06 00:21:58 +01:00
Kazu Yokomizo
705e64377b Fix CI 2017-11-06 03:08:15 +09:00
Kazu Yokomizo
5c3f0cd060 Fix layout at InfoPanel 2017-11-06 02:40:59 +09:00
voidSatisfaction
bcb1fb4331 feat multi drag and drop for changing folder 2017-11-05 21:18:39 +09:00
Kazu Yokomizo
a238be5b7c Fix font size at tag list in side bar 2017-11-05 20:40:16 +09:00
Kazu Yokomizo
2351bb78da Change "Folders" and "Tags" strings to icons. 2017-11-05 20:31:32 +09:00
Kazu Yokomizo
6bbc5a91fe Increase font size and change folder icon at side bar 2017-11-05 20:05:44 +09:00
voidSatisfaction
70b69a3bc9 refactor: inner codes 2017-11-05 19:22:55 +09:00
Kazu Yokomizo
2f52233bd0 Fix UX when press a note in note list 2017-11-05 19:21:28 +09:00
voidSatisfaction
a7e458b784 feat change name and add deletion logic 2017-11-05 19:00:03 +09:00
voidSatisfaction
a504a45d99 fix up and down key rendering problems 2017-11-05 18:45:09 +09:00
voidSatisfaction
2d0e14c1cc feat: add shiftkey multiselect notes and delete 2017-11-05 18:11:08 +09:00
Kazu Yokomizo
92eccb635a Move the notification area from main window to preference modal 2017-11-05 17:56:32 +09:00
Kazu Yokomizo
1f8acc3afc Merge pull request #1065 from BoostIO/not-quit-mac
Not quit app using close button on Mac
2017-11-05 15:18:03 +09:00
Kazu Yokomizo
f8eaa9e796 Not quit app using close button on Mac 2017-11-05 12:34:19 +09:00
Kazu Yokomizo
7c9fecd943 Merge pull request #1043 from mslourens/close-in-windows
added shortcut ctrl+q for quit and quit the app when using the Windows Close button
2017-11-05 12:13:34 +09:00
Kazu Yokomizo
3df8cbb357 Merge pull request #1049 from mslourens/selected-tag-issue
only make the selected tag active
2017-11-05 11:39:42 +09:00
Kazu Yokomizo
993cb9cb0b Merge pull request #1061 from yosmoc/master
fix solarized dark/light code block thema bug
2017-11-04 19:27:36 +09:00
Kazu Yokomizo
5bd4a3f761 Merge pull request #1033 from nathanielw/fix/pinned-next
Include each note only once when sorting by pin
2017-11-04 19:24:59 +09:00
Kazu Yokomizo
fcad84ced3 Merge pull request #1062 from asmsuechan/add-plantuml
Add PlantUML support
2017-11-04 13:25:52 +09:00
asmsuechan
0bd48981ec Add markdown-it-plantuml 2017-11-04 12:54:41 +09:00
yosmoc
0bf7e8b705 fix solarized dark/light code block thema bug
solarized dark/light shares the same css in CodeMirror.
You can see more detail info in commit 5b17808569
We need to treat solarized as a special case in code block also.
2017-11-04 01:03:56 +01:00
Paul Rosset
875c451221 Add Counters on All notes && Starred notes 2017-11-02 17:03:59 +00:00
Paul Rosset
c8256bea3a Add counter on deleted notes 2017-11-01 17:54:35 +00:00
Maurits Lourens
7d10b951b7 only make the selected tag active 2017-11-01 15:42:55 +01:00
Maurits Lourens
34e9238cc4 added shortcut ctrl+q for quit and quit the app when using the Windows Close button 2017-10-31 09:10:04 +01:00
Kazu Yokomizo
7d97784a58 Merge pull request #1042 from mslourens/image-in-anchor
only decode the url when the content of the link is a string
2017-10-31 13:23:54 +09:00
Kazu Yokomizo
1265e7c4a1 Merge pull request #1038 from voidsatisfaction/fix/pinned_note_right_click
Fix enabling no focus right click pin to top and show adequate context menu
2017-10-31 12:59:56 +09:00
Maurits Lourens
44ece2bf34 only decode the url when the content of the link is a string 2017-10-30 22:54:00 +01:00
Mac Heller-Ogden
fde2a8055d make ZZ work in vim mode 2017-10-30 15:20:59 -05:00
Kazu Yokomizo
f9643c2503 Merge pull request #1039 from BoostIO/add-props
Add props on NoteItemSimple
2017-10-31 00:53:34 +09:00
Kazu Yokomizo
e6a97e5cb3 Add props on NoteItemSimple 2017-10-31 00:46:33 +09:00
Kazu Yokomizo
73b5546ae9 Merge pull request #1036 from mslourens/context-menu
added the context menu to the list view
2017-10-31 00:37:48 +09:00
Kazu Yokomizo
f98719ee73 Merge pull request #1037 from BoostIO/change-image
Change top image
2017-10-31 00:29:58 +09:00
voidSatisfaction
7666182ae3 Fix enable no focus right click pin to top and show right context menu 2017-10-31 00:24:17 +09:00
Kazu Yokomizo
05af30f336 Change top image 2017-10-31 00:22:48 +09:00
Maurits Lourens
20de08b625 added the context menu to the list view 2017-10-30 16:18:49 +01:00
Nathaniel Watson
e60f4f4a64 Include each note only once when sorting by pin
Fixes #983
2017-10-30 21:51:44 +13:00
Kazu Yokomizo
6af25d932c Merge pull request #1022 from BoostIO/add-notification-ui-tab
Add notification at UI tab
2017-10-30 15:34:45 +09:00
Kazu Yokomizo
3f49a8a15a Merge pull request #1030 from RedBug312/fix-font-quotation
Fix custom preview fonts
2017-10-29 13:33:00 +09:00
RedBug312
bfa8db7b55 Delete escaped quotation marks between fonts
Delete addtional single quotation marks between the font name Microsoft YaHei
2017-10-29 11:42:35 +08:00
Kazu Yokomizo
d6f2e7588c Merge pull request #1025 from PaulRosset/note-uiTab_data-policy
Adjust Note in Uitab && Add message notification for data policy save
2017-10-28 18:28:27 +09:00
Kohei TAKATA
a594332ffb Merge pull request #1027 from BoostIO/feature-v0-8-16
v0.8.16
2017-10-28 14:15:41 +09:00
Kohei TAKATA
ceb18ebf1c v0.8.16 2017-10-28 12:28:06 +09:00
Paul Rosset
b9038e254e Adjust Note in Uitab && Add message notification for policy save 2017-10-27 12:38:26 +01:00
Kazu Yokomizo
b351e42538 Add notification at UI tab 2017-10-26 15:27:22 +09:00
Kazu Yokomizo
8910c26ee2 Merge pull request #1016 from PaulRosset/clear-message-HotKeyTab
ClearMessage notification HotKeyTab
2017-10-26 13:53:43 +09:00
Kazu Yokomizo
7549a7bbbe Merge pull request #1014 from asmsuechan/improve-lock-tooltip-text
Change the tooltip text of the lock button for easy to understand what its role
2017-10-26 13:49:57 +09:00
Kazu Yokomizo
17fbe6e232 Merge pull request #1020 from BoostIO/change-init-content
Make <kbd> on keyboard introduction
2017-10-26 10:15:39 +09:00
Kazu Yokomizo
ccdac8f604 Make <kbd> on keyboard introduction 2017-10-26 09:23:12 +09:00
Kazu Yokomizo
88a828c9ef Merge pull request #1019 from filoxo/add-keyboard-markdown
Add keyboard markdown
2017-10-26 08:58:45 +09:00
Carlos Filoteo
ae3291b90e Add keyboard styles 2017-10-25 11:07:03 -06:00
Carlos Filoteo
2c6f0452b8 Require markdown-it-kbd 2017-10-25 10:46:21 -06:00
Carlos Filoteo
4651acd6f4 Install markdown-it-kbd 2017-10-25 10:46:03 -06:00
Kazu Yokomizo
bba7babce3 Merge pull request #1017 from BoostIO/change-crowdfunding
Change crowdfunding Bountysource to OpenCollective.
2017-10-26 00:10:48 +09:00
Kazu Yokomizo
73dc6a4a92 Change crowdfunding Bountysource to OpenCollective. 2017-10-25 23:58:36 +09:00
Paul Rosset
992f5a525a Test build travis with change clearMessage feature 2017-10-25 13:21:30 +01:00
SuenagaRyota
b38d5789f3 Merge pull request #1013 from asmsuechan/change-ISSUETEMPLATE
Change ISSUETEMPLATE to add a sentence dealing with if a user tries to open an issue regarding boostnote mobile
2017-10-25 19:56:25 +09:00
asmsuechan
47b5945e17 Change the tooltip text of the lock button for easy to understand what its role 2017-10-25 19:49:16 +09:00
asmsuechan
73544b0f06 Change ISSUETEMPLATE to add a sentence dealing with if a user tries to open an issue regarding boostnote mobile 2017-10-25 19:45:32 +09:00
Kazu Yokomizo
e7d9311e23 Merge pull request #1010 from PaulRosset/save-notification
Adjust notification HotKeyTab
2017-10-25 10:28:58 +09:00
Paul Rosset
c97c65b707 Delete clearMessage due to problem wih test [WIP] 2017-10-24 23:54:14 +01:00
Paul Rosset
1c02b4e62a Delete setTimeout() due to problem wih test 2017-10-24 23:50:29 +01:00
Paul Rosset
d23156d11a Correction build error 2017-10-24 23:40:15 +01:00
Paul Rosset
bd013adb4d correction eslint syntax 2017-10-24 23:09:21 +01:00
Paul Rosset
c0368ce713 correction 2017-10-24 23:01:16 +01:00
Paul Rosset
80283b5f55 Adjust notification HotKeyTab 2017-10-24 22:27:03 +01:00
Kohei TAKATA
4078645958 Merge pull request #1002 from BoostIO/add-ama-event-for-snippet-lang
Add ama event for snippet lang
2017-10-23 22:33:35 +09:00
Sosuke Suzuki
955ade0b8a add space 2017-10-23 21:12:42 +09:00
Sosuke Suzuki
4b158af9f6 send snippet_lang 2017-10-23 16:35:15 +09:00
Sosuke Suzuki
642fae3ac7 some let => const 2017-10-23 13:51:32 +09:00
SuenagaRyota
d249967aee Merge pull request #1004 from ysshah/patch-1
Fix typo in "Markdown"
2017-10-23 10:47:25 +09:00
Yash Shah
7b6b7f05e0 Fix typo in "Markdown" 2017-10-22 11:10:13 -07:00
Sosuke Suzuki
35b9bf5d34 enable recordDynamicEvent func to passed attribute arg 2017-10-21 18:18:02 +09:00
Sosuke Suzuki
59f0cc4f98 send lang_name of snippet 2017-10-21 16:59:33 +09:00
Kazu Yokomizo
a29ca73fb4 Merge pull request #1000 from BoostIO/fix-sideNav-style
start point of absolute to the right.
2017-10-21 16:18:51 +09:00
Sosuke Suzuki
59b658f059 start point of absolute to the right. 2017-10-21 15:49:47 +09:00
Kazu Yokomizo
3397b3108f Merge pull request #999 from BoostIO/fix-sideNav-style
Fix side nav style
2017-10-21 15:04:26 +09:00
Sosuke Suzuki
cae7baa5e1 change from premitive value to variable 2017-10-21 14:45:26 +09:00
Sosuke Suzuki
4af71fd1dd fix Folded-SideNav size 2017-10-21 14:29:29 +09:00
Sosuke Suzuki
4194b61373 text-align of button-lebels in SideNavFilder are center 2017-10-21 14:11:35 +09:00
Sosuke Suzuki
c91fd6783d fix preference button style 2017-10-21 13:56:21 +09:00
SuenagaRyota
89bbed1dfd Merge pull request #978 from aquibm/master
Fix URIs decoded with mdurl
2017-10-21 13:40:36 +09:00
Sosuke Suzuki
2aeb53920c enable tooltip-label of Preference 2017-10-21 13:37:10 +09:00
Sosuke Suzuki
fe51c232b6 fix typo 2017-10-21 13:27:45 +09:00
Sosuke Suzuki
57b054794c fix position of preference button 2017-10-21 13:25:28 +09:00
SuenagaRyota
8318c56046 Merge pull request #998 from asmsuechan/change-issue-template
Change ISSUE_TEMPLATE because it seems unfriendly
2017-10-21 13:10:54 +09:00
Sosuke Suzuki
0d52417ee7 disable switch-buttons, when NavToggle 2017-10-21 13:06:04 +09:00
asmsuechan
6f3b1b8d6f Change ISSUE_TEMPLATE because it seems unfriendly 2017-10-21 13:05:14 +09:00
Kazu Yokomizo
a460d7722e Merge pull request #996 from BoostIO/update-readme
Update readme
2017-10-21 00:19:00 +09:00
Kazu Yokomizo
d770208d4c Update Backers 2017-10-21 00:14:59 +09:00
Kazu Yokomizo
0434109908 Update Bakcers 2017-10-21 00:11:29 +09:00
Kazu Yokomizo
289d3a4e6b Update Readme 2017-10-21 00:07:39 +09:00
Kazu Yokomizo
ffb9be63c7 Merge pull request #920 from BoostIO/add-ama-options
enable to get appVerion and platformName
2017-10-20 23:05:35 +09:00
Kazu Yokomizo
bf2b53cbce Merge pull request #994 from Visovsiouk/master
Adjust tooltip (#963)
2017-10-19 13:53:40 +09:00
visovsiouk
1d9bf65c31 Adjust tooltip (#963) 2017-10-18 20:29:38 +03:00
Kazu Yokomizo
4744b918d3 Merge pull request #988 from ringohub/fix/982-can_not_scroll_tag_list
Fix #982: Tag list cannot be scroll
2017-10-18 16:35:19 +09:00
Kazu Yokomizo
588b1809a9 Merge pull request #992 from mrseanbaines/sean/fix-grammar
Grammar fix
2017-10-18 16:13:24 +09:00
Sean Baines
dc1c19293d Grammar fix
"Quick search and copy the content of note" to "Quick search and copy the content of a note"
2017-10-17 19:49:51 +01:00
Yoshiki Aoki
1f548959e3 Fix #928: Tag list cannot be scroll 2017-10-17 11:47:06 +09:00
asmsuechan
8cae5670fc Change travis_snapcraft 2017-10-16 15:09:28 +09:00
SuenagaRyota
07c0982d4f Merge pull request #986 from asmsuechan/snapcraft
Snapcraft
2017-10-16 14:07:20 +09:00
SuenagaRyota
2f9e4b3198 Merge pull request #950 from clone1612/catchAnalyticsError
Add try/catch logic to recording of analytics events
2017-10-16 14:06:49 +09:00
asmsuechan
89dba149a3 Change to run build script only master branch 2017-10-16 14:03:19 +09:00
asmsuechan
aa71b4c1b8 Change to snap on travisCI 2017-10-16 13:57:39 +09:00
asmsuechan
43110f8f2a Add boostnote.desktop 2017-10-16 13:57:39 +09:00
asmsuechan
e48540713d Edit snapcraft.yaml to snap Boostnote 2017-10-16 13:57:35 +09:00
asmsuechan
cfd13139e0 snapcraft init 2017-10-16 13:55:32 +09:00
SuenagaRyota
ac5cdf384f Merge pull request #984 from asmsuechan/remove-unused-vars
Remove unused vars
2017-10-15 23:39:50 +09:00
asmsuechan
e9d858d902 Remove unused vars 2017-10-15 23:33:22 +09:00
Kazu Yokomizo
1beae4403a Merge pull request #979 from BoostIO/fix-layout
Fix layout
2017-10-15 17:54:59 +09:00
Kazu Yokomizo
dedf36f704 Position of navToggle button at Sidebar 2017-10-15 17:44:02 +09:00
Kazu Yokomizo
1477de3899 Fix tag layout 2017-10-15 17:40:19 +09:00
Kazu Yokomizo
0d947c7dd8 Fix CI error 2017-10-15 17:25:40 +09:00
Kazu Yokomizo
ebfd8f40e3 Pin to top layout 2017-10-15 17:20:55 +09:00
Kazu Yokomizo
3159cc0ded Add crowdfunding page in Preference 2017-10-15 17:10:58 +09:00
Kazu Yokomizo
10dcbfb891 Fix tags and folder list layout 2017-10-15 16:17:06 +09:00
Kazu Yokomizo
19dc16e14a Change sort icon and string 2017-10-15 15:52:16 +09:00
Kazu Yokomizo
95586b3156 Tag list layout 2017-10-15 15:46:30 +09:00
Kazu Yokomizo
0637daf645 Fix topbar height 2017-10-15 15:31:26 +09:00
Kazu Yokomizo
fdcd62617d Fix position of preference button 2017-10-15 15:30:03 +09:00
Kazu Yokomizo
0f3e5ee4ed Tag button at topbar in sidenav 2017-10-15 15:25:31 +09:00
Kazu Yokomizo
7b171ecc67 Fix layout
- [ ] Pin to top
- [ ] Tag area
- [ ] Preference button
- [ ] Position of New post button
- [ ] Icon of sort button at NoteList and change name of "Updated, Created"
- [ ] Set donation button at side bar in Preference
2017-10-15 11:46:37 +09:00
Sota Sugiura
7a4052ede3 Merge pull request #922 from sosukesuzuki/feature-tag-area
Feature tag area
2017-10-14 16:58:28 +09:00
Aquib Master
3f53a1f629 Fix linting issues 2017-10-14 19:28:11 +13:00
Aquib Master
31daec5fe2 Fix URIs decoded with mdurl
This PR mitigates #918 by retroactively replacing link text decoded by mdurl with the original link.
2017-10-14 19:01:04 +13:00
Sosuke Suzuki
0d7155bda6 implement convertPlatformName function 2017-10-14 12:12:35 +09:00
Sosuke Suzuki
35beec3e39 insert necessary space 2017-10-14 11:57:03 +09:00
Sosuke Suzuki
ff4b96b622 implement tagItem active styleName 2017-10-14 11:57:03 +09:00
Sosuke Suzuki
9b60814292 fix bug : isTrashedActive and isStarredActive are inverted 2017-10-14 11:57:03 +09:00
Sosuke Suzuki
3c4fa83161 modify the arg of arrayOf to the correct 2017-10-14 11:57:03 +09:00
Sosuke Suzuki
e8564f6540 modify propTypes "array" -> "arrayOf" 2017-10-14 11:57:03 +09:00
Sosuke Suzuki
a22e97d4bd add a space after "//" in comment 2017-10-14 11:57:03 +09:00
Sosuke Suzuki
046e6af489 remove extra semicolon and fix indent 2017-10-14 11:57:03 +09:00
Sosuke Suzuki
f805e8a688 cut out NavToggle component 2017-10-14 11:57:03 +09:00
Sosuke Suzuki
2fddc32eb7 cut out StorageList component 2017-10-14 11:57:03 +09:00
Sosuke Suzuki
6018cd5d81 get "data" from this.props 2017-10-14 11:57:03 +09:00
Sosuke Suzuki
3533903be3 duplicate conditions as a variable 2017-10-14 11:57:03 +09:00
Sosuke Suzuki
d867292f66 remove unnecesarry "e" variable 2017-10-14 11:57:03 +09:00
Sosuke Suzuki
7691b662d6 remove unnecesarry "return" 2017-10-14 11:57:03 +09:00
Sosuke Suzuki
86270dd856 add the comment fot complicated condition 2017-10-14 11:57:03 +09:00
Sosuke Suzuki
012e2dde4f remove unnecessary "e" variables 2017-10-14 11:57:03 +09:00
Sosuke Suzuki
ad7a3c49f9 modify to use "const" from "let" 2017-10-14 11:57:03 +09:00
Sosuke Suzuki
e8abd43c8a modify to remove unnecessary "return" 2017-10-14 11:57:03 +09:00
Sosuke Suzuki
3192ce9d39 modify to use PropTypes 2017-10-14 11:57:03 +09:00
Sosuke Suzuki
d09de09fef use color variables 2017-10-14 11:57:03 +09:00
Sosuke Suzuki
4689ddeb98 some rename plural form or easy to understand 2017-10-14 11:57:03 +09:00
Sosuke Suzuki
e300b33a4f fix a mistake in function naming 2017-10-14 11:57:03 +09:00
Sosuke Suzuki
0ca87ea407 fix type description parens () -> {} 2017-10-14 11:57:03 +09:00
Sosuke Suzuki
2886da4f63 give the key props to tagList Item 2017-10-14 11:57:03 +09:00
Sosuke Suzuki
bf9ecb02e5 remove extra parens 2017-10-14 11:57:03 +09:00
Sosuke Suzuki
852617726c complete editting file 2017-10-14 11:57:03 +09:00
Sosuke Suzuki
c2aa35104c remove button width style 2017-10-14 11:57:03 +09:00
Sosuke Suzuki
95e237d4a3 fix style tagList 2017-10-14 11:57:03 +09:00
Sosuke Suzuki
59e5c547e9 fix button style to change color when switch active/inactive 2017-10-14 11:57:03 +09:00
Sosuke Suzuki
06bd2b2b79 add title "tags" 2017-10-14 11:57:03 +09:00
Sosuke Suzuki
faede48217 feature tag search 2017-10-14 11:57:03 +09:00
Sosuke Suzuki
ad0ac19d3d handling tagButton click 2017-10-14 11:49:32 +09:00
Sosuke Suzuki
3154110de1 list tags 2017-10-14 11:49:32 +09:00
Sosuke Suzuki
5248c05e61 switching view and change routing 2017-10-14 11:49:32 +09:00
Sosuke Suzuki
8311030bec add the tag to routing 2017-10-14 11:49:32 +09:00
SuenagaRyota
c429fc6b2c Merge pull request #968 from adrichey/master
Fix: "Uncaught TypeError: Cannot read property 'className' of null"
2017-10-13 23:36:48 +09:00
Alan Richey
590aa9ab17 Fixed the "Uncaught TypeError: Cannot read property 'className' of null" bug that appears when creating folders (coding style adjustments) 2017-10-13 09:30:41 -05:00
SuenagaRyota
f9a7c2d457 Merge pull request #853 from PrimaMateria/feature-rearrange-storage
Feature rearrange storage
2017-10-13 20:16:39 +09:00
Kazu Yokomizo
b4506168fb Merge pull request #972 from asmsuechan/replace-pdf-to-print
Replace pdf to print
2017-10-13 18:11:07 +09:00
SuenagaRyota
f203ab3aaf Merge pull request #973 from asmsuechan/refactor-targetIndex
Add getTargetIndex() and replece them
2017-10-13 17:25:30 +09:00
asmsuechan
c197dd0a4b Add getTargetIndex() and replece them 2017-10-13 17:19:38 +09:00
SuenagaRyota
457e596851 Merge pull request #956 from asmsuechan/pinn-to-top
Pin to top
2017-10-13 17:02:59 +09:00
asmsuechan
d274563b2c Change order for pinned notes on /home, /trashed, /searched or /starred 2017-10-13 16:33:55 +09:00
asmsuechan
2003bea3cf Remove unnecessary lines 2017-10-13 16:24:13 +09:00
asmsuechan
f9b3284852 Fix a condition 2017-10-13 14:22:13 +09:00
asmsuechan
9bca133d88 Remove updateFolder because it's no longer used 2017-10-13 14:18:41 +09:00
asmsuechan
03fc453608 Change method names 2017-10-13 14:18:41 +09:00
asmsuechan
3027cc81b3 Add handleNoteDelete 2017-10-13 14:18:41 +09:00
asmsuechan
2415fbf676 Change to use const instead of let 2017-10-13 14:18:41 +09:00
asmsuechan
725c6a7ba9 Resolve conflicts 2017-10-13 14:18:41 +09:00
asmsuechan
c33da0cf8e Fix some pointed by eslint 2017-10-13 14:18:41 +09:00
asmsuechan
d915d19425 Add comment for NoteItem 2017-10-13 14:18:41 +09:00
asmsuechan
f3370242bf Add conditions to hide pin from /home, /starred or /trash 2017-10-13 14:18:41 +09:00
asmsuechan
0e312ba929 Change name Pinn to Pin 2017-10-13 14:18:41 +09:00
asmsuechan
6440395197 Change to use strict equation 2017-10-13 14:18:41 +09:00
asmsuechan
5433abddaf Remove console.log 2017-10-13 14:18:41 +09:00
asmsuechan
0ccb465288 Remove pinnedNotes from Folder 2017-10-13 14:18:41 +09:00
asmsuechan
8fd4deb3eb Add unpin 2017-10-13 14:18:41 +09:00
asmsuechan
fe8045c51d Add pinn to top 2017-10-13 14:18:41 +09:00
SuenagaRyota
b890c59134 Merge pull request #948 from wrhansen/master
Support for solarized dark and light themes
2017-10-13 13:29:10 +09:00
SuenagaRyota
f39caeb967 Merge pull request #954 from asmsuechan/improve-searching
Improve searching
2017-10-13 12:08:25 +09:00
asmsuechan
7ab482184b Improve tag search by changing the logic 2017-10-13 11:40:10 +09:00
asmsuechan
78b12ae686 Add test 2017-10-13 10:58:32 +09:00
asmsuechan
caa5deac4e Change searching by tag 2017-10-13 10:37:43 +09:00
Kazu Yokomizo
af3083825e Merge pull request #970 from Samatar26/fix-handleCloseButtonClick
Use the correct handler for closing the modal.
2017-10-13 09:35:13 +09:00
asmsuechan
5255708ff2 Replace pdf to print 2017-10-13 09:29:41 +09:00
Samatar26
9331f2034b Use the correct handler for closing the modal.
Relates to #967
2017-10-12 18:01:34 +01:00
Kazu Yokomizo
fc6a5c22bf Merge pull request #969 from Samatar26/fix-button-protrusion
Fixed button protrustion when creating a new folder.
2017-10-13 01:44:17 +09:00
Samatar26
51c397d177 Fixed button protrustion when creating a new folder.
Relates to #966
2017-10-12 17:33:18 +01:00
Alan Richey
7c9596308e Fixed the "Uncaught TypeError: Cannot read property 'className' of null" bug that appears when creating folders 2017-10-12 10:56:07 -05:00
asmsuechan
15dc424ade Change keycode handling scope due to global keyboard support 2017-10-12 23:27:48 +09:00
asmsuechan
49243a8010 Add to handle space key on search 2017-10-12 23:27:45 +09:00
asmsuechan
93e188d118 Change to be able to search on starred 2017-10-12 20:28:23 +09:00
asmsuechan
df3195fc1e Refactor getContextNotes() 2017-10-12 18:42:34 +09:00
asmsuechan
da6b8c30a0 Improve some 2017-10-12 18:42:34 +09:00
asmsuechan
70468b6b7d Change contextNotes a state to a variable because of lifesycle loop 2017-10-12 18:42:34 +09:00
asmsuechan
2511512d94 Improve context search 2017-10-12 18:42:34 +09:00
asmsuechan
4418617d3b Delete a unnecessary break 2017-10-12 18:42:34 +09:00
asmsuechan
6e480ba146 Add comments 2017-10-12 18:42:34 +09:00
asmsuechan
b4f5913a80 Fix to work inputting by IME on search 2017-10-12 18:42:34 +09:00
Kazu Yokomizo
6a3062709c Merge pull request #951 from mrseanbaines/sean/grammar-fix
Grammar and capitalization fix
2017-10-12 14:37:34 +09:00
Kazu Yokomizo
d66bc1faef Merge pull request #943 from voidsatisfaction/add_context_menu_delete_note
Add notelist right click(context menu) and delete note
2017-10-12 14:34:51 +09:00
Kazu Yokomizo
bef7d45c3e Merge pull request #961 from asmsuechan/fix-pasting-image
Fix to create imageDir if it does not exist
2017-10-12 11:48:20 +09:00
SuenagaRyota
bb9489a8d3 Merge pull request #955 from asmsuechan/iss-919
Change to apply font with single quotations
2017-10-12 11:34:42 +09:00
asmsuechan
700eeb8f5a Fix to create imageDir if it does not exist 2017-10-12 10:28:01 +09:00
asmsuechan
7e2f0049b6 Change to apply font with single quotations 2017-10-11 12:17:49 +09:00
Sean Baines
b2388544d8 Merge branch 'master' into sean/grammar-fix 2017-10-10 19:22:02 +01:00
Sosuke Suzuki
d772551c60 convert platformName from os.platfoem() form to AMA form 2017-10-10 14:58:52 +09:00
Wesley Hansen
31dca6f06b Fix failed build lint error 2017-10-09 20:55:04 -04:00
Sean Baines
83f68fe153 Grammar and capitalization fix 2017-10-09 19:15:52 +01:00
Kazu Yokomizo
08a2ae0fd3 Merge pull request #941 from voidsatisfaction/korean_trans_dev
Add recent translated korean developement & debug docs
2017-10-10 01:21:56 +09:00
Jannick Hemelhof
53d3f51c74 Cleaner catch 2017-10-09 14:04:02 +02:00
Jannick Hemelhof
f7cdafb087 Add try/catch logic to recording of analytics events 2017-10-09 10:45:09 +02:00
Wesley Hansen
5b17808569 Support for solarized dark and light themes
CodeMirror seems to have the two solarized themes within the
`node_modules/coemirror/theme/solarized.css`. So passing the string
"solarized light" and "solarized dark" to the editor theme appropriately
sets these themes.

The Boostnote app appears to be dynamically determining themes based
on the css files found within `node_modules/codemirror/theme/`.
So that's why there was just a single option of 'solarized' before.

The light and dark 'solarized' themes appear to be the *only* themes
that share a css style, whereas all other light and dark variarnt
themes get their own css file (ex: base16-dark.css and base16-light.css).

Weird!
2017-10-08 23:56:15 -04:00
voidSatisfaction
a7328e21f1 feat: add right click notelist delete 2017-10-08 18:20:31 +09:00
voidSatisfaction
e64370e9a2 fix: no need enter 2017-10-08 15:16:26 +09:00
voidSatisfaction
d5b37b2418 feat: add debug docs for korean and link on debug.md 2017-10-08 13:58:15 +09:00
voidSatisfaction
4da08d93fd feat: add translated korean developement docs 2017-10-08 13:39:32 +09:00
Kazu Yokomizo
c39e5c67f5 Merge pull request #937 from mrseanbaines/sean/improve-english
Sean/improve english
2017-10-06 13:21:35 +09:00
Sean Baines
00d5cf13c9 Merge branch 'master' into sean/improve-english 2017-10-05 07:46:02 +01:00
Kazu Yokomizo
1120bcfc0c Merge pull request #934 from mrseanbaines/sean/tag-select-border-box
Fix "borde-box" typo
2017-10-05 10:49:39 +09:00
Sean Baines
8e506cb7c2 Improve English
This would make more sense like this.
2017-10-04 21:50:15 +01:00
Sean Baines
145b66d375 Fix English mistake 2017-10-04 21:46:55 +01:00
Sean Baines
82e4a8bbc3 Fix borde-box typo
Mistake in code. Changed from `borde-box` to `border-box`
2017-10-04 21:33:49 +01:00
Kazu Yokomizo
bca9bfb960 Merge pull request #931 from BoostIO/fix-font-color-in-code
Fix font color in code block
2017-10-04 13:11:55 +09:00
Kazu Yokomizo
d8e19d9c17 Fix font color in code block 2017-10-04 11:39:27 +09:00
SuenagaRyota
95d74d1ca2 Merge pull request #897 from urda/urda/feature/copy-notify-toggle
Allow users to disable "Saved to clipboard" notification.
2017-10-01 19:33:17 +09:00
Peter Urda
ebdd6d77f7 Allow users to disable "Saved to clipboard" notification. 2017-10-01 02:48:54 -07:00
Sosuke Suzuki
d56bcc4fdf enable to get appVerion and platformName 2017-09-30 12:19:51 +09:00
Kazu Yokomizo
4bb18cfc9a Merge pull request #915 from cofcool/fix_doc
change grand to grunt
2017-09-27 13:01:45 +09:00
CofCool
6f30692534 grand -> grunt 2017-09-27 11:49:05 +08:00
Kazu Yokomizo
e249c1ec65 Merge pull request #911 from sferra/place-notification-to-the-right
Place notifications on the right side of the notification bar
2017-09-26 18:37:49 +09:00
Cristian Beskid
c2b4c77003 Place notifications on the right side 2017-09-26 10:35:58 +02:00
Kazu Yokomizo
e64733827a Merge pull request #906 from sferra/fix-collapse-sidebar-button-not-visible
Align notification bar with the navigation bar
2017-09-26 13:02:24 +09:00
Cristian Beskid
ea81b0d414 Fix notification bar offset in full screen mode 2017-09-25 23:08:12 +02:00
Cristian Beskid
000cf2a864 Center notification in the notification bar 2017-09-25 18:44:22 +02:00
Cristian Beskid
dc13b919b3 Fix incorrect notification bar width 2017-09-25 17:34:40 +02:00
Cristian Beskid
a0c8ec3171 Remove redundant semicolon 2017-09-25 17:26:08 +02:00
Cristian Beskid
80c13f7c4f Align notification bar with the navigation bar 2017-09-25 17:17:34 +02:00
Kazu Yokomizo
1a6f3d808b Merge pull request #899 from BoostIO/change-font-color-at-code-in-default
Change font color in code at default theme
2017-09-24 13:35:21 +09:00
Kazu Yokomizo
ec8fac1199 Change font color in code at default theme 2017-09-24 13:24:14 +09:00
Kazu Yokomizo
98c93d3248 Merge pull request #895 from Overload119/fix_grammar
Fix grammer in InfoTab
2017-09-24 12:00:18 +09:00
Amir Sharif
a053706c24 Fix grammer in InfoTab 2017-09-23 09:10:33 -07:00
Kohei TAKATA
e1a75a13e9 Merge pull request #891 from BoostIO/v0.8.15
v0.8.15
2017-09-23 17:02:56 +09:00
Kazu Yokomizo
ee6f4de183 v0.8.15 2017-09-23 14:41:13 +09:00
Kazu Yokomizo
475885b3ef Merge pull request #893 from BoostIO/downgrade_striptags_version
Downgrade striptags version
2017-09-23 14:35:54 +09:00
Kohei TAKATA
2d2b2d4c6c Downgrade striptags version 2017-09-23 14:20:53 +09:00
SuenagaRyota
4d00454539 Merge pull request #892 from asmsuechan/fix-typo
Fix typo addNotes to addNotesFromFiles
2017-09-23 12:34:15 +09:00
asmsuechan
bf590b5614 Fix typo addNotes to addNotesFromFiles 2017-09-23 12:29:08 +09:00
Kazu Yokomizo
3ef33c065c Merge pull request #793 from asmsuechan/add-drop-on-notelist
Add file drop on NoteList
2017-09-23 12:09:55 +09:00
Kazu Yokomizo
8e89fb8b92 Merge pull request #890 from asmsuechan/iss-888
iss #888 Fix letter count
2017-09-23 11:43:09 +09:00
asmsuechan
8e81cfcf89 iss #888 Fix letter count 2017-09-23 11:40:26 +09:00
Kazu Yokomizo
515736262d Merge pull request #889 from asmsuechan/cloud-sync-comment
Add link for cloud integration
2017-09-23 11:38:01 +09:00
Kazu Yokomizo
bd266dc602 Merge pull request #885 from asmsuechan/exit-by-comma-q-in-vim-mode
iss #832 Make :q work in vim mode
2017-09-23 11:34:01 +09:00
asmsuechan
9b3306157c Add link for cloud integration 2017-09-23 11:15:16 +09:00
asmsuechan
f8e6a939ca Fix comment 2017-09-23 10:20:48 +09:00
Kazu Yokomizo
8c48ee6fc1 Merge pull request #887 from BoostIO/fix-layout
Fix layout
2017-09-23 01:38:28 +09:00
Kazu Yokomizo
5e476054d7 Fix layout at NewNoteModal 2017-09-23 01:34:51 +09:00
Kazu Yokomizo
2fc8547384 Fix layout at CreateFolderModal 2017-09-23 01:20:39 +09:00
asmsuechan
2af2d71540 Rename addNotes to addNotesFromFiles 2017-09-23 00:28:55 +09:00
asmsuechan
6aaf9d9eb2 Change disable drop on trash 2017-09-23 00:28:55 +09:00
asmsuechan
42a9caf5a3 Add file drop on NoteList 2017-09-23 00:28:52 +09:00
asmsuechan
e6c1d7a383 iss #832 Make :qw work in vim mode 2017-09-23 00:21:20 +09:00
asmsuechan
02100bbc0a iss #832 Make :q! and :qw work in vim mode 2017-09-23 00:16:51 +09:00
Kazu Yokomizo
ce7c5f5d40 Merge pull request #884 from BoostIO/fix-layout-at-bottom
Fix layout at bottom
2017-09-23 00:07:14 +09:00
Kazu Yokomizo
69f1ad6eb3 Sorry CI 2017-09-23 00:00:27 +09:00
Kazu Yokomizo
8320fb5024 Fix layout at bottom 2017-09-22 23:57:43 +09:00
asmsuechan
4b79bca6bf iss #832 Make :q work in vim mode 2017-09-22 23:57:14 +09:00
Kazu Yokomizo
4a5fd41249 Fix layout at description in SnippetNoteDetail 2017-09-22 23:48:41 +09:00
Kazu Yokomizo
4e90a93b30 Fix layout at NoteDetail 2017-09-22 23:46:41 +09:00
Kazu Yokomizo
9861fbf7c8 Fix layout at RealtimeNotification 2017-09-22 22:56:32 +09:00
Kohei TAKATA
c762b9ae00 Merge pull request #877 from asmsuechan/add-realtime-info
Add RealtimeNotification
2017-09-22 20:26:20 +09:00
Kazu Yokomizo
cc667a6edf Merge pull request #882 from BoostIO/fix-layout
Fix layout
2017-09-22 16:20:05 +09:00
Kazu Yokomizo
419c57ed3f Fix CI error 2017-09-22 16:14:06 +09:00
Kazu Yokomizo
601f0b0de8 Fix layout at narrow the width of description in SnippetNoteDetail 2017-09-22 16:06:40 +09:00
Kazu Yokomizo
7b1c6c10b7 Change expand icon at NoteDetail 2017-09-22 15:56:49 +09:00
Kazu Yokomizo
5a85c257cf Fix layout at SideNab in PreferenceModal 2017-09-22 15:46:08 +09:00
Kazu Yokomizo
beceb851c2 Fix layout at selector size in PreferenceModal 2017-09-22 15:41:40 +09:00
Kazu Yokomizo
31485d3387 Fix layout at head in PreferenceModal 2017-09-22 15:27:22 +09:00
Kazu Yokomizo
fc552e030a Change save button position to top 2017-09-22 15:21:04 +09:00
Kazu Yokomizo
bf4c9f920a Fix layout at preference modal 2017-09-22 15:13:34 +09:00
Kazu Yokomizo
4ebd503664 Merge pull request #880 from xxdavid/feature-strip-html-from-title
Strip HTML tags from the note title
2017-09-22 13:05:01 +09:00
David Pavlík
0907bc80ef Strip HTML from the note title 2017-09-21 16:48:13 +02:00
Kazu Yokomizo
2cf46a3332 Merge pull request #878 from BoostIO/update-readme
Update readme
2017-09-21 00:38:34 +09:00
Kazu Yokomizo
41868f28e6 Update readme
- Renew slack invitation url
- Change name "Boostnote Team" to "10hz"
2017-09-21 00:34:13 +09:00
asmsuechan
964b7b62de Change url development to production 2017-09-20 10:31:50 +09:00
SuenagaRyota
0d34a03fe0 Merge pull request #857 from BoostIO/feature-ama-event-main-focused
add MAIN_FOCUSED event
2017-09-16 10:57:08 +09:00
Sosuke Suzuki
a62faa471c change the event name for app start from MAIN_FOCUSED to APP_STARTED 2017-09-15 00:23:31 +09:00
Sosuke Suzuki
66f3ce2cb2 add MAIN_FOCUSED event 2017-09-12 19:43:59 +09:00
Matus Benko
43c49f54d2 used findStorage function in the updateFolder 2017-09-11 21:10:44 +02:00
Matus Benko
a15dfffa44 used findStorage function in the reorderFolder 2017-09-11 21:09:51 +02:00
Matus Benko
59985dee72 fixed test title 2017-09-09 21:53:10 +02:00
Matus Benko
6b7132f134 fixed style of dragged folder item for dark theme 2017-09-09 21:50:06 +02:00
asmsuechan
e313b5e59d Add RealtimeNotification 2017-09-09 13:47:30 +09:00
SuenagaRyota
bb26d9a0a8 Merge pull request #834 from KuangLei/master
Translate contributing.md to Simplified Chinese
2017-09-09 09:18:35 +09:00
Matus Benko
5c2c99282d added test for reorderFolder 2017-09-08 18:58:35 +02:00
Matus Benko
94e6f89d07 fixed errors from lint 2017-09-08 18:46:17 +02:00
Matus Benko
3804a746df connected sortable folder list to data api 2017-09-08 12:07:59 +02:00
Matus Benko
5c2d7e2d2a fixed typo in the useDragHandle attribute for the folder list 2017-09-08 00:59:38 +02:00
Matus Benko
c34dd462b6 made sortable item helper visible by setting proper zindex 2017-09-08 00:41:20 +02:00
Matus Benko
9141b1a641 saving new order in the storage only for now 2017-09-08 00:09:13 +02:00
Matus Benko
0fea85e2f2 added drag handle to the folder item 2017-09-07 23:52:27 +02:00
Kazu Yokomizo
0ca41fbdb4 Merge pull request #844 from BoostIO/fix-layout
Fix layout
2017-09-08 01:39:02 +09:00
Matus Benko
a58c191ded used react-sortable-hoc on folderList and folderItem 2017-09-06 22:56:32 +02:00
Matus Benko
77089a1178 extracted folder list and item to separate components 2017-09-06 20:02:47 +02:00
Kazu Yokomizo
2cfb883bad Merge pull request #847 from BoostIO/update-readme
Update Readme
2017-09-05 16:01:37 +09:00
Kazu Yokomizo
ec8c8bb669 Update Readme 2017-09-05 15:54:47 +09:00
Kazu Yokomizo
0b54f01107 Fix layout at code block in markdown 2017-09-04 11:48:16 +09:00
Kazu Yokomizo
67be198bee Merge pull request #815 from asmsuechan/add-word-count-to-InfoPanel
Add wordcount and lettercount to InfoPanel
2017-09-01 12:22:38 +09:00
Kazu Yokomizo
32a4a1aae1 Merge pull request #837 from BoostIO/update-readme
Update Readme
2017-08-30 16:22:47 +09:00
Kazu Yokomizo
dac7372839 Update Readme 2017-08-30 16:09:38 +09:00
Kazu Yokomizo
521c261a37 Merge pull request #836 from BoostIO/move-to-image
Create repository folder and move top image to here.
2017-08-30 16:06:43 +09:00
Kazu Yokomizo
27367488c2 Create repository folder and move top image to here. 2017-08-30 14:39:37 +09:00
Kazu Yokomizo
0d5c3b1be6 Merge pull request #835 from BoostIO/delete-top-image
Delete top image
2017-08-30 14:35:25 +09:00
Kazu Yokomizo
a67d5ffacb Upload new top image 2017-08-30 14:23:52 +09:00
Kazu Yokomizo
687440a7c7 Delete old top.png 2017-08-30 14:21:40 +09:00
KLsz
bafdc24a6d Add a link from the english doc pages to doc pages in other languages 2017-08-28 19:54:13 +08:00
KLsz
047f9c93c5 Translate debug.md to Simplified Chinese 2017-08-28 19:46:36 +08:00
KLsz
116fafc117 Translate build.md to Simplified Chinese 2017-08-28 18:00:12 +08:00
KLsz
060c92091c Translate contributing.md to Simplified Chinese 2017-08-28 16:26:00 +08:00
Kazu Yokomizo
5802525b73 Merge pull request #829 from BoostIO/add-backers
Add Backers
2017-08-25 09:57:32 +09:00
Kazu Yokomizo
c3580caabc Merge pull request #828 from BoostIO/update-slack-link
Update slack invitation link
2017-08-25 09:57:16 +09:00
Kazu Yokomizo
08c027acc5 Add Backers 2017-08-25 09:52:54 +09:00
Kazu Yokomizo
4468792346 Update slack invitation link 2017-08-25 09:51:25 +09:00
SuenagaRyota
1b16c68cf9 Merge pull request #818 from asmsuechan/work-paste-image
Make pasting an image in CodeEditor work
2017-08-23 13:05:28 +09:00
asmsuechan
b99c1e3b32 Remove unnecessary bind 2017-08-22 13:48:50 +09:00
SuenagaRyota
eb2994e3c2 Merge pull request #820 from asmsuechan/drag-drop-image-caret-ubuntu
Fix image insesration problem
2017-08-20 09:19:23 +09:00
asmsuechan
d88dd26186 Fix image insesration problem, Use a function of CodeMirror instead of dom function 2017-08-20 08:11:00 +09:00
asmsuechan
59fcc58e9c Make pasting an image in CodeEditor work 2017-08-19 18:37:51 +09:00
SuenagaRyota
acba61f36a Merge pull request #816 from asmsuechan/iss-809
iss #809 normalize text works only in img tag
2017-08-19 09:44:56 +09:00
asmsuechan
a3a55a8bb4 iss #809 normalize text only in img tag 2017-08-19 09:25:45 +09:00
SuenagaRyota
22929d84fc Merge pull request #812 from asmsuechan/add-anchor
Add markdown-it-named-headers and adjust to use Japanese or Chinese
2017-08-19 08:42:00 +09:00
asmsuechan
9ea9d30947 Add wordcount and lettercount to InfoPanel 2017-08-19 08:26:55 +09:00
SuenagaRyota
7f08428fe2 Merge pull request #813 from asmsuechan/fix-build.md
Add to specify npm version
2017-08-18 23:51:10 +09:00
asmsuechan
0d80a7d961 Add to specify npm version 2017-08-18 18:52:18 +09:00
SuenagaRyota
2899264b54 Merge pull request #806 from sferra/fix-app-name-and-icon-on-linux
Fix app name and icon on linux
2017-08-18 18:44:37 +09:00
asmsuechan
923de0aa0d Add markdown-it-named-headers and adjust to use Japanese or Chinese 2017-08-18 16:07:15 +09:00
SuenagaRyota
2b729dad15 Merge pull request #811 from RedBug312/multimd-table
Support for extended table markdown syntax
2017-08-18 14:16:20 +09:00
RedBug312
b9b5bae78a Add markdown-it plugin for multimarkdown table 2017-08-18 12:48:55 +08:00
Cristian Beskid
a9acde07d1 Add missing 'productName' attribute to package.json
Fixes incorrect application name displayed in linux/gnome-shell. See
BoostIO/Boostnote#251
2017-08-15 13:52:01 +02:00
Cristian Beskid
a46b8d3079 Configure the icon of the main window
Fixes the incorrect application icon in linux/gnome-shell. See
BoostIO/Boostnote#251
2017-08-15 13:49:32 +02:00
SuenagaRyota
8b92e2cbb7 Merge pull request #805 from asmsuechan/fix-typo-of-fullscreen
Fix typo regarding fullscreen
2017-08-15 20:29:22 +09:00
asmsuechan
881f5a5110 Fix typo regarding fullscreen 2017-08-15 20:22:26 +09:00
SuenagaRyota
4e986a6384 Merge pull request #802 from asmsuechan/fix-by-eslint-react
Fix by eslint react
2017-08-15 10:16:33 +09:00
SuenagaRyota
ce5e1babb7 Merge pull request #803 from asmsuechan/fix-note-creation-in-all-notes
Fix note creation in All Notes
2017-08-15 10:16:07 +09:00
asmsuechan
b85790d2fa Fix note creation in All Notes 2017-08-15 10:09:17 +09:00
asmsuechan
6bc3e7fcf1 [eslint] react/no-direct-mutation-state 2017-08-15 10:01:04 +09:00
asmsuechan
1ca968201d [eslint] react/no-unescaped-entities 2017-08-14 11:56:55 +09:00
SuenagaRyota
f2a03e4cc7 Merge pull request #800 from asmsuechan/componentnize-NewNoteButton
Componentnize NewNoteButton
2017-08-14 11:45:37 +09:00
asmsuechan
a752730718 Change to use sdialog.showMEssageBox() instead of window.alert 2017-08-14 11:39:06 +09:00
asmsuechan
9d20fd91ec Change ee to eventEmitter 2017-08-14 11:20:54 +09:00
asmsuechan
70a6a3acb8 Remove unused variable 2017-08-14 11:20:54 +09:00
asmsuechan
7f52eed4d5 Move condition of trash or not 2017-08-14 11:20:54 +09:00
asmsuechan
105119e1a4 Change let to const 2017-08-14 11:20:54 +09:00
asmsuechan
169e30e029 Fix by lint 2017-08-14 11:20:54 +09:00
asmsuechan
a5fa3e9e7a 🗑️ Remove unused file 2017-08-14 11:20:54 +09:00
asmsuechan
8985062d34 ♻️ Refactor 2017-08-14 11:20:54 +09:00
asmsuechan
56eb9c76ae Componentnize NewNoteButton 2017-08-14 11:20:54 +09:00
SuenagaRyota
e5b6762bf3 Merge pull request #801 from asmsuechan/add-eslint-plugin-react
Add eslint plugin react
2017-08-14 11:17:06 +09:00
asmsuechan
e8bccaef88 Add a rule 2017-08-14 11:13:25 +09:00
asmsuechan
afdb038244 Add rules 2017-08-14 11:10:15 +09:00
asmsuechan
56942d55eb Add eslint-plugin-react to .eslintrc 2017-08-14 11:02:56 +09:00
asmsuechan
9d742c8435 Add eslint-plugin-react 2017-08-14 10:46:40 +09:00
Kazu Yokomizo
6ee4e48de2 Merge pull request #795 from BoostIO/feature-v0-8-14
v0.8.14
2017-08-12 14:50:16 +09:00
Kazu Yokomizo
184f3dc04b v0.8.14 2017-08-12 12:20:30 +09:00
Kazu Yokomizo
2027f60014 Merge pull request #794 from BoostIO/fix-progress-bar-ui
Fix progress bar ui at Noteitem
2017-08-12 12:17:34 +09:00
Kazu Yokomizo
076edd375f Fix progress bar UI at NoteItem 2017-08-12 12:13:07 +09:00
SuenagaRyota
ab1aa56059 Merge pull request #792 from asmsuechan/add-tests
Add tests
2017-08-12 10:31:42 +09:00
asmsuechan
46f7dfdfeb Change " to ' 2017-08-12 10:17:36 +09:00
asmsuechan
fcaa5e21cf Add tests for RcParser 2017-08-12 09:36:39 +09:00
asmsuechan
1e202db50f Change the directory of RcParser 2017-08-12 09:36:30 +09:00
asmsuechan
9405b95825 Change RcParser testable 2017-08-12 09:11:22 +09:00
asmsuechan
f05e256afc Fix .boostnoterc.sample 2017-08-12 09:08:42 +09:00
asmsuechan
731ffd4a22 Add spec for getTodoStatus 2017-08-12 09:07:58 +09:00
SuenagaRyota
8f4566b7e1 Merge pull request #789 from asmsuechan/try-catch-in-rc-parser
Make RcParser ignore errors at JSON.parse()
2017-08-11 08:38:00 +09:00
asmsuechan
95aec54f60 Make RcParser ignore errors at JSON.parse() 2017-08-11 08:24:08 +09:00
SuenagaRyota
f14ce0d68e Merge pull request #787 from asmsuechan/fix-configmanager
Fix configmanager
2017-08-11 00:41:16 +09:00
asmsuechan
cc1c7f3820 Fix stupid JSON 2017-08-11 00:30:57 +09:00
SuenagaRyota
2df901288a Merge pull request #776 from XGHeaven/master
add todo percentage in item list
2017-08-10 23:39:12 +09:00
asmsuechan
821a7c780e Change variable names 2017-08-10 23:30:42 +09:00
asmsuechan
6e2e48fa64 Fix ConfigManger to load the settings from localStorage properly 2017-08-10 23:19:57 +09:00
Kazu Yokomizo
2864ac88f5 Merge pull request #784 from BoostIO/feature-v0-8-13
v0.8.13
2017-08-10 22:05:33 +09:00
XGHeaven
4a292d6518 add transition for TodoProcess inner bar 2017-08-10 20:00:57 +08:00
XGHeaven
e934182e86 change getTodoState to getTodoStatus 2017-08-10 19:58:58 +08:00
Kazu Yokomizo
d8fa73287b v0.8.13 2017-08-10 19:44:20 +09:00
SuenagaRyota
35938c09e8 Merge pull request #783 from asmsuechan/add-boostnoterc-for-config
Add boostnoterc revival
2017-08-10 16:58:23 +09:00
SuenagaRyota
9eaa90c691 Merge pull request #768 from BoostIO/fix-infoPanel-layout-in-trash
Fixed InfoPanel layout in Trash
2017-08-10 16:52:07 +09:00
Kazu Yokomizo
049835d426 Fix typo “in Trash” to “Trash” 2017-08-10 16:43:25 +09:00
asmsuechan
af91c40406 Remove unused variable 2017-08-10 16:42:14 +09:00
asmsuechan
4940ad6825 Add .boostnoterc.sample 2017-08-10 16:38:20 +09:00
asmsuechan
d02b740300 Fix assignConfigValues because it didn't return proper hash object 2017-08-10 16:37:31 +09:00
asmsuechan
9cb443dc2f Remove unused variable 2017-08-10 15:18:55 +09:00
XGHeaven
1c7cba2951 add todo percentage in item list 2017-08-10 12:31:07 +08:00
asmsuechan
473b80710d Remove RcParser.exec 2017-08-10 11:14:42 +09:00
asmsuechan
2247c0835d Ignore any errors in ~/.boostnoterc 2017-08-10 09:54:56 +09:00
asmsuechan
b7b715ba3d Fix a return value of RcParser.parse 2017-08-10 09:44:00 +09:00
asmsuechan
6c43fb2325 Enable to set configs in ~/.boostnoterc 2017-08-10 09:43:50 +09:00
asmsuechan
a6fe3c27d4 Fix a cyclic object value error 2017-08-10 09:43:19 +09:00
asmsuechan
d47ff96b13 Enable to set configs in ~/.boostnoterc 2017-08-10 09:43:19 +09:00
SuenagaRyota
a0def654bd Merge pull request #782 from MrBMT/update-app-wording
Update application wording
2017-08-10 07:15:18 +09:00
Ben
4873b40e49 Update application text
Updates application text in various places to correct the wording of or better reflect the functionality provided.
2017-08-09 16:48:39 +01:00
SuenagaRyota
0a758f20a7 Merge pull request #780 from asmsuechan/fix-storageKey-undefined
Add storageKey to MarkdownEditor
2017-08-09 23:16:53 +09:00
asmsuechan
5e58d457a3 Add storageKey to MarkdownEditor 2017-08-09 23:12:07 +09:00
SuenagaRyota
0f745361ad Merge pull request #779 from MrBMT/fix-default-fonts
Add Lato font-face definition to main.html
2017-08-09 22:51:43 +09:00
Ben
bf6cae9a0e Add Lato font definition to main.html
Lato is the default "Preview font family" font. However, in the case that Lato is not installed on the user's system, it falls back to using a different font instead of using the font included with the application.
2017-08-09 14:42:07 +01:00
SuenagaRyota
ab640a7676 Merge pull request #777 from MrBMT/finder-fix
Finder UI Fix (My Storage positioning)
2017-08-09 21:04:26 +09:00
Ben
820171e19e Fixes storage positioning
Fixes positioning of the My Storage section on Finder, so that it is not shown over the top of the Trash.
2017-08-09 12:34:14 +01:00
Kazu Yokomizo
f1e9d0ab81 Merge pull request #767 from mrseanbaines/fix-typo
Fixes typo in welcome screen
2017-08-09 11:55:58 +09:00
Sean Baines
0646484c83 Fixes wording on folder delete
"This work will deletes all notes in the folder and can not be undone." to "This will delete all notes in the folder and can not be undone."
2017-08-06 11:34:39 +01:00
Kazu Yokomizo
a27b79c213 Add newline at InfoPanel 2017-08-06 13:59:51 +09:00
Kazu Yokomizo
773a9b4b7f Fixed InfoPanel layout in Trash 2017-08-06 12:56:52 +09:00
Sean Baines
07b838ef7b Fixes typo in welcome screen 2017-08-05 17:17:33 +01:00
SuenagaRyota
85217a7171 Merge pull request #766 from asmsuechan/improve-search
Context search
2017-08-05 22:56:31 +09:00
asmsuechan
886d7b7227 Fix test 2017-08-05 22:48:04 +09:00
asmsuechan
6987b762dd Make context-search work 2017-08-05 22:23:33 +09:00
SuenagaRyota
f32ac81f84 Merge pull request #764 from asmsuechan/fix-noteCount-by-trash
Fix noteCount on a note trased
2017-08-05 22:03:44 +09:00
asmsuechan
87ea66bb92 Change iterate variable 2017-08-05 22:00:12 +09:00
SuenagaRyota
ff6fd62932 Merge pull request #765 from asmsuechan/iss-758
iss #758 Add InfoButton and InfoPanel in Trash
2017-08-05 21:56:22 +09:00
asmsuechan
76728448ff iss #758 Add InfoButton and InfoPanel in SnippetNoteDetail 2017-08-05 17:34:26 +09:00
asmsuechan
3b7225e0fa iss #758 Add InfoButton and InfoPanel in Trash 2017-08-05 17:30:59 +09:00
asmsuechan
d6280f4397 Fix noteCount on a note trased 2017-08-05 17:16:57 +09:00
SuenagaRyota
8df867046f Merge pull request #753 from asmsuechan/work-shortcuts-in-right-click
Change to work ctrl-e and ctrl-w in RIGHTCLICK
2017-08-05 12:05:22 +09:00
SuenagaRyota
331c822816 Merge pull request #754 from asmsuechan/add-print
Add Print
2017-08-05 11:59:16 +09:00
SuenagaRyota
6219173945 Merge pull request #763 from asmsuechan/fix-issue_template
Stress words in ISSUE_TEMPLATE
2017-08-04 08:36:27 +09:00
asmsuechan
6207e02e7f Stress words in ISSUE_TEMPLATE 2017-08-04 08:33:09 +09:00
asmsuechan
537ba537dc Add Print 2017-08-01 19:30:48 +09:00
asmsuechan
3e919241e6 Change to work ctrl-e and ctrl-w in RIGHTCLICK 2017-08-01 09:20:22 +09:00
SuenagaRyota
2324327e7e Merge pull request #749 from asmsuechan/change-order-for-ama-event
Change order for AMA event
2017-07-31 23:08:41 +09:00
asmsuechan
b8374494ea Change order for AMA event 2017-07-31 23:02:49 +09:00
SuenagaRyota
a480ca7b55 Merge pull request #731 from asmsuechan/add-find-storage-path-module
Add find storage path module
2017-07-30 16:15:33 +09:00
Kohei TAKATA
bfd67fb7f1 Merge pull request #735 from BoostIO/feature-v0-8-12
v0.8.12
2017-07-29 15:04:53 +09:00
Kohei TAKATA
e6bd6a5077 v0.8.12 2017-07-29 13:58:11 +09:00
Kohei TAKATA
350af844ca Merge pull request #736 from BoostIO/fix-layout
Fix layout
2017-07-29 13:56:21 +09:00
Kazu Yokomizo
a49d53179a Change “Trashed” to “Trash” 2017-07-29 13:49:45 +09:00
Kazu Yokomizo
e68069fd2f Fix undo button layout at notedetail 2017-07-29 13:40:04 +09:00
SuenagaRyota
a193ba3e6c Merge pull request #732 from asmsuechan/fix-image-path-on-dropped
Make it work on windows
2017-07-26 15:55:30 +09:00
asmsuechan
9034b3ab54 Make it work on windows 2017-07-26 15:47:38 +09:00
asmsuechan
f39b7594ab Add tests for findStorage() 2017-07-26 15:16:21 +09:00
asmsuechan
f79734391e Change to use the module 2017-07-26 15:16:18 +09:00
asmsuechan
e54f516418 Add findStorage() 2017-07-26 14:40:50 +09:00
SuenagaRyota
e86464535d Merge pull request #730 from asmsuechan/make-image-path-relative
Make image path relative
2017-07-26 14:33:41 +09:00
asmsuechan
e8553caa65 Make finder work fine 2017-07-26 14:29:55 +09:00
asmsuechan
044e6b7180 Make image path relative 2017-07-26 13:16:32 +09:00
SuenagaRyota
0266770657 Merge pull request #726 from asmsuechan/fix-default-font-on-windows
iss #155 Change defaut font on windows_
2017-07-25 16:04:12 +09:00
SuenagaRyota
2d6f7c08e8 Merge pull request #728 from asmsuechan/iss-727
iss #727 fix incorrect encoding in link
2017-07-25 15:59:28 +09:00
asmsuechan
0a27819a7f iss #727 fix incorrect encoding in link 2017-07-25 15:35:44 +09:00
SuenagaRyota
3185c25ee1 Merge pull request #724 from BoostIO/fix-button-at-markdown-eidt
Fix hidden button
2017-07-25 14:32:37 +09:00
asmsuechan
820802cdc2 iss #155 Change defaut font on windows_ 2017-07-25 14:04:39 +09:00
Kazu Yokomizo
2c169e6f15 Merge pull request #723 from asmsuechan/fix-typo-for-info-data-collection
Fix typo in InfoTab
2017-07-25 13:45:23 +09:00
Kazu Yokomizo
49be1320f9 Fix hidden button 2017-07-25 13:44:17 +09:00
asmsuechan
6bc4ecce48 Fix typo in InfoTab 2017-07-25 11:57:04 +09:00
SuenagaRyota
0290d23832 Merge pull request #722 from BoostIO/fix-layout
Fix layout
2017-07-25 10:18:12 +09:00
Kazu Yokomizo
e001c97e01 Make padding between Storages at SideNav. 2017-07-24 19:18:48 +09:00
Kazu Yokomizo
976d1bb4f3 Delete unnecessary comma. 2017-07-24 19:09:13 +09:00
Kazu Yokomizo
dee6495b08 Fix typo 2017-07-24 18:12:33 +09:00
Kazu Yokomizo
4a77f250f1 Fix button layout 2017-07-24 17:55:47 +09:00
Kazu Yokomizo
c697f19642 Fix layout at export note in InfoPanel 2017-07-24 17:00:08 +09:00
Kazu Yokomizo
f7d1f9e949 Fix InfoTab layout 2017-07-24 14:47:10 +09:00
Kazu Yokomizo
27b9952f8e Change policy content 2017-07-24 14:39:43 +09:00
Kazu Yokomizo
961dab4a97 Merge pull request #721 from BoostIO/udpate-slack-link
Update readme
2017-07-24 12:11:56 +09:00
Kazu Yokomizo
930b6bc927 Update readme
Update the slack link.
2017-07-24 11:53:39 +09:00
SuenagaRyota
ed7d8258cf Merge pull request #720 from asmsuechan/goodby-ctrl-g
Goodbye Ctrl-G
2017-07-22 16:44:48 +09:00
asmsuechan
762425125f Remove Ctrl-G because it's unneccessary and conflicts with CodeMirror 2017-07-22 16:41:08 +09:00
SuenagaRyota
03bdf0c653 Merge pull request #719 from asmsuechan/fix-inseration-image
Fix inseration position
2017-07-22 16:13:25 +09:00
asmsuechan
7002026190 Fix inseration position 2017-07-22 16:05:14 +09:00
SuenagaRyota
b35395e6bd Merge pull request #709 from asmsuechan/add-exports-to-infoPanel
Add exports to infoPanel
2017-07-22 15:47:01 +09:00
SuenagaRyota
b74fb76d03 Merge pull request #718 from asmsuechan/fix-typos
Fix typos Dynamit to Dynamic
2017-07-22 15:42:49 +09:00
asmsuechan
72d2df465b Fix typos Dynamit to Dynamic 2017-07-22 15:33:56 +09:00
SuenagaRyota
2592c943f7 Merge pull request #717 from asmsuechan/fix-a-bug-on-click
Fix a bug on click
2017-07-22 15:28:38 +09:00
asmsuechan
7b3a3ba200 Fix by eslint 2017-07-22 15:24:51 +09:00
asmsuechan
8369832585 Fix a bug on click 2017-07-22 15:11:59 +09:00
SuenagaRyota
d94a674343 Merge pull request #716 from asmsuechan/change-note-link-content
Change ["title"](note-hash) to [the exact title](note-hash)
2017-07-22 15:01:04 +09:00
SuenagaRyota
260611ffd6 Merge pull request #715 from asmsuechan/add-checkbox-for-ama
Add checkbox for ama
2017-07-22 14:57:31 +09:00
asmsuechan
690549b57f Change a word 2017-07-22 14:34:28 +09:00
asmsuechan
5d9aeb4c04 Fix typo 2017-07-22 14:32:18 +09:00
asmsuechan
eddfdea2ca Fix a closing tag and a condition 2017-07-22 14:20:42 +09:00
asmsuechan
a40385f87f Add policy 2017-07-22 13:58:02 +09:00
asmsuechan
0123526c98 Change ["title"](note-hash) to [the exact title](note-hash) 2017-07-22 11:29:39 +09:00
asmsuechan
275e3317a3 Add checkbox for ama 2017-07-22 08:59:27 +09:00
SuenagaRyota
d0b835a825 Merge pull request #710 from asmsuechan/fix-image-dropped
Change to create a new directory when storage/images does not exist
2017-07-16 15:52:03 +09:00
asmsuechan
f450260ff8 Fix a prop name noteKey to noteLink 2017-07-16 15:16:59 +09:00
asmsuechan
5f7b119e5c Remove an useless 'cancel' button 2017-07-16 15:06:22 +09:00
asmsuechan
bb9eb494e9 Change to create a new directory when storage/images does not exist 2017-07-16 15:02:18 +09:00
asmsuechan
6ea2b5e1d9 Add exports to infoPanel 2017-07-15 19:33:58 +09:00
SuenagaRyota
b21baf1ce5 Merge pull request #708 from asmsuechan/change-note-hash
Change noteHash to noteLink in the input in InfoPanel
2017-07-15 16:57:55 +09:00
SuenagaRyota
4c3fd461e4 Merge pull request #707 from asmsuechan/remove-dialogbox-from-trash
Remove a dialogbox from onTrashButtonClick
2017-07-15 14:47:38 +09:00
asmsuechan
8365a60d5d Change noteHash to noteLink in the input in InfoPanel 2017-07-15 13:41:22 +09:00
asmsuechan
804ac1aa96 Remove a dialogbox from onTrashButtonClick 2017-07-15 12:52:55 +09:00
SuenagaRyota
49fd2cfc4d Merge pull request #706 from asmsuechan/change-shotrcut-ctrl-u-to-ctrl-k
Change the keymap of Ctrl-U to Ctrl-K
2017-07-15 12:51:40 +09:00
asmsuechan
2bb2a52f27 Change the keymap of Ctrl-U to Ctrl-K 2017-07-14 18:18:38 +09:00
Kazu Yokomizo
8d71b28afa Merge pull request #705 from BoostIO/udpate-readme
Fix slack link
2017-07-13 16:23:34 +09:00
Kazu Yokomizo
86eac7054d Update Readmd 2017-07-13 15:41:56 +09:00
SuenagaRyota
dccb92d72b Merge pull request #297 from asmsuechan/add-trash-can
Add trash can
2017-07-12 15:53:49 +09:00
asmsuechan
2e628de9c6 Restore missing components 2017-07-12 15:48:11 +09:00
asmsuechan
2d243abc12 Add condition 2017-07-12 15:48:11 +09:00
asmsuechan
ae08bf4d7a Escape from addition a note when the note is in the trashbox 2017-07-12 15:48:11 +09:00
asmsuechan
cbff5fb585 Fix indents pointed by lint 2017-07-12 15:48:11 +09:00
asmsuechan
2650cc2f1c Add trash can 2017-07-12 15:48:08 +09:00
Kazu Yokomizo
ec560ceab1 Merge pull request #702 from BoostIO/update-readme
Update Readme
2017-07-11 01:01:19 +09:00
Kazu Yokomizo
ad17cb8837 Update Readme 2017-07-11 00:55:21 +09:00
Kazu Yokomizo
4f98d9641a Merge pull request #698 from BoostIO/update-layout
Update layout
2017-07-11 00:20:59 +09:00
Kazu Yokomizo
17535ccd4c Add Info Panel at SnippetDetail 2017-07-10 17:43:20 +09:00
Kazu Yokomizo
110c1ea337 Delete update date at under of NoteDetail 2017-07-10 17:13:18 +09:00
Kazu Yokomizo
7e087bfbab Fix info panel layout 2017-07-10 17:07:44 +09:00
SuenagaRyota
85e0d4b922 Merge pull request #697 from asmsuechan/remove-sharp-from-hash
Remove sharp from note hash on list:jump
2017-07-10 16:43:02 +09:00
asmsuechan
265262ccbf Remove sharp from note hash on list:jump 2017-07-10 16:37:07 +09:00
Kazu Yokomizo
345008e9b6 Fix info area layout at NoteDetail 2017-07-10 16:28:50 +09:00
SuenagaRyota
849aa05d76 Merge pull request #696 from asmsuechan/replace-cm-searchbox-to-official-search-addon
Replace cm searchbox to official search addon
2017-07-10 14:21:47 +09:00
asmsuechan
5ff5ec6a71 Remove cm-searchbox 2017-07-09 22:47:15 +09:00
asmsuechan
684c3f64aa Add search addon 2017-07-09 22:46:23 +09:00
SuenagaRyota
4ff73ede59 Merge pull request #695 from asmsuechan/bugfix-on-click-event
Fix an error on click event
2017-07-09 18:20:51 +09:00
asmsuechan
7302d83f60 Fix a bug on click event 2017-07-09 18:08:33 +09:00
SuenagaRyota
f9836fd66e Merge pull request #692 from asmsuechan/add-info-button
Add info button
2017-07-09 17:16:06 +09:00
SuenagaRyota
c0f05695fe Merge pull request #686 from asmsuechan/add-search-box
Add search-box
2017-07-09 17:15:57 +09:00
SuenagaRyota
e50a1172d6 Merge pull request #693 from asmsuechan/feature-links-among-notes
Feature links among notes
2017-07-09 17:15:50 +09:00
Kazu Yokomizo
7a4234e73c Merge pull request #690 from BoostIO/update-layout
Fix layout
2017-07-09 13:29:00 +09:00
Kazu Yokomizo
4b9640e5a3 Fix star position at NoteList 2017-07-09 12:56:02 +09:00
Kazu Yokomizo
4b58054100 Fix tag area layout at NoteItem 2017-07-09 12:46:46 +09:00
Kazu Yokomizo
8951dbf4ed Fix star position at NoteList 2017-07-08 16:05:45 +09:00
Kazu Yokomizo
5047bc94eb Fix search area layout 2017-07-08 15:58:40 +09:00
asmsuechan
75fb29c594 Change == to === 2017-07-08 15:44:20 +09:00
asmsuechan
bd75f7fd2c Remove a display of a note hash 2017-07-08 14:08:28 +09:00
asmsuechan
8b8cb3c9b4 Change styleName in each styles 2017-07-08 14:08:28 +09:00
asmsuechan
b479b21c37 Remove a blank line 2017-07-08 14:08:28 +09:00
asmsuechan
525c490704 Remove console.log 2017-07-08 14:08:28 +09:00
asmsuechan
2e3599d005 Add a feature which enables to link between notes 2017-07-08 14:08:28 +09:00
asmsuechan
f005bb1e46 hoge 2017-07-08 14:02:03 +09:00
asmsuechan
21f96febdb Fix some pointed by lint 2017-07-08 13:37:24 +09:00
asmsuechan
805829be78 Make InfoButton component stateless 2017-07-08 13:32:37 +09:00
asmsuechan
a7bd3f253f Rename getLastUpdated() to formatDate() 2017-07-08 13:16:19 +09:00
asmsuechan
81e8a290f0 Add infomation panel 2017-07-08 13:16:02 +09:00
Kazu Yokomizo
f46a967b6e Fix an error 2017-07-08 12:31:21 +09:00
Kazu Yokomizo
b78d9534aa Fix layout 2017-07-08 02:18:33 +09:00
SuenagaRyota
bdefaf7427 Merge pull request #681 from BoostIO/edit-initial-files
Edit initial files
2017-07-07 20:59:50 +09:00
SuenagaRyota
43550c7dec Merge pull request #688 from asmsuechan/remove-handle-note-context-menu
Remove the context menu for deletion
2017-07-07 20:12:11 +09:00
asmsuechan
1b07d393f2 Rename handleDeleteButtonClick to handleTrashButtonClick 2017-07-07 20:04:47 +09:00
asmsuechan
44fc356775 Remove the context menu for deletion 2017-07-07 19:45:02 +09:00
SuenagaRyota
5623c68170 Merge pull request #684 from BoostIO/change-menu-to-preferences
Change “Menu” to “Preferences”, and icon.
2017-07-07 18:11:01 +09:00
asmsuechan
d5f82943cf Edit style 2017-07-07 18:06:08 +09:00
asmsuechan
96a020341d Add search-box 2017-07-07 17:59:01 +09:00
Kazu Yokomizo
99b5f75763 Change “Menu” to “Preferences”, and icon. 2017-07-06 14:27:18 +09:00
SuenagaRyota
a128ff7cd8 Merge pull request #683 from asmsuechan/add-iss_template.md
Add issue template
2017-07-06 13:53:26 +09:00
Kazu Yokomizo
59329cc5da Fix typo 2017-07-06 13:50:39 +09:00
asmsuechan
ecd5daaf55 Add issue template 2017-07-06 13:44:19 +09:00
Kazu Yokomizo
514232d720 Merge pull request #682 from BoostIO/update-slack-link
Update slack link
2017-07-06 13:29:39 +09:00
Kazu Yokomizo
93abfe3202 Update slack link 2017-07-06 13:23:22 +09:00
Kazu Yokomizo
4d9e0e3bd7 Change default theme at editor and code-block 2017-07-06 02:32:14 +09:00
Kazu Yokomizo
75d9bf8e38 Edit initial files 2017-07-06 02:31:41 +09:00
SuenagaRyota
ccb9752254 Merge pull request #678 from asmsuechan/remove-e2e-document
Remove e2e documents
2017-07-05 13:38:42 +09:00
asmsuechan
0f2ca19c6c Remove the script of e2e test 2017-07-05 13:33:07 +09:00
asmsuechan
d5a77aade4 Remove testing.md 2017-07-05 13:32:06 +09:00
SuenagaRyota
3c89e6a128 Merge pull request #672 from asmsuechan/componentnize-top-bar-of-note-detail
Componentnize TrashButton
2017-07-02 22:29:14 +09:00
SuenagaRyota
a36be79977 Merge pull request #667 from asmsuechan/randomize-image-name
Randomize the image name when it's dropped
2017-07-02 22:22:23 +09:00
asmsuechan
617f0e0f9c Remove duplicated styles 2017-07-02 22:13:35 +09:00
asmsuechan
0db1a94105 Replace a button to TrashButton component 2017-07-02 22:13:35 +09:00
asmsuechan
f29f97a00f Add TrashButton component 2017-07-02 22:13:32 +09:00
asmsuechan
032b5a59f2 Remove encodeURI 2017-07-01 18:07:16 +09:00
SuenagaRyota
1e5bc395ac Merge pull request #671 from asmsuechan/goodbye-spectron
Goodbye spectron
2017-07-01 12:43:06 +09:00
asmsuechan
fef4afe660 Change .travis.yml 2017-07-01 12:02:52 +09:00
asmsuechan
679ed1eacf Remove e2e tests 2017-07-01 12:02:52 +09:00
asmsuechan
78a9320017 Remove spectron 2017-07-01 12:01:16 +09:00
asmsuechan
927981bf30 Randomize the image name when it's dropped 2017-06-28 13:05:40 +09:00
Kazu Yokomizo
70ea2f6c14 Merge pull request #665 from BoostIO/update-slack-link
Update slack link
2017-06-27 13:46:57 +09:00
Kazu Yokomizo
a6779414ff Delete readme.ja 2017-06-27 13:34:25 +09:00
Kazu Yokomizo
b62fcf523a Update slack link 2017-06-27 13:33:48 +09:00
Kazu Yokomizo
d0bbea5a97 Merge pull request #664 from asmsuechan/auth-aws-only-production
Exclude auth of aws only production
2017-06-26 15:38:33 +09:00
asmsuechan
212d7027fc Exclude auth of aws only production 2017-06-26 15:33:58 +09:00
Kohei TAKATA
be1a5b09da Merge pull request #663 from BoostIO/feature-v0-8-11
v0.8.11
2017-06-24 13:28:49 +09:00
Kohei TAKATA
3de2db4459 v0.8.11 2017-06-24 10:38:13 +09:00
Kazu Yokomizo
f2405a5b34 Merge pull request #661 from BoostIO/translate-to-russian
Add Russian files
2017-06-23 00:51:26 +09:00
Kazu Yokomizo
94abb8f959 Add credit 2017-06-22 23:38:23 +09:00
Kazu Yokomizo
2e30db05bc Add credit 2017-06-22 23:38:05 +09:00
Kazu Yokomizo
c93f3cc5dd Add credit 2017-06-22 23:37:14 +09:00
Kazu Yokomizo
068f8f2ba9 Update contributing.md 2017-06-22 23:35:27 +09:00
Kazu Yokomizo
0980c3b012 Add Russian files 2017-06-22 23:29:45 +09:00
SuenagaRyota
5288d6768f Merge pull request #658 from BoostIO/fix-scroll-bar-z-index
Fix not to show scroll bar when full screen mode in Windows
2017-06-22 14:42:46 +09:00
Kohei TAKATA
34491f4ea4 Remove unnecessary style 2017-06-21 22:53:22 +09:00
Kohei TAKATA
8c73ca8854 Fix not to show scroll bar when full screen mode in Windows 2017-06-21 22:51:21 +09:00
Kazu Yokomizo
0786d8eab6 Merge pull request #656 from BoostIO/add-backers-file
Add backers.md
2017-06-21 20:05:59 +09:00
Kazu Yokomizo
209518c815 Add backers.md 2017-06-21 19:42:51 +09:00
SuenagaRyota
b2753b6457 Merge pull request #644 from BoostIO/windows-fullscreen-zIndex
Fixed z-index at NoteDetail when fullscreen mode in windows.
2017-06-21 09:31:33 +09:00
Kazu Yokomizo
4775a920c0 Merge pull request #653 from BoostIO/update-slack-link
Update slack link
2017-06-20 21:51:01 +09:00
Kazu Yokomizo
0e94dc8740 Update slack link 2017-06-20 21:40:41 +09:00
Kazu Yokomizo
cf68d202d5 Update slack linl 2017-06-20 21:40:11 +09:00
SuenagaRyota
d29ba2bf16 Merge pull request #647 from clone1612/productionSizeFix
Fix dependency type of Spectron module
2017-06-18 14:39:31 +09:00
Kazu Yokomizo
720686cef5 Merge pull request #649 from asmsuechan/add-copy-on-ctrl-c-on-vim
Make Ctrl-C copy
2017-06-18 14:31:35 +09:00
asmsuechan
0625c65cf0 Make Ctrl-C copy 2017-06-18 08:14:33 +09:00
Jannick Hemelhof
acaefe22d1 Fix dependency type of spectron module 2017-06-17 18:01:01 +02:00
Kazu Yokomizo
df1f083ebf Merge pull request #645 from AlbertHilb/FixTypo
Rename `TodolistPercentage.js` to `TodoListPercentage.js`.
2017-06-17 22:25:28 +09:00
Raffaele De Feo
5a1dfc2ca9 Rename TodolistPercentage.js to TodoListPercentage.js. 2017-06-17 14:42:40 +02:00
Kazu Yokomizo
d84894f1bf Merge pull request #643 from BoostIO/update-fullscreen-btn-position
Change fullscreen button position at NoteDetail
2017-06-16 22:35:56 +09:00
SuenagaRyota
0fdc444c4c Merge pull request #642 from BoostIO/fix-analytics
Fix initialize logic of analytics
2017-06-16 22:35:33 +09:00
Kazu Yokomizo
4d216c6f13 Fixed z-index at NoteDetail when fullscreen mode in windows. 2017-06-16 22:25:48 +09:00
Kazu Yokomizo
1c0af8eede Change fullscreen button position at NoteDetail 2017-06-16 21:58:02 +09:00
Kohei TAKATA
f443f9264a Fix initialize logic of analytics 2017-06-16 21:35:21 +09:00
SuenagaRyota
d9b2981327 Merge pull request #325 from asmsuechan/feature-import-from-md-file
Add importer which imports files from .md/.txt
2017-06-16 21:28:04 +09:00
asmsuechan
68e36d2a6d Fix some pointed by lint 2017-06-16 21:17:39 +09:00
SuenagaRyota
68b91bf98c Merge pull request #611 from asmsuechan/error-handling-for-loading-notes
Fix errorhandling on cases of invalid notes
2017-06-16 21:09:48 +09:00
Kazu Yokomizo
b91ddfad05 Merge pull request #640 from BoostIO/update-slack-link
Update slack link
2017-06-13 11:44:21 +09:00
Kazu Yokomizo
a110cbfb5d Update slack link 2017-06-13 11:38:57 +09:00
Kazu Yokomizo
d69f45b0c9 Update slack link 2017-06-13 11:38:22 +09:00
asmsuechan
07df26d5c4 Change to use findNoteTitle() 2017-06-13 11:13:07 +09:00
asmsuechan
d24bcb7f86 Change to template literal 2017-06-13 11:06:09 +09:00
asmsuechan
9f383ba491 Add an option to enable selectiong multiple files 2017-06-13 11:06:09 +09:00
asmsuechan
c38a76d587 Add importer which imports files from .md/.txt 2017-06-13 11:06:00 +09:00
SuenagaRyota
994ca5dd02 Merge pull request #639 from hassec/FixAWSInclude
Fix typo in includes
2017-06-13 10:24:56 +09:00
Christoph Hasse
9281f8f6cb fix spelling to avoid include error 2017-06-13 00:59:37 +02:00
Kohei TAKATA
324f579474 Merge pull request #636 from BoostIO/feature-v0-8-10
v0.8.10
2017-06-13 02:52:07 +09:00
asmsuechan
95e7f4f645 Fix errorhandling on cases of invalid notes 2017-06-03 18:47:56 +09:00
Kazuhisa Hara
b6304a04e6 add rpm support
Make it able to build rpm package using grunt-electron-installer-redhat.
https://www.npmjs.com/package/grunt-electron-installer-redhat

Signed-off-by: Kazuhisa Hara <kazuhisya@gmail.com>
2017-01-25 21:14:30 +09:00
220 changed files with 6477 additions and 2805 deletions

33
.boostnoterc.sample Normal file
View File

@@ -0,0 +1,33 @@
{
"amaEnabled": true,
"editor": {
"fontFamily": "Monaco, Consolas",
"fontSize": "14",
"indentSize": "2",
"indentType": "space",
"keyMap": "vim",
"switchPreview": "BLUR",
"theme": "monokai"
},
"hotkey": {
"toggleFinder": "Cmd + Alt + S",
"toggleMain": "Cmd + Alt + L"
},
"isSideNavFolded": false,
"listStyle": "DEFAULT",
"listWidth": 174,
"navWidth": 200,
"preview": {
"codeBlockTheme": "dracula",
"fontFamily": "Lato",
"fontSize": "14",
"lineNumber": true
},
"sortBy": "UPDATED_AT",
"ui": {
"defaultNote": "ALWAYS_ASK",
"disableDirectWrite": false,
"theme": "default"
},
"zoom": 1
}

View File

@@ -1,10 +1,21 @@
{
"extends": ["standard", "standard-jsx"],
"extends": ["standard", "standard-jsx", "plugin:react/recommended"],
"plugins": ["react"],
"rules": {
"no-useless-escape": 0,
"prefer-const": "warn",
"no-unused-vars": "warn",
"no-undef": "warn",
"no-lone-blocks": "warn"
"no-lone-blocks": "warn",
"react/prop-types": 0,
"react/no-string-refs": 0,
"react/no-find-dom-node": "warn",
"react/no-render-return-value": "warn",
"react/no-deprecated": "warn"
},
"globals": {
"FileReader": true,
"localStorage": true,
"fetch": true
}
}

Binary file not shown.

View File

@@ -1,26 +1,20 @@
language: node_js
# To fix the npm version in 4.x
# refs: https://github.com/travis-ci/travis-ci/issues/4653#issuecomment-194051953
before_install: 'if [[ `npm -v` != 4* ]]; then npm i -g npm@4; fi'
matrix:
include:
- os: osx
install:
- 'if [[ ${TRAVIS_PULL_REQUEST_BRANCH:-$TRAVIS_BRANCH} = "master" ]]; then yarn; fi'
node_js:
- 'stable'
- 'lts/*'
script:
- 'if [[ ${TRAVIS_PULL_REQUEST_BRANCH:-$TRAVIS_BRANCH} = "master" && ${TRAVIS_NODE_VERSION} = "stable" ]]; then
npm run lint;
npm run test;
./script/e2e-runner.sh;
fi'
- os: linux
node_js:
- 'stable'
- 'lts/*'
script:
- 'npm run lint'
- 'npm run test'
- 'if [[ ${TRAVIS_PULL_REQUEST_BRANCH:-$TRAVIS_BRANCH} = "master" && ${TRAVIS_NODE_VERSION} = "stable" ]]; then ./script/e2e-runner.sh; fi'
node_js:
- stable
- lts/*
script:
- npm run lint && npm run test
- 'if [[ ${TRAVIS_PULL_REQUEST_BRANCH:-$TRAVIS_BRANCH} = "master" ]]; then npm install -g grunt npm@5.2 && grunt pre-build; fi'
after_success:
- openssl aes-256-cbc -K $encrypted_440d7f9a3c38_key -iv $encrypted_440d7f9a3c38_iv
-in .snapcraft/travis_snapcraft.cfg -out .snapcraft/snapcraft.cfg -d
sudo: required
services:
- docker
deploy:
'on':
branch: master
provider: script
script: if [ ${TRAVIS_NODE_VERSION} = "stable" ];then docker run -v $(pwd):$(pwd) -t snapcore/snapcraft sh -c "apt update -qq
&& cd $(pwd) && snapcraft && snapcraft push *.snap --release edge"; fi
skip_cleanup: true

72
Backers.md Normal file
View File

@@ -0,0 +1,72 @@
<h1 align="center">Sponsors &amp; Backers</h1>
Boostnote is an open source project. It's an independent project with its ongoing development made possible entirely thanks to the support by these awesome backers. If you'd like to join them, please consider:
- [Become a backer or sponsor on Open Collective.](https://opencollective.com/boostnoteio)
---
## Backers via OpenCollective
### [Gold Sponsors / $1,000 per month](https://opencollective.com/boostnoteio/order/2259)
- Get your logo on our Readme.md on GitHub and the frontpage of https://boostnote.io/.
### [Silver Sponsors / $250 per month](https://opencollective.com/boostnoteio/order/2257)
- Get your logo on our Readme.md on GitHub and the frontpage of https://boostnote.io/.
### [Bronze Sponsors / $50 per month](https://opencollective.com/boostnoteio/order/2258)
- Get your name and Url (or E-mail) on Readme.md on GitHub.
### [Backers3 / $10 per month](https://opencollective.com/boostnoteio/order/2176)
- [Ralph03](https://opencollective.com/ralph03)
- [Nikolas Dan](https://opencollective.com/nikolas-dan)
### [Backers2 / $5 per month](https://opencollective.com/boostnoteio/order/2175)
- [Yeojong Kim](https://twitter.com/yeojoy)
- [Scotia Draven](https://opencollective.com/scotia-draven)
- [A. J. Vargas](https://opencollective.com/aj-vargas)
### [Backers1](https://opencollective.com/boostnoteio/order/2563) and One-time sponsors
- Ryosuke Tamura - $30
- tatoosh11 - $10
- Alexander Borovkov - $10
- spoonhoop - $5
- Drew Williams - $2
- Andy Shaw - $2
- mysafesky -$2
---
## Backers via Bountysource
https://salt.bountysource.com/teams/boostnote
- Kuzz - $65
- Intense Raiden - $45
- ravy22 - $25
- trentpolack - $20
- hikariru - $10
- kolchan11 - $10
- RonWalker22 - $10
- hocchuc - $5
- Adam - $5
- Steve - $5
- evmin - $5

10
ISSUE_TEMPLATE.md Normal file
View File

@@ -0,0 +1,10 @@
<!--
Please paste some **screenshots** with the **developer tool** open (console tab) when you report a bug.
If your issue is regarding boostnote mobile, move to https://github.com/BoostIO/boostnote-mobile.
-->
<!--
Love Boostnote? Please consider supporting us via OpenCollective:
👉 https://opencollective.com/boostnoteio
-->

View File

@@ -1,8 +1,11 @@
import React, { PropTypes } from 'react'
import PropTypes from 'prop-types'
import React from 'react'
import _ from 'lodash'
import CodeMirror from 'codemirror'
import path from 'path'
import copyImage from 'browser/main/lib/dataApi/copyImage'
import { findStorage } from 'browser/lib/findStorage'
import fs from 'fs'
CodeMirror.modeURL = '../node_modules/codemirror/mode/%N/%N.js'
@@ -39,6 +42,7 @@ export default class CodeEditor extends React.Component {
}
this.props.onBlur != null && this.props.onBlur(e)
}
this.pasteHandler = (editor, e) => this.handlePaste(editor, e)
this.loadStyleHandler = (e) => {
this.editor.refresh()
}
@@ -64,7 +68,7 @@ export default class CodeEditor extends React.Component {
if (cm.somethingSelected()) cm.indentSelection('add')
else {
const tabs = cm.getOption('indentWithTabs')
if (line.trimLeft() === '- ' || line.trimLeft() === '* ' || line.trimLeft() === '+ ') {
if (line.trimLeft().match(/^(-|\*|\+) (\[( |x)\] )?$/)) {
cm.execCommand('goLineStart')
if (tabs) {
cm.execCommand('insertTab')
@@ -84,7 +88,13 @@ export default class CodeEditor extends React.Component {
'Cmd-T': function (cm) {
// Do nothing
},
Enter: 'newlineAndIndentContinueMarkdownList'
Enter: 'newlineAndIndentContinueMarkdownList',
'Ctrl-C': (cm) => {
if (cm.getOption('keyMap').substr(0, 3) === 'vim') {
document.execCommand('copy')
}
return CodeMirror.Pass
}
}
})
@@ -92,15 +102,27 @@ export default class CodeEditor extends React.Component {
this.editor.on('blur', this.blurHandler)
this.editor.on('change', this.changeHandler)
this.editor.on('paste', this.pasteHandler)
let editorTheme = document.getElementById('editorTheme')
const editorTheme = document.getElementById('editorTheme')
editorTheme.addEventListener('load', this.loadStyleHandler)
CodeMirror.Vim.defineEx('quit', 'q', this.quitEditor)
CodeMirror.Vim.defineEx('q!', 'q!', this.quitEditor)
CodeMirror.Vim.defineEx('wq', 'wq', this.quitEditor)
CodeMirror.Vim.defineEx('qw', 'qw', this.quitEditor)
CodeMirror.Vim.map('ZZ', ':q', 'normal')
}
quitEditor () {
document.querySelector('textarea').blur()
}
componentWillUnmount () {
this.editor.off('blur', this.blurHandler)
this.editor.off('change', this.changeHandler)
let editorTheme = document.getElementById('editorTheme')
this.editor.off('paste', this.pasteHandler)
const editorTheme = document.getElementById('editorTheme')
editorTheme.removeEventListener('load', this.loadStyleHandler)
}
@@ -176,7 +198,7 @@ export default class CodeEditor extends React.Component {
}
setValue (value) {
let cursor = this.editor.getCursor()
const cursor = this.editor.getCursor()
this.editor.setValue(value)
this.editor.setCursor(cursor)
}
@@ -186,19 +208,43 @@ export default class CodeEditor extends React.Component {
const imagePath = e.dataTransfer.files[0].path
const filename = path.basename(imagePath)
copyImage(imagePath, this.props.storageKey).then((imagePathInTheStorage) => {
const imageMd = `![${encodeURI(filename)}](${imagePathInTheStorage})`
copyImage(imagePath, this.props.storageKey).then((imagePath) => {
const imageMd = `![${filename}](${path.join('/:storage', imagePath)})`
this.insertImageMd(imageMd)
})
}
insertImageMd (imageMd) {
const cm = this.editor
cm.setValue(cm.getValue() + imageMd)
this.editor.replaceSelection(imageMd)
}
handlePaste (editor, e) {
const dataTransferItem = e.clipboardData.items[0]
if (!dataTransferItem.type.match('image')) return
const blob = dataTransferItem.getAsFile()
const reader = new FileReader()
let base64data
reader.readAsDataURL(blob)
reader.onloadend = () => {
base64data = reader.result.replace(/^data:image\/png;base64,/, '')
base64data += base64data.replace('+', ' ')
const binaryData = new Buffer(base64data, 'base64').toString('binary')
const imageName = Math.random().toString(36).slice(-16)
const storagePath = findStorage(this.props.storageKey).path
const imageDir = path.join(storagePath, 'images')
if (!fs.existsSync(imageDir)) fs.mkdirSync(imageDir)
const imagePath = path.join(imageDir, `${imageName}.png`)
fs.writeFile(imagePath, binaryData, 'binary')
const imageMd = `![${imageName}](${path.join('/:storage', `${imageName}.png`)})`
this.insertImageMd(imageMd)
}
}
render () {
let { className, fontFamily, fontSize } = this.props
const { className, fontSize } = this.props
let fontFamily = this.props.className
fontFamily = _.isString(fontFamily) && fontFamily.length > 0
? [fontFamily].concat(defaultEditorFontFamily)
: defaultEditorFontFamily

View File

@@ -1,9 +1,11 @@
import React, { PropTypes } from 'react'
import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './MarkdownEditor.styl'
import CodeEditor from 'browser/components/CodeEditor'
import MarkdownPreview from 'browser/components/MarkdownPreview'
import eventEmitter from 'browser/main/lib/eventEmitter'
import { findStorage } from 'browser/lib/findStorage'
class MarkdownEditor extends React.Component {
constructor (props) {
@@ -68,9 +70,9 @@ class MarkdownEditor extends React.Component {
}
handleContextMenu (e) {
let { config } = this.props
const { config } = this.props
if (config.editor.switchPreview === 'RIGHTCLICK') {
let newStatus = this.state.status === 'PREVIEW'
const newStatus = this.state.status === 'PREVIEW'
? 'CODE'
: 'PREVIEW'
this.setState({
@@ -79,7 +81,6 @@ class MarkdownEditor extends React.Component {
if (newStatus === 'CODE') {
this.refs.code.focus()
} else {
this.refs.code.blur()
this.refs.preview.focus()
}
eventEmitter.emit('topbar:togglelockbutton', this.state.status)
@@ -90,9 +91,9 @@ class MarkdownEditor extends React.Component {
handleBlur (e) {
if (this.state.isLocked) return
this.setState({ keyPressed: new Set() })
let { config } = this.props
const { config } = this.props
if (config.editor.switchPreview === 'BLUR') {
let cursorPosition = this.refs.code.editor.getCursor()
const cursorPosition = this.refs.code.editor.getCursor()
this.setState({
status: 'PREVIEW'
}, () => {
@@ -108,7 +109,7 @@ class MarkdownEditor extends React.Component {
}
handlePreviewMouseUp (e) {
let { config } = this.props
const { config } = this.props
if (config.editor.switchPreview === 'BLUR' && new Date() - this.previewMouseDownedAt < 200) {
this.setState({
status: 'CODE'
@@ -122,15 +123,15 @@ class MarkdownEditor extends React.Component {
handleCheckboxClick (e) {
e.preventDefault()
e.stopPropagation()
let idMatch = /checkbox-([0-9]+)/
let checkedMatch = /\[x\]/i
let uncheckedMatch = /\[ \]/
const idMatch = /checkbox-([0-9]+)/
const checkedMatch = /\[x\]/i
const uncheckedMatch = /\[ \]/
if (idMatch.test(e.target.getAttribute('id'))) {
let lineIndex = parseInt(e.target.getAttribute('id').match(idMatch)[1], 10) - 1
let lines = this.refs.code.value
const lineIndex = parseInt(e.target.getAttribute('id').match(idMatch)[1], 10) - 1
const lines = this.refs.code.value
.split('\n')
let targetLine = lines[lineIndex]
const targetLine = lines[lineIndex]
if (targetLine.match(checkedMatch)) {
lines[lineIndex] = targetLine.replace(checkedMatch, '[ ]')
@@ -162,15 +163,18 @@ class MarkdownEditor extends React.Component {
}
handleKeyDown (e) {
const { config } = this.props
if (this.state.status !== 'CODE') return false
const keyPressed = this.state.keyPressed
keyPressed.add(e.keyCode)
this.setState({ keyPressed })
let isNoteHandlerKey = (el) => { return keyPressed.has(el) }
const isNoteHandlerKey = (el) => { return keyPressed.has(el) }
// These conditions are for ctrl-e and ctrl-w
if (keyPressed.size === this.escapeFromEditor.length &&
!this.state.isLocked && this.state.status === 'CODE' &&
this.escapeFromEditor.every(isNoteHandlerKey)) {
document.activeElement.blur()
this.handleContextMenu()
if (config.editor.switchPreview === 'BLUR') document.activeElement.blur()
}
if (keyPressed.size === this.supportMdSelectionBold.length && this.supportMdSelectionBold.every(isNoteHandlerKey)) {
this.addMdAroundWord('**')
@@ -203,16 +207,18 @@ class MarkdownEditor extends React.Component {
}
render () {
let { className, value, config, storageKey } = this.props
const { className, value, config, storageKey } = this.props
let editorFontSize = parseInt(config.editor.fontSize, 10)
if (!(editorFontSize > 0 && editorFontSize < 101)) editorFontSize = 14
let editorIndentSize = parseInt(config.editor.indentSize, 10)
if (!(editorFontSize > 0 && editorFontSize < 132)) editorIndentSize = 4
let previewStyle = {}
const previewStyle = {}
if (this.props.ignorePreviewPointerEvents) previewStyle.pointerEvents = 'none'
const storage = findStorage(storageKey)
return (
<div className={className == null
? 'MarkdownEditor'
@@ -260,6 +266,8 @@ class MarkdownEditor extends React.Component {
onMouseUp={(e) => this.handlePreviewMouseUp(e)}
onMouseDown={(e) => this.handlePreviewMouseDown(e)}
onCheckboxClick={(e) => this.handleCheckboxClick(e)}
showCopyNotification={config.ui.showCopyNotification}
storagePath={storage.path}
/>
</div>
)

View File

@@ -1,4 +1,5 @@
import React, { PropTypes } from 'react'
import PropTypes from 'prop-types'
import React from 'react'
import markdown from 'browser/lib/markdown'
import _ from 'lodash'
import CodeMirror from 'codemirror'
@@ -10,6 +11,7 @@ import eventEmitter from 'browser/main/lib/eventEmitter'
import fs from 'fs'
import htmlTextHelper from 'browser/lib/htmlTextHelper'
import copy from 'copy-to-clipboard'
import mdurl from 'mdurl'
const { remote } = require('electron')
const { app } = remote
@@ -32,15 +34,23 @@ function buildStyle (fontFamily, fontSize, codeBlockFontFamily, lineNumber) {
font-weight: normal;
text-rendering: optimizeLegibility;
}
@font-face {
font-family: 'Lato';
src: url('${appPath}/resources/fonts/Lato-Black.woff2') format('woff2'), /* Modern Browsers */
url('${appPath}/resources/fonts/Lato-Black.woff') format('woff'), /* Modern Browsers */
url('${appPath}/resources/fonts/Lato-Black.ttf') format('truetype');
font-style: normal;
font-weight: 700;
text-rendering: optimizeLegibility;
}
${markdownStyle}
body {
font-family: ${fontFamily.join(', ')};
font-family: '${fontFamily.join("','")}';
font-size: ${fontSize}px;
}
code {
font-family: ${codeBlockFontFamily.join(', ')};
background-color: rgba(0,0,0,0.04);
color: #CC305F;
}
.lineNumber {
${lineNumber && 'display: block !important;'}
@@ -51,12 +61,12 @@ code {
color: rgba(147,147,149,0.8);;
fill: rgba(147,147,149,1);;
border-radius: 50%;
margin: 7px;
margin: 0px 10px;
border: none;
background-color: transparent;
outline: none;
height: 32px;
width: 32px;
height: 15px;
width: 15px;
cursor: pointer;
}
@@ -92,7 +102,7 @@ const OSX = global.process.platform === 'darwin'
const defaultFontFamily = ['helvetica', 'arial', 'sans-serif']
if (!OSX) {
defaultFontFamily.unshift('\'Microsoft YaHei\'')
defaultFontFamily.unshift('Microsoft YaHei')
defaultFontFamily.unshift('meiryo')
}
const defaultCodeBlockFontFamily = ['Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', 'monospace']
@@ -108,16 +118,19 @@ export default class MarkdownPreview extends React.Component {
this.checkboxClickHandler = (e) => this.handleCheckboxClick(e)
this.saveAsTextHandler = () => this.handleSaveAsText()
this.saveAsMdHandler = () => this.handleSaveAsMd()
this.printHandler = () => this.handlePrint()
this.linkClickHandler = this.handlelinkClick.bind(this)
}
handlePreviewAnchorClick (e) {
e.preventDefault()
e.stopPropagation()
let anchor = e.target.closest('a')
let href = anchor.getAttribute('href')
const anchor = e.target.closest('a')
const href = anchor.getAttribute('href')
if (_.isString(href) && href.match(/^#/)) {
let targetElement = this.refs.root.contentWindow.document.getElementById(href.substring(1, href.length))
const targetElement = this.refs.root.contentWindow.document.getElementById(href.substring(1, href.length))
if (targetElement != null) {
this.getWindow().scrollTo(0, targetElement.offsetTop)
}
@@ -160,6 +173,10 @@ export default class MarkdownPreview extends React.Component {
this.exportAsDocument('md')
}
handlePrint () {
this.refs.root.contentWindow.print()
}
exportAsDocument (fileType) {
const options = {
filters: [
@@ -177,6 +194,16 @@ export default class MarkdownPreview extends React.Component {
})
}
fixDecodedURI (node) {
if (node && node.children.length === 1 && typeof node.children[0] === 'string') {
const { innerText, href } = node
node.innerText = mdurl.decode(href) === innerText
? href
: innerText
}
}
componentDidMount () {
this.refs.root.setAttribute('sandbox', 'allow-scripts')
this.refs.root.contentWindow.document.body.addEventListener('contextmenu', this.contextMenuHandler)
@@ -196,6 +223,7 @@ export default class MarkdownPreview extends React.Component {
this.refs.root.contentWindow.document.addEventListener('dragover', this.preventImageDroppedHandler)
eventEmitter.on('export:save-text', this.saveAsTextHandler)
eventEmitter.on('export:save-md', this.saveAsMdHandler)
eventEmitter.on('print', this.printHandler)
}
componentWillUnmount () {
@@ -206,6 +234,7 @@ export default class MarkdownPreview extends React.Component {
this.refs.root.contentWindow.document.removeEventListener('dragover', this.preventImageDroppedHandler)
eventEmitter.off('export:save-text', this.saveAsTextHandler)
eventEmitter.off('export:save-md', this.saveAsMdHandler)
eventEmitter.off('print', this.printHandler)
}
componentDidUpdate (prevProps) {
@@ -215,6 +244,7 @@ export default class MarkdownPreview extends React.Component {
prevProps.codeBlockFontFamily !== this.props.codeBlockFontFamily ||
prevProps.codeBlockTheme !== this.props.codeBlockTheme ||
prevProps.lineNumber !== this.props.lineNumber ||
prevProps.showCopyNotification !== this.props.showCopyNotification ||
prevProps.theme !== this.props.theme) {
this.applyStyle()
this.rewriteIframe()
@@ -222,7 +252,8 @@ export default class MarkdownPreview extends React.Component {
}
applyStyle () {
let { fontFamily, fontSize, codeBlockFontFamily, lineNumber, codeBlockTheme } = this.props
const { fontSize, lineNumber, codeBlockTheme } = this.props
let { fontFamily, codeBlockFontFamily } = this.props
fontFamily = _.isString(fontFamily) && fontFamily.trim().length > 0
? [fontFamily].concat(defaultFontFamily)
: defaultFontFamily
@@ -238,7 +269,9 @@ export default class MarkdownPreview extends React.Component {
theme = consts.THEMES.some((_theme) => _theme === theme) && theme !== 'default'
? theme
: 'elegant'
this.getWindow().document.getElementById('codeTheme').href = `${appPath}/node_modules/codemirror/theme/${theme}.css`
this.getWindow().document.getElementById('codeTheme').href = theme.startsWith('solarized')
? `${appPath}/node_modules/codemirror/theme/solarized.css`
: `${appPath}/node_modules/codemirror/theme/${theme}.css`
}
rewriteIframe () {
@@ -249,7 +282,12 @@ export default class MarkdownPreview extends React.Component {
el.removeEventListener('click', this.checkboxClickHandler)
})
let { value, theme, indentSize, codeBlockTheme } = this.props
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('a'), (el) => {
el.removeEventListener('click', this.linkClickHandler)
})
const { theme, indentSize, showCopyNotification, storagePath } = this.props
let { value, codeBlockTheme } = this.props
this.refs.root.contentWindow.document.body.setAttribute('data-theme', theme)
@@ -266,6 +304,7 @@ export default class MarkdownPreview extends React.Component {
})
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('a'), (el) => {
this.fixDecodedURI(el)
el.addEventListener('click', this.anchorClickHandler)
})
@@ -273,6 +312,16 @@ export default class MarkdownPreview extends React.Component {
el.addEventListener('click', this.checkboxClickHandler)
})
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('a'), (el) => {
el.addEventListener('click', this.linkClickHandler)
})
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('img'), (el) => {
el.src = markdown.normalizeLinkText(el.src)
if (!/\/:storage/.test(el.src)) return
el.src = `file:///${markdown.normalizeLinkText(path.join(storagePath, 'images', path.basename(el.src)))}`
})
codeBlockTheme = consts.THEMES.some((_theme) => _theme === codeBlockTheme)
? codeBlockTheme
: 'default'
@@ -281,25 +330,32 @@ export default class MarkdownPreview extends React.Component {
let syntax = CodeMirror.findModeByName(el.className)
if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text')
CodeMirror.requireMode(syntax.mode, () => {
let content = htmlTextHelper.decodeEntities(el.innerHTML)
const content = htmlTextHelper.decodeEntities(el.innerHTML)
const copyIcon = document.createElement('i')
copyIcon.innerHTML = '<button class="clipboardButton"><svg width="13" height="13" viewBox="0 0 1792 1792" ><path d="M768 1664h896v-640h-416q-40 0-68-28t-28-68v-416h-384v1152zm256-1440v-64q0-13-9.5-22.5t-22.5-9.5h-704q-13 0-22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h704q13 0 22.5-9.5t9.5-22.5zm256 672h299l-299-299v299zm512 128v672q0 40-28 68t-68 28h-960q-40 0-68-28t-28-68v-160h-544q-40 0-68-28t-28-68v-1344q0-40 28-68t68-28h1088q40 0 68 28t28 68v328q21 13 36 28l408 408q28 28 48 76t20 88z"/></svg></button>'
copyIcon.onclick = (e) => {
copy(content)
this.notify('Saved to Clipboard!', {
body: 'Paste it wherever you want!',
silent: true
})
if (showCopyNotification) {
this.notify('Saved to Clipboard!', {
body: 'Paste it wherever you want!',
silent: true
})
}
}
el.parentNode.appendChild(copyIcon)
el.innerHTML = ''
el.parentNode.className += ` cm-s-${codeBlockTheme} CodeMirror`
if (codeBlockTheme.indexOf('solarized') === 0) {
const [refThema, color] = codeBlockTheme.split(' ')
el.parentNode.className += ` cm-s-${refThema} cm-s-${color} CodeMirror`
} else {
el.parentNode.className += ` cm-s-${codeBlockTheme} CodeMirror`
}
CodeMirror.runMode(content, syntax.mime, el, {
tabSize: indentSize
})
})
})
let opts = {}
const opts = {}
// if (this.props.theme === 'dark') {
// opts['font-color'] = '#DDD'
// opts['line-color'] = '#DDD'
@@ -309,7 +365,7 @@ export default class MarkdownPreview extends React.Component {
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('.flowchart'), (el) => {
Raphael.setWindow(this.getWindow())
try {
let diagram = flowchart.parse(htmlTextHelper.decodeEntities(el.innerHTML))
const diagram = flowchart.parse(htmlTextHelper.decodeEntities(el.innerHTML))
el.innerHTML = ''
diagram.drawSVG(el, opts)
_.forEach(el.querySelectorAll('a'), (el) => {
@@ -325,7 +381,7 @@ export default class MarkdownPreview extends React.Component {
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('.sequence'), (el) => {
Raphael.setWindow(this.getWindow())
try {
let diagram = SequenceDiagram.parse(htmlTextHelper.decodeEntities(el.innerHTML))
const diagram = SequenceDiagram.parse(htmlTextHelper.decodeEntities(el.innerHTML))
el.innerHTML = ''
diagram.drawSVG(el, {theme: 'simple'})
_.forEach(el.querySelectorAll('a'), (el) => {
@@ -348,11 +404,11 @@ export default class MarkdownPreview extends React.Component {
}
scrollTo (targetRow) {
let blocks = this.getWindow().document.querySelectorAll('body>[data-line]')
const blocks = this.getWindow().document.querySelectorAll('body>[data-line]')
for (let index = 0; index < blocks.length; index++) {
let block = blocks[index]
let row = parseInt(block.getAttribute('data-line'))
const row = parseInt(block.getAttribute('data-line'))
if (row > targetRow || index === blocks.length - 1) {
block = blocks[index - 1]
block != null && this.getWindow().scrollTo(0, block.offsetTop)
@@ -373,8 +429,16 @@ export default class MarkdownPreview extends React.Component {
return new window.Notification(title, options)
}
handlelinkClick (e) {
const noteHash = e.target.href.split('/').pop()
const regexIsNoteLink = /^(.{20})-(.{20})$/
if (regexIsNoteLink.test(noteHash)) {
eventEmitter.emit('list:jump', noteHash)
}
}
render () {
let { className, style, tabIndex } = this.props
const { className, style, tabIndex } = this.props
return (
<iframe className={className != null
? 'MarkdownPreview ' + className
@@ -394,5 +458,7 @@ MarkdownPreview.propTypes = {
onMouseUp: PropTypes.func,
onMouseDown: PropTypes.func,
className: PropTypes.string,
value: PropTypes.string
value: PropTypes.string,
showCopyNotification: PropTypes.bool,
storagePath: PropTypes.string
}

View File

@@ -1,4 +1,5 @@
import React, {PropTypes} from 'react'
import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './ModalEscButton.styl'
@@ -6,7 +7,7 @@ const ModalEscButton = ({
handleEscButtonClick
}) => (
<button styleName='escButton' onClick={handleEscButtonClick}>
<div styleName='esc-mark'>x</div>
<div styleName='esc-mark'>×</div>
<div styleName='esc-text'>esc</div>
</button>
)

View File

@@ -11,4 +11,6 @@
height top-bar-height
.esc-mark
font-size 15px
font-size 28px
margin-top -5px
margin-bottom -7px

View File

@@ -0,0 +1,30 @@
/**
* @fileoverview Micro component for toggle SideNav
*/
import PropTypes from 'prop-types'
import React from 'react'
import styles from './NavToggleButton.styl'
import CSSModules from 'browser/lib/CSSModules'
/**
* @param {boolean} isFolded
* @param {Function} handleToggleButtonClick
*/
const NavToggleButton = ({isFolded, handleToggleButtonClick}) => (
<button styleName='navToggle'
onClick={(e) => handleToggleButtonClick(e)}
>
{isFolded
? <i className='fa fa-angle-double-right' />
: <i className='fa fa-angle-double-left' />
}
</button>
)
NavToggleButton.propTypes = {
isFolded: PropTypes.bool.isRequired,
handleToggleButtonClick: PropTypes.func.isRequired
}
export default CSSModules(NavToggleButton, styles)

View File

@@ -0,0 +1,22 @@
.navToggle
navButtonColor()
display block
position absolute
left 5px
bottom 5px
border-radius 16.5px
height 34px
width 34px
line-height 32px
padding 0
body[data-theme="white"]
navWhiteButtonColor()
body[data-theme="dark"]
.navToggle
&:hover
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
transition 0.15s
color $ui-dark-text-color

View File

@@ -1,10 +1,13 @@
/**
* @fileoverview Note item component.
*/
import React, { PropTypes } from 'react'
import PropTypes from 'prop-types'
import React from 'react'
import { isArray } from 'lodash'
import CSSModules from 'browser/lib/CSSModules'
import { getTodoStatus } from 'browser/lib/getTodoStatus'
import styles from './NoteItem.styl'
import TodoProcess from './TodoProcess'
/**
* @description Tag element component.
@@ -43,7 +46,7 @@ const TagElementList = (tags) => {
* @param {Function} handleDragStart
* @param {string} dateDisplay
*/
const NoteItem = ({ isActive, note, dateDisplay, handleNoteClick, handleNoteContextMenu, handleDragStart }) => (
const NoteItem = ({ isActive, note, dateDisplay, handleNoteClick, handleNoteContextMenu, handleDragStart, pathname }) => (
<div styleName={isActive
? 'item--active'
: 'item'
@@ -68,7 +71,14 @@ const NoteItem = ({ isActive, note, dateDisplay, handleNoteClick, handleNoteCont
<div styleName='item-bottom-time'>{dateDisplay}</div>
{note.isStarred
? <i styleName='item-star' className='fa fa-star' /> : ''
? <img styleName='item-star' src='../resources/icon/icon-starred.svg' /> : ''
}
{note.isPinned && !pathname.match(/\/home|\/starred|\/trash/)
? <i styleName='item-pin' className='fa fa-thumb-tack' /> : ''
}
{note.type === 'MARKDOWN_NOTE'
? <TodoProcess todoStatus={getTodoStatus(note.content)} />
: ''
}
<div styleName='item-bottom'>
<div styleName='item-bottom-tagList'>
@@ -91,7 +101,8 @@ NoteItem.propTypes = {
type: PropTypes.string.isRequired,
title: PropTypes.string.isrequired,
tags: PropTypes.array,
isStarred: PropTypes.bool.isRequired
isStarred: PropTypes.bool.isRequired,
isTrashed: PropTypes.bool.isRequired
}),
handleNoteClick: PropTypes.func.isRequired,
handleNoteContextMenu: PropTypes.func.isRequired,

View File

@@ -11,9 +11,9 @@ $control-height = 30px
user-select none
cursor pointer
background-color $ui-noteList-backgroundColor
transition background-color 0.2s
transition 0.2s
&:hover
background-color alpha($ui-button--active-backgroundColor, 40%)
background-color alpha($ui-button--active-backgroundColor, 20%)
.item-title
.item-title-icon
.item-bottom-time
@@ -25,7 +25,7 @@ $control-height = 30px
.item-star
color $ui-favorite-star-button-color
&:active
background-color $ui-button--active-backgroundColor
background-color alpha($ui-button--active-backgroundColor, 40%)
color $ui-text-color
.item-title
.item-title-icon
@@ -39,10 +39,11 @@ $control-height = 30px
.item-wrapper
padding 15px 0
border-bottom $ui-border
position relative
.item--active
@extend .item
background-color $ui-button--active-backgroundColor
background-color alpha($ui-button--active-backgroundColor, 60%)
color $ui-text-color
.item-title
.item-title-empty
@@ -58,20 +59,30 @@ $control-height = 30px
.item-star
color $ui-favorite-star-button-color
&:hover
background-color $ui-button--active-backgroundColor
background-color alpha($ui-button--active-backgroundColor, 40%)
color #e74c3c
.menu-button-label
color $ui-text-color
&:active, &:active:hover
background-color alpha($ui-button--active-backgroundColor, 40%)
color #e74c3c
.menu-button-label
color $ui-text-color
.item-title-icon
position relative
font-size 12px
color $ui-inactive-text-color
top 2px
.item-title
font-size 13px
font-size 15px
font-weight 700
position relative
top -12px
left 20px
padding-right 15px
padding-bottom 4px
padding 0px 15px 0px 0px
margin-bottom 4px
overflow ellipsis
color $ui-inactive-text-color
@@ -82,7 +93,7 @@ $control-height = 30px
.item-bottom
position relative
bottom 0px
margin-top 2px
margin-top 10px
font-size 12px
line-height 20px
overflow ellipsis
@@ -91,39 +102,63 @@ $control-height = 30px
.item-bottom-tagList
flex 1
overflow ellipsis
line-height 20px
padding-top 2px
line-height 25px
padding-left 2px
margin-right 40px
.item-bottom-tagList-item
font-size 10px
font-size 11px
margin-right 8px
padding 0
height 20px
box-sizing border-box
border-radius 2px
padding 1px 2px
padding 4px
vertical-align middle
background-color white
color $ui-inactive-text-color
.item-bottom-time
color $ui-inactive-text-color
font-size 10px
font-size 13px
padding-left 2px
padding-bottom 2px
.item-star
position absolute
top 34px
left 5px
width 34px
height 34px
right -6px
bottom 23px
width 16px
height 16px
color alpha($ui-favorite-star-button-color, 60%)
font-size 12px
padding 0
border-radius 17px
.item-pin
position absolute
right 0px
bottom 2px
width 34px
height 34px
color #E54D42
font-size 14px
padding 0
border-radius 17px
body[data-theme="white"]
.item
background-color $ui-white-noteList-backgroundColor
&:hover
background-color alpha($ui-button--active-backgroundColor, 60%)
&:active
background-color $ui-button--active-backgroundColor
.item--active
@extend .item
background-color $ui-button--active-backgroundColor
&:hover
background-color alpha($ui-button--active-backgroundColor, 60%)
body[data-theme="dark"]
.root
border-color $ui-dark-borderColor
@@ -135,6 +170,7 @@ body[data-theme="dark"]
&:hover
transition 0.15s
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
color $ui-dark-text-color
.item-title
.item-title-icon
.item-bottom-time
@@ -142,11 +178,12 @@ body[data-theme="dark"]
color $ui-dark-text-color
.item-bottom-tagList-item
transition 0.15s
background-color alpha($ui-dark-button--active-backgroundColor, 40%)
background-color alpha(#fff, 20%)
color $ui-dark-text-color
&:active
transition 0.15s
background-color $ui-dark-button--active-backgroundColor
color $ui-dark-text-color
.item-title
.item-title-icon
.item-bottom-time
@@ -172,6 +209,11 @@ body[data-theme="dark"]
.item-bottom-tagList-item
background-color alpha(white, 10%)
color $ui-dark-text-color
&:hover
background-color alpha($ui-dark-button--active-backgroundColor, 60%)
color #c0392b
.item-bottom-tagList-item
background-color alpha(#fff, 20%)
.item-title
color $ui-inactive-text-color

View File

@@ -1,7 +1,8 @@
/**
* @fileoverview Note item component with simple display mode.
*/
import React, { PropTypes } from 'react'
import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './NoteItemSimple.styl'
@@ -13,7 +14,7 @@ import styles from './NoteItemSimple.styl'
* @param {Function} handleNoteContextMenu
* @param {Function} handleDragStart
*/
const NoteItemSimple = ({ isActive, note, handleNoteClick, handleNoteContextMenum, handleDragStart }) => (
const NoteItemSimple = ({ isActive, note, handleNoteClick, handleNoteContextMenu, handleDragStart }) => (
<div styleName={isActive
? 'item-simple--active'
: 'item-simple'

View File

@@ -7,19 +7,19 @@ $control-height = 30px
.item-simple
position relative
padding 0 25px
padding 0 20px
user-select none
cursor pointer
background-color $ui-noteList-backgroundColor
transition background-color 0.15s
transition 0.2s
&:hover
background-color alpha($ui-button--active-backgroundColor, 40%)
background-color alpha($ui-button--active-backgroundColor, 20%)
.item-simple-title
.item-simple-title-empty
.item-simple-title-icon
color $ui-text-color
&:active
background-color $ui-button--active-backgroundColor
background-color alpha($ui-button--active-backgroundColor, 40%)
color $ui-text-color
.item-simple-title
.item-simple-title-empty
@@ -28,7 +28,7 @@ $control-height = 30px
.item-simple--active
@extend .item-simple
background-color $ui-button--active-backgroundColor
background-color alpha($ui-button--active-backgroundColor, 60%)
color $ui-text-color
.item-simple-title
.item-simple-title-empty
@@ -37,10 +37,18 @@ $control-height = 30px
.item-simple-title-icon
color $ui-text-color
&:hover
background-color $ui-button--active-backgroundColor
background-color alpha($ui-button--active-backgroundColor, 40%)
color #e74c3c
.menu-button-label
color $ui-text-color
&:active, &:active:hover
background-color alpha($ui-button--active-backgroundColor, 40%)
color #e74c3c
.menu-button-label
color $ui-text-color
.item-simple-title
font-size 12px
font-size 13px
height 40px
box-sizing border-box
line-height 24px
@@ -48,6 +56,7 @@ $control-height = 30px
overflow ellipsis
color $ui-inactive-text-color
border-bottom $ui-border
position relative
.item-simple-title-icon
font-size 12px
@@ -58,6 +67,20 @@ $control-height = 30px
font-weight normal
color $ui-inactive-text-color
body[data-theme="white"]
.item-simple
background-color $ui-white-noteList-backgroundColor
&:hover
background-color alpha($ui-button--active-backgroundColor, 60%)
&:active
background-color $ui-button--active-backgroundColor
.item-simple--active
@extend .item-simple
background-color $ui-button--active-backgroundColor
&:hover
background-color alpha($ui-button--active-backgroundColor, 60%)
body[data-theme="dark"]
.root
border-color $ui-dark-borderColor
@@ -66,33 +89,50 @@ body[data-theme="dark"]
.item-simple
border-color $ui-dark-borderColor
background-color $ui-dark-noteList-backgroundColor
&:active
background-color $ui-dark-button--active-backgroundColor
.item-simple-title
.item-simple-title-icon
.item-simple-bottom-time
.item-simple-bottom-tagList-item
transition 0.15s
color $ui-dark-text-color
&:hover
transition 0.15s
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
color $ui-dark-text-color
.item-simple-title
.item-simple-title-icon
.item-simple-bottom-time
.item-simple-bottom-tagList-item
transition 0.15s
color $ui-dark-text-color
.item-simple-bottom-tagList-item
transition 0.15s
background-color alpha(#fff, 20%)
color $ui-dark-text-color
&:active
transition 0.15s
background-color $ui-dark-button--active-backgroundColor
color $ui-dark-text-color
.item-simple-title
.item-simple-title-icon
.item-simple-bottom-time
transition 0.15s
color $ui-dark-text-color
.item-simple-bottom-tagList-item
transition 0.15s
background-color alpha(white, 10%)
color $ui-dark-text-color
.item-simple--active
border-color $ui-dark-borderColor
background-color $ui-dark-button--active-backgroundColor
.item-simple-wrapper
border-color transparent
.item-simple-title
.item-simple-title-icon
.item-simple-bottom-time
color $ui-dark-text-color
.item-simple-bottom-tagList-item
background-color transparent
background-color alpha(white, 10%)
color $ui-dark-text-color
&:hover
background-color alpha($ui-dark-button--active-backgroundColor, 60%)
color #c0392b
.item-simple-bottom-tagList-item
background-color alpha(#fff, 20%)
.item-simple-title
color $ui-inactive-text-color

View File

@@ -0,0 +1,55 @@
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './RealtimeNotification.styl'
const electron = require('electron')
const { shell } = electron
class RealtimeNotification extends React.Component {
constructor (props) {
super(props)
this.state = {
notifications: []
}
}
componentDidMount () {
this.fetchNotifications()
}
fetchNotifications () {
const notificationsUrl = 'https://raw.githubusercontent.com/BoostIO/notification/master/notification.json'
fetch(notificationsUrl)
.then(response => {
return response.json()
})
.then(json => {
this.setState({notifications: json.notifications})
})
}
handleLinkClick (e) {
shell.openExternal(e.currentTarget.href)
e.preventDefault()
}
render () {
const { notifications } = this.state
const link = notifications.length > 0
? <a styleName='notification-link' href={notifications[0].linkUrl}
onClick={(e) => this.handleLinkClick(e)}
>
Info: {notifications[0].text}
</a>
: ''
return (
<div styleName='notification-area' style={this.props.style}>{link}</div>
)
}
}
RealtimeNotification.propTypes = {}
export default CSSModules(RealtimeNotification, styles)

View File

@@ -0,0 +1,31 @@
.notification-area
z-index 1000
font-size 12px
position: relative
top: 12px
background-color none
.notification-link
position absolute
text-decoration none
color #282A36
font-size 14px
border 1px solid #6FA8E6
background-color alpha(#6FA8E6, 0.2)
padding 5px 12px
border-radius 2px
transition 0.2s
&:hover
color #1378BD
body[data-theme="dark"]
.notification-area
background-color none
.notification-link
color #fff
border 1px solid alpha(#5CB85C, 0.6)
background-color alpha(#5CB85C, 0.2)
transition 0.2s
&:hover
color #5CB85C

View File

@@ -1,7 +1,8 @@
/**
* @fileoverview Filter for all notes.
*/
import React, { PropTypes } from 'react'
import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './SideNavFilter.styl'
@@ -15,21 +16,53 @@ import styles from './SideNavFilter.styl'
*/
const SideNavFilter = ({
isFolded, isHomeActive, handleAllNotesButtonClick,
isStarredActive, handleStarredButtonClick
isStarredActive, handleStarredButtonClick, isTrashedActive, handleTrashedButtonClick, counterDelNote,
counterTotalNote, counterStarredNote
}) => (
<div styleName={isFolded ? 'menu--folded' : 'menu'}>
<button styleName={isHomeActive ? 'menu-button--active' : 'menu-button'}
onClick={handleAllNotesButtonClick}
>
<i className='fa fa-archive fa-fw' />
<div styleName='iconWrap'>
<img src={isHomeActive
? '../resources/icon/icon-all-active.svg'
: '../resources/icon/icon-all.svg'
}
/>
</div>
<span styleName='menu-button-label'>All Notes</span>
<span styleName='counters'>{counterTotalNote}</span>
</button>
<button styleName={isStarredActive ? 'menu-button-star--active' : 'menu-button'}
onClick={handleStarredButtonClick}
>
<i className='fa fa-star fa-fw' />
<div styleName='iconWrap'>
<img src={isStarredActive
? '../resources/icon/icon-star-active.svg'
: '../resources/icon/icon-star.svg'
}
/>
</div>
<span styleName='menu-button-label'>Starred</span>
<span styleName='counters'>{counterStarredNote}</span>
</button>
<button styleName={isTrashedActive ? 'menu-button-trash--active' : 'menu-button'}
onClick={handleTrashedButtonClick}
>
<div styleName='iconWrap'>
<img src={isTrashedActive
? '../resources/icon/icon-trash-active.svg'
: '../resources/icon/icon-trash.svg'
}
/>
</div>
<span styleName='menu-button-label'>Trash</span>
<span styleName='counters'>{counterDelNote}</span>
</button>
</div>
)
@@ -38,7 +71,9 @@ SideNavFilter.propTypes = {
isHomeActive: PropTypes.bool.isRequired,
handleAllNotesButtonClick: PropTypes.func.isRequired,
isStarredActive: PropTypes.bool.isRequired,
handleStarredButtonClick: PropTypes.func.isRequired
isTrashedActive: PropTypes.bool.isRequired,
handleStarredButtonClick: PropTypes.func.isRequired,
handleTrashdButtonClick: PropTypes.func.isRequired
}
export default CSSModules(SideNavFilter, styles)

View File

@@ -3,57 +3,68 @@
.menu-button
navButtonColor()
height 32px
padding 0 15px
font-size 12px
height 36px
padding 0 15px 0 20px
font-size 14px
width 100%
text-align left
overflow ellipsis
display flex
align-items center
&:hover, &:active, &:active:hover
color #1EC38B
background-color alpha($ui-button-default--active-backgroundColor, 20%)
.iconWrap
width 20px
text-align center
.counters
float right
color $ui-inactive-text-color
.menu-button--active
@extend .menu-button
color #e74c3c
background-color $ui-button--active-backgroundColor
.menu-button-label
color $ui-text-color
SideNavFilter()
color #1EC38B
background-color alpha($ui-button-default--active-backgroundColor, 20%)
.menu-button-label, .counters
color #1EC38B
&:hover
background-color $ui-button--active-backgroundColor
color #e74c3c
.menu-button-label
color $ui-text-color
&:active, &:active:hover
background-color $ui-button--active-backgroundColor
color #e74c3c
.menu-button-label
color $ui-text-color
color #1EC38B
.menu-button-star--active
@extend .menu-button
color #F9BF3B
background-color $ui-button--active-backgroundColor
.menu-button-label
color $ui-text-color
&:hover
background-color $ui-button--active-backgroundColor
color #F9BF3B
.menu-button-label
color $ui-text-color
&:active, &:active:hover
background-color $ui-button--active-backgroundColor
color #F9BF3B
.menu-button-label
color $ui-text-color
SideNavFilter()
color #1EC38B
background-color alpha($ui-button-default--active-backgroundColor, 20%)
.menu-button-label, .counters
color #1EC38B
.menu-button-trash--active
@extend .menu-button
SideNavFilter()
color #1EC38B
background-color alpha($ui-button-default--active-backgroundColor, 20%)
.menu-button-label, .counters
color #1EC38B
.menu-button-label
margin-left 5px
margin-left 10px
flex 1
.menu--folded
@extend .menu
.menu-button, .menu-button--active
text-align center
padding 0 12px
&:hover .menu-button-label
transition opacity 0.15s
opacity 1
color $ui-tooltip-text-color
background-color $ui-tooltip-backgroundColor
.menu-button-label
position fixed
display inline-block
@@ -63,15 +74,73 @@
margin-top -8px
margin-left 0
overflow ellipsis
background-color $ui-tooltip-backgroundColor
z-index 10
color white
line-height 32px
border-top-right-radius 2px
border-bottom-right-radius 2px
pointer-events none
opacity 0
font-size 12px
font-size 13px
.counters
display none
body[data-theme="white"]
.menu-button
navWhiteButtonColor()
.counters
color $ui-inactive-text-color
.menu-button--active
@extend .menu-button
color #e74c3c
background-color $ui-button--active-backgroundColor
.menu-button-label
color $ui-text-color
&:hover
background-color alpha($ui-button--active-backgroundColor, 50%)
color #e74c3c
.menu-button-label
color $ui-text-color
&:active, &:active:hover
background-color alpha($ui-button--active-backgroundColor, 50%)
color #e74c3c
.menu-button-label
color $ui-text-color
.menu-button-star--active
@extend .menu-button
color #F9BF3B
background-color $ui-button--active-backgroundColor
.menu-button-label
color $ui-text-color
&:hover
background-color alpha($ui-button--active-backgroundColor, 50%)
color #F9BF3B
.menu-button-label
color $ui-text-color
&:active, &:active:hover
background-color alpha($ui-button--active-backgroundColor, 50%)
color #F9BF3B
.menu-button-label
color $ui-text-color
.menu-button-trash--active
@extend .menu-button
color #5D9E36
background-color $ui-button--active-backgroundColor
.menu-button-label
color $ui-text-color
&:hover
background-color alpha($ui-button--active-backgroundColor, 50%)
color #5D9E36
.menu-button-label
color $ui-text-color
&:active, &:active:hover
background-color alpha($ui-button--active-backgroundColor, 50%)
color #5D9E36
.menu-button-label
color $ui-text-color
body[data-theme="dark"]
.menu-button
@@ -88,7 +157,7 @@ body[data-theme="dark"]
.menu-button-label
color $ui-dark-text-color
&:hover
background-color $ui-dark-button--active-backgroundColor
background-color alpha($ui-dark-button--active-backgroundColor, 50%)
color #c0392b
.menu-button-label
color $ui-dark-text-color
@@ -99,7 +168,18 @@ body[data-theme="dark"]
.menu-button-label
color $ui-dark-text-color
&:hover
background-color $ui-dark-button--active-backgroundColor
background-color alpha($ui-dark-button--active-backgroundColor, 50%)
color $ui-favorite-star-button-color
.menu-button-label
color $ui-dark-text-color
.menu-button-trash--active
color #5D9E36
background-color $ui-dark-button--active-backgroundColor
.menu-button-label
color $ui-dark-text-color
&:hover
background-color alpha($ui-dark-button--active-backgroundColor, 50%)
color #5D9E36
.menu-button-label
color $ui-dark-text-color

View File

@@ -86,7 +86,7 @@ class SnippetTab extends React.Component {
}
render () {
let { isActive, snippet, isDeletable } = this.props
const { isActive, snippet, isDeletable } = this.props
return (
<div styleName={isActive
? 'root--active'

View File

@@ -1,10 +1,11 @@
/**
* @fileoverview Micro component for showing storage.
*/
import React, { PropTypes } from 'react'
import PropTypes from 'prop-types'
import React from 'react'
import styles from './StorageItem.styl'
import CSSModules from 'browser/lib/CSSModules'
import { isNumber } from 'lodash'
import _ from 'lodash'
/**
* @param {boolean} isActive
@@ -35,12 +36,10 @@ const StorageItem = ({
>
<span styleName={isFolded
? 'folderList-item-name--folded' : 'folderList-item-name'
}
style={{borderColor: folderColor}}
>
{isFolded ? folderName.substring(0, 1) : folderName}
}>
<text style={{color: folderColor, paddingRight: '10px'}}>{isActive ? <i className='fa fa-folder-open-o' /> : <i className='fa fa-folder-o' />}</text>{isFolded ? _.truncate(folderName, {length: 1, omission: ''}) : folderName}
</span>
{(!isFolded && isNumber(noteCount)) &&
{(!isFolded && _.isNumber(noteCount)) &&
<span styleName='folderList-item-noteCount'>{noteCount}</span>
}
{isFolded &&

View File

@@ -5,37 +5,36 @@
.folderList-item
display flex
width 100%
height 26px
height 34px
background-color transparent
color $ui-inactive-text-color
padding 0
margin-bottom 5px
text-align left
border none
overflow ellipsis
font-size 12px
font-size 14px
&:first-child
margin-top 0
&:hover
color $ui-text-color
background-color alpha($ui-button--active-backgroundColor, 20%)
color #1EC38B;
background-color alpha($ui-button-default--active-backgroundColor, 20%)
transition background-color 0.15s
&:active
color $ui-text-color
background-color $ui-button--active-backgroundColor
color $$ui-button-default-color
background-color alpha($ui-button-default--active-backgroundColor, 20%)
.folderList-item--active
@extend .folderList-item
color $ui-text-color
background-color $ui-button--active-backgroundColor
color #1EC38B
background-color alpha($ui-button-default--active-backgroundColor, 20%)
&:hover
color $ui-text-color
background-color $ui-button--active-backgroundColor
color #1EC38B;
background-color alpha($ui-button-default--active-backgroundColor, 50%)
.folderList-item-name
display block
flex 1
padding 0 25px
padding 0 12px
height 26px
line-height 26px
border-width 0 0 0 2px
@@ -48,7 +47,7 @@
float right
line-height 26px
padding-right 15px
font-size 12px
font-size 13px
.folderList-item-tooltip
tooltip()
@@ -69,8 +68,28 @@
.folderList-item-name--folded
@extend .folderList-item-name
padding-left 12px
padding-left 7px
text
font-size 9px
body[data-theme="white"]
.folderList-item
color $ui-inactive-text-color
&:hover
color $ui-text-color
background-color alpha($ui-button--active-backgroundColor, 20%)
transition background-color 0.15s
&:active
color $ui-text-color
background-color $ui-button--active-backgroundColor
.folderList-item--active
@extend .folderList-item
color $ui-text-color
background-color $ui-button--active-backgroundColor
&:hover
color $ui-text-color
background-color alpha($ui-button--active-backgroundColor, 50%)
body[data-theme="dark"]
.folderList-item
@@ -86,7 +105,7 @@ body[data-theme="dark"]
color $ui-dark-text-color
background-color $ui-dark-button--active-backgroundColor
&:active
background-color $ui-dark-button--active-backgroundColor
background-color alpha($ui-dark-button--active-backgroundColor, 50%)
&:hover
color $ui-dark-text-color
background-color $ui-dark-button--active-backgroundColor
background-color alpha($ui-dark-button--active-backgroundColor, 50%)

View File

@@ -0,0 +1,24 @@
/**
* @fileoverview Micro component for showing StorageList
*/
import PropTypes from 'prop-types'
import React from 'react'
import styles from './StorageList.styl'
import CSSModules from 'browser/lib/CSSModules'
/**
* @param {Array} storgaeList
*/
const StorageList = ({storageList}) => (
<div styleName='storageList'>
{storageList.length > 0 ? storageList : (
<div styleName='storgaeList-empty'>No storage mount.</div>
)}
</div>
)
StorageList.propTypes = {
storgaeList: PropTypes.arrayOf(PropTypes.element).isRequired
}
export default CSSModules(StorageList, styles)

View File

@@ -0,0 +1,20 @@
.storageList
absolute left right
bottom 37px
top 180px
overflow-y auto
.storageList-empty
padding 0 10px
margin-top 15px
line-height 24px
color $ui-inactive-text-color
body[data-theme="dark"]
.storageList-empty
color $ui-dark-inactive-text-color
.root-folded
.storageList-empty
white-space nowrap
transform rotate(90deg)

View File

@@ -0,0 +1,28 @@
/**
* @fileoverview Micro component for showing TagList.
*/
import PropTypes from 'prop-types'
import React from 'react'
import styles from './TagListItem.styl'
import CSSModules from 'browser/lib/CSSModules'
/**
* @param {string} name
* @param {Function} handleClickTagListItem
* @param {bool} isActive
*/
const TagListItem = ({name, handleClickTagListItem, isActive}) => (
<button styleName={isActive ? 'tagList-item-active' : 'tagList-item'} onClick={() => handleClickTagListItem(name)}>
<span styleName='tagList-item-name'>
{`# ${name}`}
</span>
</button>
)
TagListItem.propTypes = {
name: PropTypes.string.isRequired,
handleClickTagListItem: PropTypes.func.isRequired
}
export default CSSModules(TagListItem, styles)

View File

@@ -0,0 +1,84 @@
.tagList-item
display flex
width 100%
height 26px
background-color transparent
color $ui-inactive-text-color
padding 0
margin-bottom 5px
text-align left
border none
overflow ellipsis
font-size 13px
&:first-child
margin-top 0
&:hover
color $ui-button-default-color
background-color alpha($ui-button-default--active-backgroundColor, 20%)
transition background-color 0.15s
&:active, &:active:hover
color $ui-button-default-color
background-color $ui-button-default--active-backgroundColor
.tagList-item-active
background-color $ui-button-default--active-backgroundColor
display flex
width 100%
height 26px
padding 0
margin-bottom 5px
text-align left
border none
overflow ellipsis
font-size 13px
color $ui-button-default-color
&:hover
background-color alpha($ui-button-default--active-backgroundColor, 60%)
transition 0.2s
.tagList-item-name
display block
flex 1
padding 0 15px
height 26px
line-height 26px
border-width 0 0 0 2px
border-style solid
border-color transparent
overflow hidden
text-overflow ellipsis
body[data-theme="white"]
.tagList-item
color $ui-inactive-text-color
&:hover
color $ui-text-color
background-color alpha($ui-button--active-backgroundColor, 20%)
&:active
color $ui-text-color
background-color $ui-button--active-backgroundColor
.tagList-item-active
background-color $ui-button--active-backgroundColor
color $ui-text-color
&:hover
background-color alpha($ui-button--active-backgroundColor, 60%)
body[data-theme="dark"]
.tagList-item
color $ui-dark-inactive-text-color
&:hover
color $ui-dark-text-color
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
&:active
color $ui-dark-text-color
background-color $ui-dark-button--active-backgroundColor
.tagList-item-active
background-color $ui-dark-button--active-backgroundColor
color $ui-dark-text-color
&:active
background-color alpha($ui-dark-button--active-backgroundColor, 50%)
&:hover
color $ui-dark-text-color
background-color alpha($ui-dark-button--active-backgroundColor, 50%)

View File

@@ -2,7 +2,8 @@
* @fileoverview Percentage of todo achievement.
*/
import React, { PropTypes } from 'react'
import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './TodoListPercentage.styl'

View File

@@ -1,24 +1,25 @@
.percentageBar
position absolute
top 58px
right: 0px
top 50px
right 0px
left 0px
background-color #DADFE1
width 100%
height: 15px
height: 17px
font-size: 12px
z-index 100
border-radius 2px
.progressBar
background-color: #6C7A89
height 15px
background-color: #1EC38B
height 17px
border-radius 2px
transition 0.3s
transition 0.4s cubic-bezier(0.4, 0.4, 0, 1)
.percentageText
color #f4f4f4
padding: 2px 43%
font-weight 600
body[data-theme="dark"]
.percentageBar

View File

@@ -0,0 +1,34 @@
/**
* @fileoverview Percentage of todo achievement.
*/
import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './TodoProcess.styl'
const TodoProcess = ({
todoStatus: {
total: totalTodo,
completed: completedTodo
}
}) => (
<div styleName='todo-process' style={{display: totalTodo > 0 ? '' : 'none'}}>
<div styleName='todo-process-text'>
<i className='fa fa-fw fa-check-square-o' />
{completedTodo} of {totalTodo}
</div>
<div styleName='todo-process-bar'>
<div styleName='todo-process-bar--inner' style={{width: parseInt(completedTodo / totalTodo * 100) + '%'}} />
</div>
</div>
)
TodoProcess.propTypes = {
todoStatus: {
total: PropTypes.number.isRequired,
completed: PropTypes.number.isRequired
}
}
export default CSSModules(TodoProcess, styles)

View File

@@ -0,0 +1,45 @@
.todo-process
font-size 12px
display flex
padding-top 15px
width 85%
.todo-process-text
display inline-block
padding-right 10px
white-space nowrap
text-overflow ellipsis
color $ui-inactive-text-color
i
color $ui-inactive-text-color
padding-right 5px
.todo-process-bar
display inline-block
margin auto
height 4px
border-radius 10px
background-color #DADFE1
border-radius 2px
flex-grow 1
border 1px solid alpha(#6C7A89, 10%)
.todo-process-bar--inner
height 100%
border-radius 5px
background-color #6C7A89
transition 0.3s
body[data-theme="dark"]
.todo-process
color $ui-dark-text-color
.todo-process-bar
background-color #363A3D
.todo-process-text
color $ui-inactive-text-color
.todo-process-bar--inner
background-color: alpha(#939395, 50%)

View File

@@ -118,7 +118,7 @@ hr
h1, h2, h3, h4, h5, h6
font-weight bold
h1
font-size 2.25em
font-size 2.55em
padding-bottom 0.3em
line-height 1.2em
border-bottom solid 1px borderColor
@@ -193,6 +193,7 @@ ol
&>li>ul, &>li>ol
margin 0
code
color #CC305F
padding 0.2em 0.4em
background-color #f7f7f7
border-radius 3px
@@ -268,6 +269,16 @@ table
border-color borderColor
&:last-child
border-right solid 1px borderColor
kbd
background-color #fafbfc
border solid 1px borderColor
border-bottom-color btnColor
border-radius 3px
box-shadow inset 0 -1px 0 #959da5
display inline-block
font-size .8em
line-height 1
padding 3px 5px
themeDarkBackground = darken(#21252B, 10%)
themeDarkText = #f9f9f9
@@ -316,3 +327,6 @@ body[data-theme="dark"]
border-color themeDarkTableBorder
&:last-child
border-right solid 1px themeDarkTableBorder
kbd
background-color themeDarkBorder
color themeDarkText

View File

@@ -64,7 +64,7 @@ $list-width = 250px
.result-nav-storageList
absolute bottom left right
top 80px + 32px + 10px + 10px
top 110px + 32px + 10px + 10px + 20px
overflow-y auto
.result-list

View File

@@ -5,6 +5,7 @@ import MarkdownPreview from 'browser/components/MarkdownPreview'
import MarkdownEditor from 'browser/components/MarkdownEditor'
import CodeEditor from 'browser/components/CodeEditor'
import CodeMirror from 'codemirror'
import { findStorage } from 'browser/lib/findStorage'
const electron = require('electron')
const { clipboard } = electron
@@ -55,7 +56,7 @@ class NoteDetail extends React.Component {
}
selectPriorSnippet () {
let { note } = this.props
const { note } = this.props
if (note.type === 'SNIPPET_NOTE' && note.snippets.length > 1) {
this.setState({
snippetIndex: (this.state.snippetIndex + note.snippets.length - 1) % note.snippets.length
@@ -64,7 +65,7 @@ class NoteDetail extends React.Component {
}
selectNextSnippet () {
let { note } = this.props
const { note } = this.props
if (note.type === 'SNIPPET_NOTE' && note.snippets.length > 1) {
this.setState({
snippetIndex: (this.state.snippetIndex + 1) % note.snippets.length
@@ -73,7 +74,7 @@ class NoteDetail extends React.Component {
}
saveToClipboard () {
let { note } = this.props
const { note } = this.props
if (note.type === 'MARKDOWN_NOTE') {
clipboard.writeText(note.content)
@@ -94,7 +95,7 @@ class NoteDetail extends React.Component {
}
render () {
let { note, config } = this.props
const { note, config } = this.props
if (note == null) {
return (
<div styleName='root' />
@@ -106,9 +107,11 @@ class NoteDetail extends React.Component {
let editorIndentSize = parseInt(config.editor.indentSize, 10)
if (!(editorFontSize > 0 && editorFontSize < 132)) editorIndentSize = 4
const storage = findStorage(note.storage)
if (note.type === 'SNIPPET_NOTE') {
let tabList = note.snippets.map((snippet, index) => {
let isActive = this.state.snippetIndex === index
const tabList = note.snippets.map((snippet, index) => {
const isActive = this.state.snippetIndex === index
return <div styleName={isActive
? 'tabList-item--active'
: 'tabList-item'
@@ -128,8 +131,8 @@ class NoteDetail extends React.Component {
</div>
})
let viewList = note.snippets.map((snippet, index) => {
let isActive = this.state.snippetIndex === index
const viewList = note.snippets.map((snippet, index) => {
const isActive = this.state.snippetIndex === index
let syntax = CodeMirror.findModeByName(pass(snippet.mode))
if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text')
@@ -143,6 +146,7 @@ class NoteDetail extends React.Component {
config={config}
value={snippet.content}
ref={'code-' + index}
storageKey={note.storage}
/>
: <CodeEditor styleName='tabView-content'
mode={snippet.mode}
@@ -192,6 +196,8 @@ class NoteDetail extends React.Component {
lineNumber={config.preview.lineNumber}
indentSize={editorIndentSize}
value={note.content}
showCopyNotification={config.ui.showCopyNotification}
storagePath={storage.path}
/>
)
}

View File

@@ -2,8 +2,8 @@
.root
absolute top bottom left right
left $note-detail-left-margin
right $note-detail-right-margin
bottom 30px
margin 0 25px
height 100%
width 365px
background-color $ui-noteDetail-backgroundColor

View File

@@ -18,18 +18,18 @@ class NoteList extends React.Component {
}
componentDidUpdate () {
let { index } = this.props
const { index } = this.props
if (index > -1) {
let list = this.refs.root
let item = list.childNodes[index]
const list = this.refs.root
const item = list.childNodes[index]
if (item == null) return null
let overflowBelow = item.offsetTop + item.clientHeight - list.clientHeight - list.scrollTop > 0
const overflowBelow = item.offsetTop + item.clientHeight - list.clientHeight - list.scrollTop > 0
if (overflowBelow) {
list.scrollTop = item.offsetTop + item.clientHeight - list.clientHeight
}
let overflowAbove = list.scrollTop > item.offsetTop
const overflowAbove = list.scrollTop > item.offsetTop
if (overflowAbove) {
list.scrollTop = item.offsetTop
}
@@ -44,7 +44,7 @@ class NoteList extends React.Component {
}
handleScroll (e) {
let { notes } = this.props
const { notes } = this.props
if (e.target.offsetHeight + e.target.scrollTop > e.target.scrollHeight - 100 && notes.length > this.state.range * 10 + 10) {
this.setState({
@@ -54,9 +54,9 @@ class NoteList extends React.Component {
}
render () {
let { notes, index } = this.props
const { notes, index } = this.props
let notesList = notes
const notesList = notes
.slice(0, 10 + 10 * this.state.range)
.map((note, _index) => {
const isActive = (index === _index)
@@ -70,7 +70,6 @@ class NoteList extends React.Component {
dateDisplay={dateDisplay}
key={key}
handleNoteClick={(e) => this.props.handleNoteClick(e, _index)}
handleNoteContextMenu={() => ''}
/>
)
})

View File

@@ -19,18 +19,18 @@ class StorageSection extends React.Component {
}
handleHeaderClick (e) {
let { storage } = this.props
const { storage } = this.props
this.props.handleStorageButtonClick(e, storage.key)
}
handleFolderClick (e, folder) {
let { storage } = this.props
const { storage } = this.props
this.props.handleFolderButtonClick(e, storage.key, folder.key)
}
render () {
let { storage, filter } = this.props
let folderList = storage.folders
const { storage, filter } = this.props
const folderList = storage.folders
.map(folder => (
<StorageItem
key={folder.key}

View File

@@ -1,8 +1,8 @@
import React, { PropTypes } from 'react'
import PropTypes from 'prop-types'
import React from 'react'
import ReactDOM from 'react-dom'
import { connect, Provider } from 'react-redux'
import _ from 'lodash'
import ipc from './ipcClient'
import store from './store'
import CSSModules from 'browser/lib/CSSModules'
import styles from './FinderMain.styl'
@@ -13,13 +13,14 @@ import SideNavFilter from 'browser/components/SideNavFilter'
import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig'
require('!!style!css!stylus?sourceMap!../main/global.styl')
require('../lib/customMeta')
require('./ipcClient.js')
const electron = require('electron')
const { remote } = electron
const { Menu } = remote
function hideFinder () {
let finderWindow = remote.getCurrentWindow()
const finderWindow = remote.getCurrentWindow()
if (global.process.platform === 'win32') {
finderWindow.blur()
finderWindow.hide()
@@ -95,7 +96,7 @@ class FinderMain extends React.Component {
if (e.keyCode === 13) {
this.refs.detail.saveToClipboard()
AwsMobileAnalyticsConfig.recordDynamitCustomEvent('COPY_FINDER')
AwsMobileAnalyticsConfig.recordDynamicCustomEvent('COPY_FINDER')
hideFinder()
e.preventDefault()
}
@@ -136,7 +137,7 @@ class FinderMain extends React.Component {
}
handleOnlySnippetCheckboxChange (e) {
let { filter } = this.state
const { filter } = this.state
filter.includeSnippet = e.target.checked
this.setState({
filter: filter,
@@ -147,7 +148,7 @@ class FinderMain extends React.Component {
}
handleOnlyMarkdownCheckboxChange (e) {
let { filter } = this.state
const { filter } = this.state
filter.includeMarkdown = e.target.checked
this.refs.list.resetScroll()
this.setState({
@@ -159,7 +160,7 @@ class FinderMain extends React.Component {
}
handleAllNotesButtonClick (e) {
let { filter } = this.state
const { filter } = this.state
filter.type = 'ALL'
this.refs.list.resetScroll()
this.setState({
@@ -171,7 +172,7 @@ class FinderMain extends React.Component {
}
handleStarredButtonClick (e) {
let { filter } = this.state
const { filter } = this.state
filter.type = 'STARRED'
this.refs.list.resetScroll()
this.setState({
@@ -183,7 +184,7 @@ class FinderMain extends React.Component {
}
handleStorageButtonClick (e, storage) {
let { filter } = this.state
const { filter } = this.state
filter.type = 'STORAGE'
filter.storage = storage
this.refs.list.resetScroll()
@@ -196,7 +197,7 @@ class FinderMain extends React.Component {
}
handleFolderButtonClick (e, storage, folder) {
let { filter } = this.state
const { filter } = this.state
filter.type = 'FOLDER'
filter.storage = storage
filter.folder = folder
@@ -218,12 +219,12 @@ class FinderMain extends React.Component {
}
render () {
let { data, config } = this.props
let { filter, search } = this.state
let storageList = []
for (let key in data.storageMap) {
let storage = data.storageMap[key]
let item = (
const { data, config } = this.props
const { filter, search } = this.state
const storageList = []
for (const key in data.storageMap) {
const storage = data.storageMap[key]
const item = (
<StorageSection
filter={filter}
storage={storage}
@@ -252,7 +253,7 @@ class FinderMain extends React.Component {
notes.push(data.noteMap[id])
})
} else {
for (let key in data.noteMap) {
for (const key in data.noteMap) {
notes.push(data.noteMap[key])
}
}
@@ -264,13 +265,13 @@ class FinderMain extends React.Component {
}
if (search.trim().length > 0) {
let needle = new RegExp(_.escapeRegExp(search.trim()), 'i')
const needle = new RegExp(_.escapeRegExp(search.trim()), 'i')
notes = notes.filter((note) => note.title.match(needle))
}
notes = notes
.sort((a, b) => new Date(b.updatedAt) - new Date(a.updatedAt))
let activeNote = notes[this.state.index]
const activeNote = notes[this.state.index]
this.noteCount = notes.length
return (

View File

@@ -10,7 +10,7 @@ nodeIpc.config.retry = 1500
nodeIpc.config.silent = true
function killFinder () {
let finderWindow = remote.getCurrentWindow()
const finderWindow = remote.getCurrentWindow()
finderWindow.removeAllListeners()
if (global.process.platform === 'darwin') {
// Only OSX has another app process.
@@ -21,7 +21,7 @@ function killFinder () {
}
function toggleFinder () {
let finderWindow = remote.getCurrentWindow()
const finderWindow = remote.getCurrentWindow()
if (global.process.platform === 'darwin') {
if (finderWindow.isVisible()) {
finderWindow.hide()
@@ -84,6 +84,8 @@ nodeIpc.connectTo(
const { config } = payload
if (config.ui.theme === 'dark') {
document.body.setAttribute('data-theme', 'dark')
} else if (config.ui.theme === 'white') {
document.body.setAttribute('data-theme', 'white')
} else {
document.body.setAttribute('data-theme', 'default')
}

View File

@@ -2,7 +2,7 @@ import { combineReducers, createStore } from 'redux'
import { routerReducer } from 'react-router-redux'
import { DEFAULT_CONFIG } from 'browser/main/lib/ConfigManager'
let defaultData = {
const defaultData = {
storageMap: {},
noteMap: {},
starredSet: [],
@@ -40,12 +40,12 @@ function config (state = DEFAULT_CONFIG, action) {
return state
}
let reducer = combineReducers({
const reducer = combineReducers({
data,
config,
routing: routerReducer
})
let store = createStore(reducer)
const store = createStore(reducer)
export default store

View File

@@ -38,15 +38,15 @@ class MutableMap {
}
map (cb) {
let result = []
for (let [key, value] of this._map) {
const result = []
for (const [key, value] of this._map) {
result.push(cb(value, key))
}
return result
}
toJS () {
let result = {}
const result = {}
for (let [key, value] of this._map) {
if (value instanceof MutableSet || value instanceof MutableMap) {
value = value.toJS()
@@ -85,7 +85,7 @@ class MutableSet {
}
map (cb) {
let result = []
const result = []
this._set.forEach(function (value, key) {
result.push(cb(value, key))
})

21
browser/lib/RcParser.js Normal file
View File

@@ -0,0 +1,21 @@
import path from 'path'
import sander from 'sander'
const BOOSTNOTERC = '.boostnoterc'
const homePath = global.process.env.HOME || global.process.env.USERPROFILE
const _boostnotercPath = path.join(homePath, BOOSTNOTERC)
export function parse (boostnotercPath = _boostnotercPath) {
if (!sander.existsSync(boostnotercPath)) return {}
try {
return JSON.parse(sander.readFileSync(boostnotercPath).toString())
} catch (e) {
console.warn(e)
console.warn('Your .boostnoterc is broken so it\'s not used.')
return {}
}
}
export default {
parse
}

View File

@@ -10,6 +10,7 @@ const themes = fs.readdirSync(themePath)
.map((themePath) => {
return themePath.substring(0, themePath.lastIndexOf('.'))
})
themes.splice(themes.indexOf('solarized'), 1, 'solarized dark', 'solarized light')
const consts = {
FOLDER_COLORS: [

View File

@@ -8,7 +8,7 @@ import moment from 'moment'
* @param {mixed}
* @return {string}
*/
export function getLastUpdated (date) {
export function formatDate (date) {
const m = moment(date)
if (!m.isValid()) {
throw Error('Invalid argument.')

View File

@@ -1,11 +1,11 @@
export function findNoteTitle (value) {
let splitted = value.split('\n')
const splitted = value.split('\n')
let title = null
let isInsideCodeBlock = false
splitted.some((line, index) => {
let trimmedLine = line.trim()
let trimmedNextLine = splitted[index + 1] === undefined ? '' : splitted[index + 1].trim()
const trimmedLine = line.trim()
const trimmedNextLine = splitted[index + 1] === undefined ? '' : splitted[index + 1].trim()
if (trimmedLine.match('```')) {
isInsideCodeBlock = !isInsideCodeBlock
}

View File

@@ -0,0 +1,14 @@
const _ = require('lodash')
export function findStorage (storageKey) {
const cachedStorageList = JSON.parse(localStorage.getItem('storages'))
if (!_.isArray(cachedStorageList)) throw new Error('Target storage doesn\'t exist.')
const storage = _.find(cachedStorageList, {key: storageKey})
if (storage === undefined) throw new Error('Target storage doesn\'t exist.')
return storage
}
export default {
findStorage
}

View File

@@ -0,0 +1,25 @@
export function getTodoStatus (content) {
const splitted = content.split('\n')
let numberOfTodo = 0
let numberOfCompletedTodo = 0
splitted.forEach((line) => {
const trimmedLine = line.trim()
if (trimmedLine.match(/^[\+\-\*] \[\s|x\] ./)) {
numberOfTodo++
}
if (trimmedLine.match(/^[\+\-\*] \[x\] ./)) {
numberOfCompletedTodo++
}
})
return {
total: numberOfTodo,
completed: numberOfCompletedTodo
}
}
export function getTodoPercentageOfCompleted (content) {
const state = getTodoStatus(content)
return Math.floor(state.completed / state.total * 100)
}

View File

@@ -3,11 +3,12 @@ import emoji from 'markdown-it-emoji'
import math from '@rokt33r/markdown-it-math'
import _ from 'lodash'
// FIXME We should not depend on global variable.
const katex = window.katex
function createGutter (str) {
let lc = (str.match(/\n/g) || []).length
let lines = []
const lc = (str.match(/\n/g) || []).length
const lines = []
for (let i = 1; i <= lc; i++) {
lines.push('<span class="CodeMirror-linenumber">' + i + '</span>')
}
@@ -59,12 +60,24 @@ md.use(math, {
})
md.use(require('markdown-it-imsize'))
md.use(require('markdown-it-footnote'))
md.use(require('markdown-it-multimd-table'))
md.use(require('markdown-it-named-headers'), {
slugify: (header) => {
return encodeURI(header.trim()
.replace(/[\]\[\!\"\#\$\%\&\'\(\)\*\+\,\.\/\:\;\<\=\>\?\@\\\^\_\{\|\}\~]/g, '')
.replace(/\s+/g, '-'))
.replace(/\-+$/, '')
}
})
md.use(require('markdown-it-kbd'))
md.use(require('markdown-it-plantuml'))
// Override task item
md.block.ruler.at('paragraph', function (state, startLine/*, endLine */) {
let content, terminate, i, l, token
let nextLine = startLine + 1
let terminatorRules = state.md.block.ruler.getRules('paragraph')
let endLine = state.lineMax
const terminatorRules = state.md.block.ruler.getRules('paragraph')
const endLine = state.lineMax
// jump line-by-line until empty one or EOF
for (; nextLine < endLine && !state.isEmpty(nextLine); nextLine++) {
@@ -94,7 +107,7 @@ md.block.ruler.at('paragraph', function (state, startLine/*, endLine */) {
token.map = [ startLine, state.line ]
if (state.parentType === 'list') {
let match = content.match(/^\[( |x)\] ?(.+)/i)
const match = content.match(/^\[( |x)\] ?(.+)/i)
if (match) {
content = `<label class='taskListItem' for='checkbox-${startLine + 1}'><input type='checkbox'${match[1] !== ' ' ? ' checked' : ''} id='checkbox-${startLine + 1}'/> ${content.substring(4, content.length)}</label>`
}
@@ -111,7 +124,7 @@ md.block.ruler.at('paragraph', function (state, startLine/*, endLine */) {
})
// Add line number attribute for scrolling
let originalRender = md.renderer.render
const originalRender = md.renderer.render
md.renderer.render = function render (tokens, options, env) {
tokens.forEach((token) => {
switch (token.type) {
@@ -122,45 +135,23 @@ md.renderer.render = function render (tokens, options, env) {
token.attrPush(['data-line', token.map[0]])
}
})
let result = originalRender.call(md.renderer, tokens, options, env)
const result = originalRender.call(md.renderer, tokens, options, env)
return result
}
// FIXME We should not depend on global variable.
window.md = md
function strip (input) {
var output = input
try {
output = output
.replace(/^([\s\t]*)([\*\-\+]|\d\.)\s+/gm, '$1')
.replace(/\n={2,}/g, '\n')
.replace(/~~/g, '')
.replace(/`{3}.*\n/g, '')
.replace(/<(.*?)>/g, '$1')
.replace(/^[=\-]{2,}\s*$/g, '')
.replace(/\[\^.+?\](: .*?$)?/g, '')
.replace(/\s{0,2}\[.*?\]: .*?$/g, '')
.replace(/!\[.*?\][\[\(].*?[\]\)]/g, '')
.replace(/\[(.*?)\][\[\(].*?[\]\)]/g, '$1')
.replace(/>/g, '')
.replace(/^\s{1,2}\[(.*?)\]: (\S+)( ".*?")?\s*$/g, '')
.replace(/^#{1,6}\s*([^#]*)\s*(#{1,6})?/gm, '$1')
.replace(/([\*_]{1,3})(\S.*?\S)\1/g, '$2')
.replace(/(`{3,})(.*?)\1/gm, '$2')
.replace(/^-{3,}\s*$/g, '')
.replace(/`(.+?)`/g, '$1')
.replace(/\n{2,}/g, '\n\n')
} catch (e) {
console.error(e)
return input
}
return output
function normalizeLinkText (linkText) {
return md.normalizeLinkText(linkText)
}
const markdown = {
render: function markdown (content) {
if (!_.isString(content)) content = ''
return md.render(content)
const renderedContent = md.render(content)
return renderedContent
},
strip
normalizeLinkText
}
export default markdown

View File

@@ -0,0 +1,39 @@
/**
* @fileoverview Text trimmer for markdown note.
*/
/**
* @param {string} input
* @return {string}
*/
export function strip (input) {
let output = input
try {
output = output
.replace(/^([\s\t]*)([\*\-\+]|\d+\.)\s+/gm, '$1')
.replace(/\n={2,}/g, '\n')
.replace(/~~/g, '')
.replace(/`{3}.*\n/g, '')
.replace(/<(.*?)>/g, '$1')
.replace(/^[=\-]{2,}\s*$/g, '')
.replace(/\[\^.+?\](: .*?$)?/g, '')
.replace(/\s{0,2}\[.*?\]: .*?$/g, '')
.replace(/!\[.*?\][\[\(].*?[\]\)]/g, '')
.replace(/\[(.*?)\][\[\(].*?[\]\)]/g, '$1')
.replace(/>/g, '')
.replace(/^\s{1,2}\[(.*?)\]: (\S+)( ".*?")?\s*$/g, '')
.replace(/^#{1,6}\s*([^#]*)\s*(#{1,6})?/gm, '$1')
.replace(/(`{3,})(.*?)\1/gm, '$2')
.replace(/^-{3,}\s*$/g, '')
.replace(/`(.+?)`/g, '$1')
.replace(/\n{2,}/g, '\n\n')
} catch (e) {
console.error(e)
return input
}
return output
}
export default {
strip
}

View File

@@ -1,22 +1,22 @@
import _ from 'lodash'
export default function searchFromNotes (data, search) {
let notes = data.noteMap.map((note) => note)
export default function searchFromNotes (notes, search) {
if (search.trim().length === 0) return []
let searchBlocks = search.split(' ')
const searchBlocks = search.split(' ').filter(block => { return block !== '' })
let foundNotes = findByWord(notes, searchBlocks[0])
searchBlocks.forEach((block) => {
foundNotes = findByWord(foundNotes, block)
if (block.match(/^#.+/)) {
notes = findByTag(notes, block)
} else {
notes = findByWord(notes, block)
foundNotes = foundNotes.concat(findByTag(notes, block))
}
})
return notes
return foundNotes
}
function findByTag (notes, block) {
const tag = block.match(/#(.+)/)[1]
let regExp = new RegExp(_.escapeRegExp(tag), 'i')
const regExp = new RegExp(_.escapeRegExp(tag), 'i')
return notes.filter((note) => {
if (!_.isArray(note.tags)) return false
return note.tags.some((_tag) => {
@@ -26,7 +26,7 @@ function findByTag (notes, block) {
}
function findByWord (notes, block) {
let regExp = new RegExp(_.escapeRegExp(block), 'i')
const regExp = new RegExp(_.escapeRegExp(block), 'i')
return notes.filter((note) => {
if (_.isArray(note.tags) && note.tags.some((_tag) => {
return _tag.match(regExp)

View File

@@ -1,5 +1,8 @@
.root
absolute top bottom right
display flex
align-items center
justify-content center
.empty
height 320px
@@ -8,8 +11,9 @@
.empty-message
width 100%
font-size 42px
line-height 72px
font-size 36px
font-weight 600
line-height 56px
text-align center
color $ui-inactive-text-color

View File

@@ -3,7 +3,9 @@
*/
// Margin on the left side and the right side for NoteDetail component.
$note-detail-left-margin = 25px
$note-detail-right-margin = 25px
$note-detail-left-margin = 100px
$note-detail-right-margin = 120px
$snippet-note-detail-left-margin = 60px
$snippet-note-detail-right-margin = 80px
$note-detail-box-shadow = 2px 0 15px -8px #b1b1b1 inset

View File

@@ -1,4 +1,5 @@
import React, { PropTypes } from 'react'
import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './FolderSelect.styl'
import _ from 'lodash'
@@ -73,8 +74,8 @@ class FolderSelect extends React.Component {
case 9:
if (e.shiftKey) {
e.preventDefault()
let tabbable = document.querySelectorAll('a:not([disabled]), button:not([disabled]), input[type=text]:not([disabled]), [tabindex]:not([disabled]):not([tabindex="-1"])')
let previousEl = tabbable[Array.prototype.indexOf.call(tabbable, this.refs.root) - 1]
const tabbable = document.querySelectorAll('a:not([disabled]), button:not([disabled]), input[type=text]:not([disabled]), [tabindex]:not([disabled]):not([tabindex="-1"])')
const previousEl = tabbable[Array.prototype.indexOf.call(tabbable, this.refs.root) - 1]
if (previousEl != null) previousEl.focus()
}
}
@@ -89,9 +90,9 @@ class FolderSelect extends React.Component {
}
handleSearchInputChange (e) {
let { folders } = this.props
let search = this.refs.search.value
let optionIndex = search.length > 0
const { folders } = this.props
const search = this.refs.search.value
const optionIndex = search.length > 0
? _.findIndex(folders, (folder) => {
return folder.name.match(new RegExp('^' + _.escapeRegExp(search), 'i'))
})
@@ -129,7 +130,7 @@ class FolderSelect extends React.Component {
nextOption () {
let { optionIndex } = this.state
let { folders } = this.props
const { folders } = this.props
optionIndex++
if (optionIndex >= folders.length) optionIndex = 0
@@ -140,7 +141,7 @@ class FolderSelect extends React.Component {
}
previousOption () {
let { folders } = this.props
const { folders } = this.props
let { optionIndex } = this.state
optionIndex--
@@ -152,10 +153,10 @@ class FolderSelect extends React.Component {
}
selectOption () {
let { folders } = this.props
let optionIndex = this.state.optionIndex
const { folders } = this.props
const optionIndex = this.state.optionIndex
let folder = folders[optionIndex]
const folder = folders[optionIndex]
if (folder != null) {
this.setState({
status: 'FOCUS'
@@ -184,10 +185,10 @@ class FolderSelect extends React.Component {
}
render () {
let { className, data, value } = this.props
let splitted = value.split('-')
let storageKey = splitted.shift()
let folderKey = splitted.shift()
const { className, data, value } = this.props
const splitted = value.split('-')
const storageKey = splitted.shift()
const folderKey = splitted.shift()
let options = []
data.storageMap.forEach((storage, index) => {
storage.folders.forEach((folder) => {
@@ -198,14 +199,14 @@ class FolderSelect extends React.Component {
})
})
let currentOption = options.filter((option) => option.storage.key === storageKey && option.folder.key === folderKey)[0]
const currentOption = options.filter((option) => option.storage.key === storageKey && option.folder.key === folderKey)[0]
if (this.state.search.trim().length > 0) {
let filter = new RegExp('^' + _.escapeRegExp(this.state.search), 'i')
const filter = new RegExp('^' + _.escapeRegExp(this.state.search), 'i')
options = options.filter((option) => filter.test(option.folder.name))
}
let optionList = options
const optionList = options
.map((option, index) => {
return (
<div styleName={index === this.state.optionIndex
@@ -259,12 +260,11 @@ class FolderSelect extends React.Component {
{optionList}
</div>
</div>
: <div styleName='idle'>
: <div styleName='idle' style={{color: currentOption.folder.color}}>
<div styleName='idle-label'>
<span styleName='idle-label-name'
style={{color: currentOption.folder.color}}
>
{currentOption.folder.name} /
<i className='fa fa-folder' />
<span styleName='idle-label-name'>
{currentOption.folder.name}
</span>
</div>
</div>

View File

@@ -1,21 +1,22 @@
.root
position relative
border solid 1px transparent
line-height 34px
vertical-align middle
border-radius 2px
transition 0.15s
user-select none
margin-right 10px
&:hover
background-color $ui-button--hover-backgroundColor
.root--search, .root--focus
@extend .root
background-color $ui-noteDetail-backgroundColor = #F4F4F4
background-color $ui-noteDetail-backgroundColor = #fff
border-color $ui-input--focus-borderColor
width 100px
width 154px
height 30px
&:hover
border-color $ui-input--focus-borderColor
border-color $ui-input--focus-borderColor = #fff
.idle
position relative
@@ -24,13 +25,16 @@
.idle-label
right 20px
overflow ellipsis
display flex
align-items center
.idle-label-name
font-size 14px
padding 2px
font-size 13px
font-weight 600
margin-left 4px
.idle-label-name-surfix
font-size 10px
font-size 15px
color $ui-inactive-text-color
margin-left 5px
.idle-caret
@@ -38,40 +42,42 @@
height 34px
width 20px
line-height 34px
.search
absolute top left right bottom
line-height 34px
.search-input
vertical-align middle
position relative
top -2px
top 0
font-size 14px
outline none
border none
height 20px
line-height 20px
width 100%
background-color transparent
padding 0 10px
.search-optionList
position fixed
position absolute
top 30px
max-height 450px
min-width 150px
overflow auto
z-index 200
border $ui-border
background-color white
border-radius 2px
padding 10px 6px
.search-optionList-item
width 140px
height 34px
width 250px
display flex
align-items center
box-sizing border-box
padding 0 5px
padding 0
margin-bottom 10px
overflow ellipsis
cursor pointer
&:hover
background-color $ui-button--hover-backgroundColor
background-color $ui-button--hover-backgroundColor
.search-optionList-item--active
@extend .search-optionList-item
@@ -81,13 +87,17 @@
background-color $ui-button--active-backgroundColor
color $ui-button--active-color
.search-optionList-item-name
border-left solid 2px transparent
padding 2px 5px
border-left solid 3px transparent
padding 6px
.search-optionList-item-name-surfix
font-size 10px
color $ui-inactive-text-color
margin-left 5px
body[data-theme="dark"]
.root
color $ui-dark-text-color

View File

@@ -0,0 +1,20 @@
import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './InfoButton.styl'
const InfoButton = ({
onClick
}) => (
<button styleName='control-infoButton'
onClick={(e) => onClick(e)}
>
<img className='infoButton' src='../resources/icon/icon-info.svg' />
</button>
)
InfoButton.propTypes = {
onClick: PropTypes.func.isRequired
}
export default CSSModules(InfoButton, styles)

View File

@@ -0,0 +1,11 @@
.control-infoButton
top 10px
margin-bottom 10px
topBarButtonLight()
.infoButton
padding 0px
body[data-theme="dark"]
.control-infoButton
topBarButtonDark()

View File

@@ -0,0 +1,91 @@
import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './InfoPanel.styl'
const InfoPanel = ({
storageName, folderName, noteLink, updatedAt, createdAt, exportAsMd, exportAsTxt, wordCount, letterCount, type, print
}) => (
<div className='infoPanel' styleName='control-infoButton-panel' style={{display: 'none'}}>
<div>
<p styleName='modification-date'>{updatedAt}</p>
<p styleName='modification-date-desc'>MODIFICATION DATE</p>
</div>
<hr />
{type === 'SNIPPET_NOTE'
? ''
: <div styleName='count-wrap'>
<div styleName='count-number'>
<p styleName='infoPanel-defaul-count'>{wordCount}</p>
<p styleName='infoPanel-sub-count'>Words</p>
</div>
<div styleName='count-number'>
<p styleName='infoPanel-defaul-count'>{letterCount}</p>
<p styleName='infoPanel-sub-count'>Letters</p>
</div>
</div>
}
{type === 'SNIPPET_NOTE'
? ''
: <hr />
}
<div>
<p styleName='infoPanel-default'>{storageName}</p>
<p styleName='infoPanel-sub'>STORAGE</p>
</div>
<div>
<p styleName='infoPanel-default'>{folderName}</p>
<p styleName='infoPanel-sub'>FOLDER</p>
</div>
<div>
<p styleName='infoPanel-default'>{createdAt}</p>
<p styleName='infoPanel-sub'>CREATION DATE</p>
</div>
<div>
<input styleName='infoPanel-noteLink' value={noteLink} onClick={(e) => { e.target.select() }} />
<p styleName='infoPanel-sub'>NOTE LINK</p>
</div>
<hr />
<div id='export-wrap'>
<button styleName='export--enable' onClick={(e) => exportAsMd(e)}>
<i className='fa fa-file-code-o fa-fw' />
<p>.md</p>
</button>
<button styleName='export--enable' onClick={(e) => exportAsTxt(e)}>
<i className='fa fa-file-text-o fa-fw' />
<p>.txt</p>
</button>
<button styleName='export--enable' onClick={(e) => print(e)}>
<i className='fa fa-print fa-fw' />
<p>Print</p>
</button>
</div>
</div>
)
InfoPanel.propTypes = {
storageName: PropTypes.string.isRequired,
folderName: PropTypes.string.isRequired,
noteLink: PropTypes.string.isRequired,
updatedAt: PropTypes.string.isRequired,
createdAt: PropTypes.string.isRequired,
exportAsMd: PropTypes.func.isRequired,
exportAsTxt: PropTypes.func.isRequired,
wordCount: PropTypes.number,
letterCount: PropTypes.number,
type: PropTypes.string.isRequired,
print: PropTypes.func.isRequired
}
export default CSSModules(InfoPanel, styles)

View File

@@ -0,0 +1,163 @@
.control-infoPanel
position fixed
pointer-events none
top 50px
z-index 200
line-height normal
border-radius 4px
opacity 0
transition 0.2s
.control-infoButton-panel
z-index 200
margin-top 0px
right 0
position absolute
padding 20px 25px 0 25px
width 300px
height 350px
overflow auto
background-color $ui-noteList-backgroundColor
box-shadow 2px 12px 15px 2px rgba(0, 0, 0, 0.1), 2px 1px 50px 2px rgba(0, 0, 0, 0.1)
border-radius 2px
.modification-date
font-size 18px
line-height 30px
color $ui-text-color
.modification-date-desc
font-size 18px
color $ui-inactive-text-color
.control-infoButton-panel-trash
z-index 200
margin-top 0px
right 0px
position absolute
padding 20px 25px 0 25px
width 300px
background-color $ui-noteList-backgroundColor
box-shadow 2px 12px 15px 2px rgba(0, 0, 0, 0.1), 2px 1px 50px 2px rgba(0, 0, 0, 0.1)
border-radius 2px
.count-wrap
display flex
position relative
width 100%
.count-number
position relative
display block
width 50%
overflow hidden
margin 0
padding 0
.infoPanel-defaul-count
font-size 16px
line-height 30px
color $ui-text-color
.infoPanel-sub-count
font-size 16px
color $ui-inactive-text-color
padding-bottom 8px
.infoPanel-default
font-size 14px
line-height 30px
color $ui-text-color
.infoPanel-sub
font-size 14px
color $ui-inactive-text-color
padding-bottom 8px
.infoPanel-noteLink
padding-right 5px
width 200px
height 25px
margin-bottom 6px
.infoPanel-trash
color #EA4447
font-weight 600
font-size 14px
width 70px
background-color rgba(226,33,113,0.1)
border none
outline none
border-radius 2px
margin-right 5px
padding 2px 5px
[id=export-wrap]
height 90px
display flex
justify-content center
margin 20px 0 10px 0
button
outline none
font-size 48px
color #A0A0A0
background-color transparent
border none
margin 0 5px
border-radius 5px
&:hover
transition 0.2s
background-color alpha($ui-button--hover-backgroundColor, 30%)
color $ui-inactive-text-color
p
font-size 13px
color #A0A0A0
font-weight light
&:hover
color $ui-inactive-text-color
.export--enable
cursor pointer
.export--unable
cursor not-allowed
body[data-theme="dark"]
.control-infoButton-panel
background-color $ui-dark-noteList-backgroundColor
.control-infoButton-panel-trash
background-color $ui-dark-noteList-backgroundColor
.modification-date
color $ui-dark-text-color
.modification-date-desc
color $ui-inactive-text-color
.infoPanel-defaul-count
color $ui-dark-text-color
.infoPanel-sub-count
color $ui-inactive-text-color
.infoPanel-default
color $ui-dark-text-color
.infoPanel-sub
color $ui-inactive-text-color
.infoPanel-noteLink
background-color alpha($ui-dark-borderColor, 60%)
color $ui-dark-text-color
[id=export-wrap]
button
color $ui-dark-inactive-text-color
&:hover
background-color alpha($ui-dark-borderColor, 20%)
color $ui-dark-text-color
p
color $ui-dark-inactive-text-color
&:hover
color $ui-dark-text-color

View File

@@ -0,0 +1,60 @@
import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './InfoPanel.styl'
const InfoPanelTrashed = ({
storageName, folderName, updatedAt, createdAt, exportAsMd, exportAsTxt
}) => (
<div className='infoPanel' styleName='control-infoButton-panel-trash' style={{display: 'none'}}>
<div>
<p styleName='modification-date'>{updatedAt}</p>
<p styleName='modification-date-desc'>MODIFICATION DATE</p>
</div>
<hr />
<div>
<p styleName='infoPanel-default'>{storageName}</p>
<p styleName='infoPanel-sub'>STORAGE</p>
</div>
<div>
<p styleName='infoPanel-default'><text styleName='infoPanel-trash'>Trash</text>{folderName}</p>
<p styleName='infoPanel-sub'>FOLDER</p>
</div>
<div>
<p styleName='infoPanel-default'>{createdAt}</p>
<p styleName='infoPanel-sub'>CREATION DATE</p>
</div>
<div id='export-wrap'>
<button styleName='export--enable' onClick={(e) => exportAsMd(e)}>
<i className='fa fa-file-code-o fa-fw' />
<p>.md</p>
</button>
<button styleName='export--enable' onClick={(e) => exportAsTxt(e)}>
<i className='fa fa-file-text-o fa-fw' />
<p>.txt</p>
</button>
<button styleName='export--unable'>
<i className='fa fa-file-pdf-o fa-fw' />
<p>.pdf</p>
</button>
</div>
</div>
)
InfoPanelTrashed.propTypes = {
storageName: PropTypes.string.isRequired,
folderName: PropTypes.string.isRequired,
updatedAt: PropTypes.string.isRequired,
createdAt: PropTypes.string.isRequired,
exportAsMd: PropTypes.func.isRequired,
exportAsTxt: PropTypes.func.isRequired
}
export default CSSModules(InfoPanelTrashed, styles)

View File

@@ -1,27 +0,0 @@
/**
* @fileoverview Component for show updated date of the detail.
*/
import React, { PropTypes } from 'react'
import { getLastUpdated } from 'browser/lib/date-formatter'
import CSSModules from 'browser/lib/CSSModules'
import styles from './LastUpdatedString.styl'
const LastUpdatedString = ({ date }) => {
let text = ''
try {
text = `Last updated at ${getLastUpdated(date)}`
} catch (e) {
text = ''
}
return (
<p styleName='info-right-date'>{text}</p>
)
}
LastUpdatedString.propTypes = {
date: PropTypes.string
}
export default CSSModules(LastUpdatedString, styles)

View File

@@ -1,10 +0,0 @@
.info-right-date
display inline
line-height 24px
padding-right 25px
font-size 11px
color $ui-button-color
body[data-theme="dark"]
.info-right-date
color $ui-dark-button-color

View File

@@ -1,4 +1,5 @@
import React, { PropTypes } from 'react'
import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './MarkdownNoteDetail.styl'
import MarkdownEditor from 'browser/components/MarkdownEditor'
@@ -9,15 +10,23 @@ import FolderSelect from './FolderSelect'
import dataApi from 'browser/main/lib/dataApi'
import { hashHistory } from 'react-router'
import ee from 'browser/main/lib/eventEmitter'
import markdown from 'browser/lib/markdown'
import markdown from 'browser/lib/markdownTextHelper'
import StatusBar from '../StatusBar'
import _ from 'lodash'
import { findNoteTitle } from 'browser/lib/findNoteTitle'
import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig'
import TrashButton from './TrashButton'
import PermanentDeleteButton from './PermanentDeleteButton'
import InfoButton from './InfoButton'
import InfoPanel from './InfoPanel'
import InfoPanelTrashed from './InfoPanelTrashed'
import { formatDate } from 'browser/lib/date-formatter'
import { getTodoPercentageOfCompleted } from 'browser/lib/getTodoStatus'
import striptags from 'striptags'
const electron = require('electron')
const { remote } = electron
const { Menu, MenuItem, dialog } = remote
const { dialog } = remote
class MarkdownNoteDetail extends React.Component {
constructor (props) {
@@ -52,7 +61,7 @@ class MarkdownNoteDetail extends React.Component {
note: Object.assign({}, nextProps.note)
}, () => {
this.refs.content.reload()
this.refs.tags.reset()
if (this.refs.tags) this.refs.tags.reset()
})
}
}
@@ -65,30 +74,12 @@ class MarkdownNoteDetail extends React.Component {
ee.off('topbar:togglelockbutton', this.toggleLockButton)
}
getPercentageOfCompleteTodo (noteContent) {
let splitted = noteContent.split('\n')
let numberOfTodo = 0
let numberOfCompletedTodo = 0
splitted.forEach((line) => {
let trimmedLine = line.trim()
if (trimmedLine.match(/^[\+\-\*] \[\s|x\] ./)) {
numberOfTodo++
}
if (trimmedLine.match(/^[\+\-\*] \[x\] ./)) {
numberOfCompletedTodo++
}
})
return Math.floor(numberOfCompletedTodo / numberOfTodo * 100)
}
handleChange (e) {
let { note } = this.state
const { note } = this.state
note.content = this.refs.content.value
note.tags = this.refs.tags.value
note.title = markdown.strip(findNoteTitle(note.content))
if (this.refs.tags) note.tags = this.refs.tags.value
note.title = markdown.strip(striptags(findNoteTitle(note.content)))
note.updatedAt = new Date()
this.setState({
@@ -106,7 +97,7 @@ class MarkdownNoteDetail extends React.Component {
}
saveNow () {
let { note, dispatch } = this.props
const { note, dispatch } = this.props
clearTimeout(this.saveQueue)
this.saveQueue = null
@@ -117,16 +108,16 @@ class MarkdownNoteDetail extends React.Component {
type: 'UPDATE_NOTE',
note: note
})
AwsMobileAnalyticsConfig.recordDynamitCustomEvent('EDIT_NOTE')
AwsMobileAnalyticsConfig.recordDynamicCustomEvent('EDIT_NOTE')
})
}
handleFolderChange (e) {
let { note } = this.state
let value = this.refs.folder.value
let splitted = value.split('-')
let newStorageKey = splitted.shift()
let newFolderKey = splitted.shift()
const { note } = this.state
const value = this.refs.folder.value
const splitted = value.split('-')
const newStorageKey = splitted.shift()
const newFolderKey = splitted.shift()
dataApi
.moveNote(note.storage, note.key, newStorageKey, newFolderKey)
@@ -135,7 +126,7 @@ class MarkdownNoteDetail extends React.Component {
isMovingNote: true,
note: Object.assign({}, newNote)
}, () => {
let { dispatch, location } = this.props
const { dispatch, location } = this.props
dispatch({
type: 'MOVE_NOTE',
originNote: note,
@@ -155,8 +146,8 @@ class MarkdownNoteDetail extends React.Component {
}
handleStarButtonClick (e) {
let { note } = this.state
if (!note.isStarred) AwsMobileAnalyticsConfig.recordDynamitCustomEvent('ADD_STAR')
const { note } = this.state
if (!note.isStarred) AwsMobileAnalyticsConfig.recordDynamicCustomEvent('ADD_STAR')
note.isStarred = !note.isStarred
@@ -171,19 +162,31 @@ class MarkdownNoteDetail extends React.Component {
}
handleDeleteButtonClick (e) {
let index = dialog.showMessageBox(remote.getCurrentWindow(), {
type: 'warning',
message: 'Delete a note',
detail: 'This work cannot be undone.',
buttons: ['Confirm', 'Cancel']
})
if (index === 0) {
let { note, dispatch } = this.props
exportAsMd () {
ee.emit('export:save-md')
}
exportAsTxt () {
ee.emit('export:save-text')
}
handleTrashButtonClick (e) {
const { note } = this.state
const { isTrashed } = note
if (isTrashed) {
const dialogueButtonIndex = dialog.showMessageBox(remote.getCurrentWindow(), {
type: 'warning',
message: 'Confirm note deletion',
detail: 'This will permanently remove this note.',
buttons: ['Confirm', 'Cancel']
})
if (dialogueButtonIndex === 1) return
const { note, dispatch } = this.props
dataApi
.deleteNote(note.storage, note.key)
.then((data) => {
let dispatchHandler = () => {
const dispatchHandler = () => {
dispatch({
type: 'DELETE_NOTE',
storageKey: data.storageKey,
@@ -191,9 +194,31 @@ class MarkdownNoteDetail extends React.Component {
})
}
ee.once('list:moved', dispatchHandler)
ee.emit('list:next')
})
} else {
note.isTrashed = true
this.setState({
note
}, () => {
this.save()
})
}
ee.emit('list:next')
}
handleUndoButtonClick (e) {
const { note } = this.state
note.isTrashed = false
this.setState({
note
}, () => {
this.save()
this.refs.content.reload()
ee.emit('list:next')
})
}
handleFullScreenButton (e) {
@@ -208,7 +233,7 @@ class MarkdownNoteDetail extends React.Component {
}
getToggleLockButton () {
return this.state.isLocked ? 'fa-lock' : 'fa-unlock-alt'
return this.state.isLocked ? '../resources/icon/icon-lock.svg' : '../resources/icon/icon-unlock.svg'
}
handleDeleteKeyDown (e) {
@@ -228,76 +253,131 @@ class MarkdownNoteDetail extends React.Component {
this.focus()
}
handleInfoButtonClick (e) {
const infoPanel = document.querySelector('.infoPanel')
if (infoPanel.style) infoPanel.style.display = infoPanel.style.display === 'none' ? 'inline' : 'none'
}
print (e) {
ee.emit('print')
}
render () {
let { data, config } = this.props
let { note } = this.state
const { data, config, location } = this.props
const { note } = this.state
const storageKey = note.storage
const folderKey = note.folder
const options = []
data.storageMap.forEach((storage, index) => {
storage.folders.forEach((folder) => {
options.push({
storage: storage,
folder: folder
})
})
})
const currentOption = options.filter((option) => option.storage.key === storageKey && option.folder.key === folderKey)[0]
const trashTopBar = <div styleName='info'>
<div styleName='info-left'>
<i styleName='undo-button'
className='fa fa-undo fa-fw'
onClick={(e) => this.handleUndoButtonClick(e)}
/>
</div>
<div styleName='info-right'>
<PermanentDeleteButton onClick={(e) => this.handleTrashButtonClick(e)} />
<InfoButton
onClick={(e) => this.handleInfoButtonClick(e)}
/>
<InfoPanelTrashed
storageName={currentOption.storage.name}
folderName={currentOption.folder.name}
updatedAt={formatDate(note.updatedAt)}
createdAt={formatDate(note.createdAt)}
exportAsMd={this.exportAsMd}
exportAsTxt={this.exportAsTxt}
/>
</div>
</div>
const detailTopBar = <div styleName='info'>
<div styleName='info-left'>
<div styleName='info-left-top'>
<FolderSelect styleName='info-left-top-folderSelect'
value={this.state.note.storage + '-' + this.state.note.folder}
ref='folder'
data={data}
onChange={(e) => this.handleFolderChange(e)}
/>
</div>
<TagSelect
ref='tags'
value={this.state.note.tags}
onChange={(e) => this.handleChange(e)}
/>
<TodoListPercentage
percentageOfTodo={getTodoPercentageOfCompleted(note.content)}
/>
</div>
<div styleName='info-right'>
<InfoButton
onClick={(e) => this.handleInfoButtonClick(e)}
/>
<StarButton
onClick={(e) => this.handleStarButtonClick(e)}
isActive={note.isStarred}
/>
{(() => {
const imgSrc = `${this.getToggleLockButton()}`
const lockButtonComponent =
<button styleName='control-lockButton'
onFocus={(e) => this.handleFocus(e)}
onMouseDown={(e) => this.handleLockButtonMouseDown(e)}
>
<img styleName='iconInfo' src={imgSrc} />
</button>
return (
this.state.isLockButtonShown ? lockButtonComponent : ''
)
})()}
<button styleName='control-fullScreenButton'
onMouseDown={(e) => this.handleFullScreenButton(e)}
>
<img styleName='iconInfo' src='../resources/icon/icon-sidebar.svg' />
</button>
<TrashButton onClick={(e) => this.handleTrashButtonClick(e)} />
<InfoPanel
storageName={currentOption.storage.name}
folderName={currentOption.folder.name}
noteLink={`[${note.title}](${location.query.key})`}
updatedAt={formatDate(note.updatedAt)}
createdAt={formatDate(note.createdAt)}
exportAsMd={this.exportAsMd}
exportAsTxt={this.exportAsTxt}
wordCount={note.content.split(' ').length}
letterCount={note.content.replace(/\r?\n/g, '').length}
type={note.type}
print={this.print}
/>
</div>
</div>
return (
<div className='NoteDetail'
style={this.props.style}
styleName='root'
>
<div styleName='info'>
<div styleName='info-left'>
<StarButton styleName='info-left-button'
onClick={(e) => this.handleStarButtonClick(e)}
isActive={note.isStarred}
/>
<div styleName='info-left-top'>
<FolderSelect styleName='info-left-top-folderSelect'
value={this.state.note.storage + '-' + this.state.note.folder}
ref='folder'
data={data}
onChange={(e) => this.handleFolderChange(e)}
/>
</div>
<TagSelect
ref='tags'
value={this.state.note.tags}
onChange={(e) => this.handleChange(e)}
/>
<TodoListPercentage
percentageOfTodo={this.getPercentageOfCompleteTodo(note.content)}
/>
</div>
<div styleName='info-right'>
{(() => {
const faClassName = `fa ${this.getToggleLockButton()}`
const lockButtonComponent =
<button styleName='control-lockButton'
onFocus={(e) => this.handleFocus(e)}
onMouseDown={(e) => this.handleLockButtonMouseDown(e)}
>
<i className={faClassName} styleName='lock-button' />
<span styleName='control-lockButton-tooltip'>
{this.state.isLocked ? 'Unlock' : 'Lock'}
</span>
</button>
return (
this.state.isLockButtonShown ? lockButtonComponent : ''
)
})()}
<button styleName='control-trashButton'
onClick={(e) => this.handleDeleteButtonClick(e)}
>
<svg height='14px' id='Capa_1' style={{enableBackground: 'new 0 0 753.23 753.23'}} width='14px' version='1.1' viewBox='0 0 753.23 753.23' x='0px' y='0px' xmlSpace='preserve'>
<g>
<g id='_x34__19_'>
<g>
<path d='M494.308,659.077c12.993,0,23.538-10.546,23.538-23.539V353.077c0-12.993-10.545-23.539-23.538-23.539&#xA;&#x9;&#x9;&#x9;&#x9;s-23.538,10.545-23.538,23.539v282.461C470.77,648.531,481.314,659.077,494.308,659.077z M635.538,94.154h-141.23V47.077&#xA;&#x9;&#x9;&#x9;&#x9;C494.308,21.067,473.24,0,447.23,0H306c-26.01,0-47.077,21.067-47.077,47.077v47.077h-141.23&#xA;&#x9;&#x9;&#x9;&#x9;c-26.01,0-47.077,21.067-47.077,47.077v47.077c0,25.986,21.067,47.077,47.077,47.077v423.692&#xA;&#x9;&#x9;&#x9;&#x9;c0,51.996,42.157,94.153,94.154,94.153h329.539c51.996,0,94.153-42.157,94.153-94.153V235.385&#xA;&#x9;&#x9;&#x9;&#x9;c26.01,0,47.077-21.091,47.077-47.077V141.23C682.615,115.221,661.548,94.154,635.538,94.154z M306,70.615&#xA;&#x9;&#x9;&#x9;&#x9;c0-12.993,10.545-23.539,23.538-23.539h94.154c12.993,0,23.538,10.545,23.538,23.539v23.539c-22.809,0-141.23,0-141.23,0V70.615z&#xA;&#x9;&#x9;&#x9;&#x9; M588.461,659.077c0,25.986-21.066,47.076-47.076,47.076H211.846c-26.01,0-47.077-21.09-47.077-47.076V235.385h423.692V659.077z&#xA;&#x9;&#x9;&#x9;&#x9; M612,188.308H141.23c-12.993,0-23.538-10.545-23.538-23.539s10.545-23.539,23.538-23.539H612&#xA;&#x9;&#x9;&#x9;&#x9;c12.993,0,23.538,10.545,23.538,23.539S624.993,188.308,612,188.308z M258.923,659.077c12.993,0,23.539-10.546,23.539-23.539&#xA;&#x9;&#x9;&#x9;&#x9;V353.077c0-12.993-10.545-23.539-23.539-23.539s-23.539,10.545-23.539,23.539v282.461&#xA;&#x9;&#x9;&#x9;&#x9;C235.384,648.531,245.93,659.077,258.923,659.077z M376.615,659.077c12.993,0,23.538-10.546,23.538-23.539V353.077&#xA;&#x9;&#x9;&#x9;&#x9;c0-12.993-10.545-23.539-23.538-23.539s-23.539,10.545-23.539,23.539v282.461C353.077,648.531,363.622,659.077,376.615,659.077z' />
</g>
</g>
</g>
</svg>
</button>
<button styleName='control-fullScreenButton'
onMouseDown={(e) => this.handleFullScreenButton(e)}
>
<i className='fa fa-arrows-alt' styleName='fullScreen-button' />
</button>
</div>
</div>
{location.pathname === '/trashed' ? trashTopBar : detailTopBar}
<div styleName='body'>
<MarkdownEditor

View File

@@ -3,53 +3,47 @@
.root
absolute top right bottom
border-width 0 0 1px
border-style solid
border-color $ui-borderColor
border-left 1px solid alpha(#DEDEDE, 60%)
background-color $ui-noteDetail-backgroundColor
box-shadow $note-detail-box-shadow
box-shadow none
padding 20px 40px
.lock-button
padding-bottom 3px
.control-lockButton
top 160px
margin-bottom 10px
topBarButtonLight()
.control-lockButton-tooltip
tooltip()
position fixed
pointer-events none
top 50px
z-index 200
padding 5px
line-height normal
border-radius 2px
opacity 0
transition 0.1s
.control-trashButton
float right
topBarButtonLight()
.trashed-infopanel
top 40px
position relative
.control-fullScreenButton
float right
top 80px
topBarButtonLight()
.body
absolute left right
left $note-detail-left-margin
right $note-detail-right-margin
left 0
right 0
top $info-height + $info-margin-under-border
bottom $statusBar-height
margin 0 45px
.body-noteEditor
absolute top bottom left right
body[data-theme="white"]
.root
box-shadow $note-detail-box-shadow
border none
body[data-theme="dark"]
.root
border-color $ui-dark-borderColor
background-color $ui-dark-noteDetail-backgroundColor
box-shadow none
border none
.control-lockButton
topBarButtonDark()
@@ -57,8 +51,5 @@ body[data-theme="dark"]
.control-lockButton-tooltip
darkTooltip()
.control-trashButton
topBarButtonDark()
.control-fullScreenButton
topBarButtonDark()

View File

@@ -1,33 +1,30 @@
@import('DetailVars')
$info-height = 60px
$info-margin-under-border = 27px
$info-height = 50px
$info-margin-under-border = 30px
.info
absolute top left right
left $note-detail-left-margin
right $note-detail-right-margin
left 0
right 0
height $info-height
border-bottom $ui-border
border-bottom 1px solid #eee
background-color $ui-noteDetail-backgroundColor
width 100%
display flex
align-items center
.info-left
float left
padding 0 5px
margin 0px 2px
padding 0 10px
width 100%
display flex
align-items center
.info-left-top
display inline-block
height $info-height
line-height $info-height
.info-left-top-folderSelect
display inline-block
padding 0 3px
height 34px
line-height 34px
vertical-align middle
border-radius 3px
display flex
align-items center
justify-content center
.info-left-button
width 34px
@@ -35,6 +32,7 @@ $info-margin-under-border = 27px
navButtonColor()
color $ui-favorite-star-button-color
font-size 14px
line-height 0
margin 13px 2px
padding 0
border-radius 17px
@@ -44,15 +42,37 @@ $info-margin-under-border = 27px
border-color $ui-favorite-star-button-color
&:active, &:active:hover
background-color $ui-favorite-star-button-color
color $ui-button--active-color
color $ui-button--color
.info-right
position absolute
right 0
top 0
background $ui-noteDetail-backgroundColor
right 40px
top 60px
bottom 1px
padding-left 30px
z-index 101
.undo-button
width 34px
height 34px
border-radius 17px
font-size 14px
margin 5px 0px
border none
color $ui-button-color
display flex
align-items center
justify-content center
fill $ui-button-color
background-color transparent
cursor pointer
&:active
border-color $ui-button--active-backgroundColor
&:hover
background-color alpha($ui-button--hover-backgroundColor, 60%)
transition 0.2s
.control-lockButton-tooltip
opacity 1
body[data-theme="dark"]
.info
@@ -73,3 +93,6 @@ body[data-theme="dark"]
.info-right
background-color $ui-dark-noteDetail-backgroundColor
.undo-button
topBarButtonDark()

View File

@@ -0,0 +1,20 @@
import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './TrashButton.styl'
const PermanentDeleteButton = ({
onClick
}) => (
<button styleName='control-trashButton--in-trash'
onClick={(e) => onClick(e)}
>
<img styleName='iconInfo' src='../resources/icon/icon-trash.svg' />
</button>
)
PermanentDeleteButton.propTypes = {
onClick: PropTypes.func.isRequired
}
export default CSSModules(PermanentDeleteButton, styles)

View File

@@ -1,4 +1,5 @@
import React, { PropTypes } from 'react'
import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './SnippetNoteDetail.styl'
import CodeEditor from 'browser/components/CodeEditor'
@@ -17,6 +18,12 @@ import ConfigManager from 'browser/main/lib/ConfigManager'
import _ from 'lodash'
import { findNoteTitle } from 'browser/lib/findNoteTitle'
import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig'
import TrashButton from './TrashButton'
import PermanentDeleteButton from './PermanentDeleteButton'
import InfoButton from './InfoButton'
import InfoPanel from './InfoPanel'
import InfoPanelTrashed from './InfoPanelTrashed'
import { formatDate } from 'browser/lib/date-formatter'
function pass (name) {
switch (name) {
@@ -53,9 +60,9 @@ class SnippetNoteDetail extends React.Component {
}
componentWillReceiveProps (nextProps) {
if (nextProps.note.key !== this.props.note.key) {
if (nextProps.note.key !== this.props.note.key && !this.isMovingNote) {
if (this.saveQueue != null) this.saveNow()
let nextNote = Object.assign({
const nextNote = Object.assign({
description: ''
}, nextProps.note, {
snippets: nextProps.note.snippets.map((snippet) => Object.assign({}, snippet))
@@ -64,11 +71,11 @@ class SnippetNoteDetail extends React.Component {
snippetIndex: 0,
note: nextNote
}, () => {
let { snippets } = this.state.note
const { snippets } = this.state.note
snippets.forEach((snippet, index) => {
this.refs['code-' + index].reload()
})
this.refs.tags.reset()
if (this.refs.tags) this.refs.tags.reset()
})
}
}
@@ -78,9 +85,9 @@ class SnippetNoteDetail extends React.Component {
}
handleChange (e) {
let { note } = this.state
const { note } = this.state
note.tags = this.refs.tags.value
if (this.refs.tags) note.tags = this.refs.tags.value
note.description = this.refs.description.value
note.updatedAt = new Date()
note.title = findNoteTitle(note.description)
@@ -100,7 +107,7 @@ class SnippetNoteDetail extends React.Component {
}
saveNow () {
let { note, dispatch } = this.props
const { note, dispatch } = this.props
clearTimeout(this.saveQueue)
this.saveQueue = null
@@ -111,16 +118,16 @@ class SnippetNoteDetail extends React.Component {
type: 'UPDATE_NOTE',
note: note
})
AwsMobileAnalyticsConfig.recordDynamitCustomEvent('EDIT_NOTE')
AwsMobileAnalyticsConfig.recordDynamicCustomEvent('EDIT_NOTE')
})
}
handleFolderChange (e) {
let { note } = this.state
let value = this.refs.folder.value
let splitted = value.split('-')
let newStorageKey = splitted.shift()
let newFolderKey = splitted.shift()
const { note } = this.state
const value = this.refs.folder.value
const splitted = value.split('-')
const newStorageKey = splitted.shift()
const newFolderKey = splitted.shift()
dataApi
.moveNote(note.storage, note.key, newStorageKey, newFolderKey)
@@ -129,7 +136,7 @@ class SnippetNoteDetail extends React.Component {
isMovingNote: true,
note: Object.assign({}, newNote)
}, () => {
let { dispatch, location } = this.props
const { dispatch, location } = this.props
dispatch({
type: 'MOVE_NOTE',
originNote: note,
@@ -149,8 +156,8 @@ class SnippetNoteDetail extends React.Component {
}
handleStarButtonClick (e) {
let { note } = this.state
if (!note.isStarred) AwsMobileAnalyticsConfig.recordDynamitCustomEvent('ADD_STAR')
const { note } = this.state
if (!note.isStarred) AwsMobileAnalyticsConfig.recordDynamicCustomEvent('ADD_STAR')
note.isStarred = !note.isStarred
@@ -165,19 +172,23 @@ class SnippetNoteDetail extends React.Component {
}
handleDeleteButtonClick (e) {
let index = dialog.showMessageBox(remote.getCurrentWindow(), {
type: 'warning',
message: 'Delete a note',
detail: 'This work cannot be undone.',
buttons: ['Confirm', 'Cancel']
})
if (index === 0) {
let { note, dispatch } = this.props
handleTrashButtonClick (e) {
const { note } = this.state
const { isTrashed } = note
if (isTrashed) {
const dialogueButtonIndex = dialog.showMessageBox(remote.getCurrentWindow(), {
type: 'warning',
message: 'Confirm note deletion',
detail: 'This will permanently remove this note.',
buttons: ['Confirm', 'Cancel']
})
if (dialogueButtonIndex === 1) return
const { note, dispatch } = this.props
dataApi
.deleteNote(note.storage, note.key)
.then((data) => {
let dispatchHandler = () => {
const dispatchHandler = () => {
dispatch({
type: 'DELETE_NOTE',
storageKey: data.storageKey,
@@ -185,9 +196,30 @@ class SnippetNoteDetail extends React.Component {
})
}
ee.once('list:moved', dispatchHandler)
ee.emit('list:next')
})
} else {
note.isTrashed = true
this.setState({
note
}, () => {
this.save()
})
}
ee.emit('list:next')
}
handleUndoButtonClick (e) {
const { note } = this.state
note.isTrashed = false
this.setState({
note
}, () => {
this.save()
ee.emit('list:next')
})
}
handleFullScreenButton (e) {
@@ -207,7 +239,7 @@ class SnippetNoteDetail extends React.Component {
handleTabDeleteButtonClick (e, index) {
if (this.state.note.snippets.length > 1) {
if (this.state.note.snippets[index].content.trim().length > 0) {
let dialogIndex = dialog.showMessageBox(remote.getCurrentWindow(), {
const dialogIndex = dialog.showMessageBox(remote.getCurrentWindow(), {
type: 'warning',
message: 'Delete a snippet',
detail: 'This work cannot be undone.',
@@ -236,12 +268,17 @@ class SnippetNoteDetail extends React.Component {
}
renameSnippetByIndex (index, name) {
let snippets = this.state.note.snippets.slice()
const snippets = this.state.note.snippets.slice()
snippets[index].name = name
let syntax = CodeMirror.findModeByFileName(name.trim())
let mode = syntax != null ? syntax.name : null
if (mode != null) snippets[index].mode = mode
this.state.note.snippets = snippets
const syntax = CodeMirror.findModeByFileName(name.trim())
const mode = syntax != null ? syntax.name : null
if (mode != null) {
snippets[index].mode = mode
AwsMobileAnalyticsConfig.recordDynamicCustomEvent('SNIPPET_LANG', {
name: mode
})
}
this.setState({note: Object.assign(this.state.note, {snippets: snippets})})
this.setState({
note: this.state.note
@@ -252,23 +289,27 @@ class SnippetNoteDetail extends React.Component {
handleModeOptionClick (index, name) {
return (e) => {
let snippets = this.state.note.snippets.slice()
const snippets = this.state.note.snippets.slice()
snippets[index].mode = name
this.state.note.snippets = snippets
this.setState({note: Object.assign(this.state.note, {snippets: snippets})})
this.setState({
note: this.state.note
}, () => {
this.save()
})
AwsMobileAnalyticsConfig.recordDynamicCustomEvent('SELECT_LANG', {
name
})
}
}
handleCodeChange (index) {
return (e) => {
let snippets = this.state.note.snippets.slice()
const snippets = this.state.note.snippets.slice()
snippets[index].content = this.refs['code-' + index].value
this.state.note.snippets = snippets
this.setState({note: Object.assign(this.state.note, {snippets: snippets})})
this.setState({
note: this.state.note
}, () => {
@@ -293,7 +334,7 @@ class SnippetNoteDetail extends React.Component {
break
case 76:
{
let isSuper = global.process.platform === 'darwin'
const isSuper = global.process.platform === 'darwin'
? e.metaKey
: e.ctrlKey
if (isSuper) {
@@ -304,7 +345,7 @@ class SnippetNoteDetail extends React.Component {
break
case 84:
{
let isSuper = global.process.platform === 'darwin'
const isSuper = global.process.platform === 'darwin'
? e.metaKey
: e.ctrlKey
if (isSuper) {
@@ -317,7 +358,7 @@ class SnippetNoteDetail extends React.Component {
}
handleModeButtonClick (e, index) {
let menu = new Menu()
const menu = new Menu()
CodeMirror.modeInfo.forEach((mode) => {
menu.append(new MenuItem({
label: mode.name,
@@ -358,8 +399,8 @@ class SnippetNoteDetail extends React.Component {
}
handleIndentSizeItemClick (e, indentSize) {
let { config, dispatch } = this.props
let editor = Object.assign({}, config.editor, {
const { config, dispatch } = this.props
const editor = Object.assign({}, config.editor, {
indentSize
})
ConfigManager.set({
@@ -374,8 +415,8 @@ class SnippetNoteDetail extends React.Component {
}
handleIndentTypeItemClick (e, indentType) {
let { config, dispatch } = this.props
let editor = Object.assign({}, config.editor, {
const { config, dispatch } = this.props
const editor = Object.assign({}, config.editor, {
indentType
})
ConfigManager.set({
@@ -394,14 +435,14 @@ class SnippetNoteDetail extends React.Component {
}
addSnippet () {
let { note } = this.state
const { note } = this.state
note.snippets = note.snippets.concat([{
name: '',
mode: 'Plain Text',
content: ''
}])
let snippetIndex = note.snippets.length - 1
const snippetIndex = note.snippets.length - 1
this.setState({
note,
@@ -432,17 +473,34 @@ class SnippetNoteDetail extends React.Component {
this.refs['code-' + this.state.snippetIndex].focus()
}
handleInfoButtonClick (e) {
const infoPanel = document.querySelector('.infoPanel')
if (infoPanel.style) infoPanel.style.display = infoPanel.style.display === 'none' ? 'inline' : 'none'
}
showWarning () {
dialog.showMessageBox(remote.getCurrentWindow(), {
type: 'warning',
message: 'Sorry!',
detail: 'md/text import is available only a markdown note.',
buttons: ['OK']
})
}
render () {
let { data, config } = this.props
let { note } = this.state
const { data, config, location } = this.props
const { note } = this.state
const storageKey = note.storage
const folderKey = note.folder
let editorFontSize = parseInt(config.editor.fontSize, 10)
if (!(editorFontSize > 0 && editorFontSize < 101)) editorFontSize = 14
let editorIndentSize = parseInt(config.editor.indentSize, 10)
if (!(editorFontSize > 0 && editorFontSize < 132)) editorIndentSize = 4
let tabList = note.snippets.map((snippet, index) => {
let isActive = this.state.snippetIndex === index
const tabList = note.snippets.map((snippet, index) => {
const isActive = this.state.snippetIndex === index
return <SnippetTab
key={index}
@@ -456,8 +514,8 @@ class SnippetNoteDetail extends React.Component {
/>
})
let viewList = note.snippets.map((snippet, index) => {
let isActive = this.state.snippetIndex === index
const viewList = note.snippets.map((snippet, index) => {
const isActive = this.state.snippetIndex === index
let syntax = CodeMirror.findModeByName(pass(snippet.mode))
if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text')
@@ -473,6 +531,7 @@ class SnippetNoteDetail extends React.Component {
onChange={(e) => this.handleCodeChange(index)(e)}
ref={'code-' + index}
ignorePreviewPointerEvents={this.props.ignorePreviewPointerEvents}
storageKey={storageKey}
/>
: <CodeEditor styleName='tabView-content'
mode={snippet.mode}
@@ -490,54 +549,93 @@ class SnippetNoteDetail extends React.Component {
</div>
})
const options = []
data.storageMap.forEach((storage, index) => {
storage.folders.forEach((folder) => {
options.push({
storage: storage,
folder: folder
})
})
})
const currentOption = options.filter((option) => option.storage.key === storageKey && option.folder.key === folderKey)[0]
const trashTopBar = <div styleName='info'>
<div styleName='info-left'>
<i styleName='undo-button'
className='fa fa-undo fa-fw'
onClick={(e) => this.handleUndoButtonClick(e)}
/>
</div>
<div styleName='info-right'>
<PermanentDeleteButton onClick={(e) => this.handleTrashButtonClick(e)} />
<InfoButton
onClick={(e) => this.handleInfoButtonClick(e)}
/>
<InfoPanelTrashed
storageName={currentOption.storage.name}
folderName={currentOption.folder.name}
updatedAt={formatDate(note.updatedAt)}
createdAt={formatDate(note.createdAt)}
exportAsMd={this.showWarning}
exportAsTxt={this.showWarning}
/>
</div>
</div>
const detailTopBar = <div styleName='info'>
<div styleName='info-left'>
<div styleName='info-left-top'>
<FolderSelect styleName='info-left-top-folderSelect'
value={this.state.note.storage + '-' + this.state.note.folder}
ref='folder'
data={data}
onChange={(e) => this.handleFolderChange(e)}
/>
</div>
<TagSelect
ref='tags'
value={this.state.note.tags}
onChange={(e) => this.handleChange(e)}
/>
</div>
<div styleName='info-right'>
<InfoButton
onClick={(e) => this.handleInfoButtonClick(e)}
/>
<StarButton
onClick={(e) => this.handleStarButtonClick(e)}
isActive={note.isStarred}
/>
<button styleName='control-fullScreenButton'
onMouseDown={(e) => this.handleFullScreenButton(e)}>
<img styleName='iconInfo' src='../resources/icon/icon-sidebar.svg' />
</button>
<TrashButton onClick={(e) => this.handleTrashButtonClick(e)} />
<InfoPanel
storageName={currentOption.storage.name}
folderName={currentOption.folder.name}
noteLink={`[${note.title}](${location.query.key})`}
updatedAt={formatDate(note.updatedAt)}
createdAt={formatDate(note.createdAt)}
exportAsMd={this.showWarning}
exportAsTxt={this.showWarning}
type={note.type}
/>
</div>
</div>
return (
<div className='NoteDetail'
style={this.props.style}
styleName='root'
onKeyDown={(e) => this.handleKeyDown(e)}
>
<div styleName='info'>
<div styleName='info-left'>
<StarButton styleName='info-left-button'
onClick={(e) => this.handleStarButtonClick(e)}
isActive={note.isStarred}
/>
<div styleName='info-left-top'>
<FolderSelect styleName='info-left-top-folderSelect'
value={this.state.note.storage + '-' + this.state.note.folder}
ref='folder'
data={data}
onChange={(e) => this.handleFolderChange(e)}
/>
</div>
<TagSelect
ref='tags'
value={this.state.note.tags}
onChange={(e) => this.handleChange(e)}
/>
</div>
<div styleName='info-right'>
<button styleName='control-trashButton'
onClick={(e) => this.handleDeleteButtonClick(e)}
>
<svg height='14px' id='Capa_1' style={{enableBackground: 'new 0 0 753.23 753.23'}} width='14px' version='1.1' viewBox='0 0 753.23 753.23' x='0px' y='0px' xmlSpace='preserve'>
<g>
<g id='_x34__19_'>
<g>
<path d='M494.308,659.077c12.993,0,23.538-10.546,23.538-23.539V353.077c0-12.993-10.545-23.539-23.538-23.539&#xA;&#x9;&#x9;&#x9;&#x9;s-23.538,10.545-23.538,23.539v282.461C470.77,648.531,481.314,659.077,494.308,659.077z M635.538,94.154h-141.23V47.077&#xA;&#x9;&#x9;&#x9;&#x9;C494.308,21.067,473.24,0,447.23,0H306c-26.01,0-47.077,21.067-47.077,47.077v47.077h-141.23&#xA;&#x9;&#x9;&#x9;&#x9;c-26.01,0-47.077,21.067-47.077,47.077v47.077c0,25.986,21.067,47.077,47.077,47.077v423.692&#xA;&#x9;&#x9;&#x9;&#x9;c0,51.996,42.157,94.153,94.154,94.153h329.539c51.996,0,94.153-42.157,94.153-94.153V235.385&#xA;&#x9;&#x9;&#x9;&#x9;c26.01,0,47.077-21.091,47.077-47.077V141.23C682.615,115.221,661.548,94.154,635.538,94.154z M306,70.615&#xA;&#x9;&#x9;&#x9;&#x9;c0-12.993,10.545-23.539,23.538-23.539h94.154c12.993,0,23.538,10.545,23.538,23.539v23.539c-22.809,0-141.23,0-141.23,0V70.615z&#xA;&#x9;&#x9;&#x9;&#x9; M588.461,659.077c0,25.986-21.066,47.076-47.076,47.076H211.846c-26.01,0-47.077-21.09-47.077-47.076V235.385h423.692V659.077z&#xA;&#x9;&#x9;&#x9;&#x9; M612,188.308H141.23c-12.993,0-23.538-10.545-23.538-23.539s10.545-23.539,23.538-23.539H612&#xA;&#x9;&#x9;&#x9;&#x9;c12.993,0,23.538,10.545,23.538,23.539S624.993,188.308,612,188.308z M258.923,659.077c12.993,0,23.539-10.546,23.539-23.539&#xA;&#x9;&#x9;&#x9;&#x9;V353.077c0-12.993-10.545-23.539-23.539-23.539s-23.539,10.545-23.539,23.539v282.461&#xA;&#x9;&#x9;&#x9;&#x9;C235.384,648.531,245.93,659.077,258.923,659.077z M376.615,659.077c12.993,0,23.538-10.546,23.538-23.539V353.077&#xA;&#x9;&#x9;&#x9;&#x9;c0-12.993-10.545-23.539-23.538-23.539s-23.539,10.545-23.539,23.539v282.461C353.077,648.531,363.622,659.077,376.615,659.077z' />
</g>
</g>
</g>
</svg>
</button>
<button styleName='control-fullScreenButton'
onMouseDown={(e) => this.handleFullScreenButton(e)}
>
<i className='fa fa-arrows-alt' styleName='fullScreen-button' />
</button>
</div>
</div>
{location.pathname === '/trashed' ? trashTopBar : detailTopBar}
<div styleName='body'>
<div styleName='description'>

View File

@@ -3,23 +3,21 @@
.root
absolute top bottom right
border-width 0 0 1px
border-style solid
border-color $ui-borderColor
border-left 1px solid alpha(#DEDEDE, 60%)
background-color $ui-noteDetail-backgroundColor
box-shadow $note-detail-box-shadow
box-shadow none
.body
absolute left right
left $note-detail-left-margin
right $note-detail-right-margin
left $snippet-note-detail-left-margin
right $snippet-note-detail-right-margin
top $info-height + $info-margin-under-border
bottom $statusBar-height
background-color $ui-noteDetail-backgroundColor
.body .description
absolute top left right
height 80px
height 50px
.body .description textarea
outline none
@@ -27,14 +25,14 @@
height 100%
width 100%
resize none
border none
padding 10px
border 1px solid $ui-borderColor
padding 2px 5px
line-height 1.6
background-color $ui-noteDetail-backgroundColor
.tabList
absolute left right
top 80px
top 55px
height 30px
display flex
background-color $ui-noteDetail-backgroundColor
@@ -45,38 +43,42 @@
overflow hidden
.tabList .plusButton
navButtonColor()
navWhiteButtonColor()
width 30px
.tabView
absolute left right bottom
top 130px
top 100px
.tabView-content
absolute top left right bottom
.override
absolute bottom left
bottom 1px
left 60px
height 23px
z-index 1
z-index 101
button
navButtonColor()
height 24px
padding 0 6px
&:hover
color $ui-active-color
&:active .update-icon
color white
.control-trashButton
float right
topBarButtonLight()
color $ui-active-color
.control-fullScreenButton
float right
top 80px
margin-bottom 10px
topBarButtonLight()
body[data-theme="white"]
.root
box-shadow $note-detail-box-shadow
border none
body[data-theme="dark"]
.root
border-color $ui-dark-borderColor
border none
background-color $ui-dark-noteDetail-backgroundColor
box-shadow none
@@ -86,6 +88,7 @@ body[data-theme="dark"]
.body .description textarea
background-color $ui-dark-noteDetail-backgroundColor
color $ui-dark-text-color
border 1px solid $ui-dark-borderColor
.tabList
background-color $ui-button--active-backgroundColor
@@ -104,8 +107,5 @@ body[data-theme="dark"]
transition 0.15s
color $ui-dark-text-color
.control-trashButton
topBarButtonDark()
.control-fullScreenButton
topBarButtonDark()

View File

@@ -1,4 +1,5 @@
import React, { PropTypes } from 'react'
import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './StarButton.styl'
import _ from 'lodash'
@@ -31,7 +32,7 @@ class StarButton extends React.Component {
}
render () {
let { className } = this.props
const { className } = this.props
return (
<button className={_.isString(className)
@@ -47,10 +48,10 @@ class StarButton extends React.Component {
onMouseLeave={(e) => this.handleMouseLeave(e)}
onClick={this.props.onClick}
>
<i styleName='icon'
className={this.state.isActive || this.props.isActive
? 'fa fa-star'
: 'fa fa-star-o'
<img styleName='icon'
src={this.state.isActive || this.props.isActive
? '../resources/icon/icon-starred.svg'
: '../resources/icon/icon-star.svg'
}
/>
</button>

View File

@@ -1,47 +1,24 @@
.root
left 7px
top 0
padding 0
color alpha($ui-favorite-star-button-color, 60%)
top 45px
topBarButtonLight()
&:hover
transition 0.15s
background-color alpha($ui-button--active-backgroundColor, 40%)
color $ui-favorite-star-button-color
&:active
transition 0.15s
background-color alpha($ui-button--active-backgroundColor, 40%)
color $ui-favorite-star-button-color
transition 0.2s
color alpha($ui-favorite-star-button-color, 0.6)
.root--active
@extend .root
transition 0.15s
color $ui-favorite-star-button-color
&:hover
transition 0.15s
color $ui-favorite-star-button-color
background-color alpha($ui-button--active-backgroundColor, 40%)
&:active
transition 0.15s
color $ui-favorite-star-button-color
background-color alpha($ui-button--active-backgroundColor, 40%)
transition 0.2s
color alpha($ui-favorite-star-button-color, 0.6)
.icon
transition transform 0.15s
body[data-theme="dark"]
.root
topBarButtonDark()
&:hover
transition 0.15s
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
color $ui-favorite-star-button-color
&:active
transition 0.15s
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
color $ui-favorite-star-button-color
.root--active
@extend .root
color $ui-favorite-star-button-color
&:hover
transition 0.15s
color $ui-favorite-star-button-color
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
transition 0.2s
color alpha($ui-favorite-star-button-color, 0.6)

View File

@@ -1,4 +1,5 @@
import React, { PropTypes } from 'react'
import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './TagSelect.styl'
import _ from 'lodash'
@@ -37,6 +38,10 @@ class TagSelect extends React.Component {
}
}
handleNewTagBlur (e) {
this.submitTag()
}
removeLastTag () {
let { value } = this.props
@@ -57,9 +62,9 @@ class TagSelect extends React.Component {
}
submitTag () {
AwsMobileAnalyticsConfig.recordDynamitCustomEvent('ADD_TAG')
AwsMobileAnalyticsConfig.recordDynamicCustomEvent('ADD_TAG')
let { value } = this.props
let newTag = this.refs.newTag.value.trim().replace(/ +/g, '_')
const newTag = this.refs.newTag.value.trim().replace(/ +/g, '_')
if (newTag.length <= 0) {
this.setState({
@@ -101,9 +106,9 @@ class TagSelect extends React.Component {
}
render () {
let { value, className } = this.props
const { value, className } = this.props
let tagList = _.isArray(value)
const tagList = _.isArray(value)
? value.map((tag) => {
return (
<span styleName='tag'
@@ -113,7 +118,7 @@ class TagSelect extends React.Component {
<button styleName='tag-removeButton'
onClick={(e) => this.handleTagRemoveButtonClick(tag)(e)}
>
<i className='fa fa-times fa-fw tag-removeButton-icon' />
<img className='tag-removeButton-icon' src='../resources/icon/icon-x.svg' width='8px' />
</button>
</span>
)
@@ -134,6 +139,7 @@ class TagSelect extends React.Component {
placeholder='Add tag...'
onChange={(e) => this.handleNewTagInputChange(e)}
onKeyDown={(e) => this.handleNewTagInputKeyDown(e)}
onBlur={(e) => this.handleNewTagBlur(e)}
/>
</div>
)

View File

@@ -1,9 +1,9 @@
.root
display inline-block
top 19px
display flex
align-items center
user-select none
vertical-align middle
width 300px
width 100%
overflow-x scroll
white-space nowrap
@@ -11,52 +11,42 @@
display none
.tag
display inline-block
margin 1px 3px
padding 0
vertical-align middle
height 20px
background-color alpha($ui-tag-backgroundColor, 10%)
border-radius 3px
overflow hidden
display flex
align-items center
margin 0px 2px
padding 2px 4px
background-color alpha($ui-tag-backgroundColor, 3%)
border-radius 4px
position relative
clearfix()
.tag-removeButton
float right
height 20px
width 18px
margin 0
padding 0
border-style solid
border-width 0
border-radius 20px
line-height 18px
background-color transparent
color $ui-button-color
position absolute
right 6px
.tag-removeButton-icon
width 5px
padding-right 4px
.tag-label
font-size 11px
font-weight 600
color: alpha($ui-text-color, 80%)
float left
height 20px
line-height 20px
padding 0 6px
font-size 13px
color: $ui-text-color
padding 4px 16px 4px 8px
.newTag
display inline-block
margin 0 2px
vertical-align middle
height 24px
box-sizing borde-box
box-sizing border-box
border none
background-color transparent
outline none
padding 0 4px
font-size 13px
body[data-theme="dark"]
.tag

View File

@@ -0,0 +1,20 @@
import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './TrashButton.styl'
const TrashButton = ({
onClick
}) => (
<button styleName='control-trashButton'
onClick={(e) => onClick(e)}
>
<img styleName='iconInfo' src='../resources/icon/icon-trash.svg' />
</button>
)
TrashButton.propTypes = {
onClick: PropTypes.func.isRequired
}
export default CSSModules(TrashButton, styles)

View File

@@ -0,0 +1,15 @@
.control-trashButton
top 120px
margin-bottom 10px
topBarButtonLight()
.control-trashButton--in-trash
top 60px
topBarButtonLight()
.trashButton
padding 0px
body[data-theme="dark"]
.control-trashButton
topBarButtonDark()

View File

@@ -1,4 +1,5 @@
import React, { PropTypes } from 'react'
import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './Detail.styl'
import _ from 'lodash'
@@ -17,7 +18,7 @@ class Detail extends React.Component {
this.refs.root != null && this.refs.root.focus()
}
this.deleteHandler = () => {
this.refs.root != null && this.refs.root.handleDeleteButtonClick()
this.refs.root != null && this.refs.root.handleTrashButtonClick()
}
}
@@ -32,12 +33,12 @@ class Detail extends React.Component {
}
render () {
let { location, data, config } = this.props
const { location, data, config } = this.props
let note = null
if (location.query.key != null) {
let splitted = location.query.key.split('-')
let storageKey = splitted.shift()
let noteKey = splitted.shift()
const splitted = location.query.key.split('-')
const storageKey = splitted.shift()
const noteKey = splitted.shift()
note = data.noteMap.get(storageKey + '-' + noteKey)
}
@@ -49,7 +50,7 @@ class Detail extends React.Component {
tabIndex='0'
>
<div styleName='empty'>
<div styleName='empty-message'>{OSX ? 'Command(⌘)' : 'Ctrl(^)'} + N<br />to create a new post</div>
<div styleName='empty-message'>{OSX ? 'Command(⌘)' : 'Ctrl(^)'} + N<br />to create a new note</div>
</div>
<StatusBar
{..._.pick(this.props, ['config', 'location', 'dispatch'])}

View File

@@ -1,4 +1,5 @@
import React, { PropTypes } from 'react'
import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './Main.styl'
import { connect } from 'react-redux'
@@ -11,15 +12,11 @@ import _ from 'lodash'
import ConfigManager from 'browser/main/lib/ConfigManager'
import modal from 'browser/main/lib/modal'
import InitModal from 'browser/main/modals/InitModal'
import mixpanel from 'browser/main/lib/mixpanel'
import mobileAnalytics from 'browser/main/lib/awsMobileAnalyticsConfig'
import mobileAnalytics from 'browser/main/lib/AwsMobileAnalyticsConfig'
import eventEmitter from 'browser/main/lib/eventEmitter'
function focused () {
mixpanel.track('MAIN_FOCUSED')
}
class Main extends React.Component {
constructor (props) {
super(props)
@@ -27,7 +24,7 @@ class Main extends React.Component {
mobileAnalytics.initAwsMobileAnalytics()
}
let { config } = props
const { config } = props
this.state = {
isRightSliderFocused: false,
@@ -43,7 +40,7 @@ class Main extends React.Component {
}
getChildContext () {
let { status, config } = this.props
const { status, config } = this.props
return {
status,
@@ -52,10 +49,12 @@ class Main extends React.Component {
}
componentDidMount () {
let { dispatch, config } = this.props
const { dispatch, config } = this.props
if (config.ui.theme === 'dark') {
document.body.setAttribute('data-theme', 'dark')
} else if (config.ui.theme === 'white') {
document.body.setAttribute('data-theme', 'white')
} else {
document.body.setAttribute('data-theme', 'default')
}
@@ -75,11 +74,9 @@ class Main extends React.Component {
})
eventEmitter.on('editor:fullscreen', this.toggleFullScreen)
window.addEventListener('focus', focused)
}
componentWillUnmount () {
window.removeEventListener('focus', focused)
eventEmitter.off('editor:fullscreen', this.toggleFullScreen)
}
@@ -103,8 +100,8 @@ class Main extends React.Component {
this.setState({
isRightSliderFocused: false
}, () => {
let { dispatch } = this.props
let newListWidth = this.state.listWidth
const { dispatch } = this.props
const newListWidth = this.state.listWidth
// TODO: ConfigManager should dispatch itself.
ConfigManager.set({listWidth: newListWidth})
dispatch({
@@ -119,8 +116,8 @@ class Main extends React.Component {
this.setState({
isLeftSliderFocused: false
}, () => {
let { dispatch } = this.props
let navWidth = this.state.navWidth
const { dispatch } = this.props
const navWidth = this.state.navWidth
// TODO: ConfigManager should dispatch itself.
ConfigManager.set({ navWidth })
dispatch({
@@ -133,7 +130,7 @@ class Main extends React.Component {
handleMouseMove (e) {
if (this.state.isRightSliderFocused) {
let offset = this.refs.body.getBoundingClientRect().left
const offset = this.refs.body.getBoundingClientRect().left
let newListWidth = e.pageX - offset
if (newListWidth < 10) {
newListWidth = 10
@@ -160,30 +157,36 @@ class Main extends React.Component {
handleFullScreenButton (e) {
this.setState({ fullScreen: !this.state.fullScreen }, () => {
const noteDetail = document.querySelector('.NoteDetail')
const noteList = document.querySelector('.NoteList')
const mainBody = document.querySelector('#main-body')
if (this.state.fullScreen) {
this.hideLeftLists(noteDetail, mainBody)
this.hideLeftLists(noteDetail, noteList, mainBody)
} else {
this.showLeftLists(noteDetail, mainBody)
this.showLeftLists(noteDetail, noteList, mainBody)
}
})
}
hideLeftLists (noteDetail, mainBody) {
this.state.noteDetailWidth = noteDetail.style.left
this.state.mainBodyWidth = mainBody.style.left
hideLeftLists (noteDetail, noteList, mainBody) {
this.setState({noteDetailWidth: noteDetail.style.left})
this.setState({mainBodyWidth: mainBody.style.left})
noteDetail.style.left = '0px'
mainBody.style.left = '0px'
noteList.style.display = 'none'
}
showLeftLists (noteDetail, mainBody) {
showLeftLists (noteDetail, noteList, mainBody) {
noteDetail.style.left = this.state.noteDetailWidth
mainBody.style.left = this.state.mainBodyWidth
noteList.style.display = 'inline'
}
render () {
let { config } = this.props
const { config } = this.props
// the width of the navigation bar when it is folded/collapsed
const foldedNavigationWidth = 44
return (
<div
@@ -213,7 +216,7 @@ class Main extends React.Component {
<div styleName={config.isSideNavFolded ? 'body--expanded' : 'body'}
id='main-body'
ref='body'
style={{left: config.isSideNavFolded ? 44 : this.state.navWidth}}
style={{left: config.isSideNavFolded ? foldedNavigationWidth : this.state.navWidth}}
>
<TopBar style={{width: this.state.listWidth}}
{..._.pick(this.props, [

View File

@@ -0,0 +1,73 @@
.root
position relative
background-color $ui-noteList-backgroundColor
height $topBar-height - 1
margin-left: auto;
width: 64px;
.root--expanded
@extend .root
$control-height = 34px
.control
position absolute
top 13px
right 7px
height $control-height
display flex
.control-newNoteButton
display block
width 32px
height $control-height - 2
navButtonColor()
font-size 16px
line-height 28px
padding 0
&:active
border-color $ui-button--active-backgroundColor
&:hover .control-newNoteButton-tooltip
opacity 1
.control-newNoteButton-tooltip
tooltip()
position absolute
pointer-events none
top 26px
right -43px
width 124px
z-index 200
padding 5px
line-height normal
border-radius 2px
opacity 0
transition 0.1s
body[data-theme="white"]
.root, .root--expanded
background-color $ui-white-noteList-backgroundColor
.control-newNoteButton
background-color $ui-white-noteList-backgroundColor
body[data-theme="dark"]
.root, .root--expanded
background-color $ui-dark-noteList-backgroundColor
.control
border-color $ui-dark-borderColor
.control-newNoteButton
color $ui-inactive-text-color
border-color $ui-dark-borderColor
background-color $ui-dark-noteList-backgroundColor
&:hover
transition 0.15s
color $ui-dark-text-color
&:active
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
border-color $ui-dark-button--active-backgroundColor
.control-newNoteButton-tooltip
darkTooltip()

View File

@@ -0,0 +1,105 @@
import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './NewNoteButton.styl'
import _ from 'lodash'
import modal from 'browser/main/lib/modal'
import NewNoteModal from 'browser/main/modals/NewNoteModal'
import eventEmitter from 'browser/main/lib/eventEmitter'
const { remote } = require('electron')
const { dialog } = remote
const OSX = window.process.platform === 'darwin'
class NewNoteButton extends React.Component {
constructor (props) {
super(props)
this.state = {
}
this.newNoteHandler = () => {
this.handleNewNoteButtonClick()
}
}
componentDidMount () {
eventEmitter.on('top:new-note', this.newNoteHandler)
}
componentWillUnmount () {
eventEmitter.off('top:new-note', this.newNoteHandler)
}
handleNewNoteButtonClick (e) {
const { location, dispatch } = this.props
const { storage, folder } = this.resolveTargetFolder()
modal.open(NewNoteModal, {
storage: storage.key,
folder: folder.key,
dispatch,
location
})
}
resolveTargetFolder () {
const { data, params } = this.props
let storage = data.storageMap.get(params.storageKey)
// Find first storage
if (storage == null) {
for (const kv of data.storageMap) {
storage = kv[1]
break
}
}
if (storage == null) this.showMessageBox('No storage to create a note')
const folder = _.find(storage.folders, {key: params.folderKey}) || storage.folders[0]
if (folder == null) this.showMessageBox('No folder to create a note')
return {
storage,
folder
}
}
showMessageBox (message) {
dialog.showMessageBox(remote.getCurrentWindow(), {
type: 'warning',
message: message,
buttons: ['OK']
})
}
render () {
const { config, style } = this.props
return (
<div className='NewNoteButton'
styleName={config.isSideNavFolded ? 'root--expanded' : 'root'}
style={style}
>
<div styleName='control'>
<button styleName='control-newNoteButton'
onClick={(e) => this.handleNewNoteButtonClick(e)}>
<img styleName='iconTag' src='../resources/icon/icon-newnote.svg' />
<span styleName='control-newNoteButton-tooltip'>
Make a Note {OSX ? '⌘' : '^'} + n
</span>
</button>
</div>
</div>
)
}
}
NewNoteButton.propTypes = {
dispatch: PropTypes.func,
config: PropTypes.shape({
isSideNavFolded: PropTypes.bool
})
}
export default CSSModules(NewNoteButton, styles)

View File

@@ -17,22 +17,25 @@ $control-height = 30px
.control-sortBy
flex 1
padding-left 25px
padding-left 22px
.control-sortBy-select
appearance: none;
margin-left 3px
margin-left 5px
color $ui-inactive-text-color
padding 0
border none
background-color transparent
outline none
cursor pointer
font-size 10px
font-size 12px
&:hover
transition 0.2s
color $ui-text-color
.control-button-area
margin-right 12px
.control-button
width 25px
padding 0
@@ -56,6 +59,13 @@ $control-height = 30px
top $control-height
overflow auto
body[data-theme="white"]
.root
background-color $ui-white-noteList-backgroundColor
.control
background-color $ui-white-noteList-backgroundColor
body[data-theme="dark"]
.root
border-color $ui-dark-borderColor

View File

@@ -1,4 +1,5 @@
import React, { PropTypes } from 'react'
import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './NoteList.styl'
import moment from 'moment'
@@ -9,6 +10,12 @@ import ConfigManager from 'browser/main/lib/ConfigManager'
import NoteItem from 'browser/components/NoteItem'
import NoteItemSimple from 'browser/components/NoteItemSimple'
import searchFromNotes from 'browser/lib/search'
import fs from 'fs'
import { hashHistory } from 'react-router'
import markdown from 'browser/lib/markdown'
import { findNoteTitle } from 'browser/lib/findNoteTitle'
import store from 'browser/main/store'
import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig'
const { remote } = require('electron')
const { Menu, MenuItem, dialog } = remote
@@ -25,6 +32,18 @@ function sortByUpdatedAt (a, b) {
return new Date(b.updatedAt) - new Date(a.updatedAt)
}
function findNoteByKey (notes, noteKey) {
return notes.find((note) => `${note.storage}-${note.key}` === noteKey)
}
function findNotesByKeys (notes, noteKeys) {
return notes.filter((note) => noteKeys.includes(getNoteKey(note)))
}
function getNoteKey (note) {
return `${note.storage}-${note.key}`
}
class NoteList extends React.Component {
constructor (props) {
super(props)
@@ -42,13 +61,21 @@ class NoteList extends React.Component {
this.alertIfSnippetHandler = () => {
this.alertIfSnippet()
}
this.importFromFileHandler = this.importFromFile.bind(this)
this.jumpNoteByHash = this.jumpNoteByHashHandler.bind(this)
this.handleNoteListKeyUp = this.handleNoteListKeyUp.bind(this)
this.getNoteKeyFromTargetIndex = this.getNoteKeyFromTargetIndex.bind(this)
this.deleteNote = this.deleteNote.bind(this)
this.focusNote = this.focusNote.bind(this)
this.pinToTop = this.pinToTop.bind(this)
this.jumpToTopHandler = () => {
this.jumpToTop()
}
// TODO: not Selected noteKeys but SelectedNote(for reusing)
this.state = {
shiftKeyDown: false,
selectedNoteKeys: []
}
this.contextNotes = []
}
componentDidMount () {
@@ -57,8 +84,8 @@ class NoteList extends React.Component {
ee.on('list:prior', this.selectPriorNoteHandler)
ee.on('list:focus', this.focusHandler)
ee.on('list:isMarkdownNote', this.alertIfSnippetHandler)
ee.on('list:top', this.jumpToTopHandler)
ee.on('list:jumpToTop', this.jumpToTopHandler)
ee.on('import:file', this.importFromFileHandler)
ee.on('list:jump', this.jumpNoteByHash)
}
componentWillReceiveProps (nextProps) {
@@ -78,15 +105,16 @@ class NoteList extends React.Component {
ee.off('list:prior', this.selectPriorNoteHandler)
ee.off('list:focus', this.focusHandler)
ee.off('list:isMarkdownNote', this.alertIfSnippetHandler)
ee.off('list:top', this.jumpToTopHandler)
ee.off('list:jumpToTop', this.jumpToTopHandler)
ee.off('import:file', this.importFromFileHandler)
ee.off('list:jump', this.jumpNoteByHash)
}
componentDidUpdate (prevProps) {
let { location } = this.props
const { location } = this.props
if (this.notes.length > 0 && location.query.key == null) {
let { router } = this.context
const { router } = this.context
if (!location.pathname.match(/\/searched/)) this.contextNotes = this.getContextNotes()
router.replace({
pathname: location.pathname,
query: {
@@ -98,20 +126,18 @@ class NoteList extends React.Component {
// Auto scroll
if (_.isString(location.query.key) && prevProps.location.query.key === location.query.key) {
let targetIndex = _.findIndex(this.notes, (note) => {
return note != null && note.storage + '-' + note.key === location.query.key
})
const targetIndex = this.getTargetIndex()
if (targetIndex > -1) {
let list = this.refs.list
let item = list.childNodes[targetIndex]
const list = this.refs.list
const item = list.childNodes[targetIndex]
if (item == null) return false
let overflowBelow = item.offsetTop + item.clientHeight - list.clientHeight - list.scrollTop > 0
const overflowBelow = item.offsetTop + item.clientHeight - list.clientHeight - list.scrollTop > 0
if (overflowBelow) {
list.scrollTop = item.offsetTop + item.clientHeight - list.clientHeight
}
let overflowAbove = list.scrollTop > item.offsetTop
const overflowAbove = list.scrollTop > item.offsetTop
if (overflowAbove) {
list.scrollTop = item.offsetTop
}
@@ -119,29 +145,54 @@ class NoteList extends React.Component {
}
}
focusNote (selectedNoteKeys, noteKey) {
const { router } = this.context
const { location } = this.props
this.setState({
selectedNoteKeys
})
router.push({
pathname: location.pathname,
query: {
key: noteKey
}
})
}
getNoteKeyFromTargetIndex (targetIndex) {
const note = Object.assign({}, this.notes[targetIndex])
const noteKey = getNoteKey(note)
return noteKey
}
selectPriorNote () {
if (this.notes == null || this.notes.length === 0) {
return
}
let { router } = this.context
let { location } = this.props
let { selectedNoteKeys, shiftKeyDown } = this.state
let targetIndex = _.findIndex(this.notes, (note) => {
return note.storage + '-' + note.key === location.query.key
})
let targetIndex = this.getTargetIndex()
if (targetIndex === 0) {
return
}
targetIndex--
if (targetIndex < 0) targetIndex = 0
router.push({
pathname: location.pathname,
query: {
key: this.notes[targetIndex].storage + '-' + this.notes[targetIndex].key
}
})
if (!shiftKeyDown) { selectedNoteKeys = [] }
const priorNoteKey = this.getNoteKeyFromTargetIndex(targetIndex)
if (selectedNoteKeys.includes(priorNoteKey)) {
selectedNoteKeys.pop()
} else {
selectedNoteKeys.push(priorNoteKey)
}
this.focusNote(selectedNoteKeys, priorNoteKey)
ee.emit('list:moved')
}
selectNextNote () {
@@ -150,29 +201,58 @@ class NoteList extends React.Component {
}
let { router } = this.context
let { location } = this.props
let { selectedNoteKeys, shiftKeyDown } = this.state
let targetIndex = _.findIndex(this.notes, (note) => {
return note.storage + '-' + note.key === location.query.key
})
let targetIndex = this.getTargetIndex()
const isTargetLastNote = targetIndex === this.notes.length - 1
if (targetIndex === this.notes.length - 1) {
if (isTargetLastNote && shiftKeyDown) {
return
} else if (isTargetLastNote) {
targetIndex = 0
} else {
targetIndex++
if (targetIndex < 0) targetIndex = 0
else if (targetIndex > this.notes.length - 1) targetIndex === this.notes.length - 1
else if (targetIndex > this.notes.length - 1) targetIndex = this.notes.length - 1
}
router.push({
pathname: location.pathname,
query: {
key: this.notes[targetIndex].storage + '-' + this.notes[targetIndex].key
}
})
if (!shiftKeyDown) { selectedNoteKeys = [] }
const nextNoteKey = this.getNoteKeyFromTargetIndex(targetIndex)
if (selectedNoteKeys.includes(nextNoteKey)) {
selectedNoteKeys.pop()
} else {
selectedNoteKeys.push(nextNoteKey)
}
this.focusNote(selectedNoteKeys, nextNoteKey)
ee.emit('list:moved')
}
jumpNoteByHashHandler (event, noteHash) {
// first argument event isn't used.
if (this.notes === null || this.notes.length === 0) {
return
}
const { router } = this.context
const { location } = this.props
let targetIndex = this.getTargetIndex()
if (targetIndex < 0) targetIndex = 0
const selectedNoteKeys = []
const nextNoteKey = this.getNoteKeyFromTargetIndex(targetIndex)
selectedNoteKeys.push(nextNoteKey)
this.focusNote(selectedNoteKeys, nextNoteKey)
ee.emit('list:moved')
}
handleNoteListKeyDown (e) {
const { shiftKeyDown } = this.state
if (e.metaKey || e.ctrlKey) return true
if (e.keyCode === 65 && !e.shiftKey) {
@@ -182,7 +262,7 @@ class NoteList extends React.Component {
if (e.keyCode === 68) {
e.preventDefault()
ee.emit('detail:delete')
this.deleteNote()
}
if (e.keyCode === 69) {
@@ -199,55 +279,110 @@ class NoteList extends React.Component {
e.preventDefault()
this.selectNextNote()
}
if (e.shiftKey) {
this.setState({ shiftKeyDown: true })
}
}
handleNoteListKeyUp (e) {
if (!e.shiftKey) {
this.setState({ shiftKeyDown: false })
}
}
getNotes () {
let { data, params, location } = this.props
let { router } = this.context
const { data, params, location } = this.props
if (location.pathname.match(/\/home/)) {
return data.noteMap.map((note) => note)
if (location.pathname.match(/\/home/) || location.pathname.match(/\alltags/)) {
const allNotes = data.noteMap.map((note) => note)
this.contextNotes = allNotes
return allNotes
}
if (location.pathname.match(/\/starred/)) {
return data.starredSet.toJS()
.map((uniqueKey) => data.noteMap.get(uniqueKey))
const starredNotes = data.starredSet.toJS().map((uniqueKey) => data.noteMap.get(uniqueKey))
this.contextNotes = starredNotes
return starredNotes
}
if (location.pathname.match(/\/searched/)) {
const searchInputText = document.getElementsByClassName('searchInput')[0].value
if (searchInputText === '') {
router.push('/home')
return this.sortByPin(this.contextNotes)
}
return searchFromNotes(this.props.data, searchInputText)
return searchFromNotes(this.contextNotes, searchInputText)
}
let storageKey = params.storageKey
let folderKey = params.folderKey
let storage = data.storageMap.get(storageKey)
if (storage == null) return []
let folder = _.find(storage.folders, {key: folderKey})
if (folder == null) {
let storageNoteSet = data.storageNoteMap
.get(storage.key)
if (storageNoteSet == null) storageNoteSet = []
return storageNoteSet
.map((uniqueKey) => data.noteMap.get(uniqueKey))
if (location.pathname.match(/\/trashed/)) {
const trashedNotes = data.trashedSet.toJS().map((uniqueKey) => data.noteMap.get(uniqueKey))
this.contextNotes = trashedNotes
return trashedNotes
}
let folderNoteKeyList = data.folderNoteMap
.get(storage.key + '-' + folder.key)
if (location.pathname.match(/\/tags/)) {
return data.noteMap.map(note => {
return note
}).filter(note => {
return note.tags.includes(params.tagname)
})
}
return folderNoteKeyList != null
? folderNoteKeyList
.map((uniqueKey) => data.noteMap.get(uniqueKey))
: []
return this.getContextNotes()
}
// get notes in the current folder
getContextNotes () {
const { data, params } = this.props
const storageKey = params.storageKey
const folderKey = params.folderKey
const storage = data.storageMap.get(storageKey)
if (storage === undefined) return []
const folder = _.find(storage.folders, {key: folderKey})
if (folder === undefined) {
const storageNoteSet = data.storageNoteMap.get(storage.key) || []
return storageNoteSet.map((uniqueKey) => data.noteMap.get(uniqueKey))
}
const folderNoteKeyList = data.folderNoteMap.get(`${storage.key}-${folder.key}`) || []
return folderNoteKeyList.map((uniqueKey) => data.noteMap.get(uniqueKey))
}
sortByPin (unorderedNotes) {
const pinnedNotes = []
const unpinnedNotes = []
unorderedNotes.forEach((note) => {
if (note.isPinned) {
pinnedNotes.push(note)
} else {
unpinnedNotes.push(note)
}
})
return pinnedNotes.concat(unpinnedNotes)
}
handleNoteClick (e, uniqueKey) {
let { router } = this.context
let { location } = this.props
let { shiftKeyDown, selectedNoteKeys } = this.state
if (shiftKeyDown && selectedNoteKeys.includes(uniqueKey)) {
const newSelectedNoteKeys = selectedNoteKeys.filter((noteKey) => noteKey !== uniqueKey)
this.setState({
selectedNoteKeys: newSelectedNoteKeys
})
return
}
if (!shiftKeyDown) {
selectedNoteKeys = []
}
selectedNoteKeys.push(uniqueKey)
this.setState({
selectedNoteKeys
})
router.push({
pathname: location.pathname,
@@ -257,53 +392,10 @@ class NoteList extends React.Component {
})
}
handleNoteContextMenu (e, uniqueKey) {
let menu = new Menu()
menu.append(new MenuItem({
label: 'Delete Note',
click: (e) => this.handleDeleteNote(e, uniqueKey)
}))
menu.popup()
}
handleDeleteNote (e, uniqueKey) {
let index = dialog.showMessageBox(remote.getCurrentWindow(), {
type: 'warning',
message: 'Delete a note',
detail: 'This work cannot be undone.',
buttons: ['Confirm', 'Cancel']
})
if (index === 0) {
let { dispatch, location } = this.props
let splitted = uniqueKey.split('-')
let storageKey = splitted.shift()
let noteKey = splitted.shift()
dataApi
.deleteNote(storageKey, noteKey)
.then((data) => {
let dispatchHandler = () => {
dispatch({
type: 'DELETE_NOTE',
storageKey: data.storageKey,
noteKey: data.noteKey
})
}
if (location.query.key === uniqueKey) {
ee.once('list:moved', dispatchHandler)
ee.emit('list:next')
} else {
dispatchHandler()
}
})
}
}
handleSortByChange (e) {
let { dispatch } = this.props
const { dispatch } = this.props
let config = {
const config = {
sortBy: e.target.value
}
@@ -315,9 +407,9 @@ class NoteList extends React.Component {
}
handleListStyleButtonClick (e, style) {
let { dispatch } = this.props
const { dispatch } = this.props
let config = {
const config = {
listStyle: style
}
@@ -329,10 +421,7 @@ class NoteList extends React.Component {
}
alertIfSnippet () {
let { location } = this.props
const targetIndex = _.findIndex(this.notes, (note) => {
return `${note.storage}-${note.key}` === location.query.key
})
const targetIndex = this.getTargetIndex()
if (this.notes[targetIndex].type === 'SNIPPET_NOTE') {
dialog.showMessageBox(remote.getCurrentWindow(), {
type: 'warning',
@@ -343,50 +432,266 @@ class NoteList extends React.Component {
}
}
jumpToTop () {
if (this.notes === null || this.notes.length === 0) {
return
handleDragStart (e, note) {
const { selectedNoteKeys } = this.state
const notes = this.notes.map((note) => Object.assign({}, note))
const selectedNotes = findNotesByKeys(notes, selectedNoteKeys)
const noteData = JSON.stringify(selectedNotes)
e.dataTransfer.setData('note', noteData)
this.setState({ selectedNoteKeys: [] })
}
handleNoteContextMenu (e, uniqueKey) {
const { location } = this.props
const { selectedNoteKeys } = this.state
const note = findNoteByKey(this.notes, uniqueKey)
const noteKey = getNoteKey(note)
if (selectedNoteKeys.length === 0 || !selectedNoteKeys.includes(noteKey)) {
this.handleNoteClick(e, uniqueKey)
}
let { router } = this.context
let { location } = this.props
const targetIndex = 0
const pinLabel = note.isPinned ? 'Remove pin' : 'Pin to Top'
const deleteLabel = 'Delete Note'
router.push({
pathname: location.pathname,
query: {
key: this.notes[targetIndex].storage + '-' + this.notes[targetIndex].key
}
const menu = new Menu()
if (!location.pathname.match(/\/home|\/starred|\/trash/)) {
menu.append(new MenuItem({
label: pinLabel,
click: this.pinToTop
}))
}
menu.append(new MenuItem({
label: deleteLabel,
click: this.deleteNote
}))
menu.popup()
}
pinToTop () {
const { selectedNoteKeys } = this.state
const { dispatch } = this.props
const notes = this.notes.map((note) => Object.assign({}, note))
const selectedNotes = findNotesByKeys(notes, selectedNoteKeys)
Promise.all(
selectedNotes.map((note) => {
note.isPinned = !note.isPinned
return dataApi
.updateNote(note.storage, note.key, note)
})
)
.then((updatedNotes) => {
updatedNotes.forEach((note) => {
dispatch({
type: 'UPDATE_NOTE',
note
})
})
})
this.setState({ selectedNoteKeys: [] })
}
deleteNote () {
const { dispatch } = this.props
const { selectedNoteKeys } = this.state
const notes = this.notes.map((note) => Object.assign({}, note))
const selectedNotes = findNotesByKeys(notes, selectedNoteKeys)
const firstNote = selectedNotes[0]
if (firstNote.isTrashed) {
const noteExp = selectedNotes.length > 1 ? 'notes' : 'note'
const dialogueButtonIndex = dialog.showMessageBox(remote.getCurrentWindow(), {
type: 'warning',
message: 'Confirm note deletion',
detail: `This will permanently remove ${selectedNotes.length} ${noteExp}.`,
buttons: ['Confirm', 'Cancel']
})
if (dialogueButtonIndex === 1) return
Promise.all(
selectedNoteKeys.map((uniqueKey) => {
const storageKey = uniqueKey.split('-')[0]
const noteKey = uniqueKey.split('-')[1]
return dataApi
.deleteNote(storageKey, noteKey)
})
)
.then((data) => {
data.forEach((item) => {
dispatch({
type: 'DELETE_NOTE',
storageKey: item.storageKey,
noteKey: item.noteKey
})
})
})
.catch((err) => {
console.error('Cannot Delete note: ' + err)
})
console.log('Notes were all deleted')
} else {
Promise.all(
selectedNotes.map((note) => {
note.isTrashed = true
return dataApi
.updateNote(note.storage, note.key, note)
})
)
.then((newNotes) => {
newNotes.forEach((newNote) => {
dispatch({
type: 'UPDATE_NOTE',
note: newNote
})
})
AwsMobileAnalyticsConfig.recordDynamicCustomEvent('EDIT_NOTE')
console.log('Notes went to trash')
})
.catch((err) => {
console.error('Notes could not go to trash: ' + err)
})
}
this.setState({ selectedNoteKeys: [] })
}
importFromFile () {
const options = {
filters: [
{ name: 'Documents', extensions: ['md', 'txt'] }
],
properties: ['openFile', 'multiSelections']
}
dialog.showOpenDialog(remote.getCurrentWindow(), options, (filepaths) => {
this.addNotesFromFiles(filepaths)
})
}
handleDragStart (e, note) {
const noteData = JSON.stringify(note)
e.dataTransfer.setData('note', noteData)
handleDrop (e) {
e.preventDefault()
const { location } = this.props
const filepaths = Array.from(e.dataTransfer.files).map(file => { return file.path })
if (!location.pathname.match(/\/trashed/)) this.addNotesFromFiles(filepaths)
}
// Add notes to the current folder
addNotesFromFiles (filepaths) {
const { dispatch, location } = this.props
const { storage, folder } = this.resolveTargetFolder()
if (filepaths === undefined) return
filepaths.forEach((filepath) => {
fs.readFile(filepath, (err, data) => {
if (err) throw Error('File reading error: ', err)
const content = data.toString()
const newNote = {
content: content,
folder: folder.key,
title: markdown.strip(findNoteTitle(content)),
type: 'MARKDOWN_NOTE'
}
dataApi.createNote(storage.key, newNote)
.then((note) => {
dispatch({
type: 'UPDATE_NOTE',
note: note
})
hashHistory.push({
pathname: location.pathname,
query: {key: getNoteKey(note)}
})
})
})
})
}
getTargetIndex () {
const { location } = this.props
const targetIndex = _.findIndex(this.notes, (note) => {
return getNoteKey(note) === location.query.key
})
return targetIndex
}
resolveTargetFolder () {
const { data, params } = this.props
let storage = data.storageMap.get(params.storageKey)
// Find first storage
if (storage == null) {
for (const kv of data.storageMap) {
storage = kv[1]
break
}
}
if (storage == null) this.showMessageBox('No storage for importing note(s)')
const folder = _.find(storage.folders, {key: params.folderKey}) || storage.folders[0]
if (folder == null) this.showMessageBox('No folder for importing note(s)')
return {
storage,
folder
}
}
showMessageBox (message) {
dialog.showMessageBox(remote.getCurrentWindow(), {
type: 'warning',
message: message,
buttons: ['OK']
})
}
render () {
let { location, notes, config, dispatch } = this.props
let { selectedNoteKeys } = this.state
let sortFunc = config.sortBy === 'CREATED_AT'
? sortByCreatedAt
: config.sortBy === 'ALPHABETICAL'
? sortByAlphabetical
: sortByUpdatedAt
this.notes = notes = this.getNotes()
.sort(sortFunc)
const sortedNotes = location.pathname.match(/\/home|\/starred|\/trash/)
? this.getNotes().sort(sortFunc)
: this.sortByPin(this.getNotes().sort(sortFunc))
this.notes = notes = sortedNotes.filter((note) => {
// this is for the trash box
if (note.isTrashed !== true || location.pathname === '/trashed') return true
})
let noteList = notes
moment.locale('en', {
relativeTime: {
future: 'in %s',
past: '%s ago',
s: '%ds',
ss: '%ss',
m: '1m',
mm: '%dm',
h: 'an hour',
hh: '%dh',
d: '1d',
dd: '%dd',
M: '1M',
MM: '%dM',
y: '1Y',
yy: '%dY'
}
})
const noteList = notes
.map(note => {
if (note == null) {
return null
}
const isDefault = config.listStyle === 'DEFAULT'
const isActive = location.query.key === note.storage + '-' + note.key
const uniqueKey = getNoteKey(note)
const isActive = selectedNoteKeys.includes(uniqueKey)
const dateDisplay = moment(
config.sortBy === 'CREATED_AT'
? note.createdAt : note.updatedAt
).fromNow()
).fromNow('D')
const key = `${note.storage}-${note.key}`
if (isDefault) {
@@ -395,10 +700,11 @@ class NoteList extends React.Component {
isActive={isActive}
note={note}
dateDisplay={dateDisplay}
key={key}
handleNoteClick={this.handleNoteClick.bind(this)}
key={uniqueKey}
handleNoteContextMenu={this.handleNoteContextMenu.bind(this)}
handleNoteClick={this.handleNoteClick.bind(this)}
handleDragStart={this.handleDragStart.bind(this)}
pathname={location.pathname}
/>
)
}
@@ -407,9 +713,9 @@ class NoteList extends React.Component {
<NoteItemSimple
isActive={isActive}
note={note}
key={key}
handleNoteClick={this.handleNoteClick.bind(this)}
key={uniqueKey}
handleNoteContextMenu={this.handleNoteContextMenu.bind(this)}
handleNoteClick={this.handleNoteClick.bind(this)}
handleDragStart={this.handleDragStart.bind(this)}
/>
)
@@ -419,40 +725,44 @@ class NoteList extends React.Component {
<div className='NoteList'
styleName='root'
style={this.props.style}
onDrop={(e) => this.handleDrop(e)}
>
<div styleName='control'>
<div styleName='control-sortBy'>
<i className='fa fa-bolt' />
<i className='fa fa-angle-down' />
<select styleName='control-sortBy-select'
value={config.sortBy}
onChange={(e) => this.handleSortByChange(e)}
>
<option value='UPDATED_AT'>Updated Time</option>
<option value='CREATED_AT'>Created Time</option>
<option value='ALPHABETICAL'>Alphabetical</option>
<option value='UPDATED_AT'>Updated</option>
<option value='CREATED_AT'>Created</option>
<option value='ALPHABETICAL'>Alphabetically</option>
</select>
</div>
<button styleName={config.listStyle === 'DEFAULT'
? 'control-button--active'
: 'control-button'
}
onClick={(e) => this.handleListStyleButtonClick(e, 'DEFAULT')}
>
<i className='fa fa-th-large' />
</button>
<button styleName={config.listStyle === 'SMALL'
? 'control-button--active'
: 'control-button'
}
onClick={(e) => this.handleListStyleButtonClick(e, 'SMALL')}
>
<i className='fa fa-list-ul' />
</button>
<div styleName='control-button-area'>
<button styleName={config.listStyle === 'DEFAULT'
? 'control-button--active'
: 'control-button'
}
onClick={(e) => this.handleListStyleButtonClick(e, 'DEFAULT')}
>
<img styleName='iconTag' src='../resources/icon/icon-column.svg' />
</button>
<button styleName={config.listStyle === 'SMALL'
? 'control-button--active'
: 'control-button'
}
onClick={(e) => this.handleListStyleButtonClick(e, 'SMALL')}
>
<img styleName='iconTag' src='../resources/icon/icon-column-list.svg' />
</button>
</div>
</div>
<div styleName='list'
ref='list'
tabIndex='-1'
onKeyDown={(e) => this.handleNoteListKeyDown(e)}
onKeyUp={this.handleNoteListKeyUp}
>
{noteList}
</div>

View File

@@ -1,62 +1,84 @@
.root
absolute top left bottom
width $sideNav-width
background-color #f9f9f9
background-color #2E3235
user-select none
color $ui-text-color
height: 100vh
display: flex
flex-direction column
.top
height $topBar-height
padding-bottom 15px
.top-menu
.top-menu-preference
navButtonColor()
height $topBar-height
padding 0 15px
font-size 12px
width 100%
text-align left
position absolute
top 22px
right 10px
width 2em
background-color transparent
&:hover
color $ui-text-color
color $ui-button-default--active-backgroundColor
background-color transparent
&:active, &:active:hover
color $ui-text-color
background-color alpha($ui-button--active-backgroundColor, 20%)
color $ui-button-default--active-backgroundColor
.switch-buttons
background-color transparent
border 0
margin 24px auto 4px 14px
display flex
text-align center
.non-active-button
color $ui-inactive-text-color
font-size 16px
border 0
background-color transparent
transition 0.2s
display flex
text-align center
margin-right 4px;
&:hover
color alpha(#239F86, 60%)
.active-button
@extend .non-active-button
color $ui-button-default--active-backgroundColor
.top-menu-label
margin-left 5px
overflow ellipsis
opacity 0
.storageList
absolute left right
bottom 37px
top 160px
.tabBody
flex 1
display flex
flex-direction column
.tag-title
padding-left 15px
padding-bottom 13px
p
color $ui-button-default-color
.tagList
overflow-y auto
.storageList-empty
padding 0 10px
margin-top 15px
line-height 24px
color $ui-inactive-text-color
.navToggle
navButtonColor()
display block
position absolute
right 5px
bottom 5px
border-radius 16.5px
height 34px
width 34px
line-height 32px
padding 0
flex: 1
.root--folded
@extend .root
width 44px
.storageList-empty
white-space nowrap
transform rotate(90deg)
height 100vh
width $sideNav--folded-width
background-color #2E3235
.switch-buttons
display none
.top
height 60px
.top-menu
width 44px - 1
position static
width $sideNav--folded-width
height 60px
text-align center
&:hover .top-menu-label
transition opacity 0.15s
@@ -65,44 +87,54 @@
position fixed
display inline-block
height 30px
left 32px
left $sideNav--folded-width
padding 0 10px
margin-top -8px
opacity 0
margin-left 0
overflow hidden
background-color $ui-tooltip-backgroundColor
z-index 10
color white
line-height 30px
border-top-right-radius 2px
border-bottom-right-radius 2px
pointer-events none
font-size 12px
.menu-button, .menu-button--active
text-align center
&:hover .menu-button-label
transition opacity 0.15s
opacity 1
font-size 13px
.top-menu-preference
position absolute
left 7px
.menu-button-label
position fixed
display inline-block
height 32px
left 44px
padding 0 10px
margin-top -8px
margin-left 0
overflow ellipsis
background-color $ui-tooltip-backgroundColor
z-index 10
color white
line-height 32px
border-top-right-radius 2px
border-bottom-right-radius 2px
pointer-events none
opacity 0
font-size 12px
body[data-theme="white"]
.root, .root--folded
background-color #f9f9f9
color $ui-text-color
.top-menu-preference
navWhiteButtonColor()
background-color transparent
&:hover
color #0B99F1
background-color transparent
&:active, &:active:hover
color #0B99F1
background-color transparent
.non-active-button
color $ui-inactive-text-color
&:hover
color alpha(#0B99F1, 60%)
.tag-title
p
color $ui-text-color
.non-active-button
&:hover
color alpha(#0B99F1, 60%)
.active-button
@extend .non-active-button
color #0B99F1
body[data-theme="dark"]
.root, .root--folded
@@ -113,18 +145,21 @@ body[data-theme="dark"]
.top
border-color $ui-dark-borderColor
.top-menu
.top-menu-preference
navDarkButtonColor()
background-color transparent
&:active
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
background-color transparent
&:hover
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
background-color transparent
.storageList-empty
color $ui-dark-inactive-text-color
.navToggle
.non-active-button
color alpha($ui-dark-text-color, 60%)
&:hover
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
transition 0.15s
color $ui-dark-text-color
color alpha(#0B99F1, 60%)
.tag-title
p
color alpha($ui-dark-text-color, 60%)

View File

@@ -1,4 +1,5 @@
import React, { PropTypes } from 'react'
import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './StorageItem.styl'
import { hashHistory } from 'react-router'
@@ -8,6 +9,7 @@ import RenameFolderModal from 'browser/main/modals/RenameFolderModal'
import dataApi from 'browser/main/lib/dataApi'
import StorageItemChild from 'browser/components/StorageItem'
import eventEmitter from 'browser/main/lib/eventEmitter'
import _ from 'lodash'
const { remote } = require('electron')
const { Menu, MenuItem, dialog } = remote
@@ -22,7 +24,7 @@ class StorageItem extends React.Component {
}
handleHeaderContextMenu (e) {
let menu = new Menu()
const menu = new Menu()
menu.append(new MenuItem({
label: 'Add Folder',
click: (e) => this.handleAddFolderButtonClick(e)
@@ -38,7 +40,7 @@ class StorageItem extends React.Component {
}
handleUnlinkStorageClick (e) {
let index = dialog.showMessageBox(remote.getCurrentWindow(), {
const index = dialog.showMessageBox(remote.getCurrentWindow(), {
type: 'warning',
message: 'Unlink Storage',
detail: 'This work will just detatches a storage from Boostnote. (Any data won\'t be deleted.)',
@@ -46,7 +48,7 @@ class StorageItem extends React.Component {
})
if (index === 0) {
let { storage, dispatch } = this.props
const { storage, dispatch } = this.props
dataApi.removeStorage(storage.key)
.then(() => {
dispatch({
@@ -67,7 +69,7 @@ class StorageItem extends React.Component {
}
handleAddFolderButtonClick (e) {
let { storage } = this.props
const { storage } = this.props
modal.open(CreateFolderModal, {
storage
@@ -75,19 +77,19 @@ class StorageItem extends React.Component {
}
handleHeaderInfoClick (e) {
let { storage } = this.props
const { storage } = this.props
hashHistory.push('/storages/' + storage.key)
}
handleFolderButtonClick (folderKey) {
return (e) => {
let { storage } = this.props
const { storage } = this.props
hashHistory.push('/storages/' + storage.key + '/folders/' + folderKey)
}
}
handleFolderButtonContextMenu (e, folder) {
let menu = new Menu()
const menu = new Menu()
menu.append(new MenuItem({
label: 'Rename Folder',
click: (e) => this.handleRenameFolderClick(e, folder)
@@ -103,7 +105,7 @@ class StorageItem extends React.Component {
}
handleRenameFolderClick (e, folder) {
let { storage } = this.props
const { storage } = this.props
modal.open(RenameFolderModal, {
storage,
folder
@@ -111,15 +113,15 @@ class StorageItem extends React.Component {
}
handleFolderDeleteClick (e, folder) {
let index = dialog.showMessageBox(remote.getCurrentWindow(), {
const index = dialog.showMessageBox(remote.getCurrentWindow(), {
type: 'warning',
message: 'Delete Folder',
detail: 'This work will deletes all notes in the folder and can not be undone.',
detail: 'This will delete all notes in the folder and can not be undone.',
buttons: ['Confirm', 'Cancel']
})
if (index === 0) {
let { storage, dispatch } = this.props
const { storage, dispatch } = this.props
dataApi
.deleteFolder(storage.key, folder.key)
.then((data) => {
@@ -142,52 +144,67 @@ class StorageItem extends React.Component {
e.target.style.backgroundColor = e.dataTransfer.getData('defaultColor')
}
dropNote (storage, folder, dispatch, location, noteData) {
noteData = noteData.filter((note) => folder.key !== note.folder)
if (noteData.length === 0) return
const newNoteData = noteData.map((note) => Object.assign({}, note, {storage: storage, folder: folder.key}))
Promise.all(
newNoteData.map((note) => dataApi.createNote(storage.key, note))
)
.then((createdNoteData) => {
createdNoteData.forEach((note) => {
dispatch({
type: 'UPDATE_NOTE',
note: note
})
})
})
.catch((err) => {
console.error(`error on create notes: ${err}`)
})
.then(() => {
return Promise.all(
noteData.map((note) => dataApi.deleteNote(note.storage, note.key))
)
})
.then((deletedNoteData) => {
deletedNoteData.forEach((note) => {
dispatch({
type: 'DELETE_NOTE',
storageKey: note.storageKey,
noteKey: note.noteKey
})
})
})
.catch((err) => {
console.error(`error on delete notes: ${err}`)
})
}
handleDrop (e, storage, folder, dispatch, location) {
e.target.style.opacity = '1'
e.target.style.backgroundColor = e.dataTransfer.getData('defaultColor')
const noteData = JSON.parse(e.dataTransfer.getData('note'))
const newNoteData = Object.assign({}, noteData, {storage: storage, folder: folder.key})
if (folder.key === noteData.folder) return
dataApi
.createNote(storage.key, newNoteData)
.then((note) => {
dataApi
.deleteNote(noteData.storage, noteData.key)
.then((data) => {
let dispatchHandler = () => {
dispatch({
type: 'DELETE_NOTE',
storageKey: data.storageKey,
noteKey: data.noteKey
})
}
eventEmitter.once('list:moved', dispatchHandler)
eventEmitter.emit('list:next')
})
.catch((err) => {
console.error(err)
})
dispatch({
type: 'UPDATE_NOTE',
note: note
})
hashHistory.push({
pathname: location.pathname,
query: {key: `${note.storage}-${note.key}`}
})
})
this.dropNote(storage, folder, dispatch, location, noteData)
}
render () {
let { storage, location, isFolded, data, dispatch } = this.props
let { folderNoteMap } = data
let folderList = storage.folders.map((folder) => {
let isActive = !!(location.pathname.match(new RegExp('\/storages\/' + storage.key + '\/folders\/' + folder.key)))
let noteSet = folderNoteMap.get(storage.key + '-' + folder.key)
const { storage, location, isFolded, data, dispatch } = this.props
const { folderNoteMap, trashedSet } = data
const folderList = storage.folders.map((folder) => {
const isActive = !!(location.pathname.match(new RegExp('\/storages\/' + storage.key + '\/folders\/' + folder.key)))
const noteSet = folderNoteMap.get(storage.key + '-' + folder.key)
let noteCount = noteSet != null
? noteSet.size
: 0
let noteCount = 0
if (noteSet) {
let trashedNoteCount = 0
const noteKeys = noteSet.map(noteKey => { return noteKey })
trashedSet.toJS().forEach(trashedKey => {
if (noteKeys.some(noteKey => { return noteKey === trashedKey })) trashedNoteCount++
})
noteCount = noteSet.size - trashedNoteCount
}
return (
<StorageItemChild
key={folder.key}
@@ -205,7 +222,7 @@ class StorageItem extends React.Component {
)
})
let isActive = location.pathname.match(new RegExp('\/storages\/' + storage.key + '$'))
const isActive = location.pathname.match(new RegExp('\/storages\/' + storage.key + '$'))
return (
<div styleName={isFolded ? 'root--folded' : 'root'}
@@ -220,9 +237,9 @@ class StorageItem extends React.Component {
<button styleName='header-toggleButton'
onMouseDown={(e) => this.handleToggleButtonClick(e)}
>
<i className={this.state.isOpen
? 'fa fa-caret-down'
: 'fa fa-caret-right'
<img src={this.state.isOpen
? '../resources/icon/icon-down.svg'
: '../resources/icon/icon-right.svg'
}
/>
</button>
@@ -231,7 +248,7 @@ class StorageItem extends React.Component {
<button styleName='header-addFolderButton'
onClick={(e) => this.handleAddFolderButtonClick(e)}
>
<i className='fa fa-plus' />
<img styleName='iconTag' src='../resources/icon/icon-plus.svg' />
</button>
}
@@ -239,7 +256,7 @@ class StorageItem extends React.Component {
onClick={(e) => this.handleHeaderInfoClick(e)}
>
<span styleName='header-info-name'>
{isFolded ? storage.name.substring(0, 1) : storage.name}
{isFolded ? _.truncate(storage.name, {length: 1, omission: ''}) : storage.name}
</span>
{isFolded &&
<span styleName='header-info--folded-tooltip'>

View File

@@ -1,30 +1,27 @@
.root
width 100%
user-select none
padding-top 20px
.header
position relative
height 25px
height 36px
width 100%
margin-bottom 5px
transition 0.15s
display flex
align-items center
.header--active
margin-bottom 5px
background-color $ui-button--active-backgroundColor
background-color alpha($ui-button-default--active-backgroundColor, 20%)
transition color background-color 0.15s
.header--active
display flex
align-items center
.header-toggleButton
color $ui-text-color
.header--active
.header-info
color $ui-text-color
.header--active
.header-addFolderButton
color $ui-text-color
color #1EC38B
.header-toggleButton
navButtonColor()
@@ -37,23 +34,31 @@
border-radius 50%
&:hover
transition 0.2s
background-color alpha($ui-button--active-backgroundColor, 40%)
background-color alpha($ui-button-default--hover-backgroundColor, 40%)
color $ui-text-color
.header-info
navButtonColor()
display block
width 100%
height 25px
padding-left 23px
padding-right 10px
height 36px
padding-left 25px
padding-right 15px
line-height 22px
cursor pointer
font-size 13px
font-size 14px
border none
overflow ellipsis
text-align left
background-color alpha($ui-button--active-backgroundColor, 20%)
font-weight 600;
background-color transparent
&:hover
color #1EC38B
background-color alpha($ui-button-default--active-backgroundColor, 20%)
transition background-color 0.15s
&:active, &:active:hover
color #1EC38B
background-color alpha($ui-button-default--active-backgroundColor, 20%)
.header-info-path
font-size 10px
@@ -62,22 +67,20 @@
.header-addFolderButton
navButtonColor()
position absolute
right 0
right 7px
width 25px
height 25px
padding 0
border none
margin-right 5px
border-radius 50%
&:hover
transition 0.2s
background-color alpha($ui-button--active-backgroundColor, 40%)
color $ui-text-color
.root--folded
@extend .root
.header
width 100%
padding-left 5px
.header-info
overflow ellipsis
padding 0 0 0 18px
@@ -87,6 +90,7 @@
display none
.header-toggleButton
width 15px
padding-left 9px
.header-info--folded-tooltip
tooltip()
position fixed
@@ -101,6 +105,33 @@
font-size 10px
margin 0 5px
body[data-theme="white"]
.header--active
background-color $ui-button--active-backgroundColor
transition color background-color 0.15s
.header-toggleButton
color $ui-text-color
.header-info
color $ui-text-color
.header-addFolderButton
color $ui-text-color
.header-toggleButton
navWhiteButtonColor()
&:hover
background-color alpha($ui-button--active-backgroundColor, 40%)
color $ui-text-color
.header-info
navWhiteButtonColor()
background-color alpha($ui-button--active-backgroundColor, 20%)
.header-addFolderButton
navWhiteButtonColor()
&:hover
background-color alpha($ui-button--active-backgroundColor, 40%)
color $ui-text-color
body[data-theme="dark"]
.header--active
background-color $ui-dark-button--active-backgroundColor

View File

@@ -1,30 +1,44 @@
import React, { PropTypes } from 'react'
import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './SideNav.styl'
import { openModal } from 'browser/main/lib/modal'
import PreferencesModal from '../modals/PreferencesModal'
import ConfigManager from 'browser/main/lib/ConfigManager'
import StorageItem from './StorageItem'
import TagListItem from 'browser/components/TagListItem'
import SideNavFilter from 'browser/components/SideNavFilter'
import StorageList from 'browser/components/StorageList'
import NavToggleButton from 'browser/components/NavToggleButton'
import EventEmitter from 'browser/main/lib/eventEmitter'
class SideNav extends React.Component {
// TODO: should not use electron stuff v0.7
componentDidMount () {
EventEmitter.on('side:preferences', this.handleMenuButtonClick)
}
componentWillUnmount () {
EventEmitter.off('side:preferences', this.handleMenuButtonClick)
}
handleMenuButtonClick (e) {
openModal(PreferencesModal)
}
handleHomeButtonClick (e) {
let { router } = this.context
const { router } = this.context
router.push('/home')
}
handleStarredButtonClick (e) {
let { router } = this.context
const { router } = this.context
router.push('/starred')
}
handleToggleButtonClick (e) {
let { dispatch, config } = this.props
const { dispatch, config } = this.props
ConfigManager.set({isSideNavFolded: !config.isSideNavFolded})
dispatch({
@@ -33,14 +47,101 @@ class SideNav extends React.Component {
})
}
handleTrashedButtonClick (e) {
const { router } = this.context
router.push('/trashed')
}
handleSwitchFoldersButtonClick () {
const { router } = this.context
router.push('/home')
}
handleSwitchTagsButtonClick () {
const { router } = this.context
router.push('/alltags')
}
SideNavComponent (isFolded, storageList) {
const { location, data } = this.props
const isHomeActive = !!location.pathname.match(/^\/home$/)
const isStarredActive = !!location.pathname.match(/^\/starred$/)
const isTrashedActive = !!location.pathname.match(/^\/trashed$/)
let component
// TagsMode is not selected
if (!location.pathname.match('/tags') && !location.pathname.match('/alltags')) {
component = (
<div>
<SideNavFilter
isFolded={isFolded}
isHomeActive={isHomeActive}
handleAllNotesButtonClick={(e) => this.handleHomeButtonClick(e)}
isStarredActive={isStarredActive}
isTrashedActive={isTrashedActive}
handleStarredButtonClick={(e) => this.handleStarredButtonClick(e)}
handleTrashedButtonClick={(e) => this.handleTrashedButtonClick(e)}
counterTotalNote={data.noteMap._map.size}
counterStarredNote={data.starredSet._set.size}
counterDelNote={data.trashedSet._set.size}
/>
<StorageList storageList={storageList} />
<NavToggleButton isFolded={isFolded} handleToggleButtonClick={this.handleToggleButtonClick.bind(this)} />
</div>
)
} else {
component = (
<div styleName='tabBody'>
<div styleName='tag-title'>
<p>Tags</p>
</div>
<div styleName='tagList'>
{this.tagListComponent(data)}
</div>
</div>
)
}
return component
}
tagListComponent () {
const { data, location } = this.props
const tagList = data.tagNoteMap.map((tag, key) => {
return key
})
return (
tagList.map(tag => (
<TagListItem
name={tag}
handleClickTagListItem={this.handleClickTagListItem.bind(this)}
isActive={this.getTagActive(location.pathname, tag)}
key={tag}
/>
))
)
}
getTagActive (path, tag) {
const pathSegments = path.split('/')
const pathTag = pathSegments[pathSegments.length - 1]
return pathTag === tag
}
handleClickTagListItem (name) {
const { router } = this.context
router.push(`/tags/${name}`)
}
render () {
let { data, location, config, dispatch } = this.props
const { data, location, config, dispatch } = this.props
let isFolded = config.isSideNavFolded
let isHomeActive = !!location.pathname.match(/^\/home$/)
let isStarredActive = !!location.pathname.match(/^\/starred$/)
const isFolded = config.isSideNavFolded
let storageList = data.storageMap.map((storage, key) => {
const storageList = data.storageMap.map((storage, key) => {
return <StorageItem
key={storage.key}
storage={storage}
@@ -50,8 +151,9 @@ class SideNav extends React.Component {
dispatch={dispatch}
/>
})
let style = {}
const style = {}
if (!isFolded) style.width = this.props.width
const isTagActive = location.pathname.match(/tag/)
return (
<div className='SideNav'
styleName={isFolded ? 'root--folded' : 'root'}
@@ -59,35 +161,31 @@ class SideNav extends React.Component {
style={style}
>
<div styleName='top'>
<button styleName='top-menu'
onClick={(e) => this.handleMenuButtonClick(e)}
>
<i className='fa fa-navicon fa-fw' />
<span styleName='top-menu-label'>Menu</span>
</button>
<div styleName='switch-buttons'>
<button styleName={isTagActive ? 'non-active-button' : 'active-button'} onClick={this.handleSwitchFoldersButtonClick.bind(this)}>
<img src={isTagActive
? '../resources/icon/icon-list.svg'
: '../resources/icon/icon-list-active.svg'
}
/>
</button>
<button styleName={isTagActive ? 'active-button' : 'non-active-button'} onClick={this.handleSwitchTagsButtonClick.bind(this)}>
<img src={isTagActive
? '../resources/icon/icon-tag-active.svg'
: '../resources/icon/icon-tag.svg'
}
/>
</button>
</div>
<div>
<button styleName='top-menu-preference'
onClick={(e) => this.handleMenuButtonClick(e)}
>
<img styleName='iconTag' src='../resources/icon/icon-setting.svg' />
</button>
</div>
</div>
<SideNavFilter
isFolded={isFolded}
isHomeActive={isHomeActive}
handleAllNotesButtonClick={(e) => this.handleHomeButtonClick(e)}
isStarredActive={isStarredActive}
handleStarredButtonClick={(e) => this.handleStarredButtonClick(e)}
/>
<div styleName='storageList'>
{storageList.length > 0 ? storageList : (
<div styleName='storageList-empty'>No storage mount.</div>
)}
</div>
<button styleName='navToggle'
onClick={(e) => this.handleToggleButtonClick(e)}
>
{isFolded
? <i className='fa fa-angle-double-right' />
: <i className='fa fa-angle-double-left' />
}
</button>
{this.SideNavComponent(isFolded, storageList)}
</div>
)
}

View File

@@ -1,9 +1,10 @@
@import('../Detail/DetailVars')
.root
absolute bottom left right
height $statusBar-height
background-color $ui-noteDetail-backgroundColor
position absolute
bottom 10px
right 10px
z-index 100
display flex
.blank
@@ -21,8 +22,19 @@
.zoom
navButtonColor()
height 24px
color rgba(0,0,0,.54)
height 20px
display flex
padding 0
align-items center
background-color transparent
&:hover
color $ui-active-color
&:active
color $ui-active-color
span
margin-left 5px
.update
navButtonColor()
height 24px
@@ -37,14 +49,14 @@
body[data-theme="dark"]
.root
background-color $ui-dark-noteDetail-backgroundColor
border-color $ui-dark-borderColor
box-shadow none
.zoom
border-color $ui-dark-borderColor
background-color transparent
color #f9f9f9
&:hover
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
transition 0.15s
color $ui-dark-text-color

View File

@@ -1,8 +1,8 @@
import React, { PropTypes } from 'react'
import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './StatusBar.styl'
import ZoomManager from 'browser/main/lib/ZoomManager'
import LastUpdatedString from '../Detail/LastUpdatedString'
const electron = require('electron')
const { remote, ipcRenderer } = electron
@@ -12,7 +12,7 @@ const zoomOptions = [0.8, 0.9, 1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2
class StatusBar extends React.Component {
updateApp () {
let index = dialog.showMessageBox(remote.getCurrentWindow(), {
const index = dialog.showMessageBox(remote.getCurrentWindow(), {
type: 'warning',
message: 'Update Boostnote',
detail: 'New Boostnote is ready to be installed.',
@@ -25,7 +25,7 @@ class StatusBar extends React.Component {
}
handleZoomButtonClick (e) {
let menu = new Menu()
const menu = new Menu()
zoomOptions.forEach((zoom) => {
menu.append(new MenuItem({
@@ -38,7 +38,7 @@ class StatusBar extends React.Component {
}
handleZoomMenuItemClick (zoomFactor) {
let { dispatch } = this.props
const { dispatch } = this.props
ZoomManager.setZoom(zoomFactor)
dispatch({
type: 'SET_ZOOM',
@@ -47,7 +47,7 @@ class StatusBar extends React.Component {
}
render () {
let { config, status } = this.context
const { config, status } = this.context
return (
<div className='StatusBar'
@@ -56,20 +56,16 @@ class StatusBar extends React.Component {
<button styleName='zoom'
onClick={(e) => this.handleZoomButtonClick(e)}
>
<i className='fa fa-search-plus' />&nbsp;
{Math.floor(config.zoom * 100)}%
<img src='../resources/icon/icon-zoom.svg' />
<span>{Math.floor(config.zoom * 100)}%</span>
</button>
<div styleName='blank' />
{status.updateReady
? <button onClick={this.updateApp} styleName='update'>
<i styleName='update-icon' className='fa fa-cloud-download' /> Ready to Update!
</button>
: null
}
<LastUpdatedString date={this.props.date} />
</div>
)
}

View File

@@ -19,26 +19,25 @@ $control-height = 34px
.control-search
height 32px
width 1px
flex 1
background-color white
position relative
.control-search-icon
absolute top bottom left
line-height 32px
width 35px
color $ui-inactive-text-color
background-color $ui-noteList-backgroundColor
.control-search-input
display block
absolute top bottom right
left 30px
width 100%
padding-left 12px
background-color $ui-noteList-backgroundColor
input
width 100%
height 100%
outline none
border none
color $ui-text-color
font-size 18px
padding-bottom 2px
background-color $ui-noteList-backgroundColor
.control-search-optionList
@@ -92,9 +91,7 @@ $control-height = 34px
width 32px
height $control-height - 2
navButtonColor()
border $ui-border
border-radius 32px
font-size 14px
font-size 16px
line-height 28px
padding 0
&:active
@@ -115,6 +112,21 @@ $control-height = 34px
opacity 0
transition 0.1s
body[data-theme="white"]
.root, .root--expanded
background-color $ui-white-noteList-backgroundColor
.control
border-color $ui-dark-borderColor
.control-search
background-color $ui-white-noteList-backgroundColor
.control-search-input
background-color $ui-white-noteList-backgroundColor
input
background-color $ui-white-noteList-backgroundColor
body[data-theme="dark"]
.root, .root--expanded
background-color $ui-dark-noteList-backgroundColor
@@ -132,6 +144,7 @@ body[data-theme="dark"]
background-color $ui-dark-noteList-backgroundColor
.control-search-input
background-color $ui-dark-noteList-backgroundColor
input
background-color $ui-dark-noteList-backgroundColor
color $ui-dark-text-color

View File

@@ -1,15 +1,10 @@
import React, { PropTypes } from 'react'
import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './TopBar.styl'
import _ from 'lodash'
import modal from 'browser/main/lib/modal'
import NewNoteModal from 'browser/main/modals/NewNoteModal'
import { hashHistory } from 'react-router'
import ee from 'browser/main/lib/eventEmitter'
import ConfigManager from 'browser/main/lib/ConfigManager'
import dataApi from 'browser/main/lib/dataApi'
const OSX = window.process.platform === 'darwin'
import NewNoteButton from 'browser/main/NewNoteButton'
class TopBar extends React.Component {
constructor (props) {
@@ -18,11 +13,10 @@ class TopBar extends React.Component {
this.state = {
search: '',
searchOptions: [],
isSearching: false
}
this.newNoteHandler = () => {
this.handleNewPostButtonClick()
isSearching: false,
isAlphabet: false,
isIME: false,
isConfirmTranslation: false
}
this.focusSearchHandler = () => {
@@ -31,85 +25,64 @@ class TopBar extends React.Component {
}
componentDidMount () {
ee.on('top:new-note', this.newNoteHandler)
ee.on('top:focus-search', this.focusSearchHandler)
}
componentWillUnmount () {
ee.off('top:new-note', this.newNoteHandler)
ee.off('top:focus-search', this.focusSearchHandler)
}
handleNewPostButtonClick (e) {
let { config } = this.props
handleKeyDown (e) {
// reset states
this.setState({
isAlphabet: false,
isIME: false
})
switch (config.ui.defaultNote) {
case 'MARKDOWN_NOTE':
this.createNote('MARKDOWN_NOTE')
break
case 'SNIPPET_NOTE':
this.createNote('SNIPPET_NOTE')
break
case 'ALWAYS_ASK':
default:
let { dispatch, location } = this.props
let { storage, folder } = this.resolveTargetFolder()
modal.open(NewNoteModal, {
storage: storage.key,
folder: folder.key,
dispatch,
location
})
// When the key is an alphabet, del, enter or ctr
if (e.keyCode <= 90 || e.keyCode >= 186 && e.keyCode <= 222) {
this.setState({
isAlphabet: true
})
// When the key is an IME input (Japanese, Chinese)
} else if (e.keyCode === 229) {
this.setState({
isIME: true
})
}
}
resolveTargetFolder () {
let { data, params } = this.props
let storage = data.storageMap.get(params.storageKey)
handleKeyUp (e) {
const { router } = this.context
// reset states
this.setState({
isConfirmTranslation: false
})
// Find first storage
if (storage == null) {
for (let kv of data.storageMap) {
storage = kv[1]
break
}
}
if (storage == null) window.alert('No storage to create a note')
let folder = _.find(storage.folders, {key: params.folderKey})
if (folder == null) folder = storage.folders[0]
if (folder == null) window.alert('No folder to create a note')
return {
storage,
folder
// When the key is translation confirmation (Enter, Space)
if (this.state.isIME && (e.keyCode === 32 || e.keyCode === 13)) {
this.setState({
isConfirmTranslation: true
})
router.push('/searched')
this.setState({
search: this.refs.searchInput.value
})
}
}
handleSearchChange (e) {
let { router } = this.context
router.push('/searched')
const { router } = this.context
if (this.state.isAlphabet || this.state.isConfirmTranslation) {
router.push('/searched')
} else {
e.preventDefault()
}
this.setState({
search: this.refs.searchInput.value
})
}
handleOptionClick (uniqueKey) {
return (e) => {
this.setState({
isSearching: false
}, () => {
let { location } = this.props
hashHistory.push({
pathname: location.pathname,
query: {
key: uniqueKey
}
})
})
}
}
handleSearchFocus (e) {
this.setState({
isSearching: true
@@ -134,60 +107,6 @@ class TopBar extends React.Component {
}
}
createNote (noteType) {
let { dispatch, location } = this.props
if (noteType !== 'MARKDOWN_NOTE' && noteType !== 'SNIPPET_NOTE') throw new Error('Invalid note type.')
let { storage, folder } = this.resolveTargetFolder()
let newNote = noteType === 'MARKDOWN_NOTE'
? {
type: 'MARKDOWN_NOTE',
folder: folder.key,
title: '',
content: ''
}
: {
type: 'SNIPPET_NOTE',
folder: folder.key,
title: '',
description: '',
snippets: [{
name: '',
mode: 'text',
content: ''
}]
}
dataApi
.createNote(storage.key, newNote)
.then((note) => {
dispatch({
type: 'UPDATE_NOTE',
note: note
})
hashHistory.push({
pathname: location.pathname,
query: {key: note.storage + '-' + note.key}
})
ee.emit('detail:focus')
})
}
setDefaultNote (defaultNote) {
let { config, dispatch } = this.props
let ui = Object.assign(config.ui)
ui.defaultNote = defaultNote
ConfigManager.set({
ui
})
dispatch({
type: 'SET_UI',
config: ConfigManager.get()
})
}
handleOnSearchFocus () {
if (this.state.isSearching) {
this.refs.search.childNodes[0].blur()
@@ -197,7 +116,7 @@ class TopBar extends React.Component {
}
render () {
let { config, style, data } = this.props
const { config, style, location } = this.props
return (
<div className='TopBar'
styleName={config.isSideNavFolded ? 'root--expanded' : 'root'}
@@ -205,7 +124,6 @@ class TopBar extends React.Component {
>
<div styleName='control'>
<div styleName='control-search'>
<i styleName='control-search-icon' className='fa fa-search fa-fw' />
<div styleName='control-search-input'
onFocus={(e) => this.handleSearchFocus(e)}
onBlur={(e) => this.handleSearchBlur(e)}
@@ -216,6 +134,8 @@ class TopBar extends React.Component {
ref='searchInput'
value={this.state.search}
onChange={(e) => this.handleSearchChange(e)}
onKeyDown={(e) => this.handleKeyDown(e)}
onKeyUp={(e) => this.handleKeyUp(e)}
placeholder='Search'
type='text'
className='searchInput'
@@ -230,14 +150,17 @@ class TopBar extends React.Component {
}
</div>
<button styleName='control-newPostButton'
onClick={(e) => this.handleNewPostButtonClick(e)}>
<i className='fa fa-plus' />
<span styleName='control-newPostButton-tooltip'>
Make a Note {OSX ? '⌘' : '^'} + n
</span>
</button>
</div>
{location.pathname === '/trashed' ? ''
: <NewNoteButton
{..._.pick(this.props, [
'dispatch',
'data',
'config',
'params',
'location'
])}
/>}
</div>
)
}

View File

@@ -12,6 +12,7 @@ body
color textColor
font-size fontSize
font-weight 200
-webkit-font-smoothing antialiased
button, input, select, textarea
font-family DEFAULT_FONTS
@@ -64,7 +65,7 @@ textarea.block-input
fullsize()
modalZIndex= 1000
modalBackColor = transparentify(white, 65%)
modalBackColor = white
.ace_focus
outline-color rgb(59, 153, 252)
outline-offset 0px
@@ -86,12 +87,12 @@ modalBackColor = transparentify(white, 65%)
body[data-theme="dark"]
.ModalBase
.modalBack
background-color alpha(black, 60%)
background-color $ui-dark-backgroundColor
.CodeMirror
font-family inherit !important
line-height 1.4em
height 100%
height 96%
.CodeMirror > div > textarea
margin-bottom -1em
.CodeMirror-focused .CodeMirror-selected
@@ -102,3 +103,10 @@ body[data-theme="dark"]
background #B1D7FE
::selection
background #B1D7FE
.sortableItemHelper
z-index modalZIndex + 5
body[data-theme="dark"]
.sortableItemHelper
color: $ui-dark-text-color

View File

@@ -23,7 +23,20 @@ document.addEventListener('dragover', function (e) {
e.stopPropagation()
})
let el = document.getElementById('content')
document.addEventListener('click', function (e) {
const className = e.target.className
if (!className && typeof (className) !== 'string') return
const isInfoButton = className.includes('infoButton')
const offsetParent = e.target.offsetParent
const isInfoPanel = offsetParent !== null
? offsetParent.className.includes('infoPanel')
: false
if (isInfoButton || isInfoPanel) return
const infoPanel = document.querySelector('.infoPanel')
if (infoPanel) infoPanel.style.display = 'none'
})
const el = document.getElementById('content')
const history = syncHistoryWithStore(hashHistory, store)
function notify (...args) {
@@ -31,7 +44,7 @@ function notify (...args) {
}
function updateApp () {
let index = dialog.showMessageBox(remote.getCurrentWindow(), {
const index = dialog.showMessageBox(remote.getCurrentWindow(), {
type: 'warning',
message: 'Update Boostnote',
detail: 'New Boostnote is ready to be installed.',
@@ -51,6 +64,12 @@ ReactDOM.render((
<Route path='home' />
<Route path='starred' />
<Route path='searched' />
<Route path='trashed' />
<Route path='alltags' />
<Route path='tags'>
<IndexRedirect to='/alltags' />
<Route path=':tagname' />
</Route>
<Route path='storages'>
<IndexRedirect to='/home' />
<Route path=':storageKey'>
@@ -62,7 +81,7 @@ ReactDOM.render((
</Router>
</Provider>
), el, function () {
let loadingCover = document.getElementById('loadingCover')
const loadingCover = document.getElementById('loadingCover')
loadingCover.parentNode.removeChild(loadingCover)
ipcRenderer.on('update-ready', function () {

View File

@@ -2,36 +2,74 @@ const AWS = require('aws-sdk')
const AMA = require('aws-sdk-mobile-analytics')
const ConfigManager = require('browser/main/lib/ConfigManager')
const mobileAnalyticsClient = new AMA.Manager({
appId: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
appTitle: 'xxxxxxxxxx'
})
const remote = require('electron').remote
const os = require('os')
let mobileAnalyticsClient
function initAwsMobileAnalytics () {
AWS.config.region = 'us-east-1'
AWS.config.region = 'us-east-1'
if (process.env.NODE_ENV === 'production' && ConfigManager.default.get().amaEnabled) {
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'us-east-1:xxxxxxxxxxxxxxxxxxxxxxxxx'
})
const validPlatformName = convertPlatformName(os.platform())
mobileAnalyticsClient = new AMA.Manager({
appId: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
appTitle: 'xxxxxxxxxx',
appVersionName: remote.app.getVersion().toString(),
platform: validPlatformName
})
}
function convertPlatformName (platformName) {
if (platformName === 'darwin') {
return 'MacOS'
} else if (platformName === 'win32') {
return 'Windows'
} else if (platformName === 'linux') {
return 'Linux'
} else {
return ''
}
}
function initAwsMobileAnalytics () {
if (process.env.NODE_ENV !== 'production' || !ConfigManager.default.get().amaEnabled) return
AWS.config.credentials.get((err) => {
if (!err) {
console.log('Cognito Identity ID: ' + AWS.config.credentials.identityId)
recordDynamicCustomEvent('APP_STARTED')
recordStaticCustomEvent()
}
})
recordStaticCustomEvent()
}
function recordDynamitCustomEvent (type) {
mobileAnalyticsClient.recordEvent(type)
function recordDynamicCustomEvent (type, options = {}) {
if (process.env.NODE_ENV !== 'production' || !ConfigManager.default.get().amaEnabled) return
try {
mobileAnalyticsClient.recordEvent(type, options)
} catch (analyticsError) {
if (analyticsError instanceof ReferenceError) {
console.log(analyticsError.name + ': ' + analyticsError.message)
}
}
}
function recordStaticCustomEvent () {
mobileAnalyticsClient.recordEvent('UI_COLOR_THEME', {
uiColorTheme: ConfigManager.default.get().ui.theme
})
if (process.env.NODE_ENV !== 'production' || !ConfigManager.default.get().amaEnabled) return
try {
mobileAnalyticsClient.recordEvent('UI_COLOR_THEME', {
uiColorTheme: ConfigManager.default.get().ui.theme
})
} catch (analyticsError) {
if (analyticsError instanceof ReferenceError) {
console.log(analyticsError.name + ': ' + analyticsError.message)
}
}
}
module.exports = {
initAwsMobileAnalytics,
recordDynamitCustomEvent
recordDynamicCustomEvent
}

View File

@@ -13,10 +13,10 @@ function release (el) {
function fire (command) {
console.info('COMMAND >>', command)
let splitted = command.split(':')
let target = splitted[0]
let targetCommand = splitted[1]
let targetCallees = callees
const splitted = command.split(':')
const target = splitted[0]
const targetCommand = splitted[1]
const targetCallees = callees
.filter((callee) => callee.name === target)
targetCallees.forEach((callee) => {

View File

@@ -1,6 +1,8 @@
import _ from 'lodash'
import RcParser from 'browser/lib/RcParser'
const OSX = global.process.platform === 'darwin'
const win = global.process.platform === 'win32'
const electron = require('electron')
const { ipcRenderer } = electron
const consts = require('browser/lib/consts')
@@ -14,28 +16,30 @@ export const DEFAULT_CONFIG = {
navWidth: 200,
sortBy: 'UPDATED_AT', // 'CREATED_AT', 'UPDATED_AT', 'APLHABETICAL'
listStyle: 'DEFAULT', // 'DEFAULT', 'SMALL'
amaEnabled: true,
hotkey: {
toggleFinder: OSX ? 'Cmd + Alt + S' : 'Super + Alt + S',
toggleMain: OSX ? 'Cmd + Alt + L' : 'Super + Alt + E'
},
ui: {
theme: 'default',
showCopyNotification: true,
disableDirectWrite: false,
defaultNote: 'ALWAYS_ASK' // 'ALWAYS_ASK', 'SNIPPET_NOTE', 'MARKDOWN_NOTE'
},
editor: {
theme: 'default',
theme: 'base16-light',
keyMap: 'sublime',
fontSize: '14',
fontFamily: 'Monaco, Consolas',
fontFamily: win ? 'Segoe UI' : 'Monaco, Consolas',
indentType: 'space',
indentSize: '2',
switchPreview: 'BLUR' // Available value: RIGHTCLICK, BLUR
},
preview: {
fontSize: '14',
fontFamily: 'Lato',
codeBlockTheme: 'elegant',
fontFamily: win ? 'Segoe UI' : 'Lato',
codeBlockTheme: 'dracula',
lineNumber: true
}
}
@@ -55,17 +59,17 @@ function _save (config) {
}
function get () {
let config = window.localStorage.getItem('config')
const rawStoredConfig = window.localStorage.getItem('config')
const storedConfig = Object.assign({}, DEFAULT_CONFIG, JSON.parse(rawStoredConfig))
let config = storedConfig
try {
config = Object.assign({}, DEFAULT_CONFIG, JSON.parse(config))
config.hotkey = Object.assign({}, DEFAULT_CONFIG.hotkey, config.hotkey)
config.ui = Object.assign({}, DEFAULT_CONFIG.ui, config.ui)
config.editor = Object.assign({}, DEFAULT_CONFIG.editor, config.editor)
config.preview = Object.assign({}, DEFAULT_CONFIG.preview, config.preview)
const boostnotercConfig = RcParser.parse()
config = assignConfigValues(storedConfig, boostnotercConfig)
if (!validate(config)) throw new Error('INVALID CONFIG')
} catch (err) {
console.warn('Boostnote resets the malformed configuration.')
console.warn('Boostnote resets the invalid configuration.')
config = DEFAULT_CONFIG
_save(config)
}
@@ -85,7 +89,11 @@ function get () {
: 'default'
if (config.editor.theme !== 'default') {
editorTheme.setAttribute('href', '../node_modules/codemirror/theme/' + config.editor.theme + '.css')
if (config.editor.theme.startsWith('solarized')) {
editorTheme.setAttribute('href', '../node_modules/codemirror/theme/solarized.css')
} else {
editorTheme.setAttribute('href', '../node_modules/codemirror/theme/' + config.editor.theme + '.css')
}
}
}
@@ -93,13 +101,15 @@ function get () {
}
function set (updates) {
let currentConfig = get()
let newConfig = Object.assign({}, DEFAULT_CONFIG, currentConfig, updates)
const currentConfig = get()
const newConfig = Object.assign({}, DEFAULT_CONFIG, currentConfig, updates)
if (!validate(newConfig)) throw new Error('INVALID CONFIG')
_save(newConfig)
if (newConfig.ui.theme === 'dark') {
document.body.setAttribute('data-theme', 'dark')
} else if (newConfig.ui.theme === 'white') {
document.body.setAttribute('data-theme', 'white')
} else {
document.body.setAttribute('data-theme', 'default')
}
@@ -111,12 +121,16 @@ function set (updates) {
editorTheme.setAttribute('rel', 'stylesheet')
document.head.appendChild(editorTheme)
}
let newTheme = consts.THEMES.some((theme) => theme === newConfig.editor.theme)
const newTheme = consts.THEMES.some((theme) => theme === newConfig.editor.theme)
? newConfig.editor.theme
: 'default'
if (newTheme !== 'default') {
editorTheme.setAttribute('href', '../node_modules/codemirror/theme/' + newTheme + '.css')
if (newTheme.startsWith('solarized')) {
editorTheme.setAttribute('href', '../node_modules/codemirror/theme/solarized.css')
} else {
editorTheme.setAttribute('href', '../node_modules/codemirror/theme/' + newTheme + '.css')
}
}
ipcRenderer.send('config-renew', {
@@ -124,6 +138,15 @@ function set (updates) {
})
}
function assignConfigValues (originalConfig, rcConfig) {
const config = Object.assign({}, DEFAULT_CONFIG, originalConfig, rcConfig)
config.hotkey = Object.assign({}, DEFAULT_CONFIG.hotkey, originalConfig.hotkey, rcConfig.hotkey)
config.ui = Object.assign({}, DEFAULT_CONFIG.ui, originalConfig.ui, rcConfig.ui)
config.editor = Object.assign({}, DEFAULT_CONFIG.editor, originalConfig.editor, rcConfig.editor)
config.preview = Object.assign({}, DEFAULT_CONFIG.preview, originalConfig.preview, rcConfig.preview)
return config
}
export default {
get,
set,

View File

@@ -19,7 +19,7 @@ function setZoom (zoomFactor, noSave = false) {
}
function getZoom () {
let config = ConfigManager.get()
const config = ConfigManager.get()
return config.zoom
}

View File

@@ -1,7 +1,6 @@
const fs = require('fs')
const path = require('path')
const _ = require('lodash')
const sander = require('sander')
const { findStorage } = require('browser/lib/findStorage')
/**
* @description To copy an image and return the path.
@@ -12,17 +11,17 @@ const sander = require('sander')
function copyImage (filePath, storageKey) {
return new Promise((resolve, reject) => {
try {
const cachedStorageList = JSON.parse(localStorage.getItem('storages'))
if (!_.isArray(cachedStorageList)) throw new Error('Target storage doesn\'t exist.')
const storage = _.find(cachedStorageList, {key: storageKey})
if (storage === undefined) throw new Error('Target storage doesn\'t exist.')
const targetStorage = storage
const targetStorage = findStorage(storageKey)
const inputImage = fs.createReadStream(filePath)
const imageName = path.basename(filePath)
const outputImage = fs.createWriteStream(path.join(targetStorage.path, 'images', imageName))
const imageExt = path.extname(filePath)
const imageName = Math.random().toString(36).slice(-16)
const basename = `${imageName}${imageExt}`
const imageDir = path.join(targetStorage.path, 'images')
if (!fs.existsSync(imageDir)) fs.mkdirSync(imageDir)
const outputImage = fs.createWriteStream(path.join(imageDir, basename))
inputImage.pipe(outputImage)
resolve(`${encodeURI(targetStorage.path)}/images/${encodeURI(imageName)}`)
resolve(basename)
} catch (e) {
return reject(e)
}

View File

@@ -3,6 +3,7 @@ const keygen = require('browser/lib/keygen')
const path = require('path')
const resolveStorageData = require('./resolveStorageData')
const CSON = require('@rokt33r/season')
const { findStorage } = require('browser/lib/findStorage')
/**
* @param {String} storageKey
@@ -22,18 +23,13 @@ const CSON = require('@rokt33r/season')
* ```
*/
function createFolder (storageKey, input) {
let rawStorages
let targetStorage
try {
if (input == null) throw new Error('No input found.')
if (!_.isString(input.name)) throw new Error('Name must be a string.')
if (!_.isString(input.color)) throw new Error('Color must be a string.')
rawStorages = JSON.parse(localStorage.getItem('storages'))
if (!_.isArray(rawStorages)) throw new Error('Target storage doesn\'t exist.')
targetStorage = _.find(rawStorages, {key: storageKey})
if (targetStorage == null) throw new Error('Target storage doesn\'t exist.')
targetStorage = findStorage(storageKey)
} catch (e) {
return Promise.reject(e)
}
@@ -44,7 +40,7 @@ function createFolder (storageKey, input) {
while (storage.folders.some((folder) => folder.key === key)) {
key = keygen()
}
let newFolder = {
const newFolder = {
key,
color: input.color,
name: input.name

View File

@@ -4,12 +4,14 @@ const _ = require('lodash')
const keygen = require('browser/lib/keygen')
const path = require('path')
const CSON = require('@rokt33r/season')
const { findStorage } = require('browser/lib/findStorage')
function validateInput (input) {
if (!_.isArray(input.tags)) input.tags = []
input.tags = input.tags.filter((tag) => _.isString(tag) && tag.trim().length > 0)
if (!_.isString(input.title)) input.title = ''
input.isStarred = !!input.isStarred
input.isTrashed = !!input.isTrashed
switch (input.type) {
case 'MARKDOWN_NOTE':
@@ -37,11 +39,7 @@ function createNote (storageKey, input) {
input = Object.assign({}, input)
validateInput(input)
let cachedStorageList = JSON.parse(localStorage.getItem('storages'))
if (!_.isArray(cachedStorageList)) throw new Error('Target storage doesn\'t exist.')
targetStorage = _.find(cachedStorageList, {key: storageKey})
if (targetStorage == null) throw new Error('Target storage doesn\'t exist.')
targetStorage = findStorage(storageKey)
} catch (e) {
return Promise.reject(e)
}
@@ -68,7 +66,7 @@ function createNote (storageKey, input) {
}
}
}
let noteData = Object.assign({}, input, {
const noteData = Object.assign({}, input, {
key,
createdAt: new Date(),
updatedAt: new Date(),

View File

@@ -4,6 +4,7 @@ const resolveStorageData = require('./resolveStorageData')
const resolveStorageNotes = require('./resolveStorageNotes')
const CSON = require('@rokt33r/season')
const sander = require('sander')
const { findStorage } = require('browser/lib/findStorage')
/**
* @param {String} storageKey
@@ -18,14 +19,9 @@ const sander = require('sander')
* ```
*/
function deleteFolder (storageKey, folderKey) {
let rawStorages
let targetStorage
try {
rawStorages = JSON.parse(localStorage.getItem('storages'))
if (!_.isArray(rawStorages)) throw new Error('Target storage doesn\'t exist.')
targetStorage = _.find(rawStorages, {key: storageKey})
if (targetStorage == null) throw new Error('Target storage doesn\'t exist.')
targetStorage = findStorage(storageKey)
} catch (e) {
return Promise.reject(e)
}
@@ -41,17 +37,17 @@ function deleteFolder (storageKey, folderKey) {
})
})
.then(function deleteFolderAndNotes (data) {
let { storage, notes } = data
const { storage, notes } = data
storage.folders = storage.folders
.filter(function excludeTargetFolder (folder) {
return folder.key !== folderKey
})
let targetNotes = notes.filter(function filterTargetNotes (note) {
const targetNotes = notes.filter(function filterTargetNotes (note) {
return note.folder === folderKey
})
let deleteAllNotes = targetNotes
const deleteAllNotes = targetNotes
.map(function deleteNote (note) {
const notePath = path.join(storage.path, 'notes', note.key + '.cson')
return sander.unlink(notePath)

View File

@@ -1,23 +1,19 @@
const resolveStorageData = require('./resolveStorageData')
const _ = require('lodash')
const path = require('path')
const sander = require('sander')
const { findStorage } = require('browser/lib/findStorage')
function deleteNote (storageKey, noteKey) {
let targetStorage
try {
let cachedStorageList = JSON.parse(localStorage.getItem('storages'))
if (!_.isArray(cachedStorageList)) throw new Error('Target storage doesn\'t exist.')
targetStorage = _.find(cachedStorageList, {key: storageKey})
if (targetStorage == null) throw new Error('Target storage doesn\'t exist.')
targetStorage = findStorage(storageKey)
} catch (e) {
return Promise.reject(e)
}
return resolveStorageData(targetStorage)
.then(function deleteNoteFile (storage) {
let notePath = path.join(storage.path, 'notes', noteKey + '.cson')
const notePath = path.join(storage.path, 'notes', noteKey + '.cson')
try {
sander.unlinkSync(notePath)

View File

@@ -6,6 +6,7 @@ const dataApi = {
createFolder: require('./createFolder'),
updateFolder: require('./updateFolder'),
deleteFolder: require('./deleteFolder'),
reorderFolder: require('./reorderFolder'),
createNote: require('./createNote'),
updateNote: require('./updateNote'),
deleteNote: require('./deleteNote'),

Some files were not shown because too many files have changed in this diff Show More