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

Compare commits

...

171 Commits

Author SHA1 Message Date
Kohei TAKATA
10bd2d4547 v0.8.10 2017-06-12 22:44:28 +09:00
SuenagaRyota
d1942868e4 Merge pull request #635 from asmsuechan/add-notification-on-copying-a-codeblock
Add notification on a copy button clicked
2017-06-12 21:54:56 +09:00
asmsuechan
a409b3e48d Add notification on a copy button clicked 2017-06-12 21:44:29 +09:00
SuenagaRyota
21004aab6a Merge pull request #634 from asmsuechan/enable-analytics-on-develop
Disable awsAnalytics on develop
2017-06-12 19:07:32 +09:00
asmsuechan
eaf88c6491 Disable awsAnalytics on develop 2017-06-12 18:57:40 +09:00
SuenagaRyota
17dcd1b6f1 Merge pull request #633 from asmsuechan/iss-619
Fix a rendering bug
2017-06-12 18:44:40 +09:00
asmsuechan
244a06eea6 Fix some pointed by lint 2017-06-12 18:12:11 +09:00
asmsuechan
afb13af7a1 iss #619 Add a test for teh helper methods 2017-06-12 18:05:32 +09:00
asmsuechan
ff9b935e98 iss #619 Add an entity to fix the rendering bug 2017-06-12 18:01:04 +09:00
SuenagaRyota
4250d6fe52 Merge pull request #632 from asmsuechan/handling-pull-602
Handling pull 602
2017-06-12 17:43:42 +09:00
SuenagaRyota
5bc2094f10 Merge pull request #628 from BoostIO/feature-improve-search-textbox-ux
improve searchTextBox UX
2017-06-12 17:41:27 +09:00
Kevin Nadro
552653c0ed for got run eslint 2017-06-12 17:34:14 +09:00
Kevin Nadro
c9bbc61c95 cleaning up code and fixing slider bug on fullscreen mode 2017-06-12 17:34:08 +09:00
Kevin Nadro
41a04aa3f1 reworking architecture of code 2017-06-12 17:34:02 +09:00
Kevin Nadro
cd421c4662 fixed the state change and cleaned up code for eslint 2017-06-12 17:33:56 +09:00
Kevin Nadro
063e2e02bd fixing some code from the comments 2017-06-12 17:33:50 +09:00
Kevin Nadro
0c3019b52e added fullscreen button for notes and snippets
allows user to hide the sidebars for fullscreen editing
2017-06-12 17:33:44 +09:00
Kevin Nadro
2ee2494dc4 for got run eslint 2017-06-12 17:33:37 +09:00
Kevin Nadro
df6ff1fffe cleaning up code and fixing slider bug on fullscreen mode 2017-06-12 17:33:31 +09:00
Kevin Nadro
0d2e6a6a12 forgot this in last commit 2017-06-12 17:33:24 +09:00
Kevin Nadro
79ed55a76f reworking architecture of code 2017-06-12 17:32:59 +09:00
Kevin Nadro
cbe58b9437 fixed the state change and cleaned up code for eslint 2017-06-12 17:30:38 +09:00
Kevin Nadro
afc729b1c3 fixing some code from the comments 2017-06-12 17:30:29 +09:00
Kevin Nadro
5d0cb0302e added fullscreen button for notes and snippets
allows user to hide the sidebars for fullscreen editing
2017-06-12 17:30:19 +09:00
Sosuke Suzuki
29e0d121cd improve searchTextBox ux 2017-06-11 15:42:07 +09:00
Kohei TAKATA
48ca13f82c Merge pull request #620 from BoostIO/feature-AmazonMobileAnalytics
Feature amazon mobile analytics
2017-06-10 13:45:38 +09:00
Sosuke Suzuki
278061e4f1 fix spacing 2017-06-10 12:58:24 +09:00
Sosuke Suzuki
186a815821 config to use dynamic customEvents 2017-06-10 12:51:01 +09:00
Sosuke Suzuki
2fea9eb874 setting AMA custom events 2017-06-10 12:50:19 +09:00
Sosuke Suzuki
f8b6453be9 config AmazonMobileAnalytics 2017-06-10 12:48:28 +09:00
SuenagaRyota
be625f8884 Merge pull request #626 from asmsuechan/iss-625
Fix a bug which is not showing dialog when user tries to export a snippet
2017-06-07 15:32:02 +09:00
asmsuechan
2271def5d3 Fix a bug which is not showing dialog when user tries to export a snippet 2017-06-07 15:23:07 +09:00
SuenagaRyota
275a8ea7cc Merge pull request #624 from asmsuechan/remove-unused-file
Remove an unused file
2017-06-07 14:19:22 +09:00
asmsuechan
dc236f33b1 Remove an unused file 2017-06-07 14:14:55 +09:00
SuenagaRyota
678d739e75 Merge pull request #623 from asmsuechan/add-emacs-keymap
Add emacs keymap
2017-06-07 12:48:39 +09:00
SuenagaRyota
8fe05a4c24 Merge pull request #548 from asmsuechan/add-a-module-findTitle
Add a module to find the title
2017-06-07 10:30:46 +09:00
asmsuechan
77adfdb9f0 Add emacs keymap 2017-06-07 09:37:21 +09:00
Kazu Yokomizo
30c94028fb Merge pull request #622 from BoostIO/fix-copy-button-layout
Fix copy button layout
2017-06-06 23:30:08 +09:00
Kazu Yokomizo
b5bf0780fa Fix copy button layout 2017-06-06 23:14:31 +09:00
SuenagaRyota
ad838d82ee Merge pull request #603 from nadr0/copyclipboard
Copy clipboard button for markdown code blocks
2017-06-06 21:38:00 +09:00
Kazu Yokomizo
d5ec0c0cdd Merge pull request #617 from CodeRi13/master
Translate contributing.md to korean
2017-06-05 16:16:50 +09:00
SuenagaRyota
ff29f1e0c6 Merge pull request #618 from BoostIO/revert-610-add-e2e-tests
Revert "Add e2e tests"
2017-06-05 13:09:10 +09:00
SuenagaRyota
1f58698a04 Revert "Add e2e tests" 2017-06-05 13:06:10 +09:00
CodeRi13
a275f331d0 Translate contributing.md to korean 2017-06-05 10:38:44 +09:00
Sota Sugiura
0862c6e059 Merge pull request #293 from asmsuechan/copy-image-on-dropped-into-CodeEditor
Add dataApi.copyImage for copying image to boostnote storage on an image dropped into CodeEditor
2017-06-05 01:29:11 +09:00
SuenagaRyota
46fddfd26c Merge pull request #614 from asmsuechan/add-documents
Add documents
2017-06-04 15:29:33 +09:00
asmsuechan
c547ccb4cb Update a document 2017-06-04 15:24:40 +09:00
asmsuechan
86e82fb149 Add required environments for building Boostnote on each local 2017-06-04 12:10:15 +09:00
SuenagaRyota
00a284bfbd Merge pull request #612 from asmsuechan/move-a-package-dev-to-prod
Move markdown-it-imsize dev-dependency to production
2017-06-03 20:52:40 +09:00
asmsuechan
f6fbec0a2e Move markdown-it-imsize dev-dependency to production 2017-06-03 20:47:43 +09:00
SuenagaRyota
b24c06bee6 Merge pull request #610 from asmsuechan/add-e2e-tests
Add e2e tests
2017-06-03 18:52:45 +09:00
asmsuechan
4b75d501f5 Edit a e2e test for editting note 2017-06-03 18:28:40 +09:00
asmsuechan
b28d5c8b25 Fix the class name of modalClose button 2017-06-03 18:28:40 +09:00
SuenagaRyota
86e61661e6 Merge pull request #609 from asmsuechan/add-tests-for-search
Add tests for searchFromeNotes()
2017-06-03 17:24:33 +09:00
asmsuechan
cdd5717e63 Fix some pointed by eslint 2017-06-03 17:10:58 +09:00
asmsuechan
42bd7fb4fb Add tests for searchFromeNotes() 2017-06-03 17:04:22 +09:00
SuenagaRyota
66e478a001 Merge pull request #600 from BoostIO/feature-change-search-ux
change search ux
2017-06-03 16:41:36 +09:00
Kazu Yokomizo
c8259abcac Merge pull request #549 from LetItRock/#535
Fix for #535:
2017-06-03 14:35:52 +09:00
SuenagaRyota
d6d16a63a4 Merge pull request #608 from asmsuechan/fix-travis-node-version
Fix travis node version
2017-06-03 14:32:11 +09:00
asmsuechan
df8d1f0714 Add a comment 2017-06-03 14:24:28 +09:00
asmsuechan
fbbc6411c3 Fix the node_version to 4.x 2017-06-03 14:07:56 +09:00
Kazu Yokomizo
bc1e94fcab Merge pull request #607 from BoostIO/update-slack-link
Update slack link
2017-06-02 15:44:32 +09:00
Kazu Yokomizo
418439e3d5 Update slack group link 2017-06-02 15:27:36 +09:00
Kazu Yokomizo
46cbd04ba7 Update slack group link 2017-06-02 15:27:06 +09:00
asmsuechan
0ade6d9ece Add e2e tests for modal click and input texts 2017-06-01 17:26:28 +09:00
asmsuechan
823599192f Fix e2e tests 2017-06-01 15:59:12 +09:00
Sosuke Suzuki
9496ab88f7 fix spacings 2017-05-31 20:52:36 +09:00
Sosuke Suzuki
290e6ab170 change arg findByTag 2017-05-31 20:49:34 +09:00
asmsuechan
a9e3572f4f Add a e2e-test for Modal 2017-05-31 15:38:55 +09:00
Kazu Yokomizo
92b2b7dde3 Merge pull request #605 from BoostIO/improve-dark-theme
I improved the dark theme.
2017-05-31 15:26:55 +09:00
Kazu Yokomizo
dad1e40234 Fix white theme layout 2017-05-31 01:43:07 +09:00
Kazu Yokomizo
b75c8b0373 Fix code highlight 2017-05-31 01:26:40 +09:00
Sosuke Suzuki
fc4c471a87 fix spacing for eslint 2017-05-31 00:30:55 +09:00
Kazu Yokomizo
24de71f240 I improved the dark theme. 2017-05-30 23:33:59 +09:00
Sosuke Suzuki
805c39e60c refactor search function 2017-05-30 22:16:27 +09:00
Sosuke Suzuki
10e75041e8 add className 'searchInput' 2017-05-30 18:54:13 +09:00
Sosuke Suzuki
2364348df4 fix indent 2017-05-30 18:49:39 +09:00
Sosuke Suzuki
e643443660 split search function to module. 2017-05-30 18:27:57 +09:00
Kevin Nadro
d72e876b23 fixing eslint 2017-05-28 17:58:10 -05:00
Kevin Nadro
f3612774ba clipboard button is added 2017-05-28 17:31:23 -05:00
SuenagaRyota
67d8b02c49 Merge pull request #598 from dangvanthanh/master
#595 - Resize an image with markdown
2017-05-27 07:08:31 +09:00
Sosuke Suzuki
7abf53009a change search ux 2017-05-26 18:01:44 +09:00
Kazu Yokomizo
90bb4632b6 Merge pull request #599 from BoostIO/update-slack-link
Update slack link
2017-05-26 13:59:50 +09:00
Kazu Yokomizo
630da00235 Update slack link 2017-05-26 13:57:01 +09:00
Kazu Yokomizo
245e0dd7ee update slack group link 2017-05-26 13:56:23 +09:00
Dang Van Thanh
824e288a80 #595 - Resize an image with markdown 2017-05-26 10:58:39 +07:00
asmsuechan
99228f2e60 ♻️ Refactor findeTitle() 2017-05-23 17:01:07 +09:00
asmsuechan
0f71139eba Delete a comma pointed by eslint 2017-05-23 15:51:21 +09:00
asmsuechan
2bbe7056d1 Add test cases 2017-05-23 15:43:54 +09:00
asmsuechan
005d8f84fd Change for to some 2017-05-23 15:43:48 +09:00
asmsuechan
908cd7a890 Edit the method 2017-05-23 15:31:32 +09:00
asmsuechan
d4865adf6a Change findNoteTitle.find() to findNoteTitle.findNoteTitle() in test 2017-05-23 15:23:16 +09:00
asmsuechan
20411a2fd5 Change the function name of findNoteTitle.js from find to findNoteTitle 2017-05-23 15:23:16 +09:00
asmsuechan
19f8930f5a Fix to use strict equal 2017-05-23 15:23:16 +09:00
asmsuechan
ffc4ca1dd4 Add an unit test for findNoteTitle 2017-05-23 15:23:16 +09:00
asmsuechan
0bc9c6fb5a Change to use the module instead of a function findTitle() implemented inside the file 2017-05-23 15:23:11 +09:00
asmsuechan
983f453afd Add findTitle as a module 2017-05-23 15:17:47 +09:00
Kazu Yokomizo
60a0293495 Merge pull request #594 from BoostIO/edit-noteList-layout
If there is no tag, narrow the width of NoteList.
2017-05-22 21:19:36 +09:00
Kazu Yokomizo
4807d22763 If there is no tag, narrow the width of NoteList. 2017-05-22 20:13:47 +09:00
Kazu Yokomizo
e8d451bcbb Merge pull request #587 from BoostIO/fix-task-bar-ui
Fix task bar UI
2017-05-20 15:18:50 +09:00
Kazu Yokomizo
4809bb6f06 Change the z-index postiion 2017-05-20 15:09:52 +09:00
Kazu Yokomizo
d6d694cf66 Change z-index position and delete unnecessary class 2017-05-20 15:07:21 +09:00
Kazu Yokomizo
132e3c3088 Fix task bar UI 2017-05-20 14:53:06 +09:00
SuenagaRyota
3c7ff78549 Merge pull request #374 from BoostIO/feature_move_note_between_folder
Move note between folders
2017-05-20 14:33:55 +09:00
SuenagaRyota
adbdc2cb1c Merge pull request #510 from BoostIO/fix-note-title-bug-for-h1
fix the bug when using markdown syntax h1
2017-05-20 14:27:52 +09:00
Kazu Yokomizo
e52b74bbc9 Merge pull request #557 from BoostIO/feature-todo-percentage
display percentage of achievement of todo in markdown
2017-05-20 13:51:30 +09:00
Sosuke Suzuki
575f1b4b33 change from forEach to some 2017-05-20 12:28:11 +09:00
Sosuke Suzuki
a528c99900 change from 'undefined' to undefined 2017-05-20 12:24:58 +09:00
Sosuke Suzuki
e6047ed383 use String template literal 2017-05-20 11:38:09 +09:00
Kazu Yokomizo
381b7d960a Merge pull request #584 from BoostIO/update-slack-invitation
Update slack invitation
2017-05-18 17:01:15 +09:00
Kazu Yokomizo
045a8bde22 Update slack group link 2017-05-18 16:57:06 +09:00
Kazu Yokomizo
e528182c2a Update slack group link 2017-05-18 16:56:30 +09:00
Sosuke Suzuki
b86cdb461a fix the spacing 2017-05-18 01:27:37 +09:00
Sosuke Suzuki
7265f76770 for loop change to forEach 2017-05-18 00:31:46 +09:00
Sosuke Suzuki
2ed092279d avoid undefined 2017-05-18 00:07:48 +09:00
SuenagaRyota
7d71819be0 Merge pull request #578 from asmsuechan/add-a-documentation-of-e2e-testiong
Add a documentation of e2e testiong
2017-05-16 15:20:59 +09:00
asmsuechan
e848c74511 Fix the document in English 2017-05-16 15:17:20 +09:00
asmsuechan
968ed146b2 Fix the document in Japanese 2017-05-16 15:16:55 +09:00
asmsuechan
5e03f12875 Add a documentation of e2e testing in Japanese 2017-05-16 15:13:56 +09:00
asmsuechan
09f8e5f25a Add a documentation of e2e testing in English 2017-05-16 15:11:20 +09:00
SuenagaRyota
89f4ed3006 Merge pull request #471 from BoostIO/escButton-to-ReactComponent
made escButton ReactComponent
2017-05-13 17:15:21 +09:00
Sosuke Suzuki
8d14a9557d fix the indent 2017-05-13 17:07:02 +09:00
Sosuke Suzuki
513042d769 made escButton ReactComponent 2017-05-13 17:00:34 +09:00
Sosuke Suzuki
c28980c2a9 fix the spacing 2017-05-12 19:54:52 +09:00
Sosuke Suzuki
1c31ff4e98 use string template literal 2017-05-10 23:47:09 +09:00
Sosuke Suzuki
7c9d3904b3 change to forEach from for 2017-05-10 23:10:25 +09:00
Sosuke Suzuki
e23707f0a0 add + and * to matching pattern 2017-05-10 22:49:15 +09:00
Kazu Yokomizo
a952b18b96 Merge pull request #560 from BoostIO/update-slack-link-1
Update slack link 1
2017-05-09 21:55:29 +09:00
Kazu Yokomizo
35cc07bf63 Update readme-ja.md 2017-05-09 21:49:07 +09:00
Kazu Yokomizo
118bf18434 Update slack link 2017-05-09 21:48:40 +09:00
Sosuke Suzuki
34da15208b fix spacing 2017-05-06 16:57:15 +09:00
Sosuke Suzuki
e0dc62c00b fix indents 2017-05-06 16:40:42 +09:00
Sosuke Suzuki
b58df2f172 display percentage of achievement of todo in markdown 2017-05-06 16:30:03 +09:00
Pavlo Tymchuk
90846fab81 Fix for #535:
- hide/show code editor based on markdown editor state (could be CODE or PREVIEW);
- changed styles for code editor;
2017-05-04 23:09:48 +02:00
SuenagaRyota
efd80d5c0c Merge pull request #545 from LeoLamCY/revamp-keypress-handling
Revamp handling of multiple key press
2017-05-04 10:59:28 -07:00
SuenagaRyota
e37e28a22e Merge pull request #541 from asmsuechan/fix-travis-for-spectron
Fix viriables for travis
2017-05-03 09:45:24 -07:00
Leo Lam
a1e71b318c fix lint error again 2017-05-03 01:20:57 -04:00
Leo Lam
aee6541d45 fix lint errors 2017-05-03 01:17:13 -04:00
Leo Lam
50ad5e3791 minor improvement on multi-key detection
-check if number of keys pressed is the same as number of keys in a
shortcut key combo before matching to avoid incorrect detection
2017-05-03 01:04:50 -04:00
Leo Lam
b34d72f21a revamp handling of multi-key presses
- use keyCodes instead of strings to detect keys
- delete key from keyPressed array on key up
- fix #540
2017-05-03 00:29:18 -04:00
asmsuechan
e5ae420ef6 Fix viriables for travis 2017-05-02 09:49:49 -07:00
Sota Sugiura
7269750264 Merge pull request #502 from asmsuechan/add-spectron
Add spectron
2017-05-02 18:23:02 +09:00
Sosuke Suzuki
11f7fcbaef fix the bug that is markdown titile starts with second letter 2017-04-26 18:32:46 +09:00
Sosuke Suzuki
6b8488ae0f fix the bug when using markdown syntax h1 2017-04-26 18:14:29 +09:00
asmsuechan
ee5268a07e Change to separation osx and linux 2017-04-25 17:05:10 -07:00
asmsuechan
28bc331318 Change to run only master branch 2017-04-25 11:48:45 -07:00
asmsuechan
098153b6ba Add osx support to .travis.yml
Change to run xvfb only linux
2017-04-25 09:19:46 -07:00
asmsuechan
878f31e91e Remove unnecessary break 2017-04-25 09:18:45 -07:00
asmsuechan
b40d09fa86 Edit .travis.yml to handle ES7 syntax problem 2017-04-25 00:08:59 -07:00
asmsuechan
eb48f48f6b Edit spectron.js to deal with linux 2017-04-25 00:08:49 -07:00
asmsuechan
7961008500 Edit gruntfile.js 2017-04-25 00:08:49 -07:00
asmsuechan
ff87c6b226 Fix .travis.yml to add grunt-cli because travis needs grunt to run $ grund pre-build for e2e test 2017-04-24 19:46:39 -07:00
asmsuechan
0c2807a08b Rename spectron-test.js to spectron.js because of avoidance from be ran it by npm run test 2017-04-24 19:42:02 -07:00
asmsuechan
f0cf369317 Edit .travis.yml for e2e test 2017-04-24 19:28:29 -07:00
asmsuechan
5e9954c060 Add a yarn task 2017-04-24 19:23:23 -07:00
asmsuechan
0c7bdf20af Add a smiple test to confirm Boostnote starts 2017-04-24 19:23:23 -07:00
Sosuke Suzuki
9c1179a6f9 Fixed indent which could not be corrected 2017-04-25 02:53:29 +09:00
Sosuke Suzuki
43cb290c80 fix the indent 2017-04-25 02:41:16 +09:00
Sosuke Suzuki
f8b7b7df9f change how to use dataApi and fix indent 2017-04-25 02:31:03 +09:00
Sosuke Suzuki
b73f0a8012 refactor some dirty code 2017-04-25 01:40:49 +09:00
asmsuechan
4e7204bdbc Add spectron 2017-04-23 12:13:58 -07:00
Sosuke Suzuki
25685dc8b0 Several small fixes 2017-04-06 21:52:23 +09:00
Sosuke Suzuki
50cd0b794b change color with hover during dragging 2017-04-05 10:08:09 +09:00
Sosuke Suzuki
720f07f62c Move note between folders 2017-04-01 02:36:48 +09:00
asmsuechan
893a92c87b Fix from reviews 2017-03-19 12:39:46 -07:00
asmsuechan
0f3230110c Change to use const instead of let 2017-03-10 20:40:11 -08:00
asmsuechan
65573bd4db Add comments 2017-03-10 20:33:16 -08:00
asmsuechan
928df018dc Fix the assginment of targetStorage 2017-03-10 20:33:16 -08:00
asmsuechan
b2187b72ab Fix let to const 2017-03-10 20:33:16 -08:00
asmsuechan
aae2bddd32 Improve the Promise of copyImage 2017-03-10 20:33:11 -08:00
asmsuechan
b6eddf0821 Add dataApi.copyImage for copying image to boostnote storage on an image to boostnote storage on an image doropped into CodeEditor 2017-03-10 20:31:54 -08:00
65 changed files with 1730 additions and 876 deletions

View File

@@ -1,6 +1,26 @@
language: node_js
node_js:
- 'stable'
- 'lts/*'
script: npm run lint && npm run test
# 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'

View File

@@ -2,6 +2,7 @@ import React, { PropTypes } from 'react'
import _ from 'lodash'
import CodeMirror from 'codemirror'
import path from 'path'
import copyImage from 'browser/main/lib/dataApi/copyImage'
CodeMirror.modeURL = '../node_modules/codemirror/mode/%N/%N.js'
@@ -184,13 +185,16 @@ export default class CodeEditor extends React.Component {
e.preventDefault()
const imagePath = e.dataTransfer.files[0].path
const filename = path.basename(imagePath)
const imageMd = `![${encodeURI(filename)}](${encodeURI(imagePath)})`
this.insertImage(imageMd)
copyImage(imagePath, this.props.storageKey).then((imagePathInTheStorage) => {
const imageMd = `![${encodeURI(filename)}](${imagePathInTheStorage})`
this.insertImageMd(imageMd)
})
}
insertImage (imageMd) {
const textarea = this.editor.getInputField()
textarea.value = textarea.value.substr(0, textarea.selectionStart) + imageMd + textarea.value.substr(textarea.selectionEnd)
insertImageMd (imageMd) {
const cm = this.editor
cm.setValue(cm.getValue() + imageMd)
}
render () {

View File

@@ -9,14 +9,16 @@ class MarkdownEditor extends React.Component {
constructor (props) {
super(props)
this.escapeFromEditor = ['Control', 'w']
// char codes for ctrl + w
this.escapeFromEditor = [17, 87]
this.supportMdSelectionBold = ['Control', ':']
// ctrl + shift + ;
this.supportMdSelectionBold = [16, 17, 186]
this.state = {
status: 'PREVIEW',
renderValue: props.value,
keyPressed: {},
keyPressed: new Set(),
isLocked: false
}
@@ -87,7 +89,7 @@ class MarkdownEditor extends React.Component {
handleBlur (e) {
if (this.state.isLocked) return
this.setState({ keyPressed: [] })
this.setState({ keyPressed: new Set() })
let { config } = this.props
if (config.editor.switchPreview === 'BLUR') {
let cursorPosition = this.refs.code.editor.getCursor()
@@ -161,15 +163,16 @@ class MarkdownEditor extends React.Component {
handleKeyDown (e) {
if (this.state.status !== 'CODE') return false
const keyPressed = Object.assign(this.state.keyPressed, {
[e.key]: true
})
const keyPressed = this.state.keyPressed
keyPressed.add(e.keyCode)
this.setState({ keyPressed })
let isNoteHandlerKey = (el) => { return this.state.keyPressed[el] }
if (!this.state.isLocked && this.state.status === 'CODE' && this.escapeFromEditor.every(isNoteHandlerKey)) {
let isNoteHandlerKey = (el) => { return keyPressed.has(el) }
if (keyPressed.size === this.escapeFromEditor.length &&
!this.state.isLocked && this.state.status === 'CODE' &&
this.escapeFromEditor.every(isNoteHandlerKey)) {
document.activeElement.blur()
}
if (this.supportMdSelectionBold.every(isNoteHandlerKey)) {
if (keyPressed.size === this.supportMdSelectionBold.length && this.supportMdSelectionBold.every(isNoteHandlerKey)) {
this.addMdAroundWord('**')
}
}
@@ -190,9 +193,8 @@ class MarkdownEditor extends React.Component {
}
handleKeyUp (e) {
const keyPressed = Object.assign(this.state.keyPressed, {
[e.key]: false
})
const keyPressed = this.state.keyPressed
keyPressed.delete(e.keyCode)
this.setState({ keyPressed })
}
@@ -201,7 +203,7 @@ class MarkdownEditor extends React.Component {
}
render () {
let { className, value, config } = this.props
let { className, value, config, storageKey } = this.props
let editorFontSize = parseInt(config.editor.fontSize, 10)
if (!(editorFontSize > 0 && editorFontSize < 101)) editorFontSize = 14
@@ -221,7 +223,10 @@ class MarkdownEditor extends React.Component {
onKeyDown={(e) => this.handleKeyDown(e)}
onKeyUp={(e) => this.handleKeyUp(e)}
>
<CodeEditor styleName='codeEditor'
<CodeEditor styleName={this.state.status === 'CODE'
? 'codeEditor'
: 'codeEditor--hide'
}
ref='code'
mode='GitHub Flavored Markdown'
value={value}
@@ -231,6 +236,7 @@ class MarkdownEditor extends React.Component {
fontSize={editorFontSize}
indentType={config.editor.indentType}
indentSize={editorIndentSize}
storageKey={storageKey}
onChange={(e) => this.handleChange(e)}
onBlur={(e) => this.handleBlur(e)}
/>

View File

@@ -4,8 +4,14 @@
.codeEditor
absolute top bottom left right
.hide
z-index 0
opacity 0
pointer-events none
.codeEditor--hide
@extend .codeEditor
@extend .hide
.preview
display block
@@ -17,7 +23,5 @@
.preview--hide
@extend .preview
z-index 0
opacity 0
pointer-events none
@extend .hide

View File

@@ -9,6 +9,7 @@ import SequenceDiagram from 'js-sequence-diagrams'
import eventEmitter from 'browser/main/lib/eventEmitter'
import fs from 'fs'
import htmlTextHelper from 'browser/lib/htmlTextHelper'
import copy from 'copy-to-clipboard'
const { remote } = require('electron')
const { app } = remote
@@ -46,6 +47,26 @@ code {
font-family: ${codeBlockFontFamily.join(', ')};
}
.clipboardButton {
color: rgba(147,147,149,0.8);;
fill: rgba(147,147,149,1);;
border-radius: 50%;
margin: 7px;
border: none;
background-color: transparent;
outline: none;
height: 32px;
width: 32px;
cursor: pointer;
}
.clipboardButton:hover {
transition: 0.2s;
color: #939395;
fill: #939395;
background-color: rgba(0,0,0,0.1);
}
h1, h2 {
border: none;
}
@@ -261,6 +282,16 @@ export default class MarkdownPreview extends React.Component {
if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text')
CodeMirror.requireMode(syntax.mode, () => {
let 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
})
}
el.parentNode.appendChild(copyIcon)
el.innerHTML = ''
el.parentNode.className += ` cm-s-${codeBlockTheme} CodeMirror`
CodeMirror.runMode(content, syntax.mime, el, {
@@ -335,6 +366,13 @@ export default class MarkdownPreview extends React.Component {
e.stopPropagation()
}
notify (title, options) {
if (global.process.platform === 'win32') {
options.icon = path.join('file://', global.__dirname, '../../resources/app.png')
}
return new window.Notification(title, options)
}
render () {
let { className, style, tabIndex } = this.props
return (

View File

@@ -0,0 +1,18 @@
import React, {PropTypes} from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './ModalEscButton.styl'
const ModalEscButton = ({
handleEscButtonClick
}) => (
<button styleName='escButton' onClick={handleEscButtonClick}>
<div styleName='esc-mark'>x</div>
<div styleName='esc-text'>esc</div>
</button>
)
ModalEscButton.propTypes = {
handleEscButtonClick: PropTypes.func.isRequired
}
export default CSSModules(ModalEscButton, styles)

View File

@@ -0,0 +1,14 @@
.escButton
height 50px
position absolute
background-color transparent
color $ui-inactive-text-color
border none
top 1px
right 10px
text-align center
width top-bar--height
height top-bar-height
.esc-mark
font-size 15px

View File

@@ -40,9 +40,10 @@ const TagElementList = (tags) => {
* @param {Object} note
* @param {Function} handleNoteClick
* @param {Function} handleNoteContextMenu
* @param {Function} handleDragStart
* @param {string} dateDisplay
*/
const NoteItem = ({ isActive, note, dateDisplay, handleNoteClick, handleNoteContextMenu }) => (
const NoteItem = ({ isActive, note, dateDisplay, handleNoteClick, handleNoteContextMenu, handleDragStart }) => (
<div styleName={isActive
? 'item--active'
: 'item'
@@ -50,6 +51,8 @@ const NoteItem = ({ isActive, note, dateDisplay, handleNoteClick, handleNoteCont
key={`${note.storage}-${note.key}`}
onClick={e => handleNoteClick(e, `${note.storage}-${note.key}`)}
onContextMenu={e => handleNoteContextMenu(e, `${note.storage}-${note.key}`)}
onDragStart={e => handleDragStart(e, note)}
draggable='true'
>
<div styleName='item-wrapper'>
{note.type === 'SNIPPET_NOTE'
@@ -91,7 +94,9 @@ NoteItem.propTypes = {
isStarred: PropTypes.bool.isRequired
}),
handleNoteClick: PropTypes.func.isRequired,
handleNoteContextMenu: PropTypes.func.isRequired
handleNoteContextMenu: PropTypes.func.isRequired,
handleDragStart: PropTypes.func.isRequired,
handleDragEnd: PropTypes.func.isRequired
}
export default CSSModules(NoteItem, styles)

View File

@@ -83,7 +83,6 @@ $control-height = 30px
position relative
bottom 0px
margin-top 2px
height 20px
font-size 12px
line-height 20px
overflow ellipsis
@@ -93,6 +92,7 @@ $control-height = 30px
flex 1
overflow ellipsis
line-height 20px
padding-top 2px
padding-left 2px
.item-bottom-tagList-item

View File

@@ -11,8 +11,9 @@ import styles from './NoteItemSimple.styl'
* @param {Object} note
* @param {Function} handleNoteClick
* @param {Function} handleNoteContextMenu
* @param {Function} handleDragStart
*/
const NoteItemSimple = ({ isActive, note, handleNoteClick, handleNoteContextMenu }) => (
const NoteItemSimple = ({ isActive, note, handleNoteClick, handleNoteContextMenum, handleDragStart }) => (
<div styleName={isActive
? 'item-simple--active'
: 'item-simple'
@@ -20,6 +21,8 @@ const NoteItemSimple = ({ isActive, note, handleNoteClick, handleNoteContextMenu
key={`${note.storage}-${note.key}`}
onClick={e => handleNoteClick(e, `${note.storage}-${note.key}`)}
onContextMenu={e => handleNoteContextMenu(e, `${note.storage}-${note.key}`)}
onDragStart={e => handleDragStart(e, note)}
draggable='true'
>
<div styleName='item-simple-title'>
{note.type === 'SNIPPET_NOTE'
@@ -43,7 +46,8 @@ NoteItemSimple.propTypes = {
title: PropTypes.string.isrequired
}),
handleNoteClick: PropTypes.func.isRequired,
handleNoteContextMenu: PropTypes.func.isRequired
handleNoteContextMenu: PropTypes.func.isRequired,
handleDragStart: PropTypes.func.isRequired
}
export default CSSModules(NoteItemSimple, styles)

View File

@@ -1,24 +0,0 @@
import React, { PropTypes } from 'react'
import md5 from 'md5'
export default class ProfileImage extends React.Component {
render () {
let className = this.props.className == null ? 'ProfileImage' : 'ProfileImage ' + this.props.className
let email = this.props.email != null ? this.props.email : ''
let src = 'http://www.gravatar.com/avatar/' + md5(email.trim().toLowerCase()) + '?s=' + this.props.size
return (
<img
className={className}
width={this.props.size}
height={this.props.size}
src={src} />
)
}
}
ProfileImage.propTypes = {
email: PropTypes.string,
size: PropTypes.string,
className: PropTypes.string
}

View File

@@ -14,11 +14,14 @@ import { isNumber } from 'lodash'
* @param {string} folderColor
* @param {boolean} isFolded
* @param {number} noteCount
* @param {Function} handleDrop
* @param {Function} handleDragEnter
* @param {Function} handleDragOut
* @return {React.Component}
*/
const StorageItem = ({
isActive, handleButtonClick, handleContextMenu, folderName,
folderColor, isFolded, noteCount
folderColor, isFolded, noteCount, handleDrop, handleDragEnter, handleDragLeave
}) => (
<button styleName={isActive
? 'folderList-item--active'
@@ -26,6 +29,9 @@ const StorageItem = ({
}
onClick={handleButtonClick}
onContextMenu={handleContextMenu}
onDrop={handleDrop}
onDragEnter={handleDragEnter}
onDragLeave={handleDragLeave}
>
<span styleName={isFolded
? 'folderList-item-name--folded' : 'folderList-item-name'
@@ -52,6 +58,8 @@ StorageItem.propTypes = {
folderName: PropTypes.string.isRequired,
folderColor: PropTypes.string,
isFolded: PropTypes.bool.isRequired,
handleDragEnter: PropTypes.func.isRequired,
handleDragLeave: PropTypes.func.isRequired,
noteCount: PropTypes.number
}

View File

@@ -0,0 +1,31 @@
.percentageBar
position absolute
top 58px
right: 0px
left 0px
background-color #DADFE1
width 100%
height: 15px
font-size: 12px
z-index 100
border-radius 2px
.progressBar
background-color: #6C7A89
height 15px
border-radius 2px
transition 0.3s
.percentageText
color #f4f4f4
padding: 2px 43%
body[data-theme="dark"]
.percentageBar
background-color #363A3D
.progressBar
background-color: alpha(#939395, 50%)
.percentageText
color $ui-dark-text-color

View File

@@ -0,0 +1,27 @@
/**
* @fileoverview Percentage of todo achievement.
*/
import React, { PropTypes } from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './TodoListPercentage.styl'
/**
* @param {number} percentageOfTodo
*/
const TodoListPercentage = ({
percentageOfTodo
}) => (
<div styleName='percentageBar' style={{display: isNaN(percentageOfTodo) ? 'none' : ''}}>
<div styleName='progressBar' style={{width: `${percentageOfTodo}%`}}>
<p styleName='percentageText'>{percentageOfTodo}%</p>
</div>
</div>
)
TodoListPercentage.propTypes = {
percentageOfTodo: PropTypes.number.isRequired
}
export default CSSModules(TodoListPercentage, styles)

View File

@@ -270,7 +270,7 @@ table
border-right solid 1px borderColor
themeDarkBackground = darken(#21252B, 10%)
themeDarkText = #DDDDDD
themeDarkText = #f9f9f9
themeDarkBorder = lighten(themeDarkBackground, 20%)
themeDarkPreview = $ui-dark-noteDetail-backgroundColor
themeDarkTableOdd = themeDarkPreview
@@ -288,8 +288,9 @@ body[data-theme="dark"]
background-color alpha(lighten(brandColor, 30%), 0.2) !important
code
color #EA6730
border-color darken(themeDarkBorder, 10%)
background-color lighten(themeDarkPreview, 10%)
background-color lighten(themeDarkPreview, 5%)
pre
border-color lighten(#21252B, 20%)

View File

@@ -10,6 +10,7 @@ import StorageSection from './StorageSection'
import NoteList from './NoteList'
import NoteDetail from './NoteDetail'
import SideNavFilter from 'browser/components/SideNavFilter'
import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig'
require('!!style!css!stylus?sourceMap!../main/global.styl')
require('../lib/customMeta')
@@ -94,6 +95,7 @@ class FinderMain extends React.Component {
if (e.keyCode === 13) {
this.refs.detail.saveToClipboard()
AwsMobileAnalyticsConfig.recordDynamitCustomEvent('COPY_FINDER')
hideFinder()
e.preventDefault()
}

View File

@@ -0,0 +1,33 @@
export function findNoteTitle (value) {
let 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()
if (trimmedLine.match('```')) {
isInsideCodeBlock = !isInsideCodeBlock
}
if (isInsideCodeBlock === false && (trimmedLine.match(/^# +/) || trimmedNextLine.match(/^=+$/))) {
title = trimmedLine
return true
}
})
if (title === null) {
title = ''
splitted.some((line) => {
if (line.trim().length > 0) {
title = line.trim()
return true
}
})
}
return title
}
export default {
findNoteTitle
}

View File

@@ -13,7 +13,8 @@ export function decodeEntities (text) {
['amp', '&'],
['lt', '<'],
['gt', '>'],
['#63', '\\?']
['#63', '\\?'],
['#36', '\\$']
]
for (var i = 0, max = entities.length; i < max; ++i) {
@@ -28,7 +29,8 @@ export function encodeEntities (text) {
['\'', 'apos'],
['<', 'lt'],
['>', 'gt'],
['\\?', '#63']
['\\?', '#63'],
['\\$', '#36']
]
entities.forEach((entity) => {

View File

@@ -57,6 +57,7 @@ md.use(math, {
return output
}
})
md.use(require('markdown-it-imsize'))
md.use(require('markdown-it-footnote'))
// Override task item
md.block.ruler.at('paragraph', function (state, startLine/*, endLine */) {

43
browser/lib/search.js Normal file
View File

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

View File

@@ -2,6 +2,7 @@ import React, { PropTypes } from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './MarkdownNoteDetail.styl'
import MarkdownEditor from 'browser/components/MarkdownEditor'
import TodoListPercentage from 'browser/components/TodoListPercentage'
import StarButton from './StarButton'
import TagSelect from './TagSelect'
import FolderSelect from './FolderSelect'
@@ -11,6 +12,8 @@ import ee from 'browser/main/lib/eventEmitter'
import markdown from 'browser/lib/markdown'
import StatusBar from '../StatusBar'
import _ from 'lodash'
import { findNoteTitle } from 'browser/lib/findNoteTitle'
import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig'
const electron = require('electron')
const { remote } = electron
@@ -62,37 +65,22 @@ class MarkdownNoteDetail extends React.Component {
ee.off('topbar:togglelockbutton', this.toggleLockButton)
}
findTitle (value) {
let splitted = value.split('\n')
let title = null
let isMarkdownInCode = false
getPercentageOfCompleteTodo (noteContent) {
let splitted = noteContent.split('\n')
let numberOfTodo = 0
let numberOfCompletedTodo = 0
for (let i = 0; i < splitted.length; i++) {
let trimmedLine = splitted[i].trim()
if (trimmedLine.match('```')) {
isMarkdownInCode = !isMarkdownInCode
} else if (isMarkdownInCode === false && trimmedLine.match(/^# +/)) {
title = trimmedLine.substring(1, trimmedLine.length).trim()
break
splitted.forEach((line) => {
let trimmedLine = line.trim()
if (trimmedLine.match(/^[\+\-\*] \[\s|x\] ./)) {
numberOfTodo++
}
}
if (title == null) {
for (let i = 0; i < splitted.length; i++) {
let trimmedLine = splitted[i].trim()
if (trimmedLine.length > 0) {
title = trimmedLine
break
}
if (trimmedLine.match(/^[\+\-\*] \[x\] ./)) {
numberOfCompletedTodo++
}
if (title == null) {
title = ''
}
}
})
title = markdown.strip(title)
return title
return Math.floor(numberOfCompletedTodo / numberOfTodo * 100)
}
handleChange (e) {
@@ -100,7 +88,7 @@ class MarkdownNoteDetail extends React.Component {
note.content = this.refs.content.value
note.tags = this.refs.tags.value
note.title = this.findTitle(note.content)
note.title = markdown.strip(findNoteTitle(note.content))
note.updatedAt = new Date()
this.setState({
@@ -129,6 +117,7 @@ class MarkdownNoteDetail extends React.Component {
type: 'UPDATE_NOTE',
note: note
})
AwsMobileAnalyticsConfig.recordDynamitCustomEvent('EDIT_NOTE')
})
}
@@ -167,6 +156,7 @@ class MarkdownNoteDetail extends React.Component {
handleStarButtonClick (e) {
let { note } = this.state
if (!note.isStarred) AwsMobileAnalyticsConfig.recordDynamitCustomEvent('ADD_STAR')
note.isStarred = !note.isStarred
@@ -206,6 +196,10 @@ class MarkdownNoteDetail extends React.Component {
}
}
handleFullScreenButton (e) {
ee.emit('editor:fullscreen')
}
handleLockButtonMouseDown (e) {
e.preventDefault()
ee.emit('editor:lock')
@@ -263,6 +257,9 @@ class MarkdownNoteDetail extends React.Component {
value={this.state.note.tags}
onChange={(e) => this.handleChange(e)}
/>
<TodoListPercentage
percentageOfTodo={this.getPercentageOfCompleteTodo(note.content)}
/>
</div>
<div styleName='info-right'>
{(() => {
@@ -294,6 +291,11 @@ class MarkdownNoteDetail extends React.Component {
</g>
</svg>
</button>
<button styleName='control-fullScreenButton'
onMouseDown={(e) => this.handleFullScreenButton(e)}
>
<i className='fa fa-arrows-alt' styleName='fullScreen-button' />
</button>
</div>
</div>
@@ -303,6 +305,7 @@ class MarkdownNoteDetail extends React.Component {
styleName='body-noteEditor'
config={config}
value={this.state.note.content}
storageKey={this.state.note.storage}
onChange={(e) => this.handleChange(e)}
ignorePreviewPointerEvents={this.props.ignorePreviewPointerEvents}
/>

View File

@@ -31,6 +31,10 @@
float right
topBarButtonLight()
.control-fullScreenButton
float right
topBarButtonLight()
.body
absolute left right
left $note-detail-left-margin
@@ -55,3 +59,6 @@ body[data-theme="dark"]
.control-trashButton
topBarButtonDark()
.control-fullScreenButton
topBarButtonDark()

View File

@@ -15,6 +15,8 @@ import StatusBar from '../StatusBar'
import context from 'browser/lib/context'
import ConfigManager from 'browser/main/lib/ConfigManager'
import _ from 'lodash'
import { findNoteTitle } from 'browser/lib/findNoteTitle'
import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig'
function pass (name) {
switch (name) {
@@ -75,41 +77,13 @@ class SnippetNoteDetail extends React.Component {
if (this.saveQueue != null) this.saveNow()
}
findTitle (value) {
let splitted = value.split('\n')
let title = null
for (let i = 0; i < splitted.length; i++) {
let trimmedLine = splitted[i].trim()
if (trimmedLine.match(/^# .+/)) {
title = trimmedLine.substring(1, trimmedLine.length).trim()
break
}
}
if (title == null) {
for (let i = 0; i < splitted.length; i++) {
let trimmedLine = splitted[i].trim()
if (trimmedLine.length > 0) {
title = trimmedLine
break
}
}
if (title == null) {
title = ''
}
}
return title
}
handleChange (e) {
let { note } = this.state
note.tags = this.refs.tags.value
note.description = this.refs.description.value
note.updatedAt = new Date()
note.title = this.findTitle(note.description)
note.title = findNoteTitle(note.description)
this.setState({
note
@@ -137,6 +111,7 @@ class SnippetNoteDetail extends React.Component {
type: 'UPDATE_NOTE',
note: note
})
AwsMobileAnalyticsConfig.recordDynamitCustomEvent('EDIT_NOTE')
})
}
@@ -175,6 +150,7 @@ class SnippetNoteDetail extends React.Component {
handleStarButtonClick (e) {
let { note } = this.state
if (!note.isStarred) AwsMobileAnalyticsConfig.recordDynamitCustomEvent('ADD_STAR')
note.isStarred = !note.isStarred
@@ -214,6 +190,10 @@ class SnippetNoteDetail extends React.Component {
}
}
handleFullScreenButton (e) {
ee.emit('editor:fullscreen')
}
handleTabPlusButtonClick (e) {
this.addSnippet()
}
@@ -551,6 +531,11 @@ class SnippetNoteDetail extends React.Component {
</g>
</svg>
</button>
<button styleName='control-fullScreenButton'
onMouseDown={(e) => this.handleFullScreenButton(e)}
>
<i className='fa fa-arrows-alt' styleName='fullScreen-button' />
</button>
</div>
</div>

View File

@@ -70,6 +70,10 @@
float right
topBarButtonLight()
.control-fullScreenButton
float right
topBarButtonLight()
body[data-theme="dark"]
.root
border-color $ui-dark-borderColor
@@ -102,3 +106,6 @@ body[data-theme="dark"]
.control-trashButton
topBarButtonDark()
.control-fullScreenButton
topBarButtonDark()

View File

@@ -2,6 +2,7 @@ import React, { PropTypes } from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './TagSelect.styl'
import _ from 'lodash'
import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig'
class TagSelect extends React.Component {
constructor (props) {
@@ -56,6 +57,7 @@ class TagSelect extends React.Component {
}
submitTag () {
AwsMobileAnalyticsConfig.recordDynamitCustomEvent('ADD_TAG')
let { value } = this.props
let newTag = this.refs.newTag.value.trim().replace(/ +/g, '_')

View File

@@ -12,6 +12,8 @@ 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 eventEmitter from 'browser/main/lib/eventEmitter'
function focused () {
mixpanel.track('MAIN_FOCUSED')
@@ -21,14 +23,23 @@ class Main extends React.Component {
constructor (props) {
super(props)
if (process.env.NODE_ENV === 'production') {
mobileAnalytics.initAwsMobileAnalytics()
}
let { config } = props
this.state = {
isRightSliderFocused: false,
listWidth: config.listWidth,
navWidth: config.navWidth,
isLeftSliderFocused: false
isLeftSliderFocused: false,
fullScreen: false,
noteDetailWidth: 0,
mainBodyWidth: 0
}
this.toggleFullScreen = () => this.handleFullScreenButton()
}
getChildContext () {
@@ -63,11 +74,13 @@ 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)
}
handleLeftSlideMouseDown (e) {
@@ -144,6 +157,31 @@ class Main extends React.Component {
}
}
handleFullScreenButton (e) {
this.setState({ fullScreen: !this.state.fullScreen }, () => {
const noteDetail = document.querySelector('.NoteDetail')
const mainBody = document.querySelector('#main-body')
if (this.state.fullScreen) {
this.hideLeftLists(noteDetail, mainBody)
} else {
this.showLeftLists(noteDetail, mainBody)
}
})
}
hideLeftLists (noteDetail, mainBody) {
this.state.noteDetailWidth = noteDetail.style.left
this.state.mainBodyWidth = mainBody.style.left
noteDetail.style.left = '0px'
mainBody.style.left = '0px'
}
showLeftLists (noteDetail, mainBody) {
noteDetail.style.left = this.state.noteDetailWidth
mainBody.style.left = this.state.mainBodyWidth
}
render () {
let { config } = this.props
@@ -173,6 +211,7 @@ class Main extends React.Component {
</div>
}
<div styleName={config.isSideNavFolded ? 'body--expanded' : 'body'}
id='main-body'
ref='body'
style={{left: config.isSideNavFolded ? 44 : this.state.navWidth}}
>

View File

@@ -13,10 +13,12 @@
absolute top bottom
top -2px
width 0
z-index 0
.slider-right
@extend .slider
width 1px
z-index 0
.slider--active
@extend .slider

View File

@@ -8,6 +8,7 @@ import dataApi from 'browser/main/lib/dataApi'
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'
const { remote } = require('electron')
const { Menu, MenuItem, dialog } = remote
@@ -202,6 +203,7 @@ class NoteList extends React.Component {
getNotes () {
let { data, params, location } = this.props
let { router } = this.context
if (location.pathname.match(/\/home/)) {
return data.noteMap.map((note) => note)
@@ -212,6 +214,14 @@ class NoteList extends React.Component {
.map((uniqueKey) => data.noteMap.get(uniqueKey))
}
if (location.pathname.match(/\/searched/)) {
const searchInputText = document.getElementsByClassName('searchInput')[0].value
if (searchInputText === '') {
router.push('/home')
}
return searchFromNotes(this.props.data, searchInputText)
}
let storageKey = params.storageKey
let folderKey = params.folderKey
let storage = data.storageMap.get(storageKey)
@@ -327,7 +337,8 @@ class NoteList extends React.Component {
dialog.showMessageBox(remote.getCurrentWindow(), {
type: 'warning',
message: 'Sorry!',
detail: 'md/text import is available only a markdown note.'
detail: 'md/text import is available only a markdown note.',
buttons: ['OK', 'Cancel']
})
}
}
@@ -349,8 +360,13 @@ class NoteList extends React.Component {
})
}
handleDragStart (e, note) {
const noteData = JSON.stringify(note)
e.dataTransfer.setData('note', noteData)
}
render () {
let { location, notes, config } = this.props
let { location, notes, config, dispatch } = this.props
let sortFunc = config.sortBy === 'CREATED_AT'
? sortByCreatedAt
: config.sortBy === 'ALPHABETICAL'
@@ -382,6 +398,7 @@ class NoteList extends React.Component {
key={key}
handleNoteClick={this.handleNoteClick.bind(this)}
handleNoteContextMenu={this.handleNoteContextMenu.bind(this)}
handleDragStart={this.handleDragStart.bind(this)}
/>
)
}
@@ -393,6 +410,7 @@ class NoteList extends React.Component {
key={key}
handleNoteClick={this.handleNoteClick.bind(this)}
handleNoteContextMenu={this.handleNoteContextMenu.bind(this)}
handleDragStart={this.handleDragStart.bind(this)}
/>
)
})

View File

@@ -1,7 +1,7 @@
.root
absolute top left bottom
width $sideNav-width
background-color $ui-backgroundColor
background-color #f9f9f9
user-select none
color $ui-text-color

View File

@@ -7,6 +7,7 @@ import CreateFolderModal from 'browser/main/modals/CreateFolderModal'
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'
const { remote } = require('electron')
const { Menu, MenuItem, dialog } = remote
@@ -131,8 +132,54 @@ class StorageItem extends React.Component {
}
}
handleDragEnter (e) {
e.dataTransfer.setData('defaultColor', e.target.style.backgroundColor)
e.target.style.backgroundColor = 'rgba(129, 130, 131, 0.08)'
}
handleDragLeave (e) {
e.target.style.opacity = '1'
e.target.style.backgroundColor = e.dataTransfer.getData('defaultColor')
}
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}`}
})
})
}
render () {
let { storage, location, isFolded, data } = this.props
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)))
@@ -151,6 +198,9 @@ class StorageItem extends React.Component {
folderColor={folder.color}
isFolded={isFolded}
noteCount={noteCount}
handleDrop={(e) => this.handleDrop(e, storage, folder, dispatch, location)}
handleDragEnter={this.handleDragEnter}
handleDragLeave={this.handleDragLeave}
/>
)
})

View File

@@ -18,7 +18,7 @@ class TopBar extends React.Component {
this.state = {
search: '',
searchOptions: [],
searchPopupOpen: false
isSearching: false
}
this.newNoteHandler = () => {
@@ -87,79 +87,17 @@ class TopBar extends React.Component {
}
handleSearchChange (e) {
let { router } = this.context
router.push('/searched')
this.setState({
search: this.refs.searchInput.value
})
}
getOptions () {
let { data } = this.props
let { search } = this.state
let notes = data.noteMap.map((note) => note)
if (search.trim().length === 0) return []
let searchBlocks = search.split(' ')
searchBlocks.forEach((block) => {
if (block.match(/^!#.+/)) {
let tag = block.match(/^!#(.+)/)[1]
let regExp = new RegExp(_.escapeRegExp(tag), 'i')
notes = notes
.filter((note) => {
if (!_.isArray(note.tags)) return false
return note.tags.some((_tag) => {
return _tag.match(regExp)
})
})
} else if (block.match(/^!.+/)) {
let block = block.match(/^!(.+)/)[1]
let regExp = new RegExp(_.escapeRegExp(block), 'i')
notes = notes.filter((note) => {
if (!_.isArray(note.tags) || !note.tags.some((_tag) => {
return _tag.match(regExp)
})) {
return true
}
if (note.type === 'SNIPPET_NOTE') {
return !note.description.match(regExp)
} else if (note.type === 'MARKDOWN_NOTE') {
return !note.content.match(regExp)
}
return false
})
} else if (block.match(/^#.+/)) {
let tag = block.match(/#(.+)/)[1]
let regExp = new RegExp(_.escapeRegExp(tag), 'i')
notes = notes
.filter((note) => {
if (!_.isArray(note.tags)) return false
return note.tags.some((_tag) => {
return _tag.match(regExp)
})
})
} else {
let regExp = new RegExp(_.escapeRegExp(block), 'i')
notes = notes.filter((note) => {
if (_.isArray(note.tags) && note.tags.some((_tag) => {
return _tag.match(regExp)
})) {
return true
}
if (note.type === 'SNIPPET_NOTE') {
return note.description.match(regExp)
} else if (note.type === 'MARKDOWN_NOTE') {
return note.content.match(regExp)
}
return false
})
}
})
return notes
}
handleOptionClick (uniqueKey) {
return (e) => {
this.setState({
searchPopupOpen: false
isSearching: false
}, () => {
let { location } = this.props
hashHistory.push({
@@ -174,7 +112,7 @@ class TopBar extends React.Component {
handleSearchFocus (e) {
this.setState({
searchPopupOpen: true
isSearching: true
})
}
handleSearchBlur (e) {
@@ -191,7 +129,7 @@ class TopBar extends React.Component {
}
if (!isStillFocused) {
this.setState({
searchPopupOpen: false
isSearching: false
})
}
}
@@ -251,7 +189,7 @@ class TopBar extends React.Component {
}
handleOnSearchFocus () {
if (this.state.searchPopupOpen) {
if (this.state.isSearching) {
this.refs.search.childNodes[0].blur()
} else {
this.refs.search.childNodes[0].focus()
@@ -260,27 +198,6 @@ class TopBar extends React.Component {
render () {
let { config, style, data } = this.props
let searchOptionList = this.getOptions()
.map((note) => {
let storage = data.storageMap.get(note.storage)
let folder = _.find(storage.folders, {key: note.folder})
return <div styleName='control-search-optionList-item'
key={note.storage + '-' + note.key}
onClick={(e) => this.handleOptionClick(note.storage + '-' + note.key)(e)}
>
<div styleName='control-search-optionList-item-folder'
style={{borderColor: folder.color}}>
{folder.name}
<span styleName='control-search-optionList-item-folder-surfix'>in {storage.name}</span>
</div>
{note.type === 'SNIPPET_NOTE'
? <i styleName='control-search-optionList-item-type' className='fa fa-code' />
: <i styleName='control-search-optionList-item-type' className='fa fa-file-text-o' />
}&nbsp;
{note.title}
</div>
})
return (
<div className='TopBar'
styleName={config.isSideNavFolded ? 'root--expanded' : 'root'}
@@ -301,15 +218,8 @@ class TopBar extends React.Component {
onChange={(e) => this.handleSearchChange(e)}
placeholder='Search'
type='text'
className='searchInput'
/>
{this.state.searchPopupOpen &&
<div styleName='control-search-optionList'>
{searchOptionList.length > 0
? searchOptionList
: <div styleName='control-search-optionList-empty'>Empty List</div>
}
</div>
}
</div>
{this.state.search > 0 &&
<button styleName='left-search-clearButton'

View File

@@ -50,6 +50,7 @@ ReactDOM.render((
<IndexRedirect to='/home' />
<Route path='home' />
<Route path='starred' />
<Route path='searched' />
<Route path='storages'>
<IndexRedirect to='/home' />
<Route path=':storageKey'>

View File

@@ -0,0 +1,37 @@
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'
})
function initAwsMobileAnalytics () {
AWS.config.region = 'us-east-1'
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'us-east-1:xxxxxxxxxxxxxxxxxxxxxxxxx'
})
AWS.config.credentials.get((err) => {
if (!err) {
console.log('Cognito Identity ID: ' + AWS.config.credentials.identityId)
}
})
recordStaticCustomEvent()
}
function recordDynamitCustomEvent (type) {
mobileAnalyticsClient.recordEvent(type)
}
function recordStaticCustomEvent () {
mobileAnalyticsClient.recordEvent('UI_COLOR_THEME', {
uiColorTheme: ConfigManager.default.get().ui.theme
})
}
module.exports = {
initAwsMobileAnalytics,
recordDynamitCustomEvent
}

View File

@@ -0,0 +1,32 @@
const fs = require('fs')
const path = require('path')
const _ = require('lodash')
const sander = require('sander')
/**
* @description To copy an image and return the path.
* @param {String} filePath
* @param {String} storageKey
* @return {String} an image path
*/
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 inputImage = fs.createReadStream(filePath)
const imageName = path.basename(filePath)
const outputImage = fs.createWriteStream(path.join(targetStorage.path, 'images', imageName))
inputImage.pipe(outputImage)
resolve(`${encodeURI(targetStorage.path)}/images/${encodeURI(imageName)}`)
} catch (e) {
return reject(e)
}
})
}
module.exports = copyImage

View File

@@ -4,6 +4,8 @@ import styles from './CreateFolderModal.styl'
import dataApi from 'browser/main/lib/dataApi'
import store from 'browser/main/store'
import consts from 'browser/lib/consts'
import ModalEscButton from 'browser/components/ModalEscButton'
import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig'
class CreateFolderModal extends React.Component {
constructor (props) {
@@ -47,6 +49,7 @@ class CreateFolderModal extends React.Component {
}
confirm () {
AwsMobileAnalyticsConfig.recordDynamitCustomEvent('ADD_FOLDER')
if (this.state.name.trim().length > 0) {
let { storage } = this.props
let input = {
@@ -77,11 +80,7 @@ class CreateFolderModal extends React.Component {
<div styleName='header'>
<div styleName='title'>Create new folder</div>
</div>
<button styleName='close' onClick={(e) => this.handleCloseButtonClick(e)}>
<div styleName='close-mark'>×</div>
<div styleName='close-text'>esc</div>
</button>
<ModalEscButton handleEscButtonClick={(e) => this.handleCloseButtonClick(e)} />
<div styleName='control'>
<div styleName='control-folder'>
<div styleName='control-folder-label'>Folder name</div>

View File

@@ -15,21 +15,6 @@
background-color $ui-backgroundColor
color $ui-text-color
.close-mark
font-size 15px
.close
height 70px
position absolute
background-color transparent
color $ui-inactive-text-color
border none
top 7px
right 30px
text-align center
width top-bar--height
height top-bar--height
.control-folder-label
text-align left
font-size 12px
@@ -85,12 +70,7 @@ body[data-theme="dark"]
border 1px solid #C9C9C9 // TODO: use variable.
color white
.closeButton
border-color $ui-dark-borderColor
color $ui-dark-text-color
colorDarkDefaultButton()
.description
.description
color $ui-inactive-text-color
.control-confirmButton

View File

@@ -5,6 +5,7 @@ import dataApi from 'browser/main/lib/dataApi'
import store from 'browser/main/store'
import { hashHistory } from 'react-router'
import _ from 'lodash'
import ModalEscButton from 'browser/components/ModalEscButton'
const CSON = require('@rokt33r/season')
const path = require('path')
@@ -199,6 +200,7 @@ class InitModal extends React.Component {
<div styleName='header'>
<div styleName='header-title'>Initialize Storage</div>
</div>
<ModalEscButton handleEscButtonClick={(e) => this.handleCloseButtonClick(e)} />
<div styleName='body'>
<div styleName='body-welcome'>
Welcome!

View File

@@ -22,15 +22,6 @@
border-bottom solid 1px $ui-borderColor
color $ui-text-color
.closeButton
position absolute
top 10px
right 10px
height 30px
padding 0 25px
color $ui-text-color
colorDefaultButton()
.body
padding 30px

View File

@@ -4,6 +4,8 @@ import styles from './NewNoteModal.styl'
import dataApi from 'browser/main/lib/dataApi'
import { hashHistory } from 'react-router'
import ee from 'browser/main/lib/eventEmitter'
import ModalEscButton from 'browser/components/ModalEscButton'
import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig'
class NewNoteModal extends React.Component {
constructor (props) {
@@ -22,6 +24,8 @@ class NewNoteModal extends React.Component {
}
handleMarkdownNoteButtonClick (e) {
AwsMobileAnalyticsConfig.recordDynamitCustomEvent('ADD_MARKDOWN')
AwsMobileAnalyticsConfig.recordDynamitCustomEvent('ADD_ALLNOTE')
let { storage, folder, dispatch, location } = this.props
dataApi
.createNote(storage, {
@@ -52,6 +56,8 @@ class NewNoteModal extends React.Component {
}
handleSnippetNoteButtonClick (e) {
AwsMobileAnalyticsConfig.recordDynamitCustomEvent('ADD_SNIPPET')
AwsMobileAnalyticsConfig.recordDynamitCustomEvent('ADD_ALLNOTE')
let { storage, folder, dispatch, location } = this.props
dataApi
@@ -102,11 +108,7 @@ class NewNoteModal extends React.Component {
<div styleName='header'>
<div styleName='title'>Make a Note</div>
</div>
<button styleName='closeButton' onClick={(e) => this.handleCloseButtonClick(e)}>
<div styleName='close-mark'>×</div>
<div styleName='close-text'>esc</div>
</button>
<ModalEscButton handleEscButtonClick={(e) => this.handleCloseButtonClick(e)} />
<div styleName='control'>
<button styleName='control-button'
onClick={(e) => this.handleMarkdownNoteButtonClick(e)}

View File

@@ -13,25 +13,10 @@
border-bottom solid 1px $ui-borderColor
color $ui-text-color
.closeButton
height 50px
position absolute
background-color transparent
color $ui-inactive-text-color
border none
top 1px
right 10px
text-align center
width top-bar--height
height top-bar--height
.control
padding 25px 15px 15px
text-align center
.close-mark
font-size 15px
.control-button
width 220px
height 220px

View File

@@ -20,17 +20,6 @@ top-bar--height = 50px
font-size 18px
line-height top-bar--height
.top-bar-close
position absolute
background-color transparent
color $ui-inactive-text-color
border none
top 0
right 0
text-align center
width top-bar--height
height top-bar--height
.nav
absolute top left right
top top-bar--height

View File

@@ -216,6 +216,7 @@ class UiTab extends React.Component {
>
<option value='sublime'>default</option>
<option value='vim'>vim</option>
<option value='emacs'>emacs</option>
</select>
<span styleName='note-for-keymap'>Please reload boostnote after you change the keymap</span>
</div>

View File

@@ -5,6 +5,7 @@ import HotkeyTab from './HotkeyTab'
import UiTab from './UiTab'
import InfoTab from './InfoTab'
import StoragesTab from './StoragesTab'
import ModalEscButton from 'browser/components/ModalEscButton'
import CSSModules from 'browser/lib/CSSModules'
import styles from './PreferencesModal.styl'
@@ -117,10 +118,7 @@ class Preferences extends React.Component {
<div styleName='top-bar'>
<p>Your menu for Boostnote</p>
</div>
<button styleName='top-bar-close' onClick={(e) => this.handleEscButtonClick(e)}>
<div styleName='top-bar-close-mark'>×</div>
<div styleName='top-bar-close-text'>esc</div>
</button>
<ModalEscButton handleEscButtonClick={(e) => this.handleEscButtonClick(e)} />
<div styleName='nav'>
{navButtons}
</div>

View File

@@ -3,6 +3,7 @@ import CSSModules from 'browser/lib/CSSModules'
import styles from './RenameFolderModal.styl'
import dataApi from 'browser/main/lib/dataApi'
import store from 'browser/main/store'
import ModalEscButton from 'browser/components/ModalEscButton'
class RenameFolderModal extends React.Component {
constructor (props) {
@@ -72,9 +73,7 @@ class RenameFolderModal extends React.Component {
<div styleName='header'>
<div styleName='title'>Rename Folder</div>
</div>
<button styleName='closeButton'
onClick={(e) => this.handleCloseButtonClick(e)}
>Close</button>
<ModalEscButton handleEscButtonClick={(e) => this.handleCloseButtonClick(e)} />
<div styleName='control'>
<input styleName='control-input'

View File

@@ -13,17 +13,6 @@
border-bottom solid 1px $ui-borderColor
color $ui-text-color
.closeButton
position absolute
top 10px
right 10px
height 30px
width 0 25px
border $ui-border
border-radius 2px
color $ui-text-color
colorDefaultButton()
.control
padding 25px 15px 15px
text-align center
@@ -65,12 +54,7 @@ body[data-theme="dark"]
border-color $ui-dark-borderColor
color $ui-dark-text-color
.closeButton
border-color $ui-dark-borderColor
color $ui-dark-text-color
colorDarkDefaultButton()
.description
.description
color $ui-inactive-text-color
.control-input

View File

@@ -176,9 +176,9 @@ topBarButtonLight()
// Dark theme
$ui-dark-active-color = #3A404C
$ui-dark-borderColor = lighten(#21252B, 20%)
$ui-dark-backgroundColor = #1D1D1D
$ui-dark-noteList-backgroundColor = #181818
$ui-dark-noteDetail-backgroundColor = #0D0D0D
$ui-dark-backgroundColor = #1E2124
$ui-dark-noteList-backgroundColor = #282C30
$ui-dark-noteDetail-backgroundColor = #2D3033
$ui-dark-tag-backgroundColor = #3A404C
$dark-background-color = lighten($ui-dark-backgroundColor, 10%)
$ui-dark-text-color = #DDDDDD

View File

@@ -16,7 +16,25 @@ Because GPL v3 is too strict to be compatible with any other License, We thought
---
# Contributing to Boostnote(Japanese)
# Contributing to Boostnote (Korean)
## 버그 리포트를 보고할 때
이슈의 양식은 없습니다. 하지만 부탁이 있습니다.
**개발자 도구를 연 상태의 Boostnote 스크린샷을 첨부해주세요**
도움을 주셔서 감사합니다.
## Pull Request의 저작권에 관하여
당신이 pull request를 요청하면, 코드 변경에 대한 저작권을 MAISIN&CO에 양도한다는 것에 동의한다는 의미입니다.
이것은 Boostnote가 유료화가 되는 것을 의미하는 건 아닙니다. 만약 우리가 자금이 필요하다면, 우리는 클라우드 연동, 모바일 앱 통합 혹은 특수한 기능 같은 것을 사용해 수입 창출을 시도할 것입니다.
GPL v3 라이센스는 다른 라이센스와 혼합해 사용하기엔 너무 엄격하므로, 우리는 BSD, MIT 라이센스와 같은 더 자유로운 라이센스로 교체하는 것을 생각하고 있습니다.
---
# Contributing to Boostnote (Japanese)
## バグレポートに関してのissueを立てる時
イシューテンプレートはありませんが、1つお願いがあります。

View File

@@ -1,5 +1,11 @@
# Build
## Environments
* npm: 4.x
* node: 7.x
You should use `npm v4.x` because `$ grand pre-build` fails on `v5.x`.
## Development
We use Webpack HMR to develop Boostnote.

15
docs/jp/testing.md Normal file
View File

@@ -0,0 +1,15 @@
# Testing for Boostnote
## e2eテスト
Boostnoteには[ava](https://github.com/avajs/ava)と[spectron](https://github.com/electron/spectron)で書かれたe2eテストがあります。
### 実行方法
以下のe2eテストのコマンドがあります。
```
$ yarn run test:e2e
```
もう一つのテストコマンドと分けた理由は、travisCIの都合です。
### travisCIでは
travisCIではmasterブランチで飲みe2eテストを実行するようにしています。もし興味がある方は `.travis.yml`をご覧ください。

15
docs/testing.md Normal file
View File

@@ -0,0 +1,15 @@
# Testing for Boostnote
## e2e testing
There is e2e tests for Boostnote written in [ava](https://github.com/avajs/ava) and [spectron](https://github.com/electron/spectron).
### How to run
There is a command for e2e testing bellow:
```
$ yarn run test:e2e
```
The reason why I seperate aother test command is because of convenience of travisCI.
### On travisCI
I set e2e tests running on travisCI only master branch. If you're interested in it, please take a look at .travis.yml

View File

@@ -259,6 +259,8 @@ module.exports = function (grunt) {
case 'osx':
grunt.task.run(['compile', 'pack:osx'])
break
case 'linux':
grunt.task.run(['compile', 'pack:linux'])
}
})

View File

@@ -32,6 +32,7 @@
<script src="../node_modules/codemirror/addon/mode/loadmode.js"></script>
<script src="../node_modules/codemirror/keymap/sublime.js"></script>
<script src="../node_modules/codemirror/keymap/vim.js"></script>
<script src="../node_modules/codemirror/keymap/emacs.js"></script>
<script src="../node_modules/codemirror/addon/runmode/runmode.js"></script>
<script src="../node_modules/raphael/raphael.min.js"></script>

View File

@@ -59,6 +59,7 @@
<script src="../node_modules/codemirror/addon/mode/loadmode.js"></script>
<script src="../node_modules/codemirror/keymap/sublime.js"></script>
<script src="../node_modules/codemirror/keymap/vim.js"></script>
<script src="../node_modules/codemirror/keymap/emacs.js"></script>
<script src="../node_modules/codemirror/addon/runmode/runmode.js"></script>
<script src="../node_modules/codemirror/addon/edit/continuelist.js"></script>

View File

@@ -1,6 +1,6 @@
{
"name": "boost",
"version": "0.8.9",
"version": "0.8.10",
"main": "index.js",
"description": "Boostnote",
"license": "GPL-3.0",
@@ -10,6 +10,7 @@
"webpack": "webpack-dev-server --hot --inline --config webpack.config.js",
"compile": "grunt compile",
"test": "PWD=$(pwd) NODE_ENV=test ava",
"test:e2e": "NODE_ENV=test ava tests/e2e/*",
"fix": "npm run lint --fix",
"lint": "eslint .",
"dev-start": "concurrently --kill-others \"npm run webpack\" \"npm run hot\""
@@ -49,6 +50,8 @@
"dependencies": {
"@rokt33r/markdown-it-math": "^4.0.1",
"@rokt33r/season": "^5.3.0",
"aws-sdk": "^2.48.0",
"aws-sdk-mobile-analytics": "^0.9.2",
"codemirror": "^5.19.0",
"electron-config": "^0.2.1",
"electron-gh-releases": "^2.0.2",
@@ -62,6 +65,7 @@
"markdown-it-checkbox": "^1.1.0",
"markdown-it-emoji": "^1.1.1",
"markdown-it-footnote": "^3.0.0",
"markdown-it-imsize": "^2.0.1",
"md5": "^2.0.0",
"mixpanel": "^0.4.1",
"moment": "^2.10.3",
@@ -73,6 +77,7 @@
"react-redux": "^4.4.5",
"redux": "^3.5.2",
"sander": "^0.5.1",
"spectron": "^3.6.2",
"superagent": "^1.2.0",
"superagent-promise": "^1.0.3"
},
@@ -87,6 +92,7 @@
"babel-preset-react-hmre": "^1.0.1",
"babel-register": "^6.11.6",
"concurrently": "^3.4.0",
"copy-to-clipboard": "^3.0.6",
"css-loader": "^0.19.0",
"devtron": "^1.1.0",
"dom-storage": "^2.0.2",
@@ -100,6 +106,7 @@
"grunt-electron-installer": "^1.2.0",
"history": "^1.17.0",
"jsdom": "^9.4.2",
"json-loader": "^0.5.4",
"merge-stream": "^1.0.0",
"nib": "^1.1.0",
"react-color": "^2.2.2",

View File

@@ -25,7 +25,7 @@
## slack group
私たちにはslack groupもあります世界中のプログラマー達と、Boostnoteについてディスカッションをしましょう <br>
[こちらから](https://join.slack.com/boostnote-group/shared_invite/MTc2NTc5MTkyMjc3LTE0OTM0NDI5MzgtNzdkNjZjMzJhNA)
[こちらから](https://join.slack.com/boostnote-group/shared_invite/MTkwOTg2NjAzNTUyLTE0OTYzODQ3ODgtMGI4NDZlY2E5OQ)
## More Information
* Website: http://boostnote.io/

View File

@@ -25,7 +25,7 @@
## Slack Group
Let's talk about Boostnote's great features, new feature requests and things like Japanese gourmet. 🍣 <br>
[Join us](https://join.slack.com/boostnote-group/shared_invite/MTc2NTc5MTkyMjc3LTE0OTM0NDI5MzgtNzdkNjZjMzJhNA)
[Join us](https://join.slack.com/boostnote-group/shared_invite/MTkwOTg2NjAzNTUyLTE0OTYzODQ3ODgtMGI4NDZlY2E5OQ)
## More Information
* [Website](https://boostnote.io)

9
script/e2e-runner.sh Executable file
View File

@@ -0,0 +1,9 @@
if [[ ${TRAVIS_OS_NAME} = "linux" ]]; then
export DISPLAY=:99.0
sh -e /etc/init.d/xvfb start;
sleep 3
fi
npm install grunt-cli -g
grunt pre-build
npm run test:e2e

39
tests/e2e/spectron.js Normal file
View File

@@ -0,0 +1,39 @@
import test from 'ava'
import {Application} from 'spectron'
import path from 'path'
test.beforeEach(async t => {
const boostnotePath = ((platform) => {
switch (platform) {
case 'darwin':
return path.join('..', '..', 'dist', 'Boostnote-darwin-x64', 'Boostnote.app', 'Contents', 'MacOS', 'Boostnote')
case 'linux':
return path.join('..', '..', 'dist', 'Boostnote-linux-x64', 'Boostnote')
}
})(process.platform)
t.context.app = new Application({
path: boostnotePath
})
await t.context.app.start()
})
test.afterEach.always(async t => {
await t.context.app.stop()
})
test(async t => {
const app = t.context.app
await app.client.waitUntilWindowLoaded()
const win = app.browserWindow
t.is(await app.client.getWindowCount(), 1)
t.false(await win.isMinimized())
t.false(await win.isDevToolsOpened())
t.true(await win.isVisible())
t.true(await win.isFocused())
const {width, height} = await win.getBounds()
t.true(width > 0)
t.true(height > 0)
})

View File

@@ -175,5 +175,6 @@ module.exports = {
dummyFolder,
dummyBoostnoteJSONData,
dummyStorage,
dummyLegacyStorage
dummyLegacyStorage,
dummyNote
}

View File

@@ -0,0 +1,25 @@
/**
* @fileoverview Unit test for browser/lib/findTitle
*/
const test = require('ava')
const { findNoteTitle } = require('browser/lib/findNoteTitle')
// Unit test
test('findNoteTitle#find should return a correct title (string)', t => {
// [input, expected]
const testCases = [
['# hoge\nfuga', '# hoge'],
['# hoge_hoge_hoge', '# hoge_hoge_hoge'],
['hoge\n====\nfuga', 'hoge'],
['====', '===='],
['```\n# hoge\n```', '```'],
['hoge', 'hoge']
]
testCases.forEach(testCase => {
const [input, expected] = testCase
t.is(findNoteTitle(input), expected, `Test for find() input: ${input} expected: ${expected}`)
})
})

View File

@@ -12,7 +12,8 @@ test('htmlTextHelper#decodeEntities should return encoded text (string)', t => {
['var test = &apos;test&apos;', 'var test = \'test\''],
['&lt;a href=&apos;https://boostnote.io&apos;&gt;Boostnote', '<a href=\'https://boostnote.io\'>Boostnote'],
['&lt;\\\\?php\n var = &apos;hoge&apos;;', '<\\\\?php\n var = \'hoge\';'],
['&amp;', '&']
['&amp;', '&'],
['a&#36;&apos;', 'a\\$\'']
]
testCases.forEach(testCase => {
@@ -27,7 +28,8 @@ test('htmlTextHelper#decodeEntities() should return decoded text (string)', t =>
['<a href=', '&lt;a href='],
['var test = \'test\'', 'var test = &apos;test&apos;'],
['<a href=\'https://boostnote.io\'>Boostnote', '&lt;a href=&apos;https://boostnote.io&apos;&gt;Boostnote'],
['<?php\n var = \'hoge\';', '&lt;&#63;php\n var = &apos;hoge&apos;;']
['<?php\n var = \'hoge\';', '&lt;&#63;php\n var = &apos;hoge&apos;;'],
['a$\'', 'a&#36;&apos;']
]
testCases.forEach(testCase => {

36
tests/lib/search-test.js Normal file
View File

@@ -0,0 +1,36 @@
import test from 'ava'
import searchFromNotes from 'browser/lib/search'
import { dummyNote } from '../fixtures/TestDummy'
import _ from 'lodash'
const pickContents = (notes) => notes.map((note) => { return note.content })
let noteList = { noteMap: [] }
let note1, note2
test.before(t => {
const data1 = { type: 'MARKDOWN_NOTE', content: 'content1', tags: ['tag1'] }
const data2 = { type: 'MARKDOWN_NOTE', content: 'content1\ncontent2', tags: ['tag1', 'tag2'] }
note1 = dummyNote(data1)
note2 = dummyNote(data2)
noteList.noteMap = [note1, note2]
})
test('it can find notes by tags or words', t => {
// [input, expected content (Array)]
const testCases = [
['#tag1', [note1.content, note2.content]],
['#tag1 #tag2', [note2.content]],
['#tag1 #tag2 #tag3', []],
['content1', [note1.content, note2.content]],
['content1 content2', [note2.content]],
['content1 content2 content3', []]
]
testCases.forEach((testCase) => {
const [input, expectedContents] = testCase
const results = searchFromNotes(noteList, input)
t.true(_.isEqual(pickContents(results).sort(), expectedContents.sort()))
})
})

View File

@@ -15,6 +15,10 @@ var config = Object.assign({}, skeleton, {
test: /\.styl$/,
exclude: /(node_modules|bower_components)/,
loader: 'style!css?modules&importLoaders=1&localIdentName=[name]__[local]___[path]!stylus?sourceMap'
},
{
test: /\.json$/,
loader: 'json'
}
]
},

View File

@@ -13,6 +13,10 @@ var config = Object.assign({}, skeleton, {
test: /\.styl$/,
exclude: /(node_modules|bower_components)/,
loader: 'style!css?modules&importLoaders=1&localIdentName=[name]__[local]___[path]!stylus?sourceMap'
},
{
test: /\.json$/,
loader: 'json'
}
]
},

1511
yarn.lock

File diff suppressed because it is too large Load Diff