Compare commits

...

1233 Commits

Author SHA1 Message Date
Saumya Pandey c10e868626 fix: aria-checkbox selector isn't working in firefox 2022-02-12 18:41:37 +05:30
Tom Moor f4af702492 lint 2022-02-11 18:55:39 -08:00
Tom Moor 4255dd24da editor: Open link toolbar automatically when editable and link in selection 2022-02-11 18:34:56 -08:00
Tom Moor 2dd86bbc2b fix: Flash of uncolored pinned documents if pinned docs load before collections 2022-02-11 18:22:21 -08:00
Tom Moor 09810f554f fix: Private vimeo embeds 2022-02-11 16:20:57 -08:00
Tom Moor e5c8176ee6 Update email.ts 2022-02-10 21:20:58 -08:00
Tom Moor 25568c101c improves: Make command menu larger, darken toolbar, order image + link higher up menu 2022-02-10 20:15:28 -08:00
Tom Moor 590c23d539 Focus link selection on click rather than navigate when editing 2022-02-10 20:00:50 -08:00
Tom Moor b5424f7af4 deps: Update refractor 2022-02-10 19:40:58 -08:00
Tom Moor 25fd8466e0 perf: Move to passive scroll listeners where supported 2022-02-10 19:29:25 -08:00
Tom Moor af6c5a1f45 fix: Email authentication callback catches all errors as expired-token 2022-02-10 19:18:06 -08:00
Tom Moor 652f432785 fix: Mod+Enter does not exit code block
closes #3089
2022-02-10 19:14:10 -08:00
Tom Moor d1e805dfc8 fix: Link to export page in admin section to avoid redirect 2022-02-09 23:00:43 -08:00
Saumya Pandey 42061edbd1 feat: add the ability to choose default collection (#3029)
Co-authored-by: Tom Moor <tom@getoutline.com>
Co-authored-by: Tom Moor <tom.moor@gmail.com>
2022-02-10 10:06:10 +05:30
Tom Moor 9dfd1ec2dd chore: Dependency updates (#3084)
* Update node-fetch, i18next-http-backend

* Update nodemon

* Update react-refresh-webpack-plugin, remove old ansi-html dep
2022-02-09 20:29:28 -08:00
Tom Moor b3f33c1101 perf: Remove withStarred scope and starred property on documents
closes #3081
2022-02-09 19:51:51 -08:00
Tom Moor 5bc4405b23 fix: Alignment of team name in settings header, closes #3083 2022-02-09 19:47:57 -08:00
Tom Moor a578d3449b fix: Copy/paste of notices, closes #3082 2022-02-09 19:43:15 -08:00
Tom Moor 8490cab9b1 fix: Export collections produces empty zip, regressed in #3064 2022-02-09 19:40:40 -08:00
Tom Moor 9da493902c lint 2022-02-08 23:14:54 -08:00
Tom Moor 193ca910f8 Remove Permissions-Policy header, FLOC has now been abandoned 2022-02-08 23:06:04 -08:00
Tom Moor f42a09be0d fix: Reduce movement of Home header on first load 2022-02-08 22:46:24 -08:00
Tom Moor 4e0f3e79c5 fix: Expired export state missing icon 2022-02-08 22:40:17 -08:00
Tom Moor 9d77baccfd fix: Pinned documents animate out when navigating between collections 2022-02-08 22:21:16 -08:00
Tom Moor 9cf3469b81 fix: Even more flexibility in LucidChart embed matching. It really is incredible how many variations this company has for share urls.
closes #3074
2022-02-08 22:11:35 -08:00
Tom Moor bc2556d004 fix: Document title allows pasting rich text, closes #3079 2022-02-08 22:07:53 -08:00
Translate-O-Tron 342bec5211 New Crowdin updates (#2988)
* fix: New Vietnamese translations from Crowdin [ci skip]

* fix: New French translations from Crowdin [ci skip]

* fix: New Portuguese translations from Crowdin [ci skip]

* fix: New Persian translations from Crowdin [ci skip]

* fix: New Portuguese, Brazilian translations from Crowdin [ci skip]

* fix: New Chinese Traditional translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]

* fix: New Swedish translations from Crowdin [ci skip]

* fix: New Russian translations from Crowdin [ci skip]

* fix: New Polish translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]

* fix: New Italian translations from Crowdin [ci skip]

* fix: New German translations from Crowdin [ci skip]

* fix: New Danish translations from Crowdin [ci skip]

* fix: New Japanese translations from Crowdin [ci skip]

* fix: New Dutch translations from Crowdin [ci skip]

* fix: New Vietnamese translations from Crowdin [ci skip]

* fix: New Korean translations from Crowdin [ci skip]

* fix: New Thai translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]

* fix: New French translations from Crowdin [ci skip]

* fix: New Portuguese translations from Crowdin [ci skip]

* fix: New Persian translations from Crowdin [ci skip]

* fix: New Portuguese, Brazilian translations from Crowdin [ci skip]

* fix: New Chinese Traditional translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]

* fix: New Swedish translations from Crowdin [ci skip]

* fix: New Russian translations from Crowdin [ci skip]

* fix: New Polish translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]

* fix: New Italian translations from Crowdin [ci skip]

* fix: New German translations from Crowdin [ci skip]

* fix: New Danish translations from Crowdin [ci skip]

* fix: New Japanese translations from Crowdin [ci skip]

* fix: New Dutch translations from Crowdin [ci skip]

* fix: New Vietnamese translations from Crowdin [ci skip]

* fix: New Korean translations from Crowdin [ci skip]

* fix: New Thai translations from Crowdin [ci skip]

* fix: New French translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]

* fix: New French translations from Crowdin [ci skip]

* fix: New Portuguese translations from Crowdin [ci skip]

* fix: New Persian translations from Crowdin [ci skip]

* fix: New Portuguese, Brazilian translations from Crowdin [ci skip]

* fix: New Chinese Traditional translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]

* fix: New Swedish translations from Crowdin [ci skip]

* fix: New Russian translations from Crowdin [ci skip]

* fix: New Polish translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]

* fix: New Italian translations from Crowdin [ci skip]

* fix: New German translations from Crowdin [ci skip]

* fix: New Danish translations from Crowdin [ci skip]

* fix: New Japanese translations from Crowdin [ci skip]

* fix: New Dutch translations from Crowdin [ci skip]

* fix: New Vietnamese translations from Crowdin [ci skip]

* fix: New Korean translations from Crowdin [ci skip]

* fix: New Thai translations from Crowdin [ci skip]

* fix: New Japanese translations from Crowdin [ci skip]

* fix: New German translations from Crowdin [ci skip]

* fix: New Portuguese, Brazilian translations from Crowdin [ci skip]

* fix: New Portuguese, Brazilian translations from Crowdin [ci skip]

* fix: New French translations from Crowdin [ci skip]

* fix: New Portuguese translations from Crowdin [ci skip]

* fix: New Persian translations from Crowdin [ci skip]

* fix: New Portuguese, Brazilian translations from Crowdin [ci skip]

* fix: New Chinese Traditional translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]

* fix: New Swedish translations from Crowdin [ci skip]

* fix: New Russian translations from Crowdin [ci skip]

* fix: New Polish translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]

* fix: New Italian translations from Crowdin [ci skip]

* fix: New German translations from Crowdin [ci skip]

* fix: New Danish translations from Crowdin [ci skip]

* fix: New Japanese translations from Crowdin [ci skip]

* fix: New Dutch translations from Crowdin [ci skip]

* fix: New Vietnamese translations from Crowdin [ci skip]

* fix: New Korean translations from Crowdin [ci skip]

* fix: New Thai translations from Crowdin [ci skip]

* fix: New French translations from Crowdin [ci skip]
2022-02-08 21:15:02 -08:00
Tom Moor 156b47b1b5 fix: Auth persistence to localStorage (#3078)
* fix: user, team, and policies should be persisted to localStorage for faster boot

* capture instead of ignore errors
2022-02-08 21:14:15 -08:00
Tom Moor aa09dc39fb fix: NudeButton should always have button height 2022-02-08 20:22:08 -08:00
Tom Moor d643c9453e feat: Import improvements (#3064)
* feat: Split and simplify import/export pages in prep for more options

* minor fixes

* File operations for imports

* test

* icons
2022-02-06 22:29:24 -08:00
Tom Moor a4e9251eb7 fix: Images no longer in original document not included in export archive (#3063)
closes #2578
2022-02-05 17:13:02 -08:00
Tom Moor 1bb57bf15a fix: Theme changes do not propagate to custom editor components in realtime
see TODO in #3031
2022-02-05 15:48:17 -08:00
Tom Moor c5a11fe17b chore: Enable eslint to enforce curly (#3060) 2022-02-05 10:15:40 -08:00
Tom Moor c7df74fcc4 fix: Improvements to image positioning (#3061)
* fix: Empty paragraph below images that confuses block insertion

* Remove spacing for caption when unused
2022-02-05 10:14:10 -08:00
Nan Yu ae0644177b fix: make mobile popover dialog styling nice (#3059)
* fix: make mobile popover dialog styling nice
Co-authored-by: Tom Moor <tom.moor@gmail.com>
2022-02-04 16:32:22 -08:00
Tom Moor 4cc7af7874 fix: Do not parse 😄 style emoji in Markdown paste handler (#3056) 2022-02-04 07:37:18 -08:00
Tom Moor 0bee0bbade fix: Remap event name for Slack integration 2022-02-02 21:31:51 -08:00
Saumya Pandey b14867c67e fix: error from api rate limiting (#3050) 2022-02-03 09:13:46 +05:30
Tom Moor c10236f7a9 fix: ESC on shared docs goes to login page, closes #3044 2022-02-01 21:33:17 -08:00
Nan Yu 735aaa668a fix: add toc to mobile views and account for branding on shared view layouts (#2997)
* fix: add toc to mobile views and center the branding

* add padding to bottom of sidebar

* put the mobile branding inline

* finesse the padding

* make spelling of sign-in email less crazy looking

* move mobile sidebar button into header

* adds scene to search and 404 pages

* fix title alignment

* make filter buttons tight

* clean up unused imports

* lint

Co-authored-by: Tom Moor <tom.moor@gmail.com>
2022-02-01 20:58:24 -08:00
Louis Phan 516e2f1b6e fix: issue 3030 HelpText layout (#3034)
* fix helptext layout

* remove unnecessary css property - word-break

Co-authored-by: Louis Phan <louisphan@notreallouisss.local>
2022-02-01 16:36:40 -08:00
Tom Moor 2d1c9c1a93 fix: Regression in header spacing 2022-01-31 23:11:10 -08:00
Tom Moor cc19e45caf fix: list editing regression (#3041)
* working state

* Selective deps upgrade, still working

* Update more deps, still working

* fix: Tweak checkbox items too bright while we're here

* Avoid prosemirror-view downgrade
2022-01-31 22:01:48 -08:00
Tom Moor 187f036c80 fix: Editing image caption broken 2022-01-30 20:45:52 -08:00
Tom Moor fac86a2a28 fix: Don't show share button on trashed docs
closes #2999
2022-01-28 21:59:29 -08:00
Tom Moor 26c73116b0 fix: Prevent navigation of document history in trash, closes #2995 2022-01-28 21:44:42 -08:00
Tom Moor eafb9c5225 fix: Truncate long titles in import 2022-01-28 21:00:52 -08:00
Tom Moor 76d83b5e82 Remove duplicative Toggle component (#3028)
fix: Tidy some styling and spacing issues in settings
2022-01-28 20:23:02 -08:00
Tom Moor 1cd770e38d fix: Disabling editor embeds should work with collaborative editing (#2968)
* fix: Disabling editor embeds should work with collaborative editing

* Design tweaks, fixed dragging
2022-01-28 18:27:27 -08:00
Tom Moor e7867e52e0 chore: Database index improvements (#3027) 2022-01-27 19:51:31 -08:00
Tom Moor 0203b2bc17 fix: Ensure copy code button appears in collab editing (#3021)
* fix: Ensure copy code button appears in collab editing

* fix: code actions should not flip in RTL doc (code is always left aligned)
2022-01-27 17:14:47 -08:00
Tom Moor bcd6e17781 fix: Cannot view history with view-only permissions and collab editing enabled (#3024)
closes #3016
2022-01-27 17:14:36 -08:00
Tom Moor 051d58011e fix: Regression in collapsible headings
closes #3010
2022-01-27 08:00:57 -08:00
Tom Moor 6d987975bc lint 2022-01-27 07:55:02 -08:00
Tom Moor 16277d1074 fix: Document search results in editor now show full path
closes #3007
2022-01-27 07:29:59 -08:00
Tom Moor 76e98c31e3 fix: Team logo shows as white in settings (#3015)
* fix: Team logo shows as white in settings
fix: Team logo doesnt update in sidebar immediately after updating
refactor to ImageUpload component

* text
2022-01-26 22:47:26 -08:00
Tom Moor afb0dad0a5 fix: Code block background on mobile, closes #2998 2022-01-26 21:17:10 -08:00
Tom Moor af8e74c373 fix: Should be able to unstar archived and trashed documents (#2983) 2022-01-26 19:39:06 -08:00
Saumya Pandey 9d82bf2a69 fix: remove scrollbar on x-axis in toc (#3008)
* fix: remove overflow x in toc

* fix: use useMemo

* fix: parsing /settings/integrations/slack

* fix: no transparency in firefox

* fix: remove debounced fn
2022-01-26 19:36:11 -08:00
Tom Moor dc29fb475d snapshot 2022-01-25 23:51:15 -08:00
Tom Moor 175857753e fix: Bag 'o fixes
Remove menu hover styles on mobile
Fixed duplicate hover+active behavior on editor menus
Fixed editor menus visibly scroll to the top when reopened
Fixed some minor editor spacing issues
Renamed shred routeHelpers -> urlHelpers
2022-01-25 23:43:11 -08:00
Tom Moor 13b8ed58fd fix: Improved pasting behavior from Dropbox Paper 2022-01-25 22:01:51 -08:00
Tom Moor bc40a0074a fix: Paragraph spacing 2022-01-25 20:02:03 -08:00
Charlie Jonas 36ee42084c fix: issue-2974 reparent nested document when dropped on current collection (#2975) 2022-01-24 19:40:42 -08:00
Tom Moor c8a67c374f Merge branch 'main' of github.com:outline/outline 2022-01-23 21:02:12 -08:00
Translate-O-Tron cd3393585c New Crowdin updates (#2924) 2022-01-23 20:52:01 -08:00
Tom Moor 797e5b63e7 tsc 2022-01-23 20:50:48 -08:00
Tom Moor 39268f471e fix: Excessive spacing at top of editor 2022-01-23 20:43:37 -08:00
Eugene Sokolov b52b1b02fe Fix: consistently check allowed domains (#2985)
* fix: ensure consistency of checking allowed domain

* chore: update comment to match the logic
2022-01-23 17:40:18 -08:00
Tom Moor 390a1343b7 docs: Remove contradicting message from .env comments 2022-01-23 09:46:29 -08:00
Tom Moor 37a29934ec fix: Reduce clickable area on star in document header, make entire line clickable to focus title
closes #2966
2022-01-22 18:25:06 -08:00
Tom Moor e8c88b3c33 fix: Flash of content when selecting text on RHS of document (#2981)
fix: Double portal
2022-01-22 18:10:23 -08:00
Tom Moor 80c6e57aa3 fix: Share popover incorrectly displays draft as publicly shared when parent document is (#2982)
closes #2978
2022-01-22 18:04:05 -08:00
Tom Moor e4dbd67ae1 feat: Support SSL without reverse proxy (#2959)
* Enable (optional) reading SSL certificates on startup

* Update gitignore

* fix: Expect ssl environment variables to be Base64 encoded

* docs: Add env variables to .env.sample
2022-01-22 17:40:55 -08:00
Tom Moor 50547ae355 fix: Increase default breathing room in documents
closes #2878
2022-01-22 17:32:07 -08:00
Tom Moor 88fcf8cb44 docs: Update README to point to new docs 2022-01-22 13:18:29 -08:00
Tom Moor 0d66055441 fix: More lenient Lucidchart embed matcher
closes #2017
2022-01-22 08:41:51 -08:00
Nan Yu f4619e0e77 fix: truncate a very long team name with ellipses (#2963) 2022-01-21 18:36:13 -08:00
Tom Moor 79e2cad5b9 feat: Add reordering to starred documents (#2953)
* draft

* reordering

* JIT Index stars on first load

* test

* Remove unused code on client

* small unrefactor
2022-01-21 18:11:50 -08:00
Tom Moor 49533d7a3f Enable 'Features' settings item on cloud hosted 2022-01-20 20:21:22 -08:00
Tom Moor 8c6ab3db37 fix: Don't show drag handle on pins when there is only 1 2022-01-20 20:18:28 -08:00
Nan Yu c2c7a7190c fix: trim collection name on save rather than on change (#2962)
* fix: trim collection name on save rather than on change

* move trim to server
2022-01-20 18:53:29 -08:00
Tom Moor 703708e5d2 fix: A couple of dark mode styling issues 2022-01-20 18:50:05 -08:00
Andy Rossmeissl 090e5f2849 fix: Missing space character (#2961)
* Fix typo

* Update Templates.tsx

Co-authored-by: Tom Moor <tom.moor@gmail.com>
2022-01-20 18:31:48 -08:00
Tom Moor 062016b164 chore: Move editor into codebase (#2930) 2022-01-19 18:43:15 -08:00
Nan Yu 266f8c96c4 fix: issue-2956 (#2957) 2022-01-19 18:24:25 -08:00
Nan Yu 8bced69828 fix: changing the title and body content in quick succession would trigger unsaved changes warning prompt (#2950)
changing the title and body content in quick succession would trigger unsaved changes warning prompt
2022-01-19 12:57:44 -08:00
Tom Moor d0e7f2de65 fix: Emoji in title positioning (#2927)
* wip

* fix measure on first render

* wip

* refactor

* tsc

* remove fragment

* refactor (again)

* cleanup
2022-01-16 17:02:33 -08:00
Tom Moor 5abc73fabc fix: Editor shortcut conflicts (#2943)
* fix: ctrl+n shortcut conflicts

* Update kbar with fix for ctrl-k on macOS
2022-01-15 17:47:53 -08:00
Tom Moor 49718f2894 fix: Submenus get stuck open, closes #2938 2022-01-15 16:21:39 -08:00
Tom Moor bb29dcaccc tsc 2022-01-14 21:04:51 -08:00
Tom Moor 19704e15ba fix: Pin icons different colored, closes #2939 2022-01-14 21:02:35 -08:00
Tom Moor 7fa2644caf chore: Allow immediate login for invites in development 2022-01-14 20:41:20 -08:00
Nan Yu 71820fb3ad feat: Add navigation sidebar to shared documents (#2899)
Co-authored-by: Tom Moor <tom@getoutline.com>
2022-01-14 19:02:01 -08:00
Tom Moor 2ad32e5009 fix: Remove innerHTML usage 2022-01-09 21:44:53 -08:00
Tom Moor 7c5514ae10 fix: Remove usage of direct HTML access 2022-01-09 21:19:18 -08:00
Max Mykhailenko ce27332ccb docs: Update instructions (#2895)
* Update instructions

Additional details for using redis or postgres installed outside docker

* Update README.md

Co-authored-by: Tom Moor <tom.moor@gmail.com>

* Update README.md

Co-authored-by: Tom Moor <tom.moor@gmail.com>
2022-01-09 09:56:10 -08:00
Tom Moor 86e171442d fix #2906 2022-01-08 16:19:57 -08:00
Tom Moor fc564cfc1a fix: Templates menu does not appear in collab editing (#2915)
* fix: Templates menu does not appear in collab editing

* Improved logic for display templates menu
2022-01-08 16:04:50 -08:00
Tom Moor c6cc04cad8 chore: More typescript FIXME's removed 2022-01-06 21:25:42 -08:00
Translate-O-Tron 8d05c752ea New Crowdin updates (#2897) 2022-01-06 19:24:59 -08:00
Tom Moor b20a341f0c chore: Typescript database models (#2886)
closes #2798
2022-01-06 18:24:28 -08:00
Tom Moor d3cbf250e6 0.61.0 2022-01-05 20:53:07 -08:00
Tom Moor ece879b2cc fix: Unresponsiveness of select inputs post-click 2022-01-05 20:10:51 -08:00
Tom Moor 3cee22da89 Add Vietnamese to language options
closes #2889
2022-01-05 19:09:42 -08:00
Tom Moor 917167cf1b fix: Spacing too large beneath 'Home' heading when no docs pinned 2022-01-05 18:58:34 -08:00
Tom Moor a01dded55a fix: Catch no path passed to /static/ route 2022-01-04 22:18:03 -08:00
Translate-O-Tron 09df6fa0d1 New Crowdin updates (#2890) 2022-01-04 21:42:42 -08:00
Translate-O-Tron 6cdf255074 New Crowdin updates (#2863) 2021-12-31 17:55:30 -08:00
Tom Moor eb0c324da8 feat: Pin to home (#2880) 2021-12-30 16:54:02 -08:00
Tom Moor 5be2eb75f3 fix: Incorrect text color on 'full width' menu item 2021-12-20 18:36:06 -08:00
Tom Moor ce614ca170 chore: Minor typescript improves 2021-12-19 22:08:25 -08:00
Tom Moor 448694a561 ui: Remove heavy underline on subheading text 2021-12-19 21:29:23 -08:00
Tom Moor 4998421cc0 fix: '+' button display in sidebar in Safari 2021-12-19 17:43:46 -08:00
Tom Moor 36c0372d62 fix: Loop loading GitHub Gist embeds in Safari
closes #2520
2021-12-19 17:38:03 -08:00
Tom Moor 66d5a567c2 feat: Optional full-width toggle for document display (#2869)
* Migration, model, presenter

* Working implementation

* fix: Account for table of contents

* Checkbox -> Toggle

* Checkbox -> Toggle
2021-12-19 13:58:16 -08:00
Tom Moor 73bc7d9f2a fix: Reduce loading jank on recent searches screen 2021-12-19 13:07:22 -08:00
Tom Moor 5507295a36 fix: '+' button display in sidebar in Safari 2021-12-19 12:58:08 -08:00
Tom Moor 6fc1b5cc22 feat: Show recent searches (#2868)
* stash

* root hookup

* recent searches UI

* feat: Add search query deletion

* simplify no results state

* lint
2021-12-19 11:08:28 -08:00
Tom Moor 81f3347ecf fix: Don't show create a group option to those without permission
closes #2621
2021-12-18 11:21:07 -08:00
Tom Moor 791be77867 feat: Add '+' button to create new doc in sidebar (#2864) 2021-12-18 11:14:17 -08:00
Tom Moor 80b2ba886a fix: Improved inline code display in dark mode
closes #2835
2021-12-18 11:08:17 -08:00
Tom Moor 0a89b7b5b9 closes #2747 2021-12-18 11:02:06 -08:00
Tom Moor 39018f16b5 Show collaborators on mobile, normalize margins 2021-12-17 16:50:34 -08:00
Tom Moor f8736758f1 fix: Redirect loop in shared documents
closes #2867
2021-12-17 16:42:12 -08:00
Tom Moor e7c3028ef2 fix: Views should be recorded for drafts closes #2862 2021-12-17 16:27:06 -08:00
Tom Moor 8b73f98b9a fix: Document titles in RTL script not correctly aligned 2021-12-17 11:29:40 -08:00
Translate-O-Tron 93efedb912 New Crowdin updates (#2852)
* fix: New German translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]

* fix: New Japanese translations from Crowdin [ci skip]

* fix: New Dutch translations from Crowdin [ci skip]

* fix: New French translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]

* fix: New Italian translations from Crowdin [ci skip]

* fix: New Korean translations from Crowdin [ci skip]

* fix: New Polish translations from Crowdin [ci skip]

* fix: New Portuguese translations from Crowdin [ci skip]

* fix: New Russian translations from Crowdin [ci skip]

* fix: New Swedish translations from Crowdin [ci skip]

* fix: New Chinese Traditional translations from Crowdin [ci skip]

* fix: New Portuguese, Brazilian translations from Crowdin [ci skip]

* fix: New Persian translations from Crowdin [ci skip]

* fix: New Thai translations from Crowdin [ci skip]

* fix: New Persian translations from Crowdin [ci skip]

* fix: New Persian translations from Crowdin [ci skip]

* fix: New German translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]

* fix: New Japanese translations from Crowdin [ci skip]

* fix: New Dutch translations from Crowdin [ci skip]

* fix: New French translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]

* fix: New Italian translations from Crowdin [ci skip]

* fix: New Korean translations from Crowdin [ci skip]

* fix: New Polish translations from Crowdin [ci skip]

* fix: New Portuguese translations from Crowdin [ci skip]

* fix: New Russian translations from Crowdin [ci skip]

* fix: New Swedish translations from Crowdin [ci skip]

* fix: New Chinese Traditional translations from Crowdin [ci skip]

* fix: New Portuguese, Brazilian translations from Crowdin [ci skip]

* fix: New Persian translations from Crowdin [ci skip]

* fix: New Thai translations from Crowdin [ci skip]
2021-12-16 22:30:34 -08:00
Tom Moor d4695f3b5b feat: Add event selection to Slack post integration (#2857) 2021-12-16 22:30:23 -08:00
Tom Moor 9a7b5ea1f4 feat: Added ability to click another user to observe them (sync scroll position) (#2858)
* feat: Added ability to click another user to observe them, mainly for fun

* language, lower debounce, prevent tooltip from hiding when toggling observation

* fix: Don't allow observing self, added banner at top of screen

* Dont edit tooltip as it's confusing between our actions and theirs

* snapshots
2021-12-16 17:36:39 -08:00
Tom Moor 4266b2eb3c feat: New heading behavior in editor 2021-12-15 23:02:34 -08:00
Tom Moor 88ce043438 fix: Revert cdnPath change 2021-12-14 18:30:57 -08:00
Tom Moor e2ee6e16d7 fix: Incorrect import syntax 🤦 2021-12-14 18:14:44 -08:00
Tom Moor 32126602fd fix: Duplicate notification for publish event, closes #2757 2021-12-14 17:21:11 -08:00
Tom Moor 0079ac90e7 chore: Remove unused screenshots from repo 2021-12-14 16:17:16 -08:00
Tom Moor 1e3f3ff047 fix: Integration icons should load from CDN if available 2021-12-14 16:14:50 -08:00
Tom Moor 632799fea6 feat: Add 'Whimsical' as embed provider 2021-12-14 16:06:31 -08:00
Tom Moor 0a7723a10f fix: Frameless embeds
fix: Vimeo embed alt url style
2021-12-14 15:54:07 -08:00
Tom Moor 6b1a1750c6 fix: Emoji text selection behavior 2021-12-14 15:32:27 -08:00
Tom Moor 2f4531ff49 fix: History sidebar behavior (more leftover bugs from TS conversion) 2021-12-13 23:42:47 -08:00
Tom Moor 74615bfe37 fix: Server error if X-Editor-Version is not valid semver string 2021-12-13 20:28:29 -08:00
Tom Moor 1a3badbb8a feat: Add collection icons: Lightning, Database, Smiley
fix: Icons Vehicle, Bicycle
2021-12-13 20:23:22 -08:00
Nan Yu 6f73cb4939 fix: various bugs on draft sidebar items (#2856)
- show children when the only child is the active draft
- disable drop onto draft items
- fix danger button hover color
2021-12-13 13:21:03 -08:00
Tom Moor e1903c4016 fix: command palette opens when editor is active 2021-12-12 21:51:42 -08:00
Tom Moor 0340149457 chore: More type improvements 2021-12-12 18:44:32 -08:00
Tom Moor 46f839431b feat: Add replace image functionality
fix: Hide image caption when not directly editing
Update iconography
2021-12-12 17:23:07 -08:00
Tom Moor 60223f4404 fix: Presenter for empty collection returns undefined for documentStructure 2021-12-11 13:53:11 -08:00
Tom Moor 19e85c63c2 lint, tsc 2021-12-11 12:48:32 -08:00
Tom Moor ca0a900c21 fix: Improved display of editing titles in sidebar 2021-12-11 12:48:32 -08:00
Translate-O-Tron 5ebfdf7b8a New Crowdin updates (#2842)
* fix: New Dutch translations from Crowdin [ci skip]

* fix: New Dutch translations from Crowdin [ci skip]
2021-12-11 09:43:00 -08:00
Zero King ff662acfe0 chore: Update Node.js version (#2849) 2021-12-11 09:34:57 -08:00
Tom Moor 7aa4709e69 feat: Show drafts in sidebar when viewing (#2820) 2021-12-11 09:34:36 -08:00
Tom Moor e5b4186faa feat: Add searching of sub actions in command menu (#2845) 2021-12-11 09:34:16 -08:00
Tom Moor 7c6ce44fe1 fix: Incorrect styling of icon color picker in dark mode 2021-12-10 23:44:03 -08:00
Zero King 6713dfcb89 fix: Bind context to util.promisify (#2844) 2021-12-10 19:40:02 -08:00
dkkb 724f641d44 fix: Document icon overflow in ReferenceListItem (#2811)
* fix: Document icon overflow in ReferenceListItem

* fix: Icon shrinking

* fix: RTL document titles

Co-authored-by: Tom Moor <tom.moor@gmail.com>
2021-12-10 19:24:13 -08:00
dependabot[bot] 0a40b3a7e2 chore(deps): bump nodemailer from 6.4.16 to 6.6.1 (#2843)
Bumps [nodemailer](https://github.com/nodemailer/nodemailer) from 6.4.16 to 6.6.1.
- [Release notes](https://github.com/nodemailer/nodemailer/releases)
- [Changelog](https://github.com/nodemailer/nodemailer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodemailer/nodemailer/compare/v6.4.16...v6.6.1)

---
updated-dependencies:
- dependency-name: nodemailer
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-10 17:19:43 -08:00
Zero King 11e14bc4f5 feat: Support IAM role authentication for S3 (#2830)
closes #2829
2021-12-10 17:08:03 -08:00
Zero King 05b9ae3e63 chore: Remove outline service in docker-compose.yml (#2824)
Commit 18cf148bd1 made Yarn run locally outside of Docker, so there is no need for an outline container.
2021-12-10 11:03:22 -08:00
Translate-O-Tron 79d27e9684 fix: New Japanese translations from Crowdin [ci skip] (#2839) 2021-12-10 08:24:00 -08:00
Saumya Pandey cda43b5c7f fix: focus input when page refresh (#2814)
* fix: focus input when page refresh

* fix: increase to 100ms
2021-12-09 22:50:06 -08:00
Translate-O-Tron 7f8db01168 New Crowdin updates (#2825)
* fix: New Chinese Simplified translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]
2021-12-09 22:49:51 -08:00
David Zhuang 29b75f84c2 fix: Optimizing Heroku deployment: more configs, add collaboration support (#2838) 2021-12-09 22:49:38 -08:00
Tom Moor 355013a160 fix: reassignment to const (from typescript conversion)
closes #2837
2021-12-09 22:48:00 -08:00
Zero King 09afe7137d feat: Support custom CDN in Dockerfile (#2833) 2021-12-08 09:13:40 -08:00
Tom Moor 8b4bd29515 fix: Typescript dependency bundled in prod causes websocket connections to fail 2021-12-06 19:48:24 -08:00
Tom Moor ac5d47e35b Update stale.yml 2021-12-06 08:00:27 -08:00
Tom Moor 6550d89d9b fix: Unable to access icon picker (from ts migration) 2021-12-05 23:20:07 -08:00
Tom Moor b16e27a790 fix: Error opening folded document breadcrumb (from ts migration) 2021-12-05 22:48:02 -08:00
Translate-O-Tron f87ac36d57 New Crowdin updates (#2816)
* fix: New Chinese Simplified translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]
2021-12-05 20:58:02 -08:00
Zero King 986ccdf355 fix: Keep tsconfig.json in Docker build (#2823)
closes #2822
2021-12-05 20:46:42 -08:00
Tom Moor 891671a45b lint 2021-12-05 19:54:21 -08:00
Tom Moor 6e371f0d03 fix: HoverPreview not showing on collaborative editing teams
types
2021-12-05 19:31:08 -08:00
Tom Moor ce2a58e83b fix: Math.random -> crypto.randomBytes
closes #2818
2021-12-05 18:42:03 -08:00
Tom Moor d9cb40ae84 fix: Error at table size in dark mode 2021-12-05 18:30:21 -08:00
Zero King 2db49820f9 fix: Catch error in promise and fix typo (#2817) 2021-12-04 10:25:05 -08:00
Tom Moor e4f0ef6ddf chore: Upgrade outline-icons (typescript) 2021-12-03 17:32:19 -06:00
Tom Moor 7c37724f88 types 2021-12-03 10:34:41 -06:00
Tom Moor 9eb72a3485 fix: Nested list items escape page boundaries 2021-12-03 10:34:41 -06:00
Tom Moor 332ab4cd40 fix: Display sidebar toggle by default on tablets 2021-12-03 10:34:41 -06:00
Tom Moor 8085c16d32 fix: Odd document header state on exact media query boundary 2021-12-03 10:34:41 -06:00
Translate-O-Tron 10da62a4e8 fix: New German translations from Crowdin [ci skip] (#2813) 2021-12-03 07:48:49 -08:00
Tom Moor 11c714e47d fix: Remove star option on publicly shared docs (#2804) 2021-12-03 06:44:21 -08:00
Tom Moor 82bc6ef45b fix: Clickable area for docs in sidebar is obstructed (#2809)
closes #2808
2021-12-03 06:44:06 -08:00
Nan Yu d8d3e2bef2 fix: adds done and publish options to document.update, this got taken out on accident during the switchover to ts (#2812) 2021-11-30 11:12:14 -08:00
ꌗᖘ꒒ꀤ꓄꒒ꀤꈤꍟ e8aed2e6c9 chore: Let dev redis, pgsql and s3 listen on localhost only. (#2776) 2021-11-30 06:36:07 -08:00
Tom Moor 77d24dd7f8 fix: Allow use of email providers with no username, closes #2767 2021-11-30 08:21:15 -06:00
Tom Moor d13254b543 fix: No reserving space in sidebar for overflow (#2807)
* fix: No reserving space in sidebar for overflow
closes #2486

Co-authored-by: Charles Harries <charles@harries.me>
2021-11-30 05:56:04 -08:00
Tom Moor e2841aebcd fix: Emoticons converted to emojis post-save, closes #2785 2021-11-30 07:26:18 -06:00
Translate-O-Tron 4545b625fe New Crowdin updates (#2691) 2021-11-29 19:53:36 -08:00
Tom Moor 98daf62a5c feat: Upgrade editor, more code block language support 2021-11-29 21:01:39 -06:00
Tom Moor 15b1069bcc chore: Move to Typescript (#2783)
This PR moves the entire project to Typescript. Due to the ~1000 ignores this will lead to a messy codebase for a while, but the churn is worth it – all of those ignore comments are places that were never type-safe previously.

closes #1282
2021-11-29 06:40:55 -08:00
Tom Moor 25ccfb5d04 lint 2021-11-26 17:16:39 -06:00
Tom Moor 5b27c0f84d fix: Background error on public shared pages 2021-11-26 17:10:45 -06:00
Tom Moor c64387668d Merge fix/path-parsing 2021-11-17 21:02:53 -08:00
Nan Yu ec5e3120d2 fix: visible groups (#2729)
* updated readme to give some light testing instructions
* updated tests to accept new behavior for group memberships
* use test factories in more places
* add debug logs for mailer events in development
2021-11-15 16:05:58 -08:00
Tom Moor 8a76dd49a0 Bump RME: Fix content in notices 2021-11-14 18:34:26 -08:00
Tom Moor 33524a1322 feat: Add 'Pitch' embed support 2021-11-14 18:33:30 -08:00
dkkb b616292fce feat: Highlight active ListItem in outline. (#2760) 2021-11-12 17:22:37 -08:00
dkkb 94a2e453eb fix: fix issue where the title can be modified in read-only mode (#2761)
The title can be changed (but not saved) when the document is in read only mode.
2021-11-12 08:21:56 -08:00
Tom Moor a674a8668b Update LICENSE 2021-11-11 07:27:39 -08:00
Tom Moor 28ab3402ac 0.60.1 2021-11-11 07:21:24 -08:00
Tom Moor 8a9c09c646 fix: Collaborative sync issue due to doc being prematurely removed in server memory 2021-11-10 17:55:20 -08:00
Tom Moor 30a80fa92d 0.60.0 2021-11-09 21:45:20 -08:00
Tom Moor e899616081 fix: Changing team settings should update in other tabs 2021-11-09 17:28:59 -08:00
dependabot[bot] 411a76f9ff chore(deps): bump y18n from 4.0.0 to 4.0.3 (#2752)
Bumps [y18n](https://github.com/yargs/y18n) from 4.0.0 to 4.0.3.
- [Release notes](https://github.com/yargs/y18n/releases)
- [Changelog](https://github.com/yargs/y18n/blob/y18n-v4.0.3/CHANGELOG.md)
- [Commits](https://github.com/yargs/y18n/compare/v4.0.0...y18n-v4.0.3)

---
updated-dependencies:
- dependency-name: y18n
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-09 07:10:48 -08:00
Tom Moor 87125223de chore: Various dependency updates (#2751) 2021-11-09 07:03:36 -08:00
Tom Moor 6a64dfe4b2 fix: code scanning alerts (#2750) 2021-11-08 22:46:30 -08:00
Tom Moor 87e8ef8fe6 Merge branch 'main' of github.com:outline/outline 2021-11-08 20:52:39 -08:00
Tom Moor c597f2d9a2 feat: Seamless Edit (#2701)
* feat: Remove explicit edit

* Restore revision remains disabled for now

* Bump RME, better differentiation of focused state

* fix: Star not visible in edit mode

* remove stray log

* fix: Occassional user context not available in collaborative persistence
2021-11-08 20:52:17 -08:00
Tom Moor 37be7f99c4 fix: UI flash when loading history sidebar 2021-11-08 20:43:19 -08:00
Tom Moor 9865eab61c fix: Occassional user context not available in collaborative persistence 2021-11-08 18:56:36 -08:00
Tom Moor a600a897c3 fix: Clash between history and table of content shortcuts
related #2733
2021-11-08 18:03:03 -08:00
Tom Moor e170a63094 Missing association cascades 2021-11-07 21:53:40 -08:00
Tom Moor c30908e858 fix: Code highlighting with collaborative editing 2021-11-07 18:48:48 -08:00
Tom Moor 3ac7a839ad fix: Improve share popover focus behavior 2021-11-07 15:48:32 -08:00
Tom Moor 7bc7d7cd6b fix: Incorrect policy returned for parent share 2021-11-07 15:44:37 -08:00
Tom Moor dcec3dd4ec Create codeql-analysis.yml
Testing this out, may revert if it's not useful
2021-11-07 11:27:42 -08:00
Tom Moor b2a1e6b309 feat: Collaborative revision restore (#2721) 2021-11-07 08:58:44 -08:00
dependabot[bot] 5dd5df6268 chore(deps): bump url-parse from 1.4.7 to 1.5.3 (#2740)
Bumps [url-parse](https://github.com/unshiftio/url-parse) from 1.4.7 to 1.5.3.
- [Release notes](https://github.com/unshiftio/url-parse/releases)
- [Commits](https://github.com/unshiftio/url-parse/compare/1.4.7...1.5.3)

---
updated-dependencies:
- dependency-name: url-parse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-07 08:30:58 -08:00
dependabot[bot] 8cdb78c94a chore(deps): bump normalize-url from 4.5.0 to 4.5.1 (#2739)
Bumps [normalize-url](https://github.com/sindresorhus/normalize-url) from 4.5.0 to 4.5.1.
- [Release notes](https://github.com/sindresorhus/normalize-url/releases)
- [Commits](https://github.com/sindresorhus/normalize-url/commits)

---
updated-dependencies:
- dependency-name: normalize-url
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-07 08:30:35 -08:00
dependabot[bot] 398c7eb25c chore(deps): bump jszip from 3.5.0 to 3.7.0 (#2738)
Bumps [jszip](https://github.com/Stuk/jszip) from 3.5.0 to 3.7.0.
- [Release notes](https://github.com/Stuk/jszip/releases)
- [Changelog](https://github.com/Stuk/jszip/blob/master/CHANGES.md)
- [Commits](https://github.com/Stuk/jszip/compare/v3.5.0...v3.7.0)

---
updated-dependencies:
- dependency-name: jszip
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-07 08:30:05 -08:00
Tom Moor ee270abbe9 fix: Ensure IntegrationAuthentication is deleted with team 2021-11-07 08:29:37 -08:00
dependabot[bot] 70ec8c551e chore(deps): bump tar from 6.0.5 to 6.1.11 (#2737)
Bumps [tar](https://github.com/npm/node-tar) from 6.0.5 to 6.1.11.
- [Release notes](https://github.com/npm/node-tar/releases)
- [Changelog](https://github.com/npm/node-tar/blob/main/CHANGELOG.md)
- [Commits](https://github.com/npm/node-tar/compare/v6.0.5...v6.1.11)

---
updated-dependencies:
- dependency-name: tar
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-05 17:03:53 -07:00
dependabot[bot] 3a29e157b2 chore(deps): bump passport-oauth2 from 1.6.0 to 1.6.1 (#2736)
Bumps [passport-oauth2](https://github.com/jaredhanson/passport-oauth2) from 1.6.0 to 1.6.1.
- [Release notes](https://github.com/jaredhanson/passport-oauth2/releases)
- [Changelog](https://github.com/jaredhanson/passport-oauth2/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jaredhanson/passport-oauth2/compare/v1.6.0...v1.6.1)

---
updated-dependencies:
- dependency-name: passport-oauth2
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-05 17:02:46 -07:00
Tom Moor ed8334d77a fix: Plug memory leak in collaboration server 2021-11-05 16:55:20 -07:00
Tom Moor 6df8e9e13f Start collaboration service if not otherwise specified 2021-11-04 19:39:41 -07:00
Tom Moor eb9ff990ac feat: Show collab cursor names upon loading document. (#2732)
Second attempt, adds a class to the editor for a couple of seconds when the awareness is loaded to force cursors to display
2021-11-04 17:24:23 -07:00
Tom Moor 1a6921f6c7 fix: Empty doc missing placeholder 2021-11-03 22:02:10 -07:00
Tom Moor 89115a53ca fix: documents.publish event not triggered if nothing else changed in doc (#2728) 2021-11-03 18:43:01 -07:00
Tom Moor ad3bb98087 fix: Various collab cursor issues (#2727) 2021-11-03 17:51:51 -07:00
dependabot[bot] a839f2ed5b chore(deps): bump validator from 5.2.0 to 13.7.0 (#2726)
Bumps [validator](https://github.com/validatorjs/validator.js) from 5.2.0 to 13.7.0.
- [Release notes](https://github.com/validatorjs/validator.js/releases)
- [Changelog](https://github.com/validatorjs/validator.js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/validatorjs/validator.js/compare/5.2.0...13.7.0)

---
updated-dependencies:
- dependency-name: validator
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-03 11:08:01 -07:00
Nan Yu ea12ebea0e Fix: increase left gutter spacing on mobile edit mode (#2720)
* fix: adds space to the left gutter in edit mode so heading annotations have room on mobile
2021-11-02 18:30:37 -07:00
Tom Moor 57fa1305a6 chore: Remove react-keydown (#2713)
* First steps of remove react-keydown, replace with hook

* RegisterKeyDown component to aid transition away from react-keydown
2021-11-01 19:52:04 -07:00
Tom Moor 5f00e1394d fix: Move notifications to be revision driven (#2709) 2021-10-31 18:36:16 -07:00
Tom Moor b6a058147e chore: Refactor two components away from withRouter 2021-10-30 10:51:33 -07:00
Tom Moor 2c6ec11708 lint 2021-10-30 08:44:36 -07:00
Tom Moor 5900176b58 feat: Show collaborative cursors on load
closes #2704
2021-10-29 23:28:41 -07:00
Tom Moor e2c80e5a28 fix: Correctly show editing tooltip
Remove edit icon
closes #2705
2021-10-29 23:04:23 -07:00
Nan Yu 61d56922d5 fix: small improvements to local dev (#2699)
* dont commit pem files to git
* update readme ngrok instructions
* quote the OIDC scope string
2021-10-28 21:44:14 -07:00
Tom Moor 9a1c5c187e fix: Bump hocuspocus for small js fix (https://github.com/ueberdosis/hocuspocus/pull/235) 2021-10-28 21:28:33 -07:00
Tom Moor f2b007bcf5 fix: Flipped load logic 2021-10-28 20:51:40 -07:00
Tom Moor 578d4c4517 Merge branch 'main' of github.com:outline/outline 2021-10-27 21:03:48 -07:00
Tom Moor 313fd0c1b4 fix: disable multiplayer editing on shared docs when logged in 2021-10-27 20:49:52 -07:00
Tom Moor 1641423106 fix: Prevent user.info request loop, keep track of requested users in component state (#2693) 2021-10-27 20:12:22 -07:00
dkkb 67f06895e7 fix: Support uppercase letters in gist link (#2696) 2021-10-27 08:20:39 -07:00
dkkb 030419fa80 fix: Remove redundant scrollbar from iframe. (#2697) 2021-10-27 08:19:13 -07:00
Tom Moor 3987de1d7e Bump kbar, related #2688 2021-10-26 18:12:20 -07:00
Tom Moor 12b9e750e9 chore: Avoid buffer alloc 2021-10-26 18:05:50 -07:00
Tom Moor 1819920c04 fix: React warning size of memo changing between renders 2021-10-26 00:20:32 -07:00
Tom Moor a33bac66e4 fix: Remove invariant from visible calculation 2021-10-25 22:47:14 -07:00
Tom Moor 043a7b41b5 feat: Add print, duplicate, template to command bar 2021-10-25 20:41:28 -07:00
Tom Moor 4266a95569 chore: Bump BME (#2690) 2021-10-24 21:28:15 -07:00
Tom Moor 1d6bae05e6 fix: After renaming collection, url does not update 2021-10-24 17:57:17 -07:00
Tom Moor bb36425175 feat: Enable 'new document' action 2021-10-24 17:51:25 -07:00
Tom Moor adca894e83 fix: Long titles in command bar should not wrap 2021-10-24 17:32:28 -07:00
Tom Moor 2e56bdc388 fix: Command bar should bust cache when docs and collections are renamed
fix: Command bar should get larger on large screens
fix: Editable titles in sidebar should enforce max length
2021-10-24 17:32:28 -07:00
Translate-O-Tron 7f3df8158a New Crowdin updates (#2673) 2021-10-24 16:40:27 -07:00
Tom Moor 1b539dcf83 lint 2021-10-24 12:42:13 -07:00
Tom Moor 1d22b7ae0c chore: Turn on command bar in prod 2021-10-24 12:40:17 -07:00
Tom Moor b1f04145e5 flow 2021-10-24 12:31:17 -07:00
Tom Moor 2a32a4095d Merge branch 'main' of github.com:outline/outline 2021-10-24 12:30:58 -07:00
Tom Moor 33b6fbdee9 feat: Command Bar (#2669) 2021-10-24 12:30:27 -07:00
Gaston Flores dc92e1ead4 fix: ignore emoji when sorting (#2687)
* fix: ignore emoji when sorting

* fix: use correct flow types

* fix: use emoji-regex
2021-10-24 12:29:57 -07:00
Tom Moor 248c8b3c01 Improve beta note 2021-10-24 10:37:19 -07:00
Tom Moor d9f8d2e6d4 fix: Allow tests to pass when default is collab (#2685) 2021-10-23 16:02:25 -07:00
Tom Moor 99684d0900 Upgrade editor, fixes #2682 2021-10-23 10:34:38 -07:00
Tom Moor 6c2d43075c Update README.md 2021-10-23 09:53:26 -07:00
polemius b44c15c6eb fix: small typo (#2683) 2021-10-22 10:23:23 -07:00
Tom Moor f7b587b5a5 fix: Dont show back link on custom domains
closes #2671
2021-10-21 21:45:57 -07:00
Tom Moor c79a22b857 flow 2021-10-21 21:23:58 -07:00
Tom Moor 63c0daf483 fix: mailto links corrupted on save, closes #1090 2021-10-20 08:56:02 -07:00
Tom Moor 51971d2c9a fix: Various aria and React warnings 2021-10-19 22:12:20 -07:00
Tom Moor d443abfc57 chore: Allow websockets and collaboration service to run in the same process (#2674) 2021-10-19 21:18:20 -07:00
Saumya Pandey 3610a7f4a2 fix: Add default role option for new users (#2665)
* Add defaultUserRole on server

* Handle defaultUserRole on frontend

* Handle tests

* Handle user role in userCreator

* Minor improvments

* Fix prettier issue

* Undefined when isNewTeam is false

* Update app/scenes/Settings/Security.js

Co-authored-by: Tom Moor <tom.moor@gmail.com>

* Update app/scenes/Settings/Security.js

Co-authored-by: Tom Moor <tom.moor@gmail.com>

* Update app/scenes/Settings/Security.js

Co-authored-by: Tom Moor <tom.moor@gmail.com>

* Remove duplicate validation

* Update Team.js

* fix: Move note out of restricted width wrapper

* Move language setting to use 'note' prop

* Remove admin option

Co-authored-by: Tom Moor <tom.moor@gmail.com>
2021-10-20 09:26:11 +05:30
Translate-O-Tron 90fdf5106a New Crowdin updates (#2639) 2021-10-18 20:13:31 -07:00
Tom Moor 77fb76ff0c lint 2021-10-15 21:09:55 -07:00
Tom Moor 583353e904 fix: Disable guest signin option with not SMTP setup
closes #2250
2021-10-14 21:59:35 -07:00
Tom Moor 26e2ae4bf1 fix: Hide notification settings when SMTP_ configuration is unset 2021-10-14 21:55:48 -07:00
Tom Moor 4f34b69cfa Display notice instead of hide when Slack integration unavailable 2021-10-14 21:49:35 -07:00
Tom Moor 8c1979465f fix: Slack integration should not display if not configured in self hosted
fix: Alignment of Slack channels in settings
closes #2553
2021-10-14 21:37:04 -07:00
Tom Moor cc7a50fbb1 memoization 2021-10-14 21:23:28 -07:00
Tom Moor 5299ada3c9 feat: Support icon prop in InputSelect 2021-10-14 19:00:30 -07:00
Tom Moor 96fc95a9f3 fix: Increase TOC gutter to allow for offset emoji
closes #2661
2021-10-14 17:04:25 -07:00
Tom Moor 2219cfd83e fix: Increase entropy of state string for OAuth process
closes #2663
2021-10-14 16:52:19 -07:00
Tom Moor 6a1566c275 fix: Regression in image upload, closes #2662 2021-10-14 16:47:16 -07:00
Tom Moor b9346fe6ea fix: Minor collab adjusts 2021-10-13 22:01:30 -07:00
Tom Moor 18572cf9de fix: Facepile appears inactive after idle disconnect 2021-10-13 20:30:16 -07:00
Tom Moor 59f4b3bd97 fix: Server markdown parser failing tests 2021-10-13 19:37:04 -07:00
Tom Moor bb9d7d310b fix: Clicking outside editor should close selection toolbar 2021-10-13 19:15:44 -07:00
Tom Moor 3a19c02e34 fix: In page anchor links not working on shared docs
closes #2652
2021-10-12 23:12:47 -07:00
Tom Moor a6b3dbc894 fix: Reduce sensitivity of dark icon switching
fix: Layout issue in icon picker in dark mode
closes #2658
2021-10-12 23:02:14 -07:00
Tom Moor e0405cca0e fix: Bump hocuspocus (memory leak fix) 2021-10-11 10:53:50 -07:00
Alexander Krantz 09a409b494 feat: add changing appearance for guests (#2632)
* Allow changing appearance when guest

* Apply suggestions from code review

Co-authored-by: Tom Moor <tom.moor@gmail.com>
2021-10-07 18:43:41 -07:00
Tom Moor ccd947c6e8 fix: Positioning of input select items when seleted item does not fit in available area
fix: Scroll selected item in input select
2021-10-06 23:31:35 -07:00
Tom Moor 4e05728218 fix: InputSelect disabled state 2021-10-06 22:36:45 -07:00
Saumya Pandey 40e09dd829 fix: Implement custom Select Input (#2571) 2021-10-06 21:48:43 -07:00
Tom Moor 99381d10ff translations 2021-10-06 21:17:17 -07:00
Translate-O-Tron 36c73051b4 New Crowdin updates (#2596) 2021-10-06 21:09:29 -07:00
Saumya Pandey 81718c8ee1 fix: Delete collection exports (#2595) 2021-10-06 21:08:45 -07:00
Tom Moor be905a6993 feat: Add idle detection and disconnect collaboration socket (#2629) 2021-10-06 17:37:21 -07:00
Tom Moor b39d4aade7 Bump editor, minor emoji trigger fixes and adds Perl language support 2021-10-06 08:38:43 -07:00
Tom Moor c5fb5f875f flow 2021-10-04 22:08:16 -07:00
Tom Moor 552755dace feat: Add admin UI for enabling collab editing 2021-10-04 22:00:47 -07:00
Tom Moor e61c71766f Add guard against overwriting text when collaborative editing enabled 2021-10-04 19:20:48 -07:00
Tom Moor df5dc2f691 fix: Improve graceful shutdown 2021-10-04 18:20:42 -07:00
Tom Moor 28097835d0 chore: Remove debounced search (#2625)
* Remove debounced search

* fix hover color on filter options
2021-10-04 08:04:56 -07:00
Tom Moor 3de51c1a67 Bump editor, closes #2620, #2619 2021-10-02 22:21:26 -07:00
Tom Moor 223a47af95 fix: Improve error when email field not returned from OIDC 2021-10-02 22:42:41 -04:00
Tom Moor 7c8675ce17 fix: Creating API token reloads app
fix: API keys unselectable in list
closes #2604
2021-10-02 22:39:37 -04:00
Tom Moor 157c3ce80f fix: Missing cascade on integration -> authentication relationship 2021-10-02 22:22:08 -04:00
Saumya Pandey 0ed7286fc6 fix: Move request helper function (#2594)
* Move request method to passport utils

* Use request method in OIDC provider
2021-09-29 07:20:05 -07:00
Tom Moor 78464f315c fix: Awareness loop in collaborative editing 2021-09-27 18:44:28 -04:00
Tom Moor 79790de9b0 fix: Editor toolbar below fixed header 2021-09-27 10:40:44 -07:00
Tom Moor 252459f1cf fix: Loading flicker in collab editor when no local cache 2021-09-27 10:27:02 -07:00
Tom Moor 20a72481dc Disable embed toggling + collaborative editing 2021-09-26 21:05:32 -07:00
Tom Moor 765c7cdc27 fix: Max menu height should not affect mobile context menus 2021-09-26 17:19:00 -07:00
Tom Moor 6f136e342f fix: Context menus can extend outside of window bounds
closes #2492
2021-09-26 17:07:44 -07:00
Tom Moor 9545113d9e feat: Emoji picker in editor (#2611) 2021-09-26 15:26:32 -07:00
Tom Moor c00001086a fix: IconPicker unclosable on mobile 2021-09-26 15:26:10 -07:00
Tom Moor 95dbc8168c feat: Add 2 collection icons 2021-09-25 14:54:19 -07:00
Tom Moor 0021553518 Typescript, we need you 2021-09-25 08:55:52 -07:00
Tom Moor bcca4b91ee feat: Add 5 new collection icons 2021-09-24 19:39:31 -07:00
Tom Moor c1bd30aac8 Add user to collaboration logs 2021-09-24 19:14:00 -07:00
Tom Moor fd7dd83a4b fix: Updated database references 2021-09-23 20:09:40 -07:00
Tom Moor 26f02cdd05 fix: Table toolbars missing when cells empty 2021-09-23 19:58:16 -07:00
Tom Moor fec2baf361 fix: Memory leak in collaborative editing service 2021-09-23 17:09:15 -07:00
Tom Moor e1601fbe72 chore: Permanent team deletion (#2493) 2021-09-20 20:58:39 -07:00
dependabot[bot] a88b54d26d chore(deps): bump tmpl from 1.0.4 to 1.0.5 (#2601)
Bumps [tmpl](https://github.com/daaku/nodejs-tmpl) from 1.0.4 to 1.0.5.
- [Release notes](https://github.com/daaku/nodejs-tmpl/releases)
- [Commits](https://github.com/daaku/nodejs-tmpl/commits/v1.0.5)

---
updated-dependencies:
- dependency-name: tmpl
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-09-20 18:43:11 -07:00
Translate-O-Tron 88cc964d69 New Crowdin updates (#2590)
* fix: New Polish translations from Crowdin [ci skip]

* fix: New Polish translations from Crowdin [ci skip]
2021-09-19 19:02:01 -07:00
Saumya Pandey b8efe772fe fix: Warning when dragging document between collections with different user permissions (#2516) 2021-09-19 19:00:54 -07:00
Tom Moor b2f00d71d3 fix: Image zoom doesn't work in read-only 2021-09-19 15:26:52 -07:00
Tom Moor c2edfca6e5 fix: 'undefined' logged 2021-09-19 15:15:13 -07:00
Saumya Pandey 9c3c0fe418 feat: Add Polish to languageOptions (#2593) 2021-09-19 09:45:26 -07:00
Tom Moor 313067ff7b Add additional logging for persistence failure 2021-09-18 20:09:08 -07:00
Tom Moor be64c2b206 fix: Restore load cache, fixes TOC not visible on load 2021-09-18 17:49:00 -07:00
Tom Moor d576ce1734 fix: Remote awareness not available on doc load (collab) 2021-09-17 17:36:48 -07:00
Tom Moor 0f624958bc Use new hocuspocus hooks for collaboration metrics 2021-09-17 17:35:20 -07:00
Tom Moor 162da9a3ad fix: Can't edit title in collaborative mode 2021-09-16 22:47:58 -07:00
Tom Moor d7e9ad4f13 Remove usage of internal api 2021-09-16 21:27:37 -07:00
Tom Moor bcf773a1d6 Billibilli default hidden 2021-09-16 18:49:05 -07:00
Tom Moor 97082e8cba Merge branch 'main' of github.com:outline/outline 2021-09-16 18:48:25 -07:00
Su Yang bc3f2e4876 Add bilibili Embed Service (#2550)
* feat: Add bilibili Embed Service

* chore: code format

* chore: update bilibili icon
2021-09-16 18:48:13 -07:00
Translate-O-Tron 49a9b91708 New Crowdin updates (#2566) 2021-09-16 18:45:55 -07:00
Greg Linklater 01cea549a5 feat: map preferred_username claim to user record (#2569) 2021-09-16 18:45:37 -07:00
Tom Moor a9df3f64cf fix: Headings and code should be toggleable 2021-09-16 18:42:42 -07:00
Tom Moor e6cc8f5550 fix: Include log level in development 2021-09-16 17:22:23 -07:00
Tom Moor f6c2a95a55 Bump i18next-parser for true --silent fix 2021-09-16 16:26:57 -07:00
Tom Moor 27736f66ef fix: Various fixes for collaborative editing beta (#2586) 2021-09-15 23:27:22 -07:00
Tom Moor cde2909296 fix: Missing translation tag 2021-09-14 20:15:37 -07:00
Tom Moor 1f6e1a71f9 fix: List reverting to '0' indexing 2021-09-14 18:34:34 -07:00
Tom Moor 15ef8f7dff chore: Upgrade i18next related deps 2021-09-14 18:15:16 -07:00
Tom Moor 83a61b87ed feat: Normalized server logging (#2567)
* feat: Normalize logging

* Remove scattered console.error + Sentry.captureException

* Remove mention of debug

* cleanup dev output

* Edge cases, docs

* Refactor: Move logger, metrics, sentry under 'logging' folder.
Trying to reduce the amount of things under generic 'utils'

* cleanup, last few console calls
2021-09-14 18:04:35 -07:00
Tom Moor 6c605cf720 fix: Forward to incorrect collection url on first signin (#2565)
closes #2560
2021-09-13 21:35:52 -07:00
Tom Moor fb335887cb preventBodyScrollhideOnEsc 2021-09-13 21:00:28 -07:00
Translate-O-Tron 88e7d4c539 New Crowdin updates (#2449) 2021-09-13 20:09:52 -07:00
Tom Moor 400e32da70 fix: Various fixes for collaborative editing beta (#2561)
* fix: Remove Saving… message when collab enabled

* chore: Add tracing extension to collaboration server

* fix: Incorrect debounce behavior due to missing timestamps on events, fixes abundence of notifications when editing in realtime collab mode

* fix: Reload document prompt when collab editing
2021-09-13 17:36:26 -07:00
Tom Moor a699dea286 fix: Cleanup forking model (#2559)
* fix: Cleanup forking model
2021-09-12 21:45:52 -07:00
Tom Moor 2aca760ee0 fix: Double document highlight in sidebar (#2551)
* fix: Single highlighted doc when starred
closes #2544

* fix: Collection expand/collapse as navigating starred docs
2021-09-11 15:54:05 -07:00
Tom Moor f1c9c6fdf9 Update LICENSE 2021-09-11 09:48:19 -07:00
Tom Moor 801f6681ba Collaborative editing (#1660) 2021-09-10 22:46:57 -07:00
Tom Moor 0a998789a3 chore: Support Redis v6 on Heroku 2021-09-10 21:05:06 -07:00
Tom Moor 92016bbd06 fix: List behavior when ordered list starts at number other than 1
fix: Image improvements
fix: Image upload race condition
2021-09-10 19:06:54 -07:00
Tom Moor 231ab2da03 fix: Add recording of job errors, remove from queues on failure, centralize options 2021-09-09 22:38:34 -07:00
Tom Moor bd880ee984 chore: Add basic logging of metrics to event queue (#2545)
* chore: Add basic logging of metrics to event queue
closes #2524

* Better naming for multiple queue types

* Add stalled event
2021-09-09 21:55:45 -07:00
Tom Moor 995c6f90b7 fix: Mount _health route before catch-alls
closes #2536
2021-09-09 21:08:34 -07:00
Tom Moor 8ac853bb8b fix: Printing from doc menu is blank in Firefox
closes #2543
2021-09-09 20:41:56 -07:00
Tom Moor 2f5cf90cb7 0.59.0 2021-09-07 22:10:29 -07:00
Tom Moor c709e54738 fix: Init dd trace sooner, closes #2528 2021-09-02 23:22:31 -07:00
Tom Moor 47953b3354 Yarn.lock 2021-09-02 23:17:41 -07:00
Tom Moor d96099b5b8 Move OIDC provider to routes directory 2021-09-02 19:55:06 -07:00
Greg Linklater 4b2bf28531 feat: Generic OAuth2 Authentication (#2388)
* chore: additional dependency

* feat: OAuth2 authentication provider

* docs: add env vars

* chore: lock file

* feat: add malformed user info error and notice

* feat: configurable scopes

* fix: explicitly enable state and disable pkce

* chore: remove externally supplied username from account provisioner use

* chore: remove upstream error

* chore: add explicit import for fetch

* chore: remove unused env var from sample

* docs: openid connect claims

* fix: forward fetch errors

* feat: configurable team claim name

* docs: move OIDC env vars together

* refactor: change provider name

* refactor: rename error to match provider

* fix: resolve claim using lodash.get

* refactor: remove OIDC_TEAM_CLAIM and hard code team name
2021-09-02 19:50:17 -07:00
Tom Moor a3df9e868f fix: Server error when loading documents.info with shareId and user token and child documents shared
closes #2527
2021-09-01 23:38:43 -07:00
Tom Moor 476b5e03f9 perf: Move exports to worker service (#2514)
* first pass

* fixes

* fix: Move export related emails to queue

* i18n
2021-08-31 17:41:57 -07:00
Tom Moor 23a6459ae8 fix: Make GoogleDrive embed links more lenient
closes #2405
2021-08-30 22:43:13 -07:00
Saumya Pandey 4929fbaccb fix: Move "public document sharing" to "Permissions" (#2496)
* Convert to functional component

* Move public sharing to permissions

* Add collections.permission_changed event

* Account for null

* Update server/events.js

Co-authored-by: Tom Moor <tom.moor@gmail.com>

* Add collections.permission_changed event

* Remove name

Co-authored-by: Tom Moor <tom.moor@gmail.com>
2021-08-30 11:43:42 +05:30
Tom Moor 08a8fea69a chore: Add /_health endpoint to all services
closes #2506
2021-08-29 19:44:06 -07:00
Tom Moor 2024c6e64f chore: Graceful server shutdown, closes #2507 2021-08-29 14:48:12 -07:00
Tom Moor 3dfd336f59 chore: Move all routes under routes directory (#2513)
closes #2504
2021-08-29 13:25:06 -07:00
Tom Moor 9a875920ac chore: Remove 'attachments' option from mailer 2021-08-29 12:35:55 -07:00
Saumya Pandey f389ac6414 fix: Improvements in share feat (#2502)
* Make request only when popover is visible

* Update policies required for shares.create shares.update

* Create withCollection scope

* Remove team share check from shares.create

* Update tests
2021-08-29 10:44:09 +05:30
Saumya Pandey e4b7aa6761 fix: Add ability to choose user permission level when inviting (#2473)
* Select user role while sending invite

* Add tests to check for role

* Update app/scenes/Invite.js

Co-authored-by: Tom Moor <tom.moor@gmail.com>

* Use select

* Use inviteUser policy

* Remove unnecessary code

* Normalize rank/role
Fix text sizing of select input, fix alignment on users invite form

* Move component to root

* cleanup

Co-authored-by: Tom Moor <tom.moor@gmail.com>
2021-08-29 03:05:37 +05:30
Saumya Pandey 00ba65f3ef fix: Refactor collection exports to not send email attachment (#2460)
Co-authored-by: Tom Moor <tom.moor@gmail.com>
2021-08-29 02:57:07 +05:30
Tom Moor 28aef82af9 chore: Refactoring event processors and service architecture (#2495) 2021-08-27 21:42:13 -07:00
Saumya Pandey 86f008293a fix: Return memberships of collections (#2501) 2021-08-27 20:03:57 -07:00
Tom Moor 835fd26a95 Squashed commit of the following:
commit ebe2fe07d1a9110a99a21772b79f189dd13b4ca8
Author: Tom Moor <tom.moor@gmail.com>
Date:   Thu Aug 26 20:18:52 2021 -0700

    fix: regex, formatting

commit 1fd17b6f8a
Author: Matheus Breguêz <matbrgz@gmail.com>
Date:   Thu Aug 26 09:37:12 2021 -0300

    fix: change image size

commit 30e9bad0f5
Merge: ef99201c cc9468e2
Author: Matheus Breguêz <matbrgz@mail.com>
Date:   Thu Aug 26 09:28:34 2021 -0300

    Merge branch 'main' into feat/google-calendar-embed

commit ef99201c9d
Author: Matheus Breguêz <matbrgz@mail.com>
Date:   Fri Jul 9 11:53:56 2021 -0300

    Update GoogleCalendar.js

commit 0e91084756
Merge: e98f94c0 ec5c47e0
Author: Matheus Breguêz <matbrgz@gmail.com>
Date:   Wed Jul 7 14:49:06 2021 -0300

    Merge remote-tracking branch 'origin/feat/google-calendar-embed' into feat/google-calendar-embed

commit e98f94c02d
Author: Matheus Breguêz <matbrgz@gmail.com>
Date:   Wed Jul 7 14:29:49 2021 -0300

    feat: Add Google Calendar Embed

commit ec5c47e0c8
Author: Matheus Breguêz <matbrgz@gmail.com>
Date:   Wed Jul 7 14:29:49 2021 -0300

    feat: Add Google Calendar Embed
2021-08-26 20:20:15 -07:00
Tom Moor cc9468e2c5 Add 4 additional collection icons, closes #2482 2021-08-25 21:44:30 -07:00
Saumya Pandey 22ba4d0f48 fix: prevent access to docs in trash from deleted private collections (#2431)
* Check for collection in deleted document

* Add tests

* Use update policy

* Set paranoid to false when fetching deleted doc

* Update policy
2021-08-26 09:35:59 +05:30
Tom Moor d335670b91 fix: Starred untitled draft has no title in sidebar
fix: Double click to edit starred document titles
2021-08-24 23:30:55 -07:00
Tom Moor cabaee2d0a Bump editor
closes #2441
closes #2459
2021-08-24 23:23:38 -07:00
Saumya Pandey f6d889f759 fix: Show starred docs in sidebar (#2317)
Co-authored-by: Tom Moor <tom.moor@gmail.com>
2021-08-23 13:07:28 +05:30
Wesley a50471959b feat: Always show share button (#2469)
This is to enable the share page also for internal team members.

closes #2444
2021-08-22 23:20:29 -07:00
Tom Moor d8ad2fc1a2 fix: Theme in account menu does not update 2021-08-22 22:19:20 -07:00
Wesley 0c48227b57 Feat: add diagrams.net/draw.io embed (#2464)
* feat: Add diagrams.net/draw.io embed

* Rename Diagrams integration to include .net

Co-authored-by: Tom Moor <tom.moor@gmail.com>
2021-08-21 11:11:47 -07:00
Tom Moor 72da0653cc Revert "feat: Add hosted domain hint when signing in through Google SSO from subdomain (#2458)" (#2467)
This reverts commit e613ec732b.
2021-08-21 11:11:01 -07:00
Tom Moor e613ec732b feat: Add hosted domain hint when signing in through Google SSO from subdomain (#2458)
* feat: Add hosted domain hint when signing in through Google SSO from subdomain

closes #2454
2021-08-20 14:03:52 -07:00
Tom Moor 0be40609ed feat: Add UI to switch teams where signed in to multiple (#2457)
* feat: Add UI to switch teams where signed in to multiple

* fix: Do not display current team in switch menu

* Refactor to hook
2021-08-18 18:37:50 -07:00
Saumya Pandey ec8fde0a5f fix: Improvements in table component (#2450) 2021-08-18 03:27:23 +05:30
Saumya Pandey 2c52a8cb8b fix: Add icons to menu items (#2373)
Co-authored-by: Tom Moor <tom.moor@gmail.com>
2021-08-13 14:21:25 -07:00
Tom Moor 1db31eed41 fix: Incorrect empty state text for /created Home tab 2021-08-13 09:57:20 -07:00
Tom Moor 8ba8013c6a fix: Suppressed notification causes missing notifications for other users on the same team 2021-08-13 09:40:43 -07:00
David Herman 1521d4dbac fix: Suppress notifications for suspended users (#2448)
* fix: Supress notifications for suspended users

* spelling

Co-authored-by: Tom Moor <tom.moor@gmail.com>
2021-08-13 09:32:19 -07:00
Saumya Pandey a1a4fd1baf fix: Redirect to collection on self-hosted (#2438) 2021-08-13 12:32:18 +05:30
Tom Moor 31f4424018 fix: Time/LocaleTime should default to relative humanized timestamps (regression) 2021-08-12 22:47:14 -07:00
Translate-O-Tron 1f5b83aaeb New Crowdin updates (#2413) 2021-08-12 15:24:40 -07:00
Tom Moor 77db0c2e95 fix: Document history event headings (#2433)
* fix: Document history events from last year but within 12 months shown as 'this year'
fix: Events older than a year have repeated headings

* lint
2021-08-12 15:24:13 -07:00
dependabot[bot] 4cbae1cf7d chore(deps): bump path-parse from 1.0.6 to 1.0.7 (#2439)
Bumps [path-parse](https://github.com/jbgutierrez/path-parse) from 1.0.6 to 1.0.7.
- [Release notes](https://github.com/jbgutierrez/path-parse/releases)
- [Commits](https://github.com/jbgutierrez/path-parse/commits/v1.0.7)

---
updated-dependencies:
- dependency-name: path-parse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-08-12 15:23:43 -07:00
Tom Moor e985078b80 fix: JS error scrolling overflowed templates page
closes #2445
2021-08-12 11:26:28 -07:00
Saumya Pandey 09b73401de fix: Sidebar links highlighting issue when a template is deleted or archived. (#2420) 2021-08-06 23:01:25 +05:30
Saumya Pandey 42b384688d fix: Options to create a document is available when the policies of collection in the context doesn't permits the user (#2424) 2021-08-06 22:58:26 +05:30
Tom Moor 5bdee1204e fix: Copying header results in '#' copied
fix: urls in text become linked when reloading doc
fix: Allow creation of links to anchors from link toolbar
2021-08-06 09:39:03 -07:00
Tom Moor 9db72217af feat: Include more events in document history sidebar (#2334)
closes #2230
2021-08-05 15:03:55 -07:00
Tom Moor 57a2524fbd fix: /public directory missing in new docker releases (#2417)
closes #2416
2021-08-04 09:21:25 -07:00
Tom Moor bd148f4790 fix: Paste handler should default to HTML when paste source is Outline editor
related #2416
2021-08-04 09:20:51 -07:00
Tom Moor 28d32af613 perf: Remove unused database indexes according to a month of data in production (#2395) 2021-08-03 20:51:12 -07:00
Tom Moor f2f550e1d2 fix: Policies missing on documents.viewed endpoint 2021-08-03 20:02:11 -07:00
Translate-O-Tron dad21b2186 New Crowdin updates (#2400) 2021-08-03 19:32:51 -07:00
Tom Moor 5fb5f1e8b5 perf: Remove backup column migration (#2397)
* perf: Remove no-longer-used 'backup' columns

These were added as part of the move to the v2 editor over a year ago incase any text was not correctly converted. After a year of use no cases of failed conversion have occurred that required the use of this column

* Remove migration, will do in 2-step release

* perf: Remove no-longer-used 'backup' columns

These were added as part of the move to the v2 editor over a year ago incase any text was not correctly converted. After a year of use no cases of failed conversion have occurred that required the use of this column
2021-08-03 18:55:52 -07:00
Tom Moor 2d0690697c 0.58.0 2021-08-03 15:17:06 -07:00
Tom Moor 6b551749d4 chore: Remove version- prefix from docker tags 2021-08-03 14:23:14 -07:00
Jack Baron 52fc861bcf feat: Optimize Dockerfile (#2337)
* feat: optimize dockerfile
use new dockerfile syntaxes
leverage multi-stage builds
strip yarn cache from image
use stricter yarn install command
run as a non-root user

* fix: mark yarn-deduplicate as a required dep
`yarn --production` will fail on a clean install otherwise

* fix: add sequelize required files for migrations

* fix: use correct ARG syntax for multistage builds

* revert: mark yarn-deduplicate as a required dep
no longer required as of 0b3adad751
2021-08-03 13:22:41 -07:00
Tom Moor c81c9a9d2d chore: CI Automated Builds (#2409)
closes #2408
2021-08-02 23:35:13 -07:00
Tom Moor 29c742a673 fix: Settings on 'Security' tab not persisting correctly after refactor (#2407)
* fix: Settings on 'Security' tab not persisting correctly after refactor
closes #2406
2021-08-02 13:37:53 -07:00
Tom Moor dd249021e7 fix: GoogleDrive embeds stopped working with new share urls
closes #2405
2021-08-02 11:09:16 -07:00
Tom Moor 21d3b9c7e0 fix: Formatting of welcome docs :rolleyes: 2021-08-01 13:03:21 -07:00
Tom Moor 6665dfff28 Merge branch 'main' of github.com:outline/outline 2021-08-01 12:55:03 -07:00
Tom Moor cdfe3a7fc3 chore: Add new 'getting started' onboarding document (#2391)
Remove support document
Remove confusing images
Added onboarding checklist
2021-08-01 12:54:41 -07:00
Tom Moor 401c91f90b perf: Correctly parallelize count query in users.list 2021-07-30 12:20:19 -04:00
Tom Moor ed5320507d perf: Separate slow joins (#2394) 2021-07-30 08:50:02 -07:00
Translate-O-Tron e34581d25f New Crowdin updates (#2372) 2021-07-30 07:45:58 -07:00
Tom Moor 65a1e2630c perf: Remove no-longer-used 'backup' columns (#2396)
* perf: Remove no-longer-used 'backup' columns

These were added as part of the move to the v2 editor over a year ago incase any text was not correctly converted. After a year of use no cases of failed conversion have occurred that required the use of this column

* Remove migration, will do in 2-step release
2021-07-30 07:22:17 -07:00
Tom Moor 59de4a7db0 feat: Default to "recently viewed" (#2390)
* feat: Default user to first collection on first app open

* Default home tab to 'recently viewed'

* fix: Styling of inactive tab
2021-07-30 07:16:03 -07:00
Tom Moor 63eb8aadaf fix: Flow, remove misused withTranslation on functional component 2021-07-30 00:52:42 -04:00
Saumya Pandey 37fd7ec97a fix: Enable offline access to google accounts (#2392)
* Enable google offline access

* Prevent overriding prompt parameter
2021-07-29 20:04:57 -07:00
Tom Moor 928106067f chore: Tone down notices (#2393) 2021-07-29 20:04:45 -07:00
Tom Moor cb7c27690f fix: Slow tooltips on timestamps 2021-07-28 20:26:04 -04:00
Tom Moor 26da8c4165 feat: Add 'done' icon when all tasks are complete 2021-07-28 19:55:46 -04:00
Tom Moor 36b8ae859e fix: Bump Editor
fix: Sticky formatting toolbar behavior on iOS
fix: Image caption localized
2021-07-28 18:01:01 -04:00
Tom Moor ad1eaa5210 fix: Jank at beginning of loading indicator bar 2021-07-28 17:56:44 -04:00
Saumya Pandey 98024f6be1 fix: "1 tasks done" incorrectly pluralized (#2382) 2021-07-29 01:39:55 +05:30
falleng0d 37c02a572b feat: Auto detect language on login page access (#2338)
* feat: Auto detect language on login page access

* fix: Apply tommoor suggested changes

* fix: QOL improvements for translators

* fix: consistency fix provider -> authProviderName

Co-authored-by: Tom Moor <tom.moor@gmail.com>
2021-07-28 12:00:02 -07:00
Tom Moor e53bb8bfbc fix: Error uploading fallback avatar when name contains characters that need to be escaped (#2387)
* Todo -> Task to match new langauge elsewhere

* fix: Correctly escape characters in Tiley url

* Move encoding to avatars logic, add test
2021-07-28 11:45:47 -07:00
Tom Moor 2a473bf7b4 Todo -> Task to match new langauge elsewhere 2021-07-28 13:15:30 -04:00
Tom Moor f3b09ab56a test 2021-07-27 21:30:00 -04:00
Tom Moor 6eb51a9cb9 chore: Allow passing of page to revisions backfill script 2021-07-27 18:53:39 -04:00
Tom Moor d01c40badb fix: Minor positioning fix of Account menu 2021-07-27 18:16:23 -04:00
Tom Moor fc551c91bd Bump editor
- Fixes enter with horizontal gap cursor
- Improves pasting behavior
- Fixes heading uncollapse when value changes
- Fixes notice blocks not hidden with other collapsed content
closes #2371
2021-07-27 17:50:16 -04:00
Tom Moor fdc1955b91 fix: Mixture of middots with different weights in document meta 2021-07-27 10:33:26 -04:00
Tom Moor b6703671e2 fix: Task progress svg shrinks width in some circumstances 2021-07-27 10:33:11 -04:00
Tom Moor 84f647674a Merge branch 'main' of github.com:outline/outline 2021-07-27 10:24:36 -04:00
Saumya Pandey a81fbd8608 fix: Show tasks completion on document list items (#2342)
Co-authored-by: Tom Moor <tom.moor@gmail.com>
2021-07-27 11:31:27 +05:30
Tom Moor 8ee018a759 feat: Web concurrency (#2347)
* feat: Fork multiple processes

* Remove boxen

* comment

* chore: Add support for Heroku DATABASE_CONNECTION_POOL_URL
closes #2306
2021-07-26 15:51:50 -07:00
Tom Moor 6815c940b2 fix: Failure case during account provision that can result in no welcome collection 2021-07-26 13:46:55 -04:00
Saumya Pandey c9bd3bbf45 fix: Editing title in sidebar allows removal of title (#2364) 2021-07-26 00:17:39 +05:30
Translate-O-Tron f61f9703f3 New Crowdin updates (#2368) 2021-07-25 08:23:53 -07:00
Tom Moor 48d538b424 fix: Server error when rendering share for deleted document
closes #2352
2021-07-23 11:25:11 -04:00
Tom Moor 84ad7c482c fix: Various editor header and metadata fixes (#2361)
* fix: Publish button disabled on drafts in read-only mode
fix: Template selector appears on edited documents

* fix: Save button does not immediately come available when selecting a template

* fix: Template menu item alignment
closes #2204

* fixes: Use policy for display of star in document title
closes #2354

* fix: Modified time is sometimes bold when last edited user is current user
closes #2355

* fix: Allow starring of drafts
2021-07-22 15:17:18 -07:00
Tom Moor d35b5d2613 tidy for blog post ;) 2021-07-22 13:43:29 -04:00
Tom Moor 3090c2cfa3 chore: Improve perf of new tab loading by caching team policy in localStorage (#2351) 2021-07-21 15:53:57 -07:00
Translate-O-Tron 140b04c126 New Crowdin updates (#2340) 2021-07-21 15:24:45 -07:00
Tom Moor 2aedf4440b feat: Enable Persian language translations (#2341) 2021-07-21 10:41:45 -07:00
Tom Moor 6e07ee3f3e chore: Move animations and globals from shared directory (#2344) 2021-07-21 10:34:55 -07:00
Saumya Pandey bba8cd183b fix: Archive and trash a document by dropping in the sidebar (#2318) 2021-07-21 00:49:41 +05:30
Saumya Pandey 0bc609634c fix: Allow searching of previous document titles (#2326)
* Add migrations

* Handle previousTitles when titles is updated

* Add necessary test cases

* Use previous title while searching

* Rewrite logic to update previousTitles in beforeSave hook

* Update weights

* Update test to match new rank order

* Add tooltip to inform user on document

* Add code comment

* Remove previous title tooltip

* fix: Remove unused string, add model tests

Co-authored-by: Tom Moor <tom.moor@gmail.com>
2021-07-20 10:35:29 -07:00
Tom Moor b3b8cb3d9c missing translation string 2021-07-20 12:02:46 -04:00
Saumya Pandey fdb85ec195 fix: Separate toasts storage to own MobX store (#2339)
Co-authored-by: Tom Moor <tom.moor@gmail.com>
2021-07-20 14:36:10 +05:30
Tom Moor f64ab37d3c fix: Interpolation on archive/delete translationsg 2021-07-19 17:26:48 -04:00
Tom Moor 0b3adad751 chore: Move yarn-deduplicate postinstall -> prepare
should not run in production
2021-07-19 17:12:24 -04:00
Tom Moor 83477de300 fix: Account for revisions.create event being debounced 2021-07-19 17:02:33 -04:00
Tom Moor 1726006858 chore: Pass problematic url to error tracking
towards #2319
2021-07-19 16:57:06 -04:00
Tom Moor 3d9eaeeeeb chore: Add revisions.create backfill script (#2330)
* chore: Add revisions.create backfill script

* fix: Correct timestamp on revisions.create events
2021-07-19 13:32:03 -07:00
falleng0d 2e955353ae feat: translations (#2275)
Co-authored-by: Tom Moor <tom.moor@gmail.com>
2021-07-19 11:12:53 -07:00
Tom Moor 05aba68457 feat: Add support for collapsible headings (#2327) 2021-07-19 09:19:36 -07:00
Tom Moor 8f6e956bc5 chore: Add documentId index to events table (#2331) 2021-07-19 09:19:26 -07:00
Tom Moor 0cad99c343 chore: Move 'templates' to bottom of sidebar (#2328)
chore: Hide trash and archive for read-only users
2021-07-19 09:18:33 -07:00
Translate-O-Tron 04746f6a2c New Crowdin updates (#2304) 2021-07-16 06:46:32 -07:00
Tom Moor 25907f5c72 chore: Reduce idle CPU usage in development 2021-07-16 09:30:43 -04:00
Jack Baron d7a21db72f fix: Remove duplicate translation key (#2325) 2021-07-16 14:36:34 +05:30
Saumya Pandey 9596979993 fix: Add translation hooks on document and collection pages (#2307) 2021-07-16 01:49:09 +05:30
Tom Moor 31714efb0b feat: useBoolean hook (#2314)
* feat: Add useBoolean hook and example usage

* More example usage

* chore: More useBoolean conversion
2021-07-15 12:27:03 -07:00
Tom Moor 8884da8a4b feat: Add revisionCreator command (#2321)
add revisions.create event
2021-07-15 12:26:43 -07:00
Tom Moor 30cf244610 chore: Loading placeholders (#2322)
* Improve visual of loading mask

* Normalize placeholder naming

* Remove unused file
2021-07-15 12:26:34 -07:00
Saumya Pandey 3f030540b3 fix: Add translation hooks on settings screen (#2298)
Co-authored-by: Tom Moor <tom.moor@gmail.com>
2021-07-15 14:50:36 +05:30
Saumya Pandey 7ae3addea0 fix: Add space to the valid index characters list (#2316) 2021-07-15 00:35:47 +05:30
Saumya Pandey a9d758bb0c fix: Add translation hooks on remaining files (#2311) 2021-07-15 00:30:08 +05:30
Matheus Breguêz 06e16eef12 feat: Add Google DataStudio embed (#2293)
Co-authored-by: Tom Moor <tom.moor@gmail.com>
2021-07-14 11:57:12 -07:00
Tom Moor 8e5a2b85c2 feat: Improved UI motion design (#2310)
* feat: Improved UI motion design

* fix: Animation direction when screen placement causes context menu to be flipped
2021-07-12 11:57:17 -07:00
Saumya Pandey 5689d96cc4 fix: Add translation hooks on groups screen (#2303)
* Refactor groups page to functional component and translate strings

* Update app/scenes/GroupNew.js

Co-authored-by: Tom Moor <tom.moor@gmail.com>

* Update app/scenes/GroupEdit.js

Co-authored-by: Tom Moor <tom.moor@gmail.com>

* Update app/scenes/GroupDelete.js

Co-authored-by: Tom Moor <tom.moor@gmail.com>

* Update app/scenes/GroupMembers/GroupMembers.js

Co-authored-by: Tom Moor <tom.moor@gmail.com>

* Format GroupMember.js

* Change Trans usage

* Format GroupDelete

* Revert "Format GroupDelete"

This reverts commit 880128f94d.

* Update app/scenes/GroupNew.js

Co-authored-by: Tom Moor <tom.moor@gmail.com>

* Update app/scenes/GroupNew.js

Co-authored-by: Tom Moor <tom.moor@gmail.com>

* Update GroupNew

* Remove newlines

Co-authored-by: Tom Moor <tom.moor@gmail.com>
2021-07-12 11:54:55 -07:00
Tom Moor 5cd4dbd9d7 fix: Mispositioned TOC control on mobile due to merge conflict
fix: Show message in mobile TOC when no headings in document
fix: MenuItem with level should still have background edge-to-edge
fix: Show developer warning when creating incorrect menu item type
2021-07-11 13:09:10 -04:00
Tom Moor 587a0e0517 chore: Update html import related deps 2021-07-11 10:02:35 -04:00
Tom Moor 686ecdfa92 fix: CSS syntax error 2021-07-09 14:09:52 -04:00
Translate-O-Tron bb019b081f New Crowdin updates (#2281) 2021-07-09 05:55:06 -07:00
Saumya Pandey 7d5fbeb7b0 fix: Add access to document TOC on mobile (#2279)
* Add TOC button for mobile

* Undo NewDocumentMenu changes

* Place the toc button in the correct position.

* Pass menu props to menuitem

* Update app/menus/TableOfContentsMenu.js

Co-authored-by: Tom Moor <tom.moor@gmail.com>

* Update app/menus/TableOfContentsMenu.js

Co-authored-by: Tom Moor <tom.moor@gmail.com>

* Use the existing prop type

* Write menu inside actions prop

* Prevent blank webpage behaviour for toc

* Use href instead of level to determine target

* Update app/scenes/Document/components/Header.js

Co-authored-by: Tom Moor <tom.moor@gmail.com>

* Add heading to menu items

* Use existing Heading component

Co-authored-by: Tom Moor <tom.moor@gmail.com>
2021-07-09 04:50:27 -07:00
Tom Moor 056f89fcfd fix: Allow TOC to scroll when larger than browser height (#2296) 2021-07-09 04:07:28 -07:00
Tom Moor 0e7d352781 chore: Add fetch-retry, remove isomorphic-fetch (#2297)
* chore: Add fetch-retry, remove isomorphic-fetch

closes #2270

* test: Mock fetch
2021-07-09 04:07:18 -07:00
Tom Moor b5e4e4fe82 fix: Various mobile fixes (#2295)
* fix: Input placeholder ellipsis

* fix: Hide scrollbar on nav tabs on mobile

* fix: Header actions should be fixed on mobile

* fix: Add fade when content in tabs does not fit in available horizontal width
2021-07-08 18:32:14 -07:00
Tom Moor e41f17c701 feat: Enable Japanese translations (#2282) 2021-07-08 18:32:05 -07:00
Tom Moor 9a1c8f07d1 feat: Add documentId filter to events.list (#2287) 2021-07-08 10:12:06 -07:00
Tom Moor 241cb11493 chore: Automate running yarn-deduplicate, see comment:
https://github.com/outline/outline/pull/2283\#discussion_r665301770
2021-07-07 22:26:56 -04:00
Saumya Pandey 8195791bb2 fix: Make search query string user friendly (#2283)
* Upgrade query-string package and skip empty string

* Run yarn-deduplicate command
2021-07-07 18:45:40 -07:00
Saumya Pandey b037ae5dc1 fix: Improve isChildDocument performance (#2284) 2021-07-07 04:53:40 -07:00
Tom Moor aeba8ce4eb fix: Empty context menu when user does not have permission to update collection 2021-07-06 22:02:31 -04:00
Tom Moor 429c5fba85 0.57.0 2021-07-06 09:12:54 -04:00
Tom Moor 9495ddba25 fix: Restore previous WSS CORS behavior 2021-07-05 23:01:25 -04:00
dependabot[bot] 486a60e97c chore(deps): bump socket.io from 2.3.0 to 2.4.0 (#1831)
Bumps [socket.io](https://github.com/socketio/socket.io) from 2.3.0 to 2.4.0.
- [Release notes](https://github.com/socketio/socket.io/releases)
- [Changelog](https://github.com/socketio/socket.io/blob/2.4.0/CHANGELOG.md)
- [Commits](https://github.com/socketio/socket.io/compare/2.3.0...2.4.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-07-05 17:11:40 -07:00
Tom Moor c687745263 fix: E-mail signin on incorrect subdomain should allow the process to continue instead of error
closes #2276
2021-07-05 19:25:21 -04:00
Translate-O-Tron 1b92993b90 fix: New Portuguese, Brazilian translations from Crowdin (#2271) 2021-07-05 13:37:09 -07:00
Tom Moor 181a20a268 fix: More context menu fixes 2021-07-05 16:35:46 -04:00
Tom Moor f8ffa4e25a tweak sidebar item background 2021-07-04 18:44:09 -04:00
Farzad 7e139ca8f7 fix: nested link positions for RTL titles in sidebar (#2272) 2021-07-04 12:08:05 -07:00
Tom Moor bb58db507d fix: ew-resize -> col-resize cursor 2021-07-04 11:54:29 -04:00
Translate-O-Tron 49bf86d6d9 New Crowdin updates (#2268) 2021-07-04 06:54:52 -07:00
Tom Moor 286a15cf10 fix: Clicking dropdown menu items in FF (#2269)
* fix: Clicking dropdown menu items in FF
closes #2264

* fix: Anchor items, add comment

* fix: CI test memory issues
2021-07-04 06:54:40 -07:00
Tom Moor f65469b777 lockfile 2021-07-03 21:22:52 -04:00
Translate-O-Tron fe65a79d66 New Crowdin updates (#2267) 2021-07-03 07:02:01 -07:00
Tom Moor a1d5ac0907 RTL document support (#2263)
* Basic RTL support in documents

* fix: DocumentListItem and ReferenceListItem for RTL content
2021-07-03 07:00:10 -07:00
Tom Moor 04eabe68a7 feat: Enable traditional Chinese translations (#2266) 2021-07-02 12:08:08 -07:00
Tom Moor 1c0c694c22 fix: Email auth should allow same guest user on multiple subdomains (#2252)
* test: Add email auth tests to establish current state of system

* fix: Update logic to account for dupe emails used between subdomains

* test

* test
2021-07-02 12:07:43 -07:00
Translate-O-Tron 2ae74f2834 New Crowdin updates (#2262) 2021-07-02 11:23:02 -07:00
Tom Moor 0f01fc5faa test: Reduce memory usage by not requiring stores into all (#2265) 2021-07-02 11:16:07 -07:00
Tom Moor 7f1322b7ba fix: Down arrow in search input should move focus to results (#2257)
closes #2253
2021-07-01 15:01:30 -07:00
dependabot[bot] 3c98133e24 chore(deps): bump socket.io-parser from 3.3.1 to 3.3.2 (#2258)
Bumps [socket.io-parser](https://github.com/socketio/socket.io-parser) from 3.3.1 to 3.3.2.
- [Release notes](https://github.com/socketio/socket.io-parser/releases)
- [Changelog](https://github.com/socketio/socket.io-parser/blob/3.3.2/CHANGELOG.md)
- [Commits](https://github.com/socketio/socket.io-parser/compare/3.3.1...3.3.2)

---
updated-dependencies:
- dependency-name: socket.io-parser
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-07-01 12:08:57 -07:00
Tom Moor 088353d61f fix: Data loading state not reset when props change to PaginatedList (#2254)
* fix: Data loading state not reset when significant props change to PaginatedList

closes #2251

* test: Add enzyme and component test
2021-06-26 21:49:25 -07:00
Translate-O-Tron 31180619e1 New Crowdin updates (#2182) 2021-06-26 13:43:54 -07:00
Saumya Pandey 9fccc280d7 fix: Add ability to permanently delete documents in trash (#2192)
* Align false conditions before true

* Update documents.delete endpoint for permanent delete

* Add permanent delete to events table and integrate with socket.io

* Add permanent delete to document menu

* Update parentDocumentId of direct child to null

* Add translation

* Add test for permanent delete

* Add space

* Update app/scenes/DocumentPermanentDelete.js

Co-authored-by: Tom Moor <tom.moor@gmail.com>

* Update app/stores/DocumentsStore.js

Co-authored-by: Tom Moor <tom.moor@gmail.com>

* Update server/commands/documentPermanentDeleter.js

Co-authored-by: Tom Moor <tom.moor@gmail.com>

* Update app/scenes/DocumentPermanentDelete.js

Co-authored-by: Tom Moor <tom.moor@gmail.com>

* Change socket room from team to collection

* Add translation

* Create log func for commands

* Move tests from utils to permanentDeleter command

* Add additional tests

* Set redirect to trash

* Return promise from beforeEach

* Add undeleted documents validation

* Include deleteAt attribute in db query

* Update server/commands/documentPermanentDeleter.js

Co-authored-by: Tom Moor <tom.moor@gmail.com>

* tweak language

Co-authored-by: Tom Moor <tom.moor@gmail.com>
2021-06-25 16:14:40 -07:00
Tom Moor c69b4efc34 fix: Aligned images do not load in publicly shared documents (#2248) 2021-06-25 10:09:44 -07:00
Tom Moor 3cec6b4903 fix: Allow for offline development 2021-06-21 21:40:28 -07:00
Tom Moor ede7f2e3e6 fix: Bump RME (table and image fixes) 2021-06-21 17:39:14 -07:00
Tom Moor cf8fa5ffa3 fix: Bump RME (checkbox list fixes) 2021-06-18 16:28:27 -07:00
Tom Moor 1a2a0f4264 fix: Long search term causes server error writing query to db (#2237)
closes #2234
2021-06-17 23:23:35 -07:00
Tom Moor 5f3a38bf87 fix: todo list checkbox consistency issue
closes #2179
2021-06-17 22:57:55 -07:00
Tom Moor afff3a6f25 fix: Server error when user cancels OAuth process with Azure (#2231) 2021-06-16 21:45:20 -07:00
Tom Moor b5824879a3 Merge branch 'fix/concat-tags' 2021-06-16 18:36:28 -07:00
Tom Moor 1c82e292e0 fix: Allow embed of private mindmeister embeds
fix: Missing right and bottom border of some embeds
2021-06-16 18:36:21 -07:00
Tom Moor 317289ac2a fix: Error in Datadog tracking, if only we had TS :( 2021-06-16 08:52:54 -07:00
Tom Moor 8331026cb3 fix: No search results from link editor search due to error parsing date (date-fns upgrade) 2021-06-16 07:54:56 -07:00
Tom Moor de285f2b63 feat: Add TLS ciphers option (#2217)
closes #2175
2021-06-15 21:37:41 -07:00
G. Santos d205c48296 docs: Fix SECRET_KEY variable description (#2229)
Updated the description of the SECRET_KEY variable in the .env.sample
file to clarify that the key needs to be 32 bytes long and hex-encoded.
The previous description of "32 character hexadecimal" was confusing
as it left open the possibility of a hex-encoded 16-byte key.
2021-06-15 21:37:19 -07:00
Tom Moor 277c37dae6 fix: Metrics lib to account for multiple server instances 2021-06-15 20:34:46 -07:00
Tom Moor 2c39cd6496 chore: Normalize "new" actions in settings (#2226)
* fix: Unauthorized request to views.list from shared documents

* Bump dep styled-components

* chore: Normalize 'new' actions in settings area to top right
chore: Add translation hooks to API tokens screen
chore: Move API tokens loading to paginated list
2021-06-15 19:10:50 -07:00
Tom Moor d85592b5f3 feat: DataDog metrics (#2228)
* wip

* chore: Change event names, add additional events

* fix: Not counting connect events
2021-06-15 19:10:38 -07:00
Tom Moor cdf0df0faa Bump dep styled-components 2021-06-13 18:26:25 -07:00
Tom Moor 48f54b5aa2 fix: Unauthorized request to views.list from shared documents 2021-06-13 18:24:02 -07:00
Tom Moor 2ca57fc7cf fix: 3 locations with return undefined (not compatible with React 17) 2021-06-13 17:47:17 -07:00
Tom Moor 470920e2c3 feat: Allow templates from any collection to be used
fix: Hover state of context menu items with icons
2021-06-13 17:43:50 -07:00
Tom Moor beee8ebee7 fix: Sidebar flash when moving between collection/document due to mobx-react upgrade 2021-06-13 17:22:35 -07:00
Tom Moor 9f05c9bd43 chore: Upgrade React to v17 (#2045)
* chore: Upgrade React v17

* chore: Upgrade additional deps to reduce warnings

* fix: Restore react-table dep

* Bump react-avatar-editor, mobx-react

* Remove unmaintained @rehooks/window-scroll-position dep

* Bump react-waypoint dep for React 17 support

* fix: Syntax error in autotrack chunk name comment
2021-06-13 15:23:53 -07:00
Tom Moor 65be808556 fix: Cause of sporadic test failures in CI, promise not returned for flushdb 2021-06-13 14:52:24 -07:00
Tom Moor 89f8df619c fix: Remove export permission for read-only users (#2220) 2021-06-13 14:41:29 -07:00
Tom Moor 756ec92cdb fix: Link copied to clipboard takes dark mode styles (#2218)
Upgrade copy-to-clipboard
closes #2207
2021-06-12 15:44:58 -07:00
Dave a8e2e349e9 fix: change metaDisplay key to Alt for "Table of contents" (#2187)
* change `metaDisplay` key to Alt for "Table of contents"

Co-authored-by: Tom Moor <tom.moor@gmail.com>
2021-06-12 15:37:35 -07:00
Tom Moor 25f745e7e5 fix: Text alignment in templates menu
closes #2204
2021-06-12 13:31:22 -07:00
Nainterceptor 07b1811993 feat: Use SMTP_SECURE environment variable to force secure parameter of smtp configuration (#2214) 2021-06-12 11:01:48 -07:00
Saumya Pandey d71f0ae6bd fix: Two restore options when an archived document is deleted (#2194)
* Merge two menu items

* Add deletedAt guard condition in document unarchive policy

* Make the parentDocumentId null

* Update test
2021-06-10 22:52:32 -07:00
Tom Moor f58032d305 fix: Flash of sidebar when first loading Document chunk 2021-06-09 18:01:35 -07:00
Saumya Pandey 6beb6febc4 fix: Use friendly urls for collections (#2162)
Co-authored-by: Tom Moor <tom.moor@gmail.com>
2021-06-09 17:48:48 -07:00
Saumya Pandey a6d4d4ea36 fix: Add Portugese, Brazil to language options (#2164)
* Add Portugese, Brazil to language options

* Upgrade date-fns package

Co-authored-by: Tom Moor <tom.moor@gmail.com>
2021-06-09 17:42:14 -07:00
Tom Moor a99f6bed42 feat: Return publicly shared document title in SSR HTML (#2191)
* feat: Return publicly shared document title in SSR HTML
closes #2146

* tests
2021-06-09 17:41:39 -07:00
Tom Moor 4cd61db1ea fix: Move views loading to avoid duplicate request 2021-06-08 21:13:56 -07:00
Tom Moor 0db7bb7f3e Bump editor
closes #2156
closes #2067
2021-06-07 20:32:41 -07:00
Tom Moor d8ca9c6111 fix: Server error if non-array passed to users.invite 2021-06-07 20:28:28 -07:00
Dave 4a8d357084 style: add option background for InputSelect (#2188) 2021-06-07 18:34:01 -07:00
Yao Wang e0fb76cb63 documentation: Instructions for local development (#2180)
* Fix the instruction for local development

* update readme for Slack OAuth in local development

* Fix the callback URL setting instruction
2021-06-07 18:11:45 -07:00
Saumya Pandey ffed38bf71 fix: Prevent API request for views (#2193) 2021-06-07 18:10:54 -07:00
Tom Moor b4c08a027b fix: Remove hover state css on sidebar items on mobile
closes #2043
2021-06-06 19:56:31 -07:00
Tom Moor 74e0f4dfb3 fix: Parallelize loading attachments in document presenter (#2184)
closes #2157
2021-06-05 18:40:55 -07:00
Tom Moor 5c7f2cf164 feat: Add optional http logging in production (#2183)
* feat: Add optional http logging in production
closes #2174

* Update app.js
2021-06-05 15:19:54 -07:00
Tom Moor f517a2cecb chore: Add React.StrictMode
closes #2177
2021-06-05 14:59:14 -07:00
Saumya Pandey a19ac6aa5f fix: Failure loading collections on frontend results in loading loop (#2176)
Co-authored-by: Tom Moor <tom.moor@gmail.com>
2021-06-05 09:52:49 -07:00
Saumya Pandey ddbbb963b6 fix: Add guard condition for matchMedia usage (#2178) 2021-06-05 09:51:42 -07:00
Viorel Cojocaru ba24a3318e Fix chunks setup (#2181)
* build: Webpack config - use named chunk ids

prevent invalidation across builds by using a deterministic chunkId algorithm

* fix: Autotrack chunk name syntax
2021-06-05 09:50:38 -07:00
Viorel Cojocaru 7a6491cf0d build: Webpack config - allow package.json module field usage (#2173)
- revert resolve.alias to default
- revert bundless-* package aliases to commonjs folder to avoid transpiling
2021-06-04 18:18:17 -07:00
Tom Moor 0c8d4428fc Update stale.yml 2021-06-04 09:11:09 -07:00
Viorel Cojocaru b19fd799ef chore: Update @relative-ci/agent to v2 (#2171) 2021-06-03 22:02:38 -07:00
Viorel Cojocaru 082ced3072 build: Add async chunk names (#2170) 2021-06-03 22:01:23 -07:00
Tom Moor 1f49b35c89 documentation: Improve notes around SECRET_KEY generation 2021-06-03 08:30:53 -07:00
Tom Moor 9817e2f3bf 0.56.0 2021-06-02 12:52:19 -07:00
Translate-O-Tron 04d7c7ac0e New Crowdin updates (#2143) 2021-06-02 12:51:14 -07:00
Tom Moor e625e77a56 fix: Data loading loop on old browsers 2021-06-02 12:45:07 -07:00
Tom Moor 636023aceb fix: Bump RME, improved image download behavior in editor 2021-05-24 20:56:58 -07:00
dependabot[bot] f2dfed4c72 chore(deps): bump browserslist from 4.14.7 to 4.16.6 (#2149)
Bumps [browserslist](https://github.com/browserslist/browserslist) from 4.14.7 to 4.16.6.
- [Release notes](https://github.com/browserslist/browserslist/releases)
- [Changelog](https://github.com/browserslist/browserslist/blob/main/CHANGELOG.md)
- [Commits](https://github.com/browserslist/browserslist/compare/4.14.7...4.16.6)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-24 18:35:53 -07:00
Tom Moor 8cfa724200 feat: Animate disclosure when toggling sidebar items 2021-05-22 21:22:28 -07:00
Tom Moor 6c011eb9b5 fix: Guard empty documentStructure 2021-05-22 21:12:47 -07:00
Tom Moor 7dc11e5b86 fix: Local cache for shared link share trees to reduce render flashing 2021-05-22 20:03:50 -07:00
Tom Moor 44920a25f4 feat: Nested document sharing (#2075)
* migration

* frontend routing, api permissioning

* feat: apiVersion=2

* feat: re-writing document links to point to share

* poc nested documents on share links

* fix: nested shareId permissions

* ui and language tweaks, comments

* breadcrumbs

* Add icons to reference list items

* refactor: Breadcrumb component

* tweaks

* Add shared parent note
2021-05-22 19:34:05 -07:00
Tom Moor dc4b5588b7 feat: Add 'Descript' embed (#2144) 2021-05-22 19:21:56 -07:00
Tom Moor 635910195b i18n 2021-05-22 17:18:10 -07:00
Tom Moor eaf2e50af8 feat: Add 'download image' button
feat: Enable Enter+Shift shortcut in blockquotes
fix: Improve behavior of caret around inline code marks
fix: Disallow pasting embeds in table cells
2021-05-22 17:17:46 -07:00
Tom Moor 505ed3403a fix: Bump RME, improves behavior typing words with underscores 2021-05-20 19:29:59 -07:00
Tom Moor b93d15e967 fix: PaginatedList loading loop 2021-05-20 19:21:30 -07:00
Tom Moor 028eb72f9c fix: Restore behavior of displaying document collaborators in facepile 2021-05-19 22:05:17 -07:00
Tom Moor b0196f0cf0 feat: Rebuilt member admin (#2139) 2021-05-19 21:36:10 -07:00
Translate-O-Tron 833bd51f4c New Crowdin updates (#2120) 2021-05-18 20:00:18 -07:00
Tom Moor 14d9adefe7 test 2021-05-15 18:22:42 -07:00
Tom Moor ec3ea09b2d fix: Return lastActiveAt 2021-05-15 18:14:44 -07:00
Tom Moor 2c0f14f07b fix: Explicit import of fetch-with-proxy 2021-05-13 17:20:24 -07:00
Tom Moor a93d034091 fix: Moving documents between collections does not update attachment permissions (#2136)
* fix: Copy attachments when neccessary and moving between collections

* test: regression
2021-05-12 22:38:24 -07:00
Tom Moor 447371f35a fix: Add server-side proxy support via fetch-with-proxy (#2044)
* fix: Add server-side proxy support via fetch-with-proxy

closes #1893

For some fun discussion on why this is required, see this issue: https://github.com/nodejs/node/issues/8381

* lint
2021-05-12 22:37:32 -07:00
Tom Moor 3bd56fff9e fix: Search query backslash replacement only touched first instance
closes #2111
2021-05-12 20:27:14 -07:00
Tom Moor 9d03c89c02 chore: Return new permissions-policy header on app pages
closes #2040
2021-05-12 20:16:55 -07:00
Tom Moor 9f226cf3b4 fix: Extra space on lhs when printing in Firefox, closes #2128 2021-05-12 20:06:58 -07:00
Tom Moor d01e3f7c72 fix: Print styles in dark mode when OS is light mode
closes #2124
2021-05-12 20:00:10 -07:00
Tom Moor 2cb0bab82a fix: Welcome emails should not be sent when inviting a user (#2132)
* chore: Bump nodemailer

* fix: Welcome email sent to invites

* test: Add regression test for emails from accountProvisioner
2021-05-11 18:59:31 -07:00
dependabot[bot] 456a7e497b chore(deps): bump nodemailer from 4.7.0 to 6.4.16 (#2131)
Bumps [nodemailer](https://github.com/nodemailer/nodemailer) from 4.7.0 to 6.4.16.
- [Release notes](https://github.com/nodemailer/nodemailer/releases)
- [Changelog](https://github.com/nodemailer/nodemailer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodemailer/nodemailer/compare/v4.7.0...v6.4.16)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-11 18:11:36 -07:00
Saumya Pandey a39f7a4e55 fix: Remove application/octet-stream as valid frontend mimetype (#2126)
* Remove application/octet-stream and add explicit extensions

* Modify the condition to check for extensions too
2021-05-11 08:07:41 -07:00
Tom Moor fed3774cee chore: Bump RME 2021-05-09 22:36:20 -07:00
Saumya Pandey 985f0da674 fix: Move collection index validation logic to a context assert function (#2116)
* Abstract validation logic for readability

* Add index validation in collections.move

* Add tests
2021-05-09 22:30:37 -07:00
dependabot[bot] 721e7466e6 chore(deps): bump hosted-git-info from 2.8.8 to 2.8.9 (#2127)
Bumps [hosted-git-info](https://github.com/npm/hosted-git-info) from 2.8.8 to 2.8.9.
- [Release notes](https://github.com/npm/hosted-git-info/releases)
- [Changelog](https://github.com/npm/hosted-git-info/blob/v2.8.9/CHANGELOG.md)
- [Commits](https://github.com/npm/hosted-git-info/compare/v2.8.8...v2.8.9)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-09 19:28:51 -07:00
Tom Moor 8e1d9f0a7d fix: Welcome collection should be visible to all by default 2021-05-05 21:12:49 -07:00
Tom Moor 71de0c7e5f fix: Currently viewing users should be ordered to top 2021-05-05 21:11:09 -07:00
Tom Moor 4f4067c449 fix: Upgrade RME, fixes image flicked post-upload in editor 2021-05-05 20:09:37 -07:00
Tom Moor b945b614f8 fix: Layout of Keyboard Shortcuts guide for languages where definition wraps onto two lines 2021-05-05 20:05:49 -07:00
Tom Moor 896ee5c20d feat: Improved viewers popover (#2106)
* refactoring popover

* feat: DocumentViews popover

* i18n

* fix: tab focus warnings

* test: Add tests around users.info changes

* snapshots
2021-05-05 19:35:23 -07:00
Translate-O-Tron e984a3dcdb New Crowdin updates (#2100)
* fix: New Polish translations from Crowdin [ci skip]

* fix: New Polish translations from Crowdin [ci skip]

* fix: New German translations from Crowdin [ci skip]
2021-05-05 19:35:04 -07:00
Tom Moor 69802cc985 fix: Add application/octet-stream as a valid mimetype for docx uploads (#2105)
* fix: Add application/octet-stream as a valid mimetype for docx uploads

* fix: Include application/octet-stream in frontend filter
fix: Add file size and file type guards

* Validate .docx extension in files with application/octet-stream mimetype

* refactor: Move MAXIMUM_IMPORT_SIZE to an optional environment config
fix: Add file size check on server too

Co-authored-by: Saumya Pandey <sp160899@gmail.com>
2021-05-05 18:48:37 -07:00
Saumya Pandey 6ef8d9ddb3 fix: Handle null case (#2118) 2021-05-05 18:47:23 -07:00
John Viscel M. Sangkal d21594a6f4 fix: Add onClick event listener to show Appearance Menu options in mobile (#2119) 2021-05-05 18:46:57 -07:00
Tom Moor 974d6b2cbe fix: Submenu overflow broken 2021-05-05 09:13:44 -07:00
Tom Moor aa3cb22703 test: Fix tests around utils.gc 2021-05-03 21:39:01 -07:00
Tom Moor 49ffcda8e0 fix: 'Post to channel' functionality does not work unless Slack SSO used (#2099)
* fix: 'Post to channel' functionality does not work unless Slack SSO used

* test: And this is why we have tests
2021-05-01 16:35:00 -07:00
Tom Moor 77d6adb73b feat: Signup query params tracking (#2098)
* feat: Add tracking of signup query params

* fix: Headers already sent to client

* fix: OAuth error wipes previously written query params cookie
2021-05-01 13:46:08 -07:00
Tom Moor 4d68a34897 fix: ReDoS attack vulnerability when searching documents that contain many space characters
see: https://github.com/outline/outline/pull/2097
see: https://snyk.io/vuln/SNYK-JS-REMOVEMARKDOWN-73635
2021-04-28 22:44:05 -07:00
Tom Moor 61b2e63a44 Merge branch 'main' of github.com:outline/outline 2021-04-28 22:40:53 -07:00
Translate-O-Tron ae940dd255 New Crowdin updates (#2048) 2021-04-27 20:30:31 -07:00
Tom Moor b13626631c fix: Space for overflow menu on sidebar items 2021-04-27 18:58:37 -07:00
Tom Moor 7221e51b96 chore: Move settings screens to Scene component (#2092)
* chore: Convert groups and people settings screens to Scene/functional

* chore: ImportExport to Scene component

* Remaining settings scenes
2021-04-27 18:46:58 -07:00
Tom Moor b89f4c36f4 chore: Rename Authentication -> IntegrationAuthentication (#2091) 2021-04-27 18:42:45 -07:00
Tom Moor 829cc14d36 build:i18n 2021-04-27 18:07:11 -07:00
Tom Moor 8009e8f691 fix: Missing bg blur, closes #2082 2021-04-27 17:29:22 -07:00
Tom Moor ab2aaf7b7b feat: Upgrade RME – includes new page break functionality 2021-04-27 17:21:45 -07:00
dependabot[bot] 65b4480e93 chore(deps): bump redis from 3.0.2 to 3.1.2 (#2090)
Bumps [redis](https://github.com/NodeRedis/node-redis) from 3.0.2 to 3.1.2.
- [Release notes](https://github.com/NodeRedis/node-redis/releases)
- [Changelog](https://github.com/NodeRedis/node-redis/blob/master/CHANGELOG.md)
- [Commits](https://github.com/NodeRedis/node-redis/compare/v3.0.2...v3.1.2)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-27 17:05:32 -07:00
Tom Moor 6de793e94e fix: uuid import broken by dep bump 🤦‍♂️ 2021-04-25 12:54:06 -07:00
Tom Moor 2d22399bbc fix: Correctly guard against last admin deleting their account (#2069)
* fix: Correctly guard against last admin deleting their account

* test
2021-04-24 20:52:46 -07:00
Tom Moor 3fbb3a2403 fix: Continued undefined error in serverWorker registration 2021-04-24 12:50:30 -07:00
Tom Moor d45178cb44 chore: Remove dependency on twemoji 2021-04-23 23:24:54 -07:00
Tom Moor 5786a03f33 chore: Update uuid package, removes dupe dependency 2021-04-23 18:48:47 -07:00
Tom Moor 011a1383ec chore: Upgrade tmp dependency 2021-04-23 18:44:24 -07:00
Tom Moor 72d7b5734d chore: Upgrade slug dependency 2021-04-23 18:40:25 -07:00
Tom Moor b6fe3cb556 chore: Upgrade mammoth for html import fixes 2021-04-23 18:35:54 -07:00
Tom Moor 1e2224cb0d chore: Upgrade dd-trace 2021-04-23 18:32:49 -07:00
Tom Moor 0477060b35 chore: Upgrade relative-ci/agent 2021-04-23 18:31:43 -07:00
Tom Moor a261abcdef Merge branch 'main' of github.com:outline/outline 2021-04-23 18:25:43 -07:00
Tom Moor f64d0ce660 feat: Share flyover (#2065)
* feat: Implement share as flyover instead of modal

* refactor

* i18n
2021-04-23 17:31:27 -07:00
Tom Moor f27072d06e feat: More space for content on larger screens 2021-04-23 16:41:40 -07:00
Tom Moor c8055e40bb fix: Content appearing behind status bar in iOS PWA on some models of phone 🤷 2021-04-23 14:57:38 -07:00
Tom Moor cfae180093 chore: Upgrade Sentry 6.1.0 -> 6.3.1 2021-04-23 12:50:40 -07:00
Tom Moor 094c6418c9 Update LICENSE 2021-04-23 12:50:19 -07:00
Tom Moor 99b1bf0ecb fix: Avoid rare 'undefined is not a function' when attempting to register a server worker on Windows Chrome 2021-04-23 12:31:27 -07:00
Tom Moor 3b696cfa9a fix: Page reloads in Firefox when clicking some menu items (#2060)
* fix: Some context menu items result in page reload in Firefox

closes #1877

* fix: Display of sidebar link actions on hover
2021-04-23 12:25:15 -07:00
Tom Moor eb6acdae20 fix: CMD+F not working on screens with keyboard shortcut guide (#2066) 2021-04-23 12:10:02 -07:00
Tom Moor a818c7a924 fix: Hover card behind subheadings, previously it relied on being a portal without any explicit depth
closes #2062
2021-04-23 12:09:30 -07:00
Tom Moor d157e9bfcd 0.55.0 2021-04-22 20:23:24 -07:00
Tom Moor f2052c2a05 fix: Escape key in keyboard shortcut guide should clear search input if search term 2021-04-22 19:37:40 -07:00
Tom Moor 40b4270e35 chore: Faster source map in dev 2021-04-22 18:59:59 -07:00
Tom Moor 16c60a0d59 fix: URLSearchParams polyfill via core-js upgrade (#2059)
* fix: URLSearchParams polyfill via core-js upgrade

* deduplicate

* testing, remove manual imports

* chore: bump rme
2021-04-22 18:21:27 -07:00
Mark Steve Samson 1a183ba0fc Document and include PGSSLMODE in sample env file (#2052) 2021-04-21 18:15:23 -07:00
Tom Moor 2ffc0ae81c feat: New keyboard shortcuts guide (#2051)
* feat: Add search

* feat: New design for keyboard shortcuts guide
feat: Include quick search
fix: Add missing shortcuts

* tweaks

* fix: Two other spots that should trigger guide-style instead of modal

* sink,lift -> indent,outdent

* fix: Animation should slide out as well as in
2021-04-21 18:15:07 -07:00
Tom Moor 50fdd73610 fix: Remove HMR in test env (#2054) 2021-04-21 17:53:53 -07:00
dependabot[bot] a134773d4e chore(deps): bump ssri from 6.0.1 to 6.0.2 (#2050)
Bumps [ssri](https://github.com/npm/ssri) from 6.0.1 to 6.0.2.
- [Release notes](https://github.com/npm/ssri/releases)
- [Changelog](https://github.com/npm/ssri/blob/v6.0.2/CHANGELOG.md)
- [Commits](https://github.com/npm/ssri/compare/v6.0.1...v6.0.2)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-20 17:37:39 -07:00
Tom Moor 317c52df62 fix: Improve error handling for Azure-specific errors not captured in OAuth2 strategy 2021-04-18 22:41:27 -07:00
Tom Moor 04b8d7ae7b Normalize sidebar style 2021-04-18 21:52:54 -07:00
Tom Moor 3569d2fee7 chore: Update caniuse 2021-04-18 19:52:47 -07:00
Tom Moor ab267ce38d fix: Disable polling on custom domain, closes #2041 2021-04-18 18:58:26 -07:00
Tom Moor fa52bc5afd chore: Slack integration screen improvements (#2049)
* feat: Add collection iconography and colors to Slack settings page
fix: Use standardized list components
fix: Slack icon size
chore: Convert to translation strings

* fix: Missing translation, convert to Scene
2021-04-18 18:34:49 -07:00
Tom Moor bf668d6347 fix: Double documents.info request loading public share links 2021-04-18 11:34:11 -07:00
Tom Moor 7f9cba9819 feat: Record share link last accessed time (#2047)
* chore: Migrations

* chore: Add recording of share link views

* feat: Add display of share link accessed date in admin

* translations

* test

* translations, admin pagination
2021-04-18 09:38:13 -07:00
Tom Moor e9f083feb8 fix: Document title with slashes produces folders in exported zip file
closes #2036
2021-04-17 19:30:31 -07:00
Tom Moor 03d90b3f15 fix: Hide secondary actions in document header on mobile
closes #2042
2021-04-17 18:14:24 -07:00
Tom Moor 2432b4dcbd fix: Editor lightbox stacked below sidebar 2021-04-17 18:08:09 -07:00
Translate-O-Tron 2c2c1341f7 fix: New Spanish translations from Crowdin [ci skip] (#2035) 2021-04-17 13:24:17 -07:00
Tom Moor 7a8ccdb229 feat: Microsoft authentication (#1953)
closes #755
2021-04-17 13:22:18 -07:00
Tom Moor b2d703bee4 fix: Improved mobile styling
fix: Severla context menus miss-positioned
fix: Search filters not large enough on mobile
fix: Deep black background on mobile to match native apps
fix: Sticky document header allowing horizontal scrolling on mobile
2021-04-17 10:40:39 -07:00
Tom Moor c46a032f0b fix: CSS stacking context issue with behind menu backdrops on mobile
Moving the animation to the same element that has position: fixed resolves
2021-04-16 19:02:43 -07:00
Tom Moor 940ad8479e perf: Remove collaborators from documents.list response (#2039)
* fix: Remove unused, unperformant query

* lint

* collaborators -> collaboratorIds
2021-04-15 22:49:16 -07:00
Tom Moor c5401a467d fix: Overlapping header, closes #2038 2021-04-15 20:30:05 -07:00
Tom Moor 1dd97c1ddd feat: Show mobile-style (slide from bottom) menus on mobile (#2025)
* feat: Show mobile-style (slide from bottom) menus at responsive viewport sizes

* More mobile improvements

* fix: Safari compatability
2021-04-13 21:43:24 -07:00
Translate-O-Tron f37371c16e New Crowdin updates (#2027) 2021-04-13 21:31:35 -07:00
Tom Moor 62f9262b2c fix: Improved handling of authentication edge-cases (#2023)
* fix: authentication records not cleaned up for deleted user
closes #2022

* fix: Improve debugging for duplicate providerId sign-in requests
2021-04-11 19:39:31 -07:00
Saumya Pandey bc4fe05147 feat: Read-only users (#1955)
* Introduce isViewer field

* Update policies

* Make users read-only feature

* Remove not demoting current user validation

* Update tests

* Catch the unhandled promise rejection

* Hide unnecessary ui elements for read-only user

* Update app/scenes/Settings/People.js

Co-authored-by: Tom Moor <tom.moor@gmail.com>

* Remove redundant logic for admin only policies

* Use can logic

* Update snapshot

* Remove lint error

* Update snapshot

* Minor fix

* Update app/menus/UserMenu.js

Co-authored-by: Tom Moor <tom.moor@gmail.com>

* Update server/api/users.js

Co-authored-by: Tom Moor <tom.moor@gmail.com>

* Update app/components/DocumentListItem.js

Co-authored-by: Tom Moor <tom.moor@gmail.com>

* Update app/stores/UsersStore.js

Co-authored-by: Tom Moor <tom.moor@gmail.com>

* Use useCurrentTeam hook in functional component

* Update translation

* Update ternary

* Remove punctuation

* Move the functions to User model

* Update share policy and shareMenu

* Rename makeAdmin to promote

* Create updateCounts function and Rank enum

* Update tests

* Remove enum

* Use async await, remove enum and create computed accessor

* Remove unused variable

* Fix lint issues

* Hide templates

* Create shared/types and use rank type from it

* Delete shared/utils/rank type file

Co-authored-by: Tom Moor <tom.moor@gmail.com>
2021-04-11 19:39:17 -07:00
Tom Moor cdc7f61fa1 chore: Enable HMR for frontend code (#2024)
* chore: Enable HMR for frontend code
closes #2021

* revert
2021-04-11 15:09:00 -07:00
Tom Moor 2a6dfdea5d fix: Highlight states and dropzones when user does not have permission to import 2021-04-10 13:54:05 -07:00
Translate-O-Tron de25ea0ed9 fix: New Chinese Simplified translations from Crowdin [ci skip] (#2020) 2021-04-09 08:43:23 -07:00
Tom Moor d2227a2488 Update stale.yml 2021-04-08 22:56:22 -07:00
Translate-O-Tron 3e050727cb fix: New Chinese Simplified translations from Crowdin [ci skip] (#2019) 2021-04-08 21:20:10 -07:00
Tom Moor 326518873e fix: Logout suspended users immediately 2021-04-08 21:04:34 -07:00
Translate-O-Tron ed779a250f New Crowdin updates (#2011) 2021-04-08 20:40:34 -07:00
Tom Moor 190f0b6dc5 fix: Improve handling of suspended users signing in with email (#2012)
* chore: Separate signin/auth middleware
fix: Email signin token parsed by JWT middleware
fix: Email signin marked as active when logging in as suspended
fix: Suspended email signin correctly redirected to login screen
closes #1740

* refactor middleware -> lib

* lint
2021-04-08 20:40:04 -07:00
Tom Moor 1a889e9913 fix: Add embed support for lucid.app domain
closes #2017
2021-04-07 21:48:45 -07:00
Tom Moor b3203857d7 Create FUNDING.yml 2021-04-06 19:32:07 -07:00
Tom Moor 5762fb33d9 chore: Improve display of configuration errors (#2014)
* chore: Show all configuration errors at once
fix: Remove requirement for deprecated Slack key
fix: Add requirement for UTILS_SECRET

* chore: Add funding/sponsorship message
2021-04-06 19:29:59 -07:00
Tom Moor 1101ea428b feat: Drop to import onto collection scene (#2005)
* Refactor to functional component

* feat: Basic drag and drop into collection
2021-04-05 19:05:27 -07:00
Translate-O-Tron b4213e498c New Crowdin updates (#1951) 2021-04-05 18:28:27 -07:00
Saumya Pandey f9f76d4438 Format local development instructions (#2007) 2021-04-05 17:19:44 -07:00
Tom Moor 4a9571a174 fix: Alignment of backlinks and references (#2006)
closes #1998
2021-04-05 17:19:31 -07:00
Tom Moor bf856dbafa Merge branch 'main' of github.com:outline/outline 2021-04-04 11:04:46 -07:00
Nilay Sharma 0e54302d75 stretch email login input (#2004)
* stretch email login input

* Update InputLarge.js

Co-authored-by: Tom Moor <tom.moor@gmail.com>
2021-04-04 09:57:44 -07:00
Tom Moor 4777176d84 fix: User afterCreate hook using deprecated column 2021-04-03 18:37:39 -07:00
Tom Moor 3ffa21b07f fix: Unneeded object keys in API response 2021-04-03 17:18:26 -07:00
Tom Moor 8cbc873451 chore: Clarify flow on /create page 2021-04-03 14:29:08 -07:00
Tom Moor d2e8311b39 chore: Add tracking ref to branding on share links 2021-03-31 21:52:49 -07:00
Tom Moor 810257bcf5 fix: Improve dark mode styling
fix: Improve user and group list styling
fix: Member list reload when changing permissions, closes #1999
2021-03-31 20:57:30 -07:00
Tom Moor 2ef0caba88 fix: Server error when invalid 'sort' field is passed from an API client (#2000) 2021-03-31 18:54:02 -07:00
Tom Moor 2e64972574 fix: Error in shares.info endpoint when user originally creating share has been deleted 2021-03-31 18:04:40 -07:00
Tom Moor 7e1b07ef98 feat: Add read-only collections (#1991)
closes #1017
2021-03-30 21:02:08 -07:00
Tom Moor d7acf616cf chore: Bump rich-markdown-editor 2021-03-30 20:39:41 -07:00
Tom Moor c5569bd365 feat: Add /logout route for SLO support 2021-03-30 20:10:52 -07:00
Tom Moor 25023fb086 chore: Fix modal nesting, remove react-modal (#1996)
* chore: Fix modal nesting, remove react-modal

* tweak

* fix: Janky route jump when accessing Document -> Move from non-document scene
2021-03-30 18:46:14 -07:00
Tom Moor 07425f4243 Minor context menu visual updates 2021-03-29 22:28:40 -07:00
Tom Moor b5dcb1b3fe fix: JS error from #1962 refactor when Google or Slack auth credentials are missing 2021-03-29 22:03:40 -07:00
Tom Moor ae57cdea2a fix: GA should load on public share pages, closes #1992 2021-03-29 22:02:38 -07:00
Tom Moor 8599b60a6c fix: group -> createGroup 2021-03-26 20:17:46 -07:00
Tom Moor e00a437f2f feat: authenticationProviders API endpoints (#1962) 2021-03-26 11:31:07 -07:00
Tom Moor 626c94ecea fix: Bump react-dropzone for eroneous 'green plus' cursor fix
https://github.com/react-dropzone/react-dropzone/issues/1042
2021-03-26 11:17:50 -07:00
Tom Moor 889186e510 fix: Close appearance menu when selecting a theme
fix: Position disclosure correctly when menu has submenu
fix: More reliably close context menus
2021-03-26 11:15:58 -07:00
Tom Moor 4166257283 chore: Improved context menu behavior 2021-03-24 19:23:16 -07:00
Tom Moor 6a7d7af767 fix: Sticky archive header 2021-03-24 18:42:12 -07:00
Tom Moor 46912f8ddb fix: Single share record per document (#1984) 2021-03-24 18:28:38 -07:00
Tom Moor 877c01f723 feat: Show 'Edit' button when visiting share link as signed in user with permission (#1980) 2021-03-23 19:22:15 -07:00
Tom Moor 97158b1337 fix: Invisible list bullets in Firefox 2021-03-23 19:02:19 -07:00
Tom Moor 8d8bde4b8b closes #1784 2021-03-22 22:23:03 -07:00
Tom Moor 059fca27b3 buildUser -> buildAdmin 2021-03-22 20:59:11 -07:00
Tom Moor 9f6ba798c8 Merge branch 'main' of github.com:outline/outline 2021-03-22 20:51:45 -07:00
Tom Moor 349e971a8a chore: Serialize domain policies on team (#1970)
* domain policies exposed on team, consistency

* fix: Remove usage of isAdmin in frontend

* test
2021-03-22 20:50:12 -07:00
Tom Moor 9af9d3a008 chore: Bump ioredis for fixes
chore: Remove sequelize language from user-facing db migrations
2021-03-22 18:41:54 -07:00
Tom Moor bb5443452b flow 2021-03-21 18:52:42 -07:00
Tom Moor b3353f20d5 chore: Move error logging in passport 2021-03-21 18:36:10 -07:00
Tom Moor 200f25c4b2 0.54.0 2021-03-21 10:27:27 -07:00
Saumya Pandey f1296cc8e3 Add import document menuitem (#1966) 2021-03-20 23:20:49 -07:00
Tom Moor ad8c08497c fix: Moving document in private collection returns incorrect policies (#1969)
* fix: Return no policies when collection doesn't change
fix: Return correct policies when it does change

* test
2021-03-19 08:01:51 -07:00
Tom Moor 7891a8ee8b fix: Cannot create collection if all collections deleted 2021-03-19 08:01:13 -07:00
Tom Moor 56c4acc18f fix: Background on error details not correct in dark theme 2021-03-18 23:56:05 -07:00
Tom Moor 1b972070d7 feat: Enforce single team when self-hosted (#1954)
* fix: Enforce single team when self hosting

* test: positive case

* refactor

* fix: Visible error message on login screen for max teams scenario

* Update Notices.js

* lint
2021-03-18 21:56:24 -07:00
Nan Yu 138336639d fix: touch up UI for compact breadcrumbs (#1961) 2021-03-18 19:44:28 -07:00
Tom Moor 8ea746dbe8 Bump RME
fixes: Code does not highlight until editor is focused
fixes: Improved pasting behavior
fixes: Error when pasting iframes from elsewhere
2021-03-18 18:01:07 -07:00
Saumya Pandey 46bcc2e2ae feat: Allow sorting collections in sidebar (#1870)
closes #1759

Co-authored-by: Tom Moor <tom.moor@gmail.com>
2021-03-18 17:27:33 -07:00
Tom Moor b93002ad93 fix: JS error when pasting gist link, closes #1963 2021-03-16 22:53:32 -07:00
Tom Moor a427d77076 fix #1964 – error when selecting no files to import after previously selecting files 2021-03-16 22:09:19 -07:00
Nan Yu eff56b758c feat: use flexbox to center the floating title (#1959)
* use flexbox to center the floating title

* simplify breadcumb alignment

* small adjustments

* fix alignment on actions
2021-03-15 19:24:14 -07:00
Tom Moor ffc270b567 fix: Incorrect calculation of subdomain when previously used more than once 2021-03-15 18:04:41 -07:00
Saleh Yusefnejad d86b7babb9 fix: correct GoogleSheets component class name (#1960) 2021-03-15 15:36:10 -07:00
Tom Moor ec57951087 feat: Upgrade editor - move list items with keyboard 2021-03-14 15:58:50 -07:00
Tom Moor 2385f41a98 chore: Show 'private' badge on private collections (#1952)
* chore: Show 'private' badge on private collections

* lint

* fix: Missing translation mapping
2021-03-12 16:41:26 -08:00
Translate-O-Tron bdb684a4be New Crowdin updates (#1950)
* fix: New Chinese Simplified translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]
2021-03-11 16:54:24 -08:00
Tom Moor 5d6f68d399 feat: Move to passport for authentication (#1934)
- Added `accountProvisioner`
- Move authentication to use passport strategies
- Make authentication more pluggable
- Change language of services -> providers

closes #1120
2021-03-11 10:02:22 -08:00
Tom Moor dc967be4fc chore: Syncs changes that were erroneously made in enterprise repo (#1949) 2021-03-10 14:56:34 -08:00
Saumya Pandey d530edcc2f fix: Remove double separator in collection menu (#1948)
* Hide separator

* Remove visible logic
2021-03-10 14:56:16 -08:00
Tom Moor 1393d1950e chore: Test performance and warnings (#1946)
* test: Do not request mailer account in test environment

* test: Dupe migrations
2021-03-10 12:04:42 -08:00
Tom Moor 0aa72036d7 fix: Templates should not be visible in collection structure 2021-03-10 09:39:42 -08:00
Tom Moor f50b88716b fix: Handle document Id in document structure does not exist in db
closes #1938
2021-03-09 22:45:40 -08:00
Tom Moor e90c02bec7 feat: Hide less popular services by default in block menu 2021-03-09 22:32:28 -08:00
Tom Moor 504b11576a bump 2021-03-09 22:04:47 -08:00
Tom Moor bac7a364d0 fix: Drag and drop images in editor, conflict with sidebar react-dnd (#1918)
* fix: Drag and drop images in editor, conflict with sidebar react-dnd
see: https://github.com/react-dnd/react-dnd/pull/3052

* Bump to non-canary

* Upgrade react-dnd

* react-dnd api changes

* lint

* fix: dnd doesn't work on first render

* remove unneccessary async

* chore: Update react-dnd (API changed again)

* restore fade
2021-03-09 18:41:30 -08:00
Tom Moor ed2a42ac27 chore: Migrate authentication to new tables (#1929)
This work provides a foundation for a more pluggable authentication system such as the one outlined in #1317.

closes #1317
2021-03-09 12:22:08 -08:00
Saumya Pandey ab7b16bbb9 fix: Centered document title moves when saving (#1943)
closes #1939
2021-03-09 12:21:29 -08:00
Translate-O-Tron d8eefc1972 New Crowdin updates (#1920) 2021-03-08 17:17:13 -08:00
dependabot[bot] b188a8ff30 chore(deps): bump elliptic from 6.5.3 to 6.5.4 (#1941)
Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.3 to 6.5.4.
- [Release notes](https://github.com/indutny/elliptic/releases)
- [Commits](https://github.com/indutny/elliptic/compare/v6.5.3...v6.5.4)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-03-08 17:16:33 -08:00
Tom Moor e1c7b07af9 fix: Image in README was removed on Github's servers 2021-03-08 09:19:42 -08:00
Tom Moor eaadeb26e5 fix: Upgrade reakit for better context menu behavior 2021-03-01 19:17:24 -08:00
Tom Moor 0c301fcf0c fix: Enlarge scrollable area in sidebar for improved UX on small screens 2021-02-26 10:51:14 -08:00
Tom Moor a3e95023dc fix: Temporary fix for outline-icons build issue 2021-02-23 22:53:55 -08:00
Tom Moor e08b17561e fix: Capitalize Pусский 2021-02-23 10:35:50 -08:00
Tom Moor ac79a4c4cc fix: Update PWA macOS icon 2021-02-23 08:36:59 -08:00
Tom Moor e085553306 feat: Add tracking of PWA installs 2021-02-22 22:30:47 -08:00
Tom Moor 38bd1d5585 translate: Search filter dropdowns, modal 2021-02-22 22:18:00 -08:00
Tom Moor cd7cbab5ac fixes #1879 – race condition in websockets service 2021-02-22 21:53:20 -08:00
Tom Moor 2195787e7d logistics -> exporter, remove cutesy naming of lib 2021-02-22 21:51:01 -08:00
Tom Moor 04f942141f fix: Tone-down separator for PWA titlebar in dark mode 2021-02-21 22:42:22 -08:00
Tom Moor d0f1fd533a feat: Add Russian language support
closes #1916
2021-02-21 20:42:01 -08:00
Translate-O-Tron a1e885f057 New Crowdin updates (#1913) 2021-02-21 20:35:39 -08:00
Tom Moor 2ad9f69f7f fix: Scrollbars should match theme, closes #1917 2021-02-21 19:59:57 -08:00
Tom Moor 65bca35bbf Merge branch 'main' of github.com:outline/outline 2021-02-21 13:33:17 -08:00
Tom Moor a96993fda9 feat: Add PWA support to subdomains (#1915)
* fix: Remove overscroll
* Remove title from fixed header in PWA as it's displayed immediately above in application title
2021-02-21 13:32:49 -08:00
Tom Moor 9fc03b6ece fix: Cannot upload images into collection description (authentication failure) 2021-02-20 22:38:28 -08:00
Tom Moor 100360adb3 fix: Drop cursor not visible in dark theme 2021-02-20 22:35:07 -08:00
Tom Moor d277d80323 Merge pull request #1914 from outline/fix/issue-1896
fix: Documents in trash should still load their attachments
2021-02-20 13:35:41 -08:00
Tom Moor c79cfbd30d fix: Documents in trash should still load their attachments
closes #1896
2021-02-20 13:22:02 -08:00
Tom Moor e66611e771 fix: Error with search term including %, closes #1891 2021-02-20 13:03:41 -08:00
Tom Moor 903e83a618 feat: Batch Import (#1747)
closes #1846
closes #914
2021-02-20 12:36:05 -08:00
Tom Moor 4ef4ef963a i18n 2021-02-20 12:31:54 -08:00
Translate-O-Tron 51c6a19dc3 New Crowdin updates (#1906) 2021-02-19 09:28:47 -08:00
Tom Moor bbf434e2f4 fix: Disable 'Invite people…' control for non-admins (#1903)
closes #1902
2021-02-18 23:35:55 -08:00
Tom Moor 5b7018058d i18n 2021-02-18 23:30:48 -08:00
Tom Moor fae54c7957 fix: Mispositioned sticky headers in modals 2021-02-18 23:20:12 -08:00
Tom Moor fabfa6a491 Tweak language, remove original attachment once complete 2021-02-18 23:08:48 -08:00
Tom Moor c5f9412ac0 fix: Collection creator not written (bad merge from refactor while this branch has been open)
refactor: Move processing to async queue now that file can be loaded from external storage
2021-02-18 22:55:29 -08:00
Tom Moor f4c871bb62 i18n 2021-02-18 22:36:18 -08:00
Tom Moor df233c95a9 refactor: Upload file to storage, and then pass attachmentId to collections.import
This avoids having large file uploads going directly to the server and allows us to fetch it async into a worker process
2021-02-18 22:36:07 -08:00
Tom Moor 568e271738 lint 2021-02-18 21:05:21 -08:00
Tom Moor 9efed11a3e Merge branch 'main' of github.com:outline/outline into feat/mass-import 2021-02-18 20:57:01 -08:00
PedroSeda c30132e558 feat: Embed Cawemo (#1890) 2021-02-18 18:48:40 -08:00
Tom Moor b152a5595e Merge branch 'main' into feat/mass-import 2021-02-17 23:57:45 -08:00
Tom Moor 887e341e48 Add NODE_ENV to app.json 2021-02-17 19:38:44 -08:00
Tom Moor ae2f1b47e7 0.53.1 2021-02-17 00:08:04 -08:00
Tom Moor 86d9a14c5c fix: is virtual host 2021-02-16 23:52:25 -08:00
Tom Moor 6a8a83610f fix: Input data is not a String when clicking on new collection description 2021-02-16 23:42:31 -08:00
Tom Moor 54bf7a9dea fix: Restore specifying AWS endpoint for non-S3 support 2021-02-16 23:41:39 -08:00
Tom Moor 43ed7d0343 0.53.0 2021-02-16 21:18:06 -08:00
Tom Moor a81a18b173 fix: Remove hard-coded ServerSideEncryption on AWS, configure on AWS or storage provider 2021-02-16 00:16:23 -08:00
Tom Moor f18a2a048d fix: Sticky heading stacking 2021-02-16 00:15:04 -08:00
Tom Moor 7e922d8716 feat: Installable PWA (#1882) 2021-02-15 15:19:51 -08:00
Tom Moor 4b603460cb chore: Standardized headers (#1883)
* feat: Collection to standard header
feat: Sticky tabs

* chore: Document to standard header

* chore: Dashboard -> Home
chore: Scene component

* chore: Trash, Templates, Drafts

* fix: Mobile improvements

* fix: Content showing at sides and occassionally ontop of sticky headers
2021-02-14 13:18:33 -08:00
Tom Moor 32a298054d Bump RME – Editor fixes and improvements 2021-02-13 12:47:44 -08:00
Tom Moor ca2459361e chore: de-dupe lockfile 2021-02-13 09:25:04 -08:00
Tom Moor e49f3ab9fb ResizeObserver loop completed with undelivered notifications 2021-02-13 08:43:22 -08:00
Tom Moor e9338df057 Update README screenshot 2021-02-12 20:29:43 -08:00
Tom Moor 2629d6db23 fix: 'Suspended' badge misaligned on user profiles
closes #1880
2021-02-12 17:34:40 -08:00
Tom Moor b017590033 fix: 'bake' release env variables at build time 2021-02-12 17:18:55 -08:00
Tom Moor 7d244dfa1f 'bake' release env variables at build time 2021-02-12 16:53:16 -08:00
Tom Moor 2a225d81d2 chore: Update Sentry to avoid duplicate packages
chore: Pass current release version to Sentry and Datadog
2021-02-12 16:39:02 -08:00
Tom Moor 41df5c74be Add description -> Add a description 2021-02-12 16:24:31 -08:00
Tom Moor ef026b34fa Return to App -> Back to App 2021-02-12 16:21:32 -08:00
Tom Moor 1dbcc12648 feat: Inline collection editing (#1865) 2021-02-12 16:20:49 -08:00
Tom Moor 2611376b21 chore: Add optional DD tracer 2021-02-11 18:58:56 -08:00
Tom Moor a1b3cfc7de Yarn.lock 2021-02-10 20:25:26 -08:00
Tom Moor 5a478ec127 fix: Incorrect policy returned after document create/import 2021-02-09 21:29:24 -08:00
Tom Moor c0325fcaf3 Merge branch 'main' into feat/mass-import 2021-02-09 20:46:57 -08:00
Tom Moor df472ac391 feat: add total users to people management screen (#1878)
* feat: add total users to pagination

* move this.total in runInAction callback

* add total counts + counts to people tabs

* progress: use raw pg query

* progress: add test

* fix: SQL interpolation

* Styling and translation of People page

Co-authored-by: Tim <timothychang94@gmail.com>
2021-02-09 20:13:09 -08:00
Tom Moor 097359bf7c feat: Added ability to disable sharing at collection (#1875)
* feat: Added ability to disable sharing at collection

* fix: Disable all previous share links when disabling collection share
Language

* fix: Disable document sharing for read-only collection members

* wip

* test

* fix: Clear policies after updating sharing settings

* chore: Less ambiguous language

* feat: Allow setting sharing choice on collection creation
2021-02-09 19:04:03 -08:00
Tom Moor 3739bb7c55 Update translation.json 2021-02-08 21:50:34 -08:00
Tom Moor cc90c8de1c feat: Sidebar Improvements (#1862)
* wip

* refactor behaviorg

* stash

* simplify
2021-02-07 21:51:56 -08:00
Tom Moor ac6c48817c fix: Unable to select .md files by default on some machines depending on installed software 2021-02-07 16:14:03 -08:00
Tom Moor 8e3534dcbc fix: File import via collection menu regression 2021-02-07 16:13:44 -08:00
Tom Moor cada91a135 Merge main 2021-02-07 12:58:17 -08:00
Yaroslav Zhavoronkov e2d7d34f30 fix: Pass credentials with API requests when required to work with Cloudflare Access (#1867) 2021-02-06 22:49:49 -08:00
Tom Moor 0d88a1dfda Update README.md 2021-02-06 21:49:07 -08:00
Tom Moor df5a2e45c5 chore: Improved deployment documentation (#1868) 2021-02-06 21:33:56 -08:00
Tom Moor 1a7a48674b fix: link in README, add ARCHITECTURE document 2021-02-06 17:46:54 -08:00
Tom Moor e23474fa1c feat: Add parameters for filtering events (#1863)
* feat: Add parameters for filtering events

* test
2021-02-04 20:20:56 -08:00
Tom Moor 37fa13d841 fix: flash of 'Deleted Collection' when loading app on doc page 2021-02-02 22:03:02 -08:00
Tom Moor 6d88c02869 chore: Remove unused Popover component 2021-02-02 21:17:17 -08:00
Tom Moor a2fb3bb9f8 fix: Favicon should load from domain root, not current path 2021-02-02 21:13:11 -08:00
Tom Moor fb0b38fb71 fix: Mobile menu toggle button appearing in print media, closes #377 2021-02-02 20:57:08 -08:00
Tom Moor 8ff2f41068 Merge branch 'develop' of github.com:outline/outline into develop 2021-02-01 21:13:51 -08:00
Tom Moor 334dce7984 chore: Add Timing-Allow-Origin header (#1860) 2021-02-01 21:13:44 -08:00
Tom Moor 61b303831f fix: Document history sidebar layout issue 2021-02-01 21:13:31 -08:00
Tom Moor a9d60d288e feat: Automatically scroll to active item in sidebar (#1858) 2021-02-01 19:29:54 -08:00
Translate-O-Tron 3f267d7745 New Crowdin updates (#1848) 2021-01-31 23:24:10 -08:00
Tom Moor e845652cb8 flow: Convert to different <Trans> component syntax for flow compatability 2021-01-31 21:14:14 -08:00
Tom Moor 7066a45323 feat: Improve star/unstarred iconography 2021-01-31 20:53:27 -08:00
Tom Moor 654fdf1c7e fix: Guard unset language 2021-01-31 15:41:33 -08:00
Tom Moor 2a5fd0b332 test 2021-01-31 14:47:28 -08:00
Tom Moor 9ba63c6054 feat: Show nested document count on document list items on collection home 2021-01-31 14:41:18 -08:00
Tom Moor 785e208c6c lint 2021-01-31 14:41:00 -08:00
Tom Moor 9d84652dff fix: Frontend translation library expects dash separated, not underscore separated languages – this fix is required to enable working pluralization 2021-01-31 14:40:50 -08:00
Tom Moor ef6ce72cf5 fix: Recently published redirect 2021-01-31 13:01:56 -08:00
Tom Moor 7777cccf3b fix: Save regression from flow refactor 2021-01-31 12:53:52 -08:00
Tom Moor 620e4942d8 feat: Update default collection tab (#1821)
* feat: Allow listing root level documents only via documents.list

* feat: New tab on collection home

* update tab layout

* fix: Correctly sort index sorted documents.list

* revert: Tab layout changes

* fix: Missing route for recently published
fix: Redirect unknown tabs
2021-01-31 12:37:27 -08:00
Tom Moor 91ee3e62f2 fix: Reassign user on unpublish (#1857)
* findOne -> findByPk
2021-01-30 18:31:08 -08:00
Tom Moor eeb7650941 fix: New documents should sort to the top of manually organized collection 2021-01-30 00:18:56 -08:00
Tom Moor ee57f1ccf5 fix 2021-01-29 23:59:48 -08:00
Tom Moor 32f0589190 chore: Upgrade flow (#1854)
* wip: upgrade flow

* chore: More sealed props improvements

* Final fixes
2021-01-29 21:36:09 -08:00
Tom Moor ce2b246e60 fix: auth.config request should only be made on Login screen (#1852) 2021-01-29 17:54:28 -08:00
Tom Moor ae13347d55 chore: Add flow support for M1 macs 2021-01-28 23:25:37 -08:00
Tom Moor 13205171d7 chore: Improve dev efficient on M1 Mac 2021-01-28 21:01:53 -08:00
Tom Moor a912ea24f6 chore: Remove references to window.Sentry 2021-01-27 22:56:40 -08:00
Tom Moor 6fa760688b fix: Adds support for VirtualHost style AWS S3 buckets (#1847)
* Bump aws-sdk

* support virtual host buckets

* fix

* fix: VirtualHost bucket without explicit AWS_S3_FORCE_PATH_STYLE=false
2021-01-27 07:46:43 -08:00
Tom Moor 2825df29de Merge branch 'develop' of github.com:outline/outline into develop 2021-01-25 20:48:42 -08:00
Translate-O-Tron 604e97a6ce New Crowdin updates (#1835)
* fix: New French translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]

* fix: New German translations from Crowdin [ci skip]

* fix: New Japanese translations from Crowdin [ci skip]

* fix: New Portuguese translations from Crowdin [ci skip]

* fix: New Russian translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]

* fix: New Portuguese, Brazilian translations from Crowdin [ci skip]

* fix: New Korean translations from Crowdin [ci skip]

* fix: New Thai translations from Crowdin [ci skip]

* fix: New Italian translations from Crowdin [ci skip]

* fix: New Italian translations from Crowdin [ci skip]

* fix: New Japanese translations from Crowdin [ci skip]

* fix: New Japanese translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]

* fix: New French translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]

* fix: New German translations from Crowdin [ci skip]

* fix: New Japanese translations from Crowdin [ci skip]

* fix: New Portuguese translations from Crowdin [ci skip]

* fix: New Russian translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]

* fix: New Portuguese, Brazilian translations from Crowdin [ci skip]

* fix: New Korean translations from Crowdin [ci skip]

* fix: New Thai translations from Crowdin [ci skip]

* fix: New Italian translations from Crowdin [ci skip]

* fix: New French translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]

* fix: New German translations from Crowdin [ci skip]

* fix: New Japanese translations from Crowdin [ci skip]

* fix: New Portuguese translations from Crowdin [ci skip]

* fix: New Russian translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]

* fix: New Portuguese, Brazilian translations from Crowdin [ci skip]

* fix: New Korean translations from Crowdin [ci skip]

* fix: New Thai translations from Crowdin [ci skip]

* fix: New Italian translations from Crowdin [ci skip]

* fix: New French translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]

* fix: New German translations from Crowdin [ci skip]

* fix: New Japanese translations from Crowdin [ci skip]

* fix: New Portuguese translations from Crowdin [ci skip]

* fix: New Russian translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]

* fix: New Portuguese, Brazilian translations from Crowdin [ci skip]

* fix: New Korean translations from Crowdin [ci skip]

* fix: New Thai translations from Crowdin [ci skip]

* fix: New Italian translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]
2021-01-25 20:48:18 -08:00
milesstoetzner dc1bc44c8f feat: Google Drawings Integration (#1814)
* add google drawings integration

* add google drawings image

* update google drawings image and regex

* allow query parameter in google drawings regex

* support CDN for google drawings image
2021-01-25 20:47:23 -08:00
Tom Moor 2a55e78722 fix: Improve some editor alignment 2021-01-25 20:36:20 -08:00
Tom Moor eaf8dc5a3c fix: Text highlight of link in dark mode is impossible to read
closes #1838
2021-01-24 22:47:27 -08:00
Tom Moor f89d5adc37 fix: Ellipisis left in translation string 2021-01-24 12:09:44 -08:00
Tom Moor 978a123122 fix: 16 linting warnings 2021-01-23 10:19:08 -08:00
Tom Moor 96e65f495e chore: Remove custom VisuallyHidden component 2021-01-23 09:47:02 -08:00
Tom Moor 4106f15450 fix: Content jump when leaving edit mode 2021-01-22 23:58:34 -08:00
Tom Moor b3cd78c833 chore: Enable parameterized route profiling 2021-01-22 23:02:12 -08:00
Tom Moor 7b87fea4f4 Merge branch 'develop' of github.com:outline/outline into develop 2021-01-22 21:16:37 -08:00
Tom Moor 7e9bcb0c37 fix: More missing a11y labels 2021-01-22 21:12:25 -08:00
Tom Moor f6370ccf6d chore: Sentry performance monitoring (#1841)
* Hook up performance monitoring

* lint
2021-01-22 20:42:45 -08:00
Tom Moor 11e1108f4a fix: Unneccessary ev.preventDefault 2021-01-22 20:40:26 -08:00
Tom Moor c9fdf48c33 chore: Add missing labels to buttons without text and search inputs 2021-01-22 19:31:30 -08:00
Tom Moor 6a206de6cd chore: Add meta description 2021-01-22 19:12:39 -08:00
Tom Moor c69b393776 fix: JS error when submitting invites from sidebar-triggered modal 2021-01-22 08:57:52 -08:00
Tom Moor 6e9c456147 isMetaKey -> isModKey 2021-01-21 07:28:10 -08:00
Tom Moor 70626ffff0 feat: Organize sidebar (#1834)
* chore: Flip chinese label in language select

* feat: Add settings to sidebar, organize secondary items to bottom
2021-01-21 07:22:20 -08:00
Translate-O-Tron 993aad004e fix: New Korean translations from Crowdin [ci skip] (#1833) 2021-01-21 07:21:23 -08:00
Tom Moor 6fa9e700c8 chore: Flip chinese label in language select 2021-01-20 23:20:06 -08:00
Tom Moor 836b2e310a chore: Missing translation hooks in settings sidebar 2021-01-20 23:13:51 -08:00
Tom Moor 24ecaa8ce4 chore: Reduce default menu width 2021-01-20 23:07:48 -08:00
Tom Moor 40491fafe9 fix: Document star/unstar from list item navigates to document (regression) 2021-01-20 23:07:39 -08:00
Tom Moor 111212b038 feat: Resizable sidebar (#1827)
* wip: First round on sidebar resizing

* feat: Saving setting, animation

* all requirements, refactoring needed

* lint

* refactor useResize

* some mobile improvements

* fix

* refactor
2021-01-20 23:00:14 -08:00
Translate-O-Tron 774c3534d8 fix: New Chinese Simplified translations from Crowdin [ci skip] (#1832) 2021-01-20 22:23:30 -08:00
Malek Hijazi 9759227d73 fix: upgrade command (#1830)
I tested this on the server. Running yarn upgrade will result in yarn self updating. To solve this issue we need to run yarn run upgrade.
2021-01-20 22:19:44 -08:00
Tom Moor f608872c11 chore: Add Chinese and Italian translations 2021-01-20 22:09:36 -08:00
Translate-O-Tron eff9544ef9 New Crowdin updates (#1810)
* fix: New Chinese Simplified translations from Crowdin [ci skip]

* fix: New Thai translations from Crowdin [ci skip]

* fix: New Italian translations from Crowdin [ci skip]
2021-01-20 21:36:03 -08:00
Tom Moor 22fb464b87 lint 2021-01-18 16:11:48 -08:00
Tom Moor 3bace8c9e4 fix: Restore DNS prefetching for static resources (#1820)
* fix: Restore DNS prefetching for static resources

* fix: CDN paths
feat: preload instead of prefetch for key bundles

* csp

* fix: Turns out prefetch-src is still behind a flag in Chrome, not publicly available yet
2021-01-18 15:48:46 -08:00
Tom Moor 27fca28450 fix: Account for rehydrated old users before language
closes #1819
2021-01-17 22:19:54 -08:00
Tom Moor afcce7a0ef fix: Add missing width/height tags to img 2021-01-17 21:49:51 -08:00
Tom Moor f33495dddc fix: Editor mod shortcuts not working on Windows
closes #1745
2021-01-17 18:32:32 -08:00
Tom Moor 51b75fa09d 0.52.0 2021-01-16 14:12:35 -08:00
Tom Moor 522df125aa feat: Add CDN support (#1817)
* chore: CSP

* chore: Optionally use CDN for serving images
2021-01-16 11:12:10 -08:00
Tom Moor 1fd2ec31fd fix: Heading positioning changing between edit/read-only
fix: List items beyond #9 chopped
2021-01-15 08:50:19 -08:00
Tom Moor 1af00a0b3d test 2021-01-14 20:15:46 -08:00
Tom Moor ab40545a01 lint 2021-01-14 20:11:04 -08:00
Tom Moor c8d305aeca fix: Unintended scroll reset when switching between view / edit (#1807)
* fix: Don't remount document when switching between edit/read-only
fix: Button vertical alignment when using as=Link

* fix: Bump RME, fixes issue with image behavior changing between read-only/edit without editor remount

* fix: Heading anchor positioning
2021-01-14 19:50:10 -08:00
Maximilian Zinke 68a65be135 feat: Embed Google Drive (#1804)
* implement google drive extension

* add current logo of google drive

* fix issue when posting gdrive links which are already a preview

* always only show the preview

* Add bottom bar to get to original url for Google Drive embeds

Co-authored-by: Tom Moor <tom.moor@gmail.com>
2021-01-14 19:49:56 -08:00
Tom Moor b4d307b3b4 fix: Confusing breadcrumb collapsing 2021-01-14 19:36:31 -08:00
Tom Moor 03cb6d66e7 fix: Alignment of collection icon in header when collection name is very long 2021-01-14 18:59:43 -08:00
Tom Moor 7b8cbc50d5 fix: Document meta unclickable when first item in a document is a heading 2021-01-14 18:51:58 -08:00
Tom Moor f501da9c0f flow 2021-01-14 18:38:02 -08:00
Translate-O-Tron 74a762a7c7 New Crowdin updates (#1790) 2021-01-14 09:08:38 -08:00
Rubén Moya 93ac9892d5 fix: take into account user lang in Time component (#1793)
This PR takes into account the user selected language to format the time in the Time component.

Co-authored-by: tommoor <tom.moor@gmail.com>
2021-01-14 09:08:14 -08:00
Tom Moor e8b7782f5e fix: Keyboard accessible context menus (#1768)
- Makes menus fully accessible and keyboard driven
- Currently adds 2.8% to initial bundle size due to the inclusion of Reakit and its dependency, popperjs.
- Converts all menus to functional components
- Remove old custom menu system
- Various layout and flow improvements around the menus

closes #1766
2021-01-13 22:00:25 -08:00
Rubén Moya 47369dd968 chore: rename collection creatorId to createdById (#1794) 2021-01-11 23:17:31 -08:00
Tom Moor d258082c5f lint 2021-01-11 18:25:21 -08:00
Tom Moor c0bbae50c4 fix: Search results not updated when changing filters 2021-01-11 00:50:44 -08:00
Tom Moor ac082e4a5f Merge branch 'develop' of github.com:outline/outline into develop 2021-01-11 00:47:48 -08:00
Tom Moor 7504d43452 fix: Add indicator of starred status when viewing a document (#1785)
* fix: Add indicator of starred status when viewing a document
closes #461

* fix: Account for shared document
2021-01-10 23:13:58 -08:00
Tom Moor 5dba68dfd3 fix: Incorrect border color on table of contents in dark mode 2021-01-07 23:50:28 -08:00
Tom Moor 4b85603f30 chore: Text highlight blue -> yellow 2021-01-07 23:25:14 -08:00
Tom Moor 34598b317d fix: Deleting a collection should not deleted archived documents within it automatially (#1776)
closes #1775
2021-01-07 19:46:12 -08:00
Translate-O-Tron cbfa25fa2f New Crowdin updates (#1736)
* fix: New Japanese translations from Crowdin [ci skip]

* fix: New Japanese translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]

* fix: New French translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]

* fix: New German translations from Crowdin [ci skip]

* fix: New Japanese translations from Crowdin [ci skip]

* fix: New Portuguese translations from Crowdin [ci skip]

* fix: New Russian translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]

* fix: New Portuguese, Brazilian translations from Crowdin [ci skip]

* fix: New Korean translations from Crowdin [ci skip]

* fix: New French translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]

* fix: New German translations from Crowdin [ci skip]

* fix: New Japanese translations from Crowdin [ci skip]

* fix: New Portuguese translations from Crowdin [ci skip]

* fix: New Russian translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]

* fix: New Portuguese, Brazilian translations from Crowdin [ci skip]

* fix: New Korean translations from Crowdin [ci skip]

* fix: New Russian translations from Crowdin [ci skip]

* fix: New Russian translations from Crowdin [ci skip]

* fix: New French translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]

* fix: New German translations from Crowdin [ci skip]

* fix: New Japanese translations from Crowdin [ci skip]

* fix: New Portuguese translations from Crowdin [ci skip]

* fix: New Russian translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]

* fix: New Portuguese, Brazilian translations from Crowdin [ci skip]

* fix: New Korean translations from Crowdin [ci skip]

* fix: New French translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]

* fix: New German translations from Crowdin [ci skip]

* fix: New Japanese translations from Crowdin [ci skip]

* fix: New Portuguese translations from Crowdin [ci skip]

* fix: New Russian translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]

* fix: New Portuguese, Brazilian translations from Crowdin [ci skip]

* fix: New Korean translations from Crowdin [ci skip]

* fix: New French translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]

* fix: New German translations from Crowdin [ci skip]

* fix: New Japanese translations from Crowdin [ci skip]

* fix: New Portuguese translations from Crowdin [ci skip]

* fix: New Russian translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]

* fix: New Portuguese, Brazilian translations from Crowdin [ci skip]

* fix: New Korean translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]
2021-01-07 08:09:44 -08:00
Tom Moor 67a2246e1a fix: Attempting to restore document in deleted collection without a collectionId override results in server error (#1777)
closes #1767
2021-01-07 08:09:19 -08:00
Tom Moor de7bf8c133 fix: Fixes padding on sidebar collection links
closes #1770
2021-01-06 20:35:02 -08:00
Tom Moor 4fda50f4ad feat: Add 'archive' option in delete confirmation modal (#1764)
* feat: Add 'archive' option in delete confirmation modal
chore: Add translation tags to delete confirmation

* i18n

* language
2021-01-03 11:04:09 -08:00
Tom Moor f4c5cc054e chore: Update sorting icons 2021-01-03 09:38:29 -08:00
Tom Moor f799758a6f feat: Allow Google sign-in users to choose account
Alternative to https://github.com/outline/outline/pull/1763
2021-01-03 08:54:47 -08:00
Tom Moor 9df02d6fd4 chore: Improve toasts 2021-01-02 21:47:02 -08:00
Tom Moor bb81aa0065 fix: Improve toast messages to not show multiple of the same 2021-01-02 21:09:43 -08:00
Tom Moor 68bbd9fa34 fix: Hold hover state on DocumentListItem while DocumentMenu is open 2021-01-02 20:02:57 -08:00
Tom Moor 308d4bd797 i18n 2021-01-02 19:19:45 -08:00
Tom Moor 5329474c85 fix: Developer warning batchingForReactDom 2021-01-02 19:13:11 -08:00
Tom Moor d90af48741 fix: Outer error boundary generates more errors as it doesnt have access to store and theme providers 2021-01-02 19:12:51 -08:00
Tom Moor 611e9b97b3 chore: Move collection sort control (#1762)
* feat: Move collection sort menu

* fix: Improve accessibility

* fix: Dedupe outline-icons (temporary until rme is next merged)
2021-01-02 19:11:13 -08:00
Nan Yu eda5adca2c feat: adds hover to expand functionality on sidebar (#1761)
* feat: adds hover to expand functionality on sidebar

* clear hover intent timeout on drag leave
2021-01-02 17:20:13 -08:00
Tom Moor f0b361158e flow 2021-01-02 09:09:06 -08:00
Tom Moor f8ab793053 fix: 'New' badge should never show to document creator, regardless of whether a view has been logged (#1758) 2020-12-31 16:46:33 -08:00
Nan Yu 2cc45187e6 feat: reordering documents in collection (#1722)
* tweaking effect details

* wrap work on this feature

* adds correct color to drop cursor

* simplify logic for early return

* much better comment so Tom doesn't fire me

* feat: Allow changing sort order of collections

* refactor: Move validation to model
feat: Make custom order the default (in prep for dnd)

* feat: Add sort choice to edit collection modal
fix: Improved styling of generic InputSelect

* fix: Vertical space left after removing previous collection description

* chore: Tweak language, menu contents, add auto-disclosure on sub menus

* only show drop-to-reorder cursor when sort is set to manual

Co-authored-by: Tom Moor <tom.moor@gmail.com>
2020-12-31 12:51:12 -08:00
Tom Moor ba61091c4c fix: Allow soft deletion of teams (#1754)
* fix: Allow soft deletion of teams

* test: regression specs
2020-12-30 09:40:23 -08:00
Tom Moor 8dba32b5e0 fix: Meta key shortcuts not bound correctly in Windows browsers (#1753) 2020-12-30 09:35:33 -08:00
Clifton Cunningham 40bd9aed0a fix: miro - use the incoming domain to ensure access to logged in boards works (#1756) 2020-12-30 09:35:18 -08:00
Tom Moor d4bb04e921 fix: Handle linked documents destroyed when document is published
closes #1739
2020-12-29 10:32:09 -08:00
Nan Yu 8a3a279c0e Merge branch 'develop' of github.com:outline/outline into develop 2020-12-28 21:35:37 -08:00
Nan Yu 37f2cc8d55 closes #1752 2020-12-28 21:35:13 -08:00
Gustavo Maronato 89903b4bbe feat: Compress avatar images before upload (#1751)
* compress avatar images before upload

* move compressImage to dedicated file

* Update ImageUpload.js
2020-12-28 21:08:10 -08:00
Tom Moor 41be18e938 test 2020-12-28 20:43:27 -08:00
Tom Moor caee7afde2 refactor: documents.batchImport -> collections.import 2020-12-28 18:51:12 -08:00
Tom Moor d79933887d fix: Don't trigger email and slack notifications when mass importing
feat: Show success message after import
2020-12-28 18:02:58 -08:00
Tom Moor 2787e56de3 test: Add additional tests and input validation 2020-12-28 15:30:01 -08:00
Tom Moor b932457fd3 fix: Improve single collection export compatability 2020-12-28 10:07:38 -08:00
Tom Moor ea5d2ea9e0 refactor, add preview 2020-12-27 23:00:26 -08:00
Tom Moor 6e9b4e8363 lint 2020-12-27 12:54:58 -08:00
Tom Moor 012e6b320e feat: Allow document metadata to be stored in zip comment 2020-12-27 12:36:06 -08:00
Tom Moor c8cd7fcf4a fix: API response 2020-12-26 23:12:22 -08:00
Tom Moor 7021c2a9e5 Hook up API 2020-12-26 17:53:56 -08:00
Tom Moor 799e639439 Merge branch 'feat/import-export' into feat/mass-import 2020-12-25 18:05:02 -08:00
Tom Moor ba2552f69f fix 2020-12-25 18:04:38 -08:00
Malek Hijazi b6ab816bb3 feat: command to upgrade outline (#1727)
* Add upgrade script to package.json

* Update the docs to include docker and yarn guides
2020-12-25 15:23:55 -08:00
Tom Moor ac1120914a fix: Unable to delete archived and templated documents (#1749)
closes #1746
2020-12-24 13:28:08 -08:00
Tom Moor a51af98d43 refactor 2020-12-24 10:18:53 -08:00
Tom Moor ad7400a4f5 Merge branch 'develop' of github.com:outline/outline into feat/mass-import 2020-12-22 20:43:58 -08:00
Tom Moor ea57cef89c fix: Reduce double reporting of errors 2020-12-21 21:10:25 -08:00
Tom Moor 087ccdd825 stash 2020-12-21 21:03:11 -08:00
Translate-O-Tron 7d44e1aeeb New Crowdin updates (#1725)
* fix: New Japanese translations from Crowdin [ci skip]

* fix: New Japanese translations from Crowdin [ci skip]

* fix: New Japanese translations from Crowdin [ci skip]

* fix: New Japanese translations from Crowdin [ci skip]

* fix: New French translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]

* fix: New German translations from Crowdin [ci skip]

* fix: New Japanese translations from Crowdin [ci skip]

* fix: New Portuguese translations from Crowdin [ci skip]

* fix: New Russian translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]

* fix: New Portuguese, Brazilian translations from Crowdin [ci skip]

* fix: New Korean translations from Crowdin [ci skip]

* fix: New Japanese translations from Crowdin [ci skip]

* fix: New Japanese translations from Crowdin [ci skip]

* fix: New German translations from Crowdin [ci skip]

* fix: New Japanese translations from Crowdin [ci skip]

* fix: New Portuguese translations from Crowdin [ci skip]

* fix: New Portuguese, Brazilian translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]

* fix: New Japanese translations from Crowdin [ci skip]

* fix: New Japanese translations from Crowdin [ci skip]

* fix: New Portuguese, Brazilian translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]
2020-12-21 19:28:41 -08:00
Tom Moor 25d5ad8a7e chore: Enable automatic generation of email server in non production environments (#1731) 2020-12-21 19:27:14 -08:00
dependabot[bot] e34ba1457e chore(deps): bump node-notifier from 8.0.0 to 8.0.1 (#1734)
Bumps [node-notifier](https://github.com/mikaelbr/node-notifier) from 8.0.0 to 8.0.1.
- [Release notes](https://github.com/mikaelbr/node-notifier/releases)
- [Changelog](https://github.com/mikaelbr/node-notifier/blob/v8.0.1/CHANGELOG.md)
- [Commits](https://github.com/mikaelbr/node-notifier/compare/v8.0.0...v8.0.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-21 19:26:12 -08:00
Tom Moor e966eb8c9a fix: Error notice not displayed to user when exceeding rate limit on signin attempt 2020-12-20 13:05:16 -08:00
Tom Moor 4684b3a3f3 fix: Server error when invalid JSON passed to API endpoint
Fix is to ensure that the errorHandling middleware is mounted before the body parser so that it can catch and return an error response
2020-12-20 12:08:47 -08:00
Tom Moor 47ce8afcc5 fix: Server Error when requesting invalid locale 2020-12-20 11:53:09 -08:00
Tom Moor decbe4f643 fix: Allow deleting attachments not linked to documents when owned by user
closes #1729
2020-12-20 11:39:09 -08:00
Tom Moor 938f6ba8c5 wip 2020-12-19 23:23:37 -08:00
Tom Moor 7f5a7d7df7 Merge branch 'develop' of github.com:outline/outline into feat/mass-import 2020-12-19 16:01:10 -08:00
Tom Moor 117d278d16 fix: Deprecated Buffer usage, closes #1726 2020-12-19 15:58:21 -08:00
Tom Moor 40ca73e684 feat: Collapsible sidebar (#1721)
* wip

* styling, add keyboard shortcut

* tweak styling
2020-12-17 22:26:04 -08:00
Tom Moor b98e4bb1ff stash 2020-12-17 21:19:31 -08:00
Tom Moor 5012104a10 refactor 2020-12-16 21:39:37 -08:00
Nan Yu 051ecab0fc feat: Moving documents via drag and drop in sidebar (#1717)
* wip: added some basic drag and drop UI for combining items

* refactor: pathToDocument to accept only id

* fix: Multiple drop backends error
fix: Incorrect styling dragging over active collection
fix: Stay in disabled state until save is complete

* Improving display while moving doc

* fix: update by user should be changed when moving a doc

* add move guard to drag

Co-authored-by: Tom Moor <tom.moor@gmail.com>
2020-12-15 19:07:29 -08:00
Tom Moor 3469b82beb feat: Add Korean as available language choice 2020-12-15 08:11:50 -08:00
Tom Moor f2c3481670 test 2020-12-14 23:04:39 -08:00
Tom Moor bc141dc40c New Crowdin updates (#1718)
* fix: New French translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]

* fix: New German translations from Crowdin [ci skip]

* fix: New Japanese translations from Crowdin [ci skip]

* fix: New Portuguese translations from Crowdin [ci skip]

* fix: New Russian translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]

* fix: New Portuguese, Brazilian translations from Crowdin [ci skip]

* fix: New Korean translations from Crowdin [ci skip]

* fix: New French translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]

* fix: New German translations from Crowdin [ci skip]

* fix: New Korean translations from Crowdin [ci skip]

* fix: New Korean translations from Crowdin [ci skip]

* fix: New Korean translations from Crowdin [ci skip]

* fix: New Korean translations from Crowdin [ci skip]

* fix: New Korean translations from Crowdin [ci skip]

* fix: New Korean translations from Crowdin [ci skip]

* fix: New Korean translations from Crowdin [ci skip]

* fix: New Korean translations from Crowdin [ci skip]

* fix: New French translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]

* fix: New German translations from Crowdin [ci skip]

* fix: New Japanese translations from Crowdin [ci skip]

* fix: New Portuguese translations from Crowdin [ci skip]

* fix: New Russian translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]

* fix: New Portuguese, Brazilian translations from Crowdin [ci skip]

* fix: New Korean translations from Crowdin [ci skip]
2020-12-14 22:28:47 -08:00
Translate-O-Tron 99814f6e2f fix: New Korean translations from Crowdin [ci skip] 2020-12-14 22:19:57 -08:00
Translate-O-Tron 3737f0b42c fix: New Portuguese translations from Crowdin [ci skip] 2020-12-14 22:19:53 -08:00
Translate-O-Tron 9ef27cb436 fix: New German translations from Crowdin [ci skip] 2020-12-14 22:19:50 -08:00
Translate-O-Tron c9fa3f93f2 fix: New Spanish translations from Crowdin [ci skip] 2020-12-14 22:19:48 -08:00
Translate-O-Tron 24ed96c9a5 fix: New French translations from Crowdin [ci skip] 2020-12-14 22:19:46 -08:00
Translate-O-Tron 956cf401bd fix: New Korean translations from Crowdin [ci skip] 2020-12-14 21:17:46 -08:00
Translate-O-Tron 7cb837f478 fix: New Portuguese, Brazilian translations from Crowdin [ci skip] 2020-12-14 21:17:44 -08:00
Translate-O-Tron a3209e9d23 fix: New Chinese Simplified translations from Crowdin [ci skip] 2020-12-14 21:17:43 -08:00
Translate-O-Tron 29582a1bb1 fix: New Russian translations from Crowdin [ci skip] 2020-12-14 21:17:41 -08:00
Translate-O-Tron 4084c91769 fix: New Portuguese translations from Crowdin [ci skip] 2020-12-14 21:17:39 -08:00
Translate-O-Tron 6772f28226 fix: New Japanese translations from Crowdin [ci skip] 2020-12-14 21:17:37 -08:00
Translate-O-Tron c6b110d339 fix: New German translations from Crowdin [ci skip] 2020-12-14 21:17:35 -08:00
Translate-O-Tron 0a43b50c66 fix: New Spanish translations from Crowdin [ci skip] 2020-12-14 21:17:33 -08:00
Translate-O-Tron 4a82cb0658 fix: New French translations from Crowdin [ci skip] 2020-12-14 21:17:31 -08:00
Tom Moor 2f7fca6106 chore: Move formatting out of translation strings 2020-12-14 21:16:02 -08:00
Translate-O-Tron 8f83cfef25 fix: New Korean translations from Crowdin [ci skip] 2020-12-14 20:39:04 -08:00
Tom Moor e2e66954b5 fix: Attachments should not always be deleted with their original document (#1715)
* fix: Attachments should not be deleted when their original document is deleted when referenced elsewhere

* fix: Attachments deleted prematurely when docs are placed in trash

* mock

* restore hook, cascading delete was the issue
2020-12-14 19:55:22 -08:00
Tom Moor 3dbe54ac1e fix: Bump RME, closes #1719 2020-12-14 19:18:46 -08:00
Translate-O-Tron 50577f6f2f fix: New Korean translations from Crowdin [ci skip] 2020-12-14 09:40:18 -08:00
Translate-O-Tron 16d504703d fix: New Korean translations from Crowdin [ci skip] 2020-12-14 08:42:26 -08:00
Translate-O-Tron 173febcaa1 fix: New Korean translations from Crowdin [ci skip] 2020-12-14 07:40:07 -08:00
Translate-O-Tron f92f4cde7a fix: New Korean translations from Crowdin [ci skip] 2020-12-14 06:45:01 -08:00
Translate-O-Tron 23bec75bd0 fix: New Korean translations from Crowdin [ci skip] 2020-12-14 05:49:49 -08:00
Translate-O-Tron 4dd667f68b fix: New Korean translations from Crowdin [ci skip] 2020-12-14 04:47:25 -08:00
Translate-O-Tron 4b3cb77cc7 fix: New Korean translations from Crowdin [ci skip] 2020-12-14 03:48:01 -08:00
Translate-O-Tron 0e83d54f93 fix: New German translations from Crowdin [ci skip] 2020-12-13 23:30:53 -08:00
Translate-O-Tron d867d9fea5 fix: New Spanish translations from Crowdin [ci skip] 2020-12-13 23:30:51 -08:00
Translate-O-Tron 28c8b8acfe fix: New French translations from Crowdin [ci skip] 2020-12-13 23:30:49 -08:00
Translate-O-Tron 51efffe2ce fix: New Korean translations from Crowdin [ci skip] 2020-12-13 22:34:18 -08:00
Tom Moor 4e9ee7249f Update LICENSE 2020-12-13 17:48:15 -08:00
Tom Moor 574fcc4bb3 0.51.0 2020-12-13 17:43:58 -08:00
Tom Moor 5c3000d5cf Bump RME, fixes table after list and image captions in Safari 2020-12-13 17:20:38 -08:00
Translate-O-Tron c0216cbb8d fix: New Portuguese, Brazilian translations from Crowdin [ci skip] 2020-12-12 22:40:33 -08:00
Translate-O-Tron cf12301077 fix: New Chinese Simplified translations from Crowdin [ci skip] 2020-12-12 22:40:31 -08:00
Translate-O-Tron 1eb7da8742 fix: New Russian translations from Crowdin [ci skip] 2020-12-12 22:40:29 -08:00
Translate-O-Tron b3c548382f fix: New Portuguese translations from Crowdin [ci skip] 2020-12-12 22:40:27 -08:00
Translate-O-Tron 7ebac53b43 fix: New Japanese translations from Crowdin [ci skip] 2020-12-12 22:40:25 -08:00
Translate-O-Tron 64428a6894 fix: New German translations from Crowdin [ci skip] 2020-12-12 22:40:23 -08:00
Translate-O-Tron d536af5269 fix: New Spanish translations from Crowdin [ci skip] 2020-12-12 22:40:22 -08:00
Translate-O-Tron 1726a88a60 fix: New French translations from Crowdin [ci skip] 2020-12-12 22:40:20 -08:00
Tom Moor 3fe807a10a fix: Object printed in UI 2020-12-12 22:29:20 -08:00
Tom Moor 72189e041b feat: attachments.delete (#1714)
* feat: Add endpoint for manually deleting attachments

* mock
2020-12-10 21:40:03 -08:00
Translate-O-Tron bc156f4cc8 New Crowdin updates (#1707)
* fix: New German translations from Crowdin [ci skip]

* fix: New German translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]

* fix: New Portuguese, Brazilian translations from Crowdin [ci skip]

* fix: New French translations from Crowdin [ci skip]

* fix: New Portuguese, Brazilian translations from Crowdin [ci skip]
2020-12-10 19:03:01 -08:00
dependabot[bot] 26693c60df chore(deps): bump ini from 1.3.5 to 1.3.7 (#1713)
Bumps [ini](https://github.com/isaacs/ini) from 1.3.5 to 1.3.7.
- [Release notes](https://github.com/isaacs/ini/releases)
- [Commits](https://github.com/isaacs/ini/compare/v1.3.5...v1.3.7)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-10 18:43:50 -08:00
Nan Yu 9e1f31e14c fix: dropzone error on image upload component (#1711) 2020-12-08 19:32:41 -08:00
Nan Yu 63d926e196 slightly nicer color definitions (#1705) 2020-12-07 08:56:07 -08:00
Reid Beels 3f9f1f0bed docs: Add note to .env.sample about Google OAuth URI (#1706) 2020-12-07 08:55:37 -08:00
Tom Moor b2bdc7f1d4 chore: Add user and auth context to server side error reports (#1693) 2020-12-06 17:59:44 -08:00
Translate-O-Tron 2e798c698d chore: New Crowdin updates (#1691) 2020-12-06 17:54:16 -08:00
Nan Yu aa59f5fe09 chore: React-Dropzone version bump (#1699)
* update dropzone to new version

* remove global styles import

* change bg on active item on drag as well

* add back background
2020-12-06 17:50:59 -08:00
Tom Moor ac2060b166 fix: Migrate attachment columns to incease available length (#1704)
closes #1703
2020-12-06 16:51:25 -08:00
Tom Moor 424c29536d chore: Bump RME (SQL language support) 2020-12-04 10:18:30 -08:00
Tom Moor 6c1ecde4e7 fix: Server error when attempting to update team with identical details to previous 2020-12-04 10:18:30 -08:00
Tom Moor aa6fc45097 Add localization status to README 2020-12-04 08:22:43 -08:00
Tom Moor 9478944718 fix: Account for non-recorded views, closes #1700 2020-12-02 20:50:54 -08:00
Tom Moor 9e1c5d1db3 fix: JS error in UserProfile introduced in refactoring to functional component 2020-12-02 20:48:24 -08:00
Nan Yu 474fbf07e6 chore: Flatten left nav in preparation to refactor drag to reorder (#1689)
* flatten hierarchy
* fix drop to import positioning on collections
2020-12-01 21:59:18 -08:00
Tom Moor fe62048890 fix: One source of transaction deadlock when invites > pg pool (#1696)
* fix: One source of transaction deadlock when invites > pg pool

* lint
2020-12-01 19:20:20 -08:00
Tom Moor 1851477290 fix: Disabling public sharing should disable all existing share links
Issue came through customer support
2020-11-30 23:39:23 -08:00
Tom Moor bde6f4b3c4 fix: Don't make request to record view for deleted document 2020-11-30 23:17:39 -08:00
Saumya Pandey 283b479689 chore: Change response of shares.info response for unshared document (#1666)
* Update server/api/share.js to send 204 status for unshared documents.

* Update shares.info endpoint to expect 204 in a few test.

* Update SharesStore and ApiClient to handle 204 status code
2020-11-30 22:49:15 -08:00
Tom Moor 183f06c2d1 fix: Policies not added to store from all fetch requests
closes #1688
2020-11-30 22:38:11 -08:00
Tom Moor 21fff8d172 fix: ui store spread onto DropToImport 2020-11-30 21:46:55 -08:00
Tom Moor 18e56aff65 fix: Editable title in sidebar impossible to see in dark mode 2020-11-30 21:45:48 -08:00
Tom Moor a97523a652 chore: Upgrade pg (performance improvements) 2020-11-30 21:31:13 -08:00
Tom Moor 2316512a19 Update crowdin.yml 2020-11-30 19:09:41 -08:00
Tom Moor 1285efc49a feat: I18n (#1653)
* feat: i18n

* Changing language single source of truth from TEAM to USER

* Changes according to @tommoor comments on PR

* Changed package.json for build:i18n and translation label

* Finished 1st MVP of i18n for outline

* new translation labels & Portuguese from Portugal translation

* Fixes from PR request

* Described language dropdown as an experimental feature

* Set keySeparator to false in order to cowork with html keys

* Added useTranslation to Breadcrumb

* Repositioned <strong> element

* Removed extra space from TemplatesMenu

* Fortified the test suite for i18n

* Fixed trans component problematic

* Check if selected language is available

* Update yarn.lock

* Removed unused Trans

* Removing debug variable from i18n init

* Removed debug variable

* test: update snapshots

* flow: Remove decorator usage to get proper flow typing
It's a shame, but hopefully we'll move to Typescript in the next 6 months and we can forget this whole Flow mistake ever happened

* translate: Drafts

* More translatable strings

* Mo translation strings

* translation: Search

* async translations loading

* cache translations in client

* Revert "cache translations in client"

This reverts commit 08fb61ce36.

* Revert localStorage cache for cache headers

* Update Crowdin configuration file

* Moved translation files to locales folder and fixed english text

* Added CONTRIBUTING File for CrowdIn

* chore: Move translations again to please CrowdIn

* fix: loading paths
chore: Add strings for editor

* fix: Improve validation on documents.import endpoint

* test: mock bull

* fix: Unknown mimetype should fallback to Markdown parsing if markdown extension (#1678)

* closes #1675

* Update CONTRIBUTING

* chore: Add link to translation portal from app UI

* refactor: Centralize language config

* fix: Ensure creation of i18n directory in build

* feat: Add language prompt

* chore: Improve contributing guidelines, add link from README

* chore: Normalize tab header casing

* chore: More string externalization

* fix: Language prompt in dark mode

Co-authored-by: André Glatzl <andreglatzl@gmail.com>
2020-11-29 20:04:58 -08:00
Tom Moor 63c73c9a51 Create auto_assign.yml 2020-11-27 09:51:52 -08:00
Tom Moor 1b7fe0f7da flow 2020-11-27 09:48:10 -08:00
Tom Moor 6eda1cc0d3 fix: Unknown mimetype should fallback to Markdown parsing if markdown extension (#1678)
closes #1675
2020-11-26 21:16:56 -08:00
Tom Moor ac349b40f5 test: mock bull 2020-11-26 21:11:35 -08:00
Tom Moor 8bddc1b338 fix: Improve validation on documents.import endpoint 2020-11-26 20:29:45 -08:00
Tom Moor 56d5f048f9 fix: Group membership count off after suspending users in groups (#1670)
* fix: Clear group memberships when suspending a user
Refactor to command

* test

* test
2020-11-22 11:21:47 -08:00
Tom Moor 273d9c4680 fix: Correctly map CMD+Shift+P to publish in title
fixes #1655
2020-11-21 22:20:59 -08:00
Tom Moor 44ca447185 fix: Scrollbar styling
closes #1665
2020-11-21 16:16:38 -08:00
Tom Moor 6b511e4251 Bump rich-markdown-editor, 2 minor fixes 2020-11-21 15:34:03 -08:00
Saumya Pandey de6ee91d96 fix: Prevent API request for views data for deleted documents (#1663)
* Prevent API request for views data for deleted documents

Added a conditional statement to check if the document.deletedAt is falsy before making a request to views.list.

* Update app/components/Collaborators.js to use isDeleted attribute.

Co-authored-by: Tom Moor <tom.moor@gmail.com>
2020-11-18 19:09:08 -08:00
Tom Moor 18fac781a9 Update LICENSE 2020-11-14 23:10:16 -08:00
Tom Moor 1ce01fa936 0.50.0 2020-11-14 23:07:41 -08:00
Tom Moor 12a2e1c387 chore: Menu templates (#1644)
* chore: Menu template system

* NewTemplateMenu

* UserMenu

* MenuItemsTemplate -> DropdownMenuItems

* support nested menus

* DocumentMenu

* BreadcrumbMenu

* isInvited
2020-11-14 20:44:31 -08:00
Tom Moor 19ab32f551 chore: Add additional missing events (#1639)
* chore: Add additional missing events
signed in
profile updated
team setting updated

* Minor refactor to DRY existing code

* Add events

* lint

* flow: Add missing ip to event types
2020-11-14 20:44:18 -08:00
Tom Moor 7bdcba46b8 fix: Virtualize move document listbox (#1650)
* fix: Virtualize move document listbox
closes #1645
2020-11-12 22:02:56 -08:00
Tom Moor 32d3053002 fix: Heading anchor position changed _again_ 2020-11-12 22:02:19 -08:00
Tom Moor 85dd54db3d fix: Consistent spacing when first item in document is a header 2020-11-12 21:51:20 -08:00
Tom Moor 5d0dd9b734 chore: Bump RME (various editor fixes) 2020-11-12 21:47:44 -08:00
Tom Moor 735d5cbf26 fix: Ignore safely ignored loop error
closes #1646
2020-11-11 17:05:05 -08:00
Tom Moor a94012a03a chore: Drop and create test database automatically between runs
docs: Update README to specify using make to run tests
2020-11-11 16:58:45 -08:00
Tom Moor 80d74c9334 fix: Clicking the share link from a context menu propagates the click onto the container (#1643)
closes #1642
2020-11-11 09:02:02 -08:00
Tom Moor 26e6db1afd fix: Update document policy to disable sharing for read-only users (#1638)
* fix: Update document policy to disable sharing for read-only users

* test: Update test for new permission logic
2020-11-10 20:35:41 -08:00
Tom Moor 5e7bbdc111 feat: Sync sessions across browser tabs (#1641)
* feat: Sync sessions across browser tabs

If the user signs in or out in one tab, then make sure their session on the same subdomain is kept in sync on other tabs
2020-11-10 19:43:14 -08:00
Tom Moor 71e9860f88 flow: Correctly type Theme 2020-11-09 00:01:50 -08:00
Tom Moor 26f4901547 fix: 'Not found' page should obey theme
closes #1626
2020-11-08 23:52:00 -08:00
Tom Moor 4c56ed40f1 feat: Hide Outline branding on custom domain share links
closes #1629
2020-11-08 23:01:57 -08:00
Tom Moor 4e7a1cd121 feat: add filters to drafts (#1631)
* feat: add filters to drafts

Co-authored-by: Guilherme Diniz <guilhermedassumpcao@gmail.com>
2020-11-08 22:33:52 -08:00
Tom Moor 14e0ed8108 fix: Various React warnings 2020-11-08 20:28:27 -08:00
Tom Moor 0372ff2727 fix: Unneccessary scrollbar 2020-11-08 20:04:46 -08:00
Tom Moor 02e7e75cb9 fix: Remove non-standardized css (console warning) 2020-11-08 20:01:47 -08:00
Tom Moor af73de4128 feat: Show table of contents when useful on publicly shared docs
closes #1625
2020-11-08 19:50:00 -08:00
Tom Moor 0125a5361d fix: Document published notification potentially sent to users without permission to view document 2020-11-05 19:49:05 -08:00
Nonpawit Teerachetmongkol fdaa36c9fd fix: gist not support username contains numbers (#1622) 2020-11-05 09:03:16 -08:00
Tom Moor 1b6a986986 chore: Refactor authentication pass between subdomains (#1619)
* fix: Use get request instead of cookie to transfer token between domains

* Add domain to database
Add redirects to team domain when present

* 30s -> 1m

* fix: Avoid redirect loop if subdomain and domain set

* fix: Create a transfer specific token to prevent replay requests

* refactor: Move isCustomDomain out of shared as it won't work on the client
2020-11-04 19:54:04 -08:00
Tom Moor 3d09c8f655 chore: Refactor backlinks and revisions (#1611)
* Update backlinks service to not rely on revisions

* fix: Add missing index for finding backlinks

* Debounce revision creation (#1616)

* refactor debounce logic to service

* Debounce slack notification

* Revisions created by service

* fix: Revision sidebar latest

* test: Add tests for notifications
2020-11-01 10:26:39 -08:00
Tom Moor 7735aa12d7 GSuite -> Google Workspace 2020-10-27 22:14:51 -07:00
Tom Moor 7ac724909d fix: Cannot duplicate document with title close to max title length
closes #1610
2020-10-27 19:15:35 -07:00
Tom Moor abd3a1ee12 Update LICENSE 2020-10-26 23:26:43 -07:00
Tom Moor 9bfc0cacae 0.49.0 2020-10-26 23:25:50 -07:00
Tom Moor bd80e8384a chore: Remove events log from settings 2020-10-26 19:32:48 -07:00
Tom Moor bfdfa3ee4b feat: Debounce notification emails by 5 minutes to avoid duplicate notifications where possible (#1598) 2020-10-25 15:06:07 -07:00
Tom Moor dba5dd14e7 fix: Put public and private uploads in separate folders to allow for restrictive AWS policies
closes #1581
2020-10-21 21:00:40 -07:00
Tom Moor a9c05adc3c fix: Account for multiple images on the same line of text when converting images for public share
closes #1602
2020-10-21 20:22:23 -07:00
Tom Moor 34bdc88003 fix: Offset heading styles 2020-10-21 09:04:21 -07:00
Tom Moor df7b9f3e88 feat: Add support for "word" files exported from Confluence (#1600)
* Display error message to end user

* fix: Improve conversion of tables

* fix: Characters at ends of lines in tables lost
2020-10-21 08:53:59 -07:00
Tom Moor b78e2f1e05 fix: Match search requests from Slack using Integration for non-Slack teams (#1599)
* Match slack hook requests to integration
2020-10-21 08:53:38 -07:00
Tom Moor 15337b5bdf fix: Improve error handling when redis connection is lost 2020-10-20 07:41:17 -07:00
Tom Moor 4103f53f2a fix: Offset headers when scrolling from TOC to account for fixed header
closes #1578
2020-10-19 23:07:37 -07:00
Tom Moor 758fcc1759 feat: Show unique views rather than total views in document meta. (#1559)
* unique-views

* fix: 'only you' displays briefly when visiting a document previously only viewed by one other
2020-10-19 22:44:28 -07:00
Debajyoti Halder b3549637fe fix: Dark mode inline code styling fixed #1593 (#1597)
background changed to props.theme.codeBackground
2020-10-19 22:14:14 -07:00
Tom Moor 7bee60a337 fix: Clicking link with blank href should not open new tab 2020-10-19 08:00:52 -07:00
Tom Moor 38a005ed8a lint 2020-10-19 07:48:51 -07:00
Tom Moor 71b7ef1186 fix: Websockets cannot connect in Safari 2020-10-19 07:43:03 -07:00
Tom Moor 3af1a80615 fix: Incorrect cursor on headings 2020-10-19 07:40:12 -07:00
Tom Moor 4044818daa fix: Archived documents should not be returned from documents.list
closes #1575
2020-10-13 20:38:28 -07:00
Tom Moor 428171a1ec closes #1587 – remove global queue events when completed 2020-10-13 20:30:37 -07:00
Tom Moor 9c3195ef25 chore(deps): Bump RME
Fixes unable to upload the same image multiple times
2020-10-13 20:13:51 -07:00
Yohann Leon a6dc708fc0 fix: spotify widget wrong size (#1579) 2020-10-09 19:02:09 -07:00
Tom Moor 9c8f125668 chore: No need to build:server on CI 2020-09-30 20:29:54 -07:00
Tom Moor f348db048e chore: Specify babelrc for server specifically (#1585) 2020-09-30 20:02:33 -07:00
Tom Moor 0b8eb326ab chore(deps): Bump RME
closes #1566
2020-09-30 19:40:00 -07:00
Tom Moor 1da1f3d6e8 chore: Update Zapier integration (no longer invite-only)
fix: Remove link to Zapier integration from self-hosted as it doesnt make sense
2020-09-28 21:08:58 -07:00
Tom Moor 7def0dfab1 chore(deps): Upgrade socket.io-redis dependency for fix related to recent downtime
https://github.com/socketio/socket.io-redis/issues/210
2020-09-28 18:51:49 -07:00
Tom Moor 96987d2091 0.48.1 2020-09-27 10:32:15 -07:00
Tom Moor 6bb32c253b fix: Unable to run migrations in latest image. Added option to run non-SSL migration in production 2020-09-27 10:32:08 -07:00
Tom Moor 1d5f735032 Update LICENSE 2020-09-26 17:19:03 -07:00
Tom Moor f3e3651222 0.48.0 2020-09-26 17:18:34 -07:00
Tom Moor b3d9478486 Merge pull request #1563 from outline/release-0.48.0
release: v48.0.0
2020-09-26 17:09:40 -07:00
Tom Moor f26aeca46a chore(migrations): Add missing indexes 2020-09-26 14:01:04 -07:00
Tom Moor f1a95e5e79 feat: Improved search results when finding links in document editor (#1573)
* feat: Improved search results when finding links in document editor

* chore(deps): Bump RME for smoother link search
2020-09-26 11:07:49 -07:00
Tom Moor 6f1f855083 feat: Add tracking of search source in UI 2020-09-24 21:56:37 -07:00
Tom Moor 40d52e9a78 fix: Cannot press down arrow to navigate via keyboard to search results
(due to withRouter converting DocumentPreview to a functional component)
2020-09-24 21:36:31 -07:00
Tom Moor a2f2971fec fix: Reduce liklihood of false search queries
fix: Reduce possibility of dupe search queries
feat: Allow 'Enter' to trigger search before debounce
2020-09-24 21:29:00 -07:00
Tom Moor d89808ce9d fix: Home link on 'Not Found' page 2020-09-24 19:38:19 -07:00
Tom Moor a43cc9c5a9 chore(deps): Upgrade RME for improved doc search results 2020-09-24 19:35:28 -07:00
Tom Moor bb7fcd1b67 feat: Allow embedding /share/ routes in iframes 2020-09-23 19:26:18 -07:00
Tom Moor c1957025ec fix: Dont dupe record search queries when paging results
feat: Record queries via api tokens separately
2020-09-21 23:31:10 -07:00
Renan Filipe 98626ebbaf feat: Record search queries (#1554)
* Record search queries

* feat: add totalCount to the search response

* feat: add comments to explain why we use setTimeout
2020-09-21 23:05:42 -07:00
Tom Moor 0fa8a6ed2e feat: Add ctx.state.authType for tracking (#1567) 2020-09-21 22:02:37 -07:00
Tom Moor fa96891c8e fix(editor): Upgrade RME, improved link pasting 2020-09-21 20:33:58 -07:00
Tom Moor 9aa81dcf82 fix: Error deleting account as only admin not displayed to user 2020-09-21 20:31:06 -07:00
Tom Moor c04d5bdfb0 flow 2020-09-21 19:47:17 -07:00
Tom Moor ea69d09562 fix: Guard usage of localStorage 2020-09-21 19:43:51 -07:00
Tom Moor c8ff5cf221 fix: Styling of 'New' badge in dark mode
fix: less than a min ago -> just now
2020-09-21 19:35:51 -07:00
Tom Moor 5638f7a687 Merge develop 2020-09-21 19:18:34 -07:00
Tom Moor 1293f52552 lint 2020-09-21 19:18:09 -07:00
Tom Moor 86812cfe76 fix(editor): Upgrade RME, fixes cursor navigation around headings in FF 2020-09-21 19:11:54 -07:00
Tom Moor d9b7384853 fix: Improve handling of invalid file type passed to documents.import API endpoint 2020-09-21 00:34:13 -07:00
Tom Moor 292afd774d fix: CMD+Enter in title should leave editing mode 2020-09-21 00:21:35 -07:00
Tom Moor d3d286b1be tweak search highlight 2020-09-21 00:07:32 -07:00
Tom Moor 26b9566b96 fix: Various fixes for unread tracking 2020-09-20 23:37:09 -07:00
Guilherme DIniz d487da8f15 feat: Visually differentiate unread documents (#1507)
* feat: Visually differentiate unread documents

* feat: add document treatment in document preview

* fix requested changes

Co-authored-by: Tom Moor <tom.moor@gmail.com>
2020-09-20 22:32:28 -07:00
Tom Moor 4ffc04bc5d fix: Allow selection of embeds (#1562)
* feat: Support importing .docx or .html files as new documents (#1551)

* Support importing .docx as new documents

* Add html file support, build types and interface for easily adding file types to importer

* fix: Upload embedded images in docx to storage

* refactor: Bulk of logic to command

* refactor: Do all importing on server, so we're not splitting logic for import into two places

* test: Add documentImporter tests


Co-authored-by: Lance Whatley <whatl3y@gmail.com>

* fix: Accessibility audit

* fix: Quick fix, non editable title
closes #1560

* fix: Embed selection

Co-authored-by: Lance Whatley <whatl3y@gmail.com>
2020-09-20 22:27:11 -07:00
Tom Moor 68148bd4d8 fix: Quick fix, non editable title
closes #1560
2020-09-18 08:36:57 -07:00
Tom Moor 881105992e fix: Accessibility audit 2020-09-16 22:31:14 -07:00
Tom Moor 2c1a111dee feat: Support importing .docx or .html files as new documents (#1551)
* Support importing .docx as new documents

* Add html file support, build types and interface for easily adding file types to importer

* fix: Upload embedded images in docx to storage

* refactor: Bulk of logic to command

* refactor: Do all importing on server, so we're not splitting logic for import into two places

* test: Add documentImporter tests


Co-authored-by: Lance Whatley <whatl3y@gmail.com>
2020-09-16 21:54:33 -07:00
Tom Moor e67d319e2b fix: Update DocumentMetaWithViews to hooks, correctly observe store changes
closes #1555
2020-09-16 21:15:21 -07:00
Tom Moor 85f7e03921 test: No hotreload in test env 2020-09-16 08:22:50 -07:00
Tom Moor e30adbaac2 fix: Flip production/development NODE_ENV logic
closes #1548
2020-09-16 00:13:12 -07:00
Tom Moor ac8f0ebaac feat: Allow Google Embeds from regular (non publish-to-web) links (#1533)
Improve styling to allow getting back to source document
2020-09-15 18:38:42 -07:00
Tom Moor b3b71d2dc7 feat: Editable titles in sidebar (#1544)
* feat/editable-titles

* feat: Double click to edit titles in the sidebar

* Take into account policies

* fix: Title update on another client

* Improved styling
2020-09-15 18:01:40 -07:00
Tom Moor ab3613af48 fix: Path to onboarding markdown files (changed with updated build process) 2020-09-15 12:20:27 -07:00
Tom Moor 3940f1a108 Update README.md 2020-09-14 19:29:55 -07:00
Tom Moor 142e7da6a5 fix: Placeholder style 2020-09-13 20:26:38 -07:00
Tom Moor 021de66f7a fix: Document titles look faded in Safari 2020-09-13 20:18:50 -07:00
Nan Yu 55858d5d7d fix: put guard around sentry init (#1547) 2020-09-13 19:54:08 -07:00
Tom Moor 93d3582ac7 fix: Dead pointer zone over links when hover card is showing 2020-09-13 19:49:25 -07:00
Tom Moor 0b2107c1ee Merge develop 2020-09-13 10:50:13 -07:00
Tom Moor f8a167fd4b Merge branch 'develop' of github.com:outline/outline into develop 2020-09-13 10:47:31 -07:00
Tom Moor 608be3deef refactor: Remove babel/register for instant production server startup (#1481)
* refactor: Remove babel/register for instant production server startup

* fix: Update procfile location

* fix: package.json must be copied, not linked for production build

* fix: Production file paths

* fix: Public assets path

* Remove unused scripts
2020-09-13 10:46:33 -07:00
Tom Moor 56551d1ab3 fix: Error in backlinks service when updating an old document (pre v1) 2020-09-13 01:03:39 -07:00
Tom Moor 450d6b7e42 fix: Focus accessibility (#1536)
* fix: Focus ring appearing on click
fix: Focus ring not appearing on sidebar links
add: focus-visible polyfill

* fix: More visible outlines on keyboard focus
fix: Header block should be a button semantically
2020-09-12 23:27:23 -07:00
Matheus Rocha Vieira fc98cf78e6 chore: Upgrade Sentry to Latest Version (#1541)
* Upgrade Sentry to Latest Version
2020-09-12 23:24:32 -07:00
Nan Yu d9aa53a094 feat: allow searching for urls of internal documents (#1529)
* core search logic

* bump version of rich markdown editor

* let shift and meta modifiers do their thing when clicking on a link in a doc

* version bump editor

* test: Add parseDocumentSlug test

Co-authored-by: Tom Moor <tom.moor@gmail.com>
2020-09-12 23:23:40 -07:00
Nan Yu ffab4fbf76 remove -1 bottom from navigation links 2020-09-12 11:24:58 -07:00
Tom Moor 2161fba1dd Update LICENSE 2020-09-11 12:28:10 -07:00
Tom Moor cd2cdd025c 0.47.1 2020-09-11 12:26:13 -07:00
Tom Moor d5f5319f80 fix: History sidebar header shrinking when lots of history 2020-09-11 12:10:40 -07:00
Tom Moor c298c73240 fix: Revision skipped after identical previous autosave 2020-09-11 12:06:57 -07:00
Tom Moor 38d1831259 fix: Send events for draft documents down user channel always 2020-09-11 11:19:30 -07:00
Tom Moor 9c9b95741c fix: Unable to edit collection description
closes #1523
2020-09-09 23:23:01 -07:00
Tom Moor cc8db7e991 fix: Save team logo automatically
closes #1521
2020-09-08 21:12:37 -07:00
Tom Moor c5b7d9be13 fix: Flash of sidebar when navigating between documents 2020-09-08 20:48:52 -07:00
Tom Moor be2e46b5d2 References -> Referenced by
Less ambiguous as to the backlink direction
2020-09-08 18:44:19 -07:00
Tom Moor 4b2a766357 fix: Missing toast message when successfully copying code 2020-09-08 07:31:03 -07:00
Tom Moor f264d67862 fix: Two API endpoints where requesting a permenantly deleted document results in server error 2020-09-08 07:29:06 -07:00
Tom Moor b1648ac2aa Update LICENSE 2020-09-07 20:25:16 -07:00
Tom Moor 25423d8c85 0.47.0 2020-09-07 20:24:40 -07:00
Nan Yu e7ab2939d4 fix: Improved handling of delete events from collection and document sockets (#1517)
* handle delete events fron collection and document sockets

* handle collection deletes to documents

* rework semantics to always reload after a delete

Co-authored-by: Tom Moor <tom.moor@gmail.com>
2020-09-07 19:05:10 -07:00
Tom Moor ceeac9b982 fix: Deeply nested document titles not updated in collection documentStructure cache
closes #1508
2020-09-07 13:41:17 -07:00
Tom Moor 5de2f969e3 fix: Preload membership 2020-09-07 12:17:04 -07:00
Tom Moor b54901d50c fix: CMD+Eneter in title should still publish 2020-09-07 12:06:03 -07:00
Tom Moor 4de3f69474 fix: Documents in deleted collection should appear in trash (#1362)
* fix: Documents from deleted collection should show in trash

* improve messaging

* test: Add documents.move tests
feat: Add ability to restore trashed documents from deleted collection

* update store

* fix

* ui

* lint

* fix: Improve breadcrumb
2020-09-07 11:51:09 -07:00
Tom Moor c5de2da115 Merge branch 'develop' of github.com:outline/outline into develop 2020-09-07 10:43:36 -07:00
Tom Moor 709c3e78bd fix: Occasional render loop in editor toolbar (#1518)
* fix: CMD+S should save when editor title is focused

* fix: Bump RME, fixes various small editor issues
2020-09-07 10:42:51 -07:00
Tom Moor acb04fdf1a fix: CMD+S should save when editor title is focused 2020-09-06 10:33:39 -07:00
Tom Moor f13696dd2a Update yarn.lock 2020-09-05 19:58:17 -07:00
Tom Moor d6f245d67e Bump RME: Fixes inline styles in table cells with newlines 2020-09-05 19:51:12 -07:00
Tom Moor f2abf38fe4 perf: Remove source once compiled 2020-09-05 12:57:27 -07:00
Tom Moor f0712e22d8 perf: Improving dockerfile 2020-09-05 12:44:40 -07:00
Tom Moor e7e289d9fa end 2020-09-04 23:28:29 -07:00
Tom Moor 713187cfb4 fix 2020-09-04 15:39:36 -07:00
Tom Moor 11d3a5c9b9 fix: Enter in document title should create a newline at the top of editor and focus
closes #1511
2020-09-04 13:23:33 -07:00
Matheus Rocha Vieira cf1e506009 feat: Add ClickUp Embed Service (#1465)
* Add Clickup Embed Service

* Transparency Icon


Co-authored-by: Tom Moor <tom.moor@gmail.com>
2020-09-04 13:21:27 -07:00
Tom Moor 6b6d67beb6 fix: Allow disabling SSL for Postgres connection with standard PGSSLMODE env
closes #1501
2020-09-04 12:51:48 -07:00
Tom Moor a98e8ad8df fix: Breadcrumb overflow color is inaccessible
closes #1505
2020-09-04 12:49:20 -07:00
Tom Moor 9049785d98 fix: e shortcut to edit doesn't work when title is selected
closes #1510
2020-09-04 12:42:41 -07:00
Tom Moor e8648d4611 fix: Error in shares.info endpoint when requesting share record for deleted document 2020-09-03 23:22:41 -07:00
Tom Moor dd7436f78c fix: async error in backlinks service when dealing with a deleted document 2020-09-03 22:44:42 -07:00
Tom Moor b93a397ab3 feat: Bump RME 2020-09-03 22:27:30 -07:00
Tom Moor 206160582e chore: Tweak ordering of unpublish menu item 2020-09-02 20:19:36 -07:00
Tom Moor 4bf5926ee3 Bump RME 2020-08-31 21:57:26 -07:00
Tom Moor 82433e02a0 feat: Add custom error state for chunk loading failed 2020-08-31 21:09:23 -07:00
Tom Moor 637a9b5cf9 refactor: Remove unused event handlers 2020-08-31 20:44:19 -07:00
Tom Moor 95b91c466a fix: Disallow creating nested document within document in trash 2020-08-31 20:32:08 -07:00
Tom Moor 4edf90a184 fix: Development warning, missing key on event list items 2020-08-31 20:29:55 -07:00
Tom Moor 31522b0d6f fix: Local shares state not cleared when document is deleted 2020-08-31 20:28:41 -07:00
Tom Moor 759d4a5ac2 fix: Handle error revoking share link on frontend 2020-08-31 20:24:05 -07:00
Tom Moor dd0d51dd9d test 2020-08-31 20:23:51 -07:00
Tom Moor 8550116c6b fix: shares.list should not return shares for deleted documents
fix: shares.info should not return info for revoked shares
closes #1492
2020-08-31 20:15:10 -07:00
Tom Moor 3c7dc93982 Merge branch 'develop' of github.com:outline/outline into develop 2020-08-31 20:06:44 -07:00
Guilherme DIniz 0aa338cccc feat: Allow unpublishing documents (#1467)
* Allow unpublishing documents

* add block unpublish files that has children

* add api tests to new route
2020-08-31 20:03:05 -07:00
Tom Moor 8f41895e66 Merge develop 2020-08-31 19:40:41 -07:00
Tom Moor de8ac4acf5 fix: Configure mobx-react-lite observer batching
Removes development warning
2020-08-31 18:42:12 -07:00
Tom Moor de59147418 chore: Upgrade Sentry to 5.22.3
closes #1498
2020-08-31 18:36:30 -07:00
Tom Moor cf522cc85f fix: Regression with TOC not showing when navigating directly to document (#1500)
fix: Editing document too wide when TOC visible in read only
2020-08-31 18:31:13 -07:00
Tom Moor 8c7200fa87 chore: yarn deduplicate 2020-08-30 19:44:30 -07:00
Tom Moor f2310be173 Updated Yarn lockfile 2020-08-29 12:11:12 -07:00
Tom Moor 29f4dc9331 Bump RME
Fixes #1107 - It's now possible to use line breaks in table cells with Shift+Enter
Fixes #1253 - Selected content can now be dragged to reorder
2020-08-29 12:00:55 -07:00
Tom Moor 03b6dd62a8 fix: Missing click action to change permissions on a collection
fix: Modals no longer stacking correctly since upgrading react-portal
2020-08-25 21:00:50 -07:00
Tom Moor 7f0c608dbb Merge branch 'guilherme-diniz-feature/document-history-header' into develop 2020-08-25 20:04:02 -07:00
Tom Moor c52fbb944e Styling tidy up 2020-08-25 20:03:52 -07:00
Tom Moor e22e952606 Merge branch 'feature/document-history-header' of git://github.com/guilherme-diniz/outline into guilherme-diniz-feature/document-history-header 2020-08-25 19:44:56 -07:00
Guilherme Diniz 197cdff6c3 fix layout issues 2020-08-25 17:22:13 -03:00
Tom Moor 85d09b2351 fix: Deleting a document should correctly show who deleted (#1488) 2020-08-25 08:51:12 -07:00
Tom Moor 69611638b9 fix: Redirect to parent document when deleting a child document if possible (#1489) 2020-08-25 08:45:04 -07:00
Tom Moor e117d5f103 fix: Unable to view all possible locations when moving document (#1490)
* fix: Remove limit of displayed results on Move dialog

* fix: Filter templates from results

* Show final document location on hover/active, reduces visual noise
2020-08-25 08:44:46 -07:00
Tom Moor 03db975217 Merge branch 'feature/document-history-header' of git://github.com/guilherme-diniz/outline into guilherme-diniz-feature/document-history-header 2020-08-24 23:46:16 -07:00
Tom Moor 76279902f9 chore: Introduce AWS_S3_FORCE_PATH_STYLE option to maintain compatability with Minio et al (#1443)
- Make AWS_S3_UPLOAD_BUCKET_NAME optional
2020-08-24 23:27:10 -07:00
Tom Moor a304e91ffc Merge branch 'develop' into perf/issue-1464 2020-08-24 20:58:56 -07:00
Tom Moor 9b5573c5e2 0.46.1 2020-08-24 20:22:08 -07:00
Tom Moor ec61efa12b Remove unused scripts 2020-08-23 21:10:32 -07:00
Tom Moor b01778a39f fix: Public assets path 2020-08-23 20:44:44 -07:00
Tom Moor 5aa092853b fix: Production file paths 2020-08-23 20:35:59 -07:00
Tom Moor 1fa3db4bdc fix: package.json must be copied, not linked for production build 2020-08-23 20:29:17 -07:00
Tom Moor 6a9f74e6cc fix: Update procfile location 2020-08-23 19:21:43 -07:00
Tom Moor e8719340d1 refactor: Remove babel/register for instant production server startup 2020-08-23 19:10:16 -07:00
Tom Moor 70838918c3 fix: Collections not collapsing 2020-08-23 12:51:35 -07:00
Tom Moor ec38f5d79c refactor: Remove old react lifecycle methods (#1480)
* refactor: Remove deprecated APIs

* bump mobx-react for hooks support

* inject -> useStores
https://mobx-react.js.org/recipes-migration\#hooks-to-the-rescue

* chore: React rules of hooks lint
2020-08-23 11:51:56 -07:00
Jonathan Killian 179176c312 fix: Update package.json build script to use yarn instead of npm. (#1476)
* fix: Update package.json build script to yarn.

Update package.json build script to use yarn instead of npm to maintain consistency with the rest of scripts. I was running into an issue with the Dockerfile when using nvm with yarn and this fixed the issue.
2020-08-22 19:56:52 -07:00
Tom Moor c446a91f7d fix: Restore Postgres SSL support on Heroku
https://github.com/brianc/node-postgres/issues/2009
2020-08-22 08:27:42 -07:00
Guilherme Diniz 05f48f054b feat: Add Header to Document History Sidebar 2020-08-21 20:58:57 -03:00
Tom Moor ec55299c8b fix: Improve websocket reliability (#1470)
* check connection on page visibility change

* fix: SocketPresence account for socket changing
2020-08-20 20:37:54 -07:00
Tom Moor 26c574ab58 chore: Upgrade pg and sequelize to support node 14+ (#1462)
* Upgrade pg and sequelize to support node 14+

When Node 14 came out the app was incompatible, we should always have a maximum version defined here until the server code has been tested to prove compatibility

Co-authored-by: Lance Whatley <whatl3y@gmail.com>
2020-08-20 20:19:44 -07:00
Tom Moor 6dd6768f07 feat: Allow moving templates between collections (#1454)
- Allow template move in document policy
- fix: Ensure that document is not added to collection structure in documentMover command
- fix: Moving a template should now show nested documents as options
- fix: Hitting 'm' should not allow moving a draft
- fix: Styling of seperators on move screen
2020-08-20 19:46:29 -07:00
1545 changed files with 97227 additions and 75552 deletions
+15 -3
View File
@@ -1,12 +1,12 @@
{
"presets": [
"@babel/preset-react",
"@babel/preset-flow",
"@babel/preset-typescript",
[
"@babel/preset-env",
{
"corejs": {
"version": "2",
"version": "3",
"proposals": true
},
"useBuiltIns": "usage"
@@ -25,5 +25,17 @@
"@babel/plugin-transform-destructuring",
"@babel/plugin-transform-regenerator",
"transform-class-properties"
]
],
"env": {
"production": {
"plugins": [
[
"styled-components",
{
"displayName": false
}
]
]
}
}
}
+98 -6
View File
@@ -1,9 +1,17 @@
version: 2
version: 2.1
executors:
docker-publisher:
environment:
IMAGE_NAME: outlinewiki/outline
docker:
- image: circleci/buildpack-deps:stretch
jobs:
build:
working_directory: ~/outline
docker:
- image: circleci/node:12
- image: circleci/node:14
- image: circleci/redis:latest
- image: circleci/postgres:9.6.5-alpine-ram
environment:
@@ -33,11 +41,95 @@ jobs:
name: lint
command: yarn lint
- run:
name: flow
command: yarn flow check --max-workers 4
name: typescript
command: yarn tsc
- run:
name: test
command: yarn test
- run:
name: build
command: yarn build
name: build-webpack
command: yarn build:webpack
build-image:
executor: docker-publisher
steps:
- checkout
- setup_remote_docker:
version: 20.10.6
- run:
name: Build Docker image
command: docker build -t $IMAGE_NAME:latest .
- run:
name: Archive Docker image
command: docker save -o image.tar $IMAGE_NAME
- persist_to_workspace:
root: .
paths:
- ./image.tar
publish-latest:
executor: docker-publisher
steps:
- attach_workspace:
at: /tmp/workspace
- setup_remote_docker:
version: 20.10.6
- run:
name: Load archived Docker image
command: docker load -i /tmp/workspace/image.tar
- run:
name: Publish Docker Image to Docker Hub
command: |
echo "$DOCKERHUB_PASS" | docker login -u "$DOCKERHUB_USERNAME" --password-stdin
IMAGE_TAG=${CIRCLE_TAG/v/''}
docker tag $IMAGE_NAME:latest $IMAGE_NAME:$IMAGE_TAG
docker push $IMAGE_NAME:latest
docker push $IMAGE_NAME:$IMAGE_TAG
publish-tag:
executor: docker-publisher
steps:
- attach_workspace:
at: /tmp/workspace
- setup_remote_docker:
version: 20.10.6
- run:
name: Load archived Docker image
command: docker load -i /tmp/workspace/image.tar
- run:
name: Publish Docker Image to Docker Hub
command: |
echo "$DOCKERHUB_PASS" | docker login -u "$DOCKERHUB_USERNAME" --password-stdin
IMAGE_TAG=${CIRCLE_TAG/v/''}
docker tag $IMAGE_NAME:latest $IMAGE_NAME:$IMAGE_TAG
docker push $IMAGE_NAME:$IMAGE_TAG
workflows:
version: 2
build-and-test:
jobs:
- build:
filters:
tags:
ignore: /^v.*/
build-docker:
jobs:
- build-image:
filters:
tags:
only: /^v.*/
branches:
ignore: /.*/
- publish-latest:
requires:
- build-image
filters:
tags:
only: /^v\d+\.\d+\.\d+$/
branches:
ignore: /.*/
- publish-tag:
requires:
- build-image
filters:
tags:
only: /^v\d+\.\d+\.\d+-.*$/
branches:
ignore: /.*/
+17
View File
@@ -0,0 +1,17 @@
__mocks__
.git
.vscode
.github
.circleci
.DS_Store
.env*
.eslint*
.log
Makefile
Procfile
app.json
crowdin.yml
build
docker-compose.yml
fakes3
node_modules
+134 -32
View File
@@ -1,61 +1,163 @@
# Copy this file to .env, remove this comment and change the keys. For development
# with docker this should mostly work out of the box other than setting the Slack
# keys (for auth) and the SECRET_KEY.
#
# Please use `openssl rand -hex 32` to create SECRET_KEY
# –––––––––––––––– REQUIRED ––––––––––––––––
# Generate a hex-encoded 32-byte random key. You should use `openssl rand -hex 32`
# in your terminal to generate a random value.
SECRET_KEY=generate_a_new_key
# Generate a unique random key. The format is not important but you could still use
# `openssl rand -hex 32` in your terminal to produce this.
UTILS_SECRET=generate_a_new_key
# For production point these at your databases, in development the default
# should work out of the box.
DATABASE_URL=postgres://user:pass@localhost:5532/outline
DATABASE_URL_TEST=postgres://user:pass@localhost:5532/outline-test
# Uncomment this to disable SSL for connecting to Postgres
# PGSSLMODE=disable
REDIS_URL=redis://localhost:6479
# URL should point to the fully qualified, publicly accessible URL. If using a
# proxy the port in URL and PORT may be different.
URL=http://localhost:3000
PORT=3000
# enforce (auto redirect to) https in production, (optional) default is true.
# set to false if your SSL is terminated at a loadbalancer, for example
FORCE_HTTPS=true
# See [documentation](docs/SERVICES.md) on running a separate collaboration
# server, for normal operation this does not need to be set.
COLLABORATION_URL=
ENABLE_UPDATES=true
DEBUG=cache,presenters,events
# To support uploading of images for avatars and document attachments an
# s3-compatible storage must be provided. AWS S3 is recommended for redundency
# however if you want to keep all file storage local an alternative such as
# minio (https://github.com/minio/minio) can be used.
# Third party signin credentials (at least one is required)
SLACK_KEY=get_a_key_from_slack
SLACK_SECRET=get_the_secret_of_above_key
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
# Comma separated list of domains to be allowed (optional)
# If not set, all Google apps domains are allowed by default
GOOGLE_ALLOWED_DOMAINS=
# Third party credentials (optional)
SLACK_VERIFICATION_TOKEN=PLxk6OlXXXXXVj3YYYY
SLACK_APP_ID=A0XXXXXXX
SLACK_MESSAGE_ACTIONS=true
GOOGLE_ANALYTICS_ID=
SENTRY_DSN=
# AWS credentials (optional in development)
# A more detailed guide on setting up S3 is available here:
# => https://wiki.generaloutline.com/share/125de1cc-9ff6-424b-8415-0d58c809a40f
#
AWS_ACCESS_KEY_ID=get_a_key_from_aws
AWS_SECRET_ACCESS_KEY=get_the_secret_of_above_key
AWS_REGION=xx-xxxx-x
AWS_S3_UPLOAD_BUCKET_URL=http://s3:4569
AWS_S3_UPLOAD_BUCKET_NAME=bucket_name_here
AWS_S3_UPLOAD_MAX_SIZE=26214400
# uploaded s3 objects permission level, default is private
# set to "public-read" to allow public access
AWS_S3_FORCE_PATH_STYLE=true
AWS_S3_ACL=private
# Emails configuration (optional)
# –––––––––––––– AUTHENTICATION ––––––––––––––
# Third party signin credentials, at least ONE OF EITHER Google, Slack,
# or Microsoft is required for a working installation or you'll have no sign-in
# options.
# To configure Slack auth, you'll need to create an Application at
# => https://api.slack.com/apps
#
# When configuring the Client ID, add a redirect URL under "OAuth & Permissions":
# https://<URL>/auth/slack.callback
SLACK_KEY=get_a_key_from_slack
SLACK_SECRET=get_the_secret_of_above_key
# To configure Google auth, you'll need to create an OAuth Client ID at
# => https://console.cloud.google.com/apis/credentials
#
# When configuring the Client ID, add an Authorized redirect URI:
# https://<URL>/auth/google.callback
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
# To configure Microsoft/Azure auth, you'll need to create an OAuth Client. See
# the guide for details on setting up your Azure App:
# => https://wiki.generaloutline.com/share/dfa77e56-d4d2-4b51-8ff8-84ea6608faa4
AZURE_CLIENT_ID=
AZURE_CLIENT_SECRET=
AZURE_RESOURCE_APP_ID=
# To configure generic OIDC auth, you'll need some kind of identity provider.
# See documentation for whichever IdP you use to acquire the following info:
# Redirect URI is https://<URL>/auth/oidc.callback
OIDC_CLIENT_ID=
OIDC_CLIENT_SECRET=
OIDC_AUTH_URI=
OIDC_TOKEN_URI=
OIDC_USERINFO_URI=
# Specify which claims to derive user information from
# Supports any valid JSON path with the JWT payload
OIDC_USERNAME_CLAIM=preferred_username
# Display name for OIDC authentication
OIDC_DISPLAY_NAME=OpenID
# Space separated auth scopes.
OIDC_SCOPES="openid profile email"
# –––––––––––––––– OPTIONAL ––––––––––––––––
# Base64 encoded private key and certificate for HTTPS termination. This is only
# required if you do not use an external reverse proxy. See documentation:
# https://wiki.generaloutline.com/share/1c922644-40d8-41fe-98f9-df2b67239d45
SSL_KEY=
SSL_CERT=
# If using a Cloudfront/Cloudflare distribution or similar it can be set below.
# This will cause paths to javascript, stylesheets, and images to be updated to
# the hostname defined in CDN_URL. In your CDN configuration the origin server
# should be set to the same as URL.
CDN_URL=
# Auto-redirect to https in production. The default is true but you may set to
# false if you can be sure that SSL is terminated at an external loadbalancer.
FORCE_HTTPS=true
# Have the installation check for updates by sending anonymized statistics to
# the maintainers
ENABLE_UPDATES=true
# How many processes should be spawned. As a reasonable rule divide your servers
# available memory by 512 for a rough estimate
WEB_CONCURRENCY=1
# Override the maxium size of document imports, could be required if you have
# especially large Word documents with embedded imagery
MAXIMUM_IMPORT_SIZE=5120000
# You can remove this line if your reverse proxy already logs incoming http
# requests and this ends up being duplicative
DEBUG=http
# Comma separated list of domains to be allowed to signin to the wiki. If not
# set, all domains are allowed by default when using Google OAuth to signin
ALLOWED_DOMAINS=
# For a complete Slack integration with search and posting to channels the
# following configs are also needed, some more details
# => https://wiki.generaloutline.com/share/be25efd1-b3ef-4450-b8e5-c4a4fc11e02a
#
SLACK_VERIFICATION_TOKEN=your_token
SLACK_APP_ID=A0XXXXXXX
SLACK_MESSAGE_ACTIONS=true
# Optionally enable google analytics to track pageviews in the knowledge base
GOOGLE_ANALYTICS_ID=
# Optionally enable Sentry (sentry.io) to track errors and performance
SENTRY_DSN=
# To support sending outgoing transactional emails such as "document updated" or
# "you've been invited" you'll need to provide authentication for an SMTP server
SMTP_HOST=
SMTP_PORT=
SMTP_USERNAME=
SMTP_PASSWORD=
SMTP_FROM_EMAIL=
SMTP_REPLY_EMAIL=
SMTP_TLS_CIPHERS=
SMTP_SECURE=true
# Custom logo that displays on the authentication screen, scaled to height: 60px
# TEAM_LOGO=https://example.com/images/logo.png
# The default interface language. See translate.getoutline.com for a list of
# available language codes and their rough percentage translated.
DEFAULT_LANGUAGE=en_US
+55 -44
View File
@@ -1,19 +1,46 @@
{
"parser": "babel-eslint",
"parser": "@typescript-eslint/parser",
"parserOptions": {
"sourceType": "module",
"extraFileExtensions": [".json"],
"ecmaFeatures": {
"jsx": true
}
},
"extends": [
"react-app",
"plugin:import/errors",
"plugin:import/warnings",
"plugin:flowtype/recommended"
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:import/recommended",
"plugin:import/typescript",
"plugin:react-hooks/recommended",
"plugin:prettier/recommended"
],
"plugins": [
"prettier",
"flowtype"
"@typescript-eslint",
"eslint-plugin-import",
"eslint-plugin-node",
"eslint-plugin-react",
"eslint-plugin-react-hooks",
"import"
],
"rules": {
"eqeqeq": 2,
"no-unused-vars": 2,
"curly": 2,
"no-mixed-operators": "off",
"no-useless-escape": "off",
"@typescript-eslint/no-unused-vars": [
"error",
{
"argsIgnorePattern": "^_",
"args": "after-used",
"ignoreRestSiblings": true
}
],
"padding-line-between-statements": ["error", { "blankLine": "always", "prev": "*", "next": "export" }],
"lines-between-class-members": ["error", "always", { "exceptAfterSingleLine": true }],
"import/no-named-as-default": "off",
"import/no-named-as-default-member": "off",
"import/newline-after-import": 2,
"import/order": [
"error",
{
@@ -22,53 +49,48 @@
},
"pathGroups": [
{
"pattern": "shared/**",
"pattern": "@shared/**",
"group": "external",
"position": "after"
},
{
"pattern": "stores",
"pattern": "@server/**",
"group": "external",
"position": "after"
},
{
"pattern": "stores/**",
"pattern": "~/stores",
"group": "external",
"position": "after"
},
{
"pattern": "models/**",
"pattern": "~/stores/**",
"group": "external",
"position": "after"
},
{
"pattern": "scenes/**",
"pattern": "~/models/**",
"group": "external",
"position": "after"
},
{
"pattern": "components/**",
"pattern": "~/scenes/**",
"group": "external",
"position": "after"
},
{
"pattern": "~/components/**",
"group": "external",
"position": "after"
},
{
"pattern": "~/**",
"group": "external",
"position": "after"
}
]
}
],
"flowtype/require-valid-file-annotation": [
2,
"always",
{
"annotationStyle": "line"
}
],
"flowtype/space-after-type-colon": [
2,
"always"
],
"flowtype/space-before-type-colon": [
2,
"never"
],
"prettier/prettier": [
"error",
{
@@ -83,22 +105,11 @@
"pragma": "React",
"version": "detect"
},
"import/resolver": {
"node": {
"paths": [
"app",
"."
]
}
"import/parsers": {
"@typescript-eslint/parser": [".ts", ".tsx"]
},
"flowtype": {
"onlyFilesWithFlowAnnotation": false
"import/resolver": {
"typescript": {}
}
},
"env": {
"jest": true
},
"globals": {
"EDITOR_VERSION": true
}
}
-37
View File
@@ -1,37 +0,0 @@
[include]
.*/app/.*
.*/server/.*
.*/shared/.*
[ignore]
.*/node_modules/tiny-cookie/flow/.*
.*/node_modules/styled-components/.*
.*/node_modules/polished/.*
.*/node_modules/mobx/.*.flow
.*/node_modules/react-side-effect/.*
.*/node_modules/fbjs/.*
.*/node_modules/config-chain/.*
.*/server/scripts/.*
*.test.js
[libs]
[options]
emoji=true
module.system.node.resolve_dirname=node_modules
module.system.node.resolve_dirname=app
module.name_mapper='^\(.*\)\.md$' -> 'empty/object'
module.name_mapper='^shared\/\(.*\)$' -> '<PROJECT_ROOT>/shared/\1'
module.file_ext=.js
module.file_ext=.md
module.file_ext=.json
esproposal.decorators=ignore
esproposal.class_static_fields=enable
esproposal.class_instance_fields=enable
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue
+3
View File
@@ -0,0 +1,3 @@
# These are supported funding model platforms
github: [outline]
+10
View File
@@ -0,0 +1,10 @@
# Set to true to add reviewers to pull requests
addReviewers: true
# A list of reviewers to be added to pull requests (GitHub user name)
reviewers:
- tommoor
# A list of keywords to be skipped the process that add reviewers if pull requests include it
skipKeywords:
- wip
+3 -2
View File
@@ -1,12 +1,13 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 60
daysUntilStale: 120
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
daysUntilClose: 14
# Issues with these labels will never be considered stale
exemptLabels:
- security
- pinned
# Label to use when marking an issue as stale
staleLabel: stale
+70
View File
@@ -0,0 +1,70 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ main ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ main ]
schedule:
- cron: '28 15 * * 2'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'javascript' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Learn more about CodeQL language support at https://git.io/codeql-language-support
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
+5 -1
View File
@@ -1,9 +1,13 @@
dist
build
node_modules/*
server/scripts
.env
.log
npm-debug.log
stats.json
.DS_Store
fakes3/*
.idea
*.pem
*.key
*.cert
+4 -3
View File
@@ -1,6 +1,7 @@
{
"javascript.validate.enable": false,
"typescript.validate.enable": false,
"javascript.validate.enable": true,
"javascript.format.enable": true,
"typescript.validate.enable": true,
"typescript.format.enable": true,
"editor.formatOnSave": true,
"typescript.format.enable": false
}
+44 -10
View File
@@ -1,17 +1,51 @@
FROM node:12-alpine
ENV PATH /opt/outline/node_modules/.bin:/opt/node_modules/.bin:$PATH
ENV NODE_PATH /opt/outline/node_modules:/opt/node_modules
ENV APP_PATH /opt/outline
RUN mkdir -p $APP_PATH
# syntax=docker/dockerfile:1.2
ARG APP_PATH=/opt/outline
FROM node:16-alpine AS deps-common
ARG APP_PATH
WORKDIR $APP_PATH
COPY . $APP_PATH
COPY ./package.json ./yarn.lock ./
RUN yarn install --pure-lockfile
# ---
FROM deps-common AS deps-dev
RUN yarn install --no-optional --frozen-lockfile && \
yarn cache clean
# ---
FROM deps-common AS deps-prod
RUN yarn install --production=true --frozen-lockfile && \
yarn cache clean
# ---
FROM node:16-alpine AS builder
ARG APP_PATH
WORKDIR $APP_PATH
COPY . .
COPY --from=deps-dev $APP_PATH/node_modules ./node_modules
ARG CDN_URL
RUN yarn build
RUN cp -r /opt/outline/node_modules /opt/node_modules
CMD yarn start
# ---
FROM node:16-alpine AS runner
ARG APP_PATH
WORKDIR $APP_PATH
ENV NODE_ENV production
COPY --from=builder $APP_PATH/build ./build
COPY --from=builder $APP_PATH/server ./server
COPY --from=builder $APP_PATH/public ./public
COPY --from=builder $APP_PATH/.sequelizerc ./.sequelizerc
COPY --from=deps-prod $APP_PATH/node_modules ./node_modules
COPY --from=builder $APP_PATH/package.json ./package.json
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001 && \
chown -R nodejs:nodejs $APP_PATH/build
USER nodejs
EXPOSE 3000
CMD ["yarn", "start"]
+2 -2
View File
@@ -3,7 +3,7 @@ Business Source License 1.1
Parameters
Licensor: General Outline, Inc.
Licensed Work: Outline 0.46.0
Licensed Work: Outline 0.60.1
The Licensed Work is (c) 2020 General Outline, Inc.
Additional Use Grant: You may make use of the Licensed Work, provided that
you may not use the Licensed Work for a Document
@@ -15,7 +15,7 @@ Additional Use Grant: You may make use of the Licensed Work, provided that
Licensed Work by creating teams and documents
controlled by such third parties.
Change Date: 2023-08-12
Change Date: 2025-11-11
Change License: Apache License, Version 2.0
+7 -1
View File
@@ -2,17 +2,23 @@ up:
docker-compose up -d redis postgres s3
yarn install --pure-lockfile
yarn sequelize db:migrate
yarn dev
yarn dev:watch
build:
docker-compose build --pull outline
test:
docker-compose up -d redis postgres s3
yarn sequelize db:drop --env=test
yarn sequelize db:create --env=test
yarn sequelize db:migrate --env=test
yarn test
watch:
docker-compose up -d redis postgres s3
yarn sequelize db:drop --env=test
yarn sequelize db:create --env=test
yarn sequelize db:migrate --env=test
yarn test:watch
destroy:
+2 -1
View File
@@ -1 +1,2 @@
web: node index.js
web: yarn start --services=web,websockets,collaboration
worker: yarn start --services=worker
+49 -115
View File
@@ -1,136 +1,57 @@
<p align="center">
<img src="https://user-images.githubusercontent.com/31465/34380645-bd67f474-eb0b-11e7-8d03-0151c1730654.png" height="29" />
</p>
<p align="center">
<i>An open, extensible, wiki for your team built using React and Node.js.<br/>Try out Outline using our hosted version at <a href="https://www.getoutline.com">www.getoutline.com</a>.</i>
<i>A fast, collaborative, knowledge base for your team built using React and Node.js.<br/>Try out Outline using our hosted version at <a href="https://www.getoutline.com">www.getoutline.com</a>.</i>
<br/>
<img src="https://user-images.githubusercontent.com/380914/78513257-153ae080-775f-11ea-9b49-1e1939451a3e.png" alt="Outline" width="800" />
<img width="1640" alt="screenshot" src="https://user-images.githubusercontent.com/380914/110356468-26374600-7fef-11eb-9f6a-f2cc2c8c6590.png">
</p>
<p align="center">
<a href="https://circleci.com/gh/outline/outline" rel="nofollow"><img src="https://circleci.com/gh/outline/outline.svg?style=shield&amp;circle-token=c0c4c2f39990e277385d5c1ae96169c409eb887a"></a>
<a href="https://github.com/prettier/prettier"><img src="https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat"></a>
<a href="https://github.com/styled-components/styled-components"><img src="https://img.shields.io/badge/style-%F0%9F%92%85%20styled--components-orange.svg"></a>
<a href="http://www.typescriptlang.org" rel="nofollow"><img src="https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg" alt="TypeScript"></a>
<a href="https://github.com/prettier/prettier"><img src="https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat" alt="Prettier"></a>
<a href="https://github.com/styled-components/styled-components"><img src="https://img.shields.io/badge/style-%F0%9F%92%85%20styled--components-orange.svg" alt="Styled Components"></a>
<a href="https://translate.getoutline.com/project/outline" alt="Localized"><img src="https://badges.crowdin.net/outline/localized.svg"></a>
</p>
This is the source code that runs [**Outline**](https://www.getoutline.com) and all the associated services. If you want to use Outline then you don't need to run this code, we offer a hosted version of the app at [getoutline.com](https://www.getoutline.com).
If you'd like to run your own copy of Outline or contribute to development then this is the place for you.
## Installation
# Installation
Outline requires the following dependencies:
Please see the [documentation](https://app.getoutline.com/share/770a97da-13e5-401e-9f8a-37949c19f97e/) for running your own copy of Outline in a production configuration.
- Node.js >= 12
- Postgres >=9.5
- Redis >= 4
- AWS S3 storage bucket for media and other attachments
- Slack or Google developer application for authentication
If you have questions or improvements for the docs please create a thread in [GitHub discussions](https://github.com/outline/outline/discussions).
# Development
### Development
There is a short guide for [setting up a development environment](https://app.getoutline.com/share/770a97da-13e5-401e-9f8a-37949c19f97e/doc/local-development-5hEhFRXow7) if you wish to contribute changes, fixes, and improvements to Outline.
In development you can quickly get an environment running using Docker by following these steps:
## Contributing
1. Install these dependencies if you don't already have them
1. [Docker for Desktop](https://www.docker.com)
1. [Node.js](https://nodejs.org/) (v12 LTS preferred)
1. [Yarn](https://yarnpkg.com)
1. Clone this repo
1. Register a Slack app at https://api.slack.com/apps
1. Copy the file `.env.sample` to `.env`
1. Fill out the following fields:
1. `SECRET_KEY` (follow instructions in the comments at the top of `.env`)
1. `SLACK_KEY` (this is called "Client ID" in Slack admin)
1. `SLACK_SECRET` (this is called "Client Secret" in Slack admin)
1. Configure your Slack app's Oauth & Permissions settings
1. Add `http://localhost:3000/auth/slack.callback` as an Oauth redirect URL
1. Ensure that the bot token scope contains at least `users:read`
1. Run `make up`. This will download dependencies, build and launch a development version of Outline
Outline is built and maintained by a small team we'd love your help to fix bugs and add features!
Before submitting a pull request _please_ discuss with the core team by creating or commenting in an issue on [GitHub](https://www.github.com/outline/outline/issues) we'd also love to hear from you in the [discussions](https://www.github.com/outline/outline/discussions). This way we can ensure that an approach is agreed on before code is written. This will result in a much higher liklihood of your code being accepted.
### Production
If youre looking for ways to get started, here's a list of ways to help us improve Outline:
For a self-hosted production installation there is more flexibility, but these are the suggested steps:
- [Translation](docs/TRANSLATION.md) into other languages
- Issues with [`good first issue`](https://github.com/outline/outline/labels/good%20first%20issue) label
- Performance improvements, both on server and frontend
- Developer happiness and documentation
- Bugs and other issues listed on GitHub
1. Clone this repo and install dependencies with `yarn` or `npm install`
## Architecture
> Requires [Node.js](https://nodejs.org/) and [yarn](https://yarnpkg.com) installed
If you're interested in contributing or learning more about the Outline codebase
please refer to the [architecture document](docs/ARCHITECTURE.md) first for a high level overview of how the application is put together.
1. Build the web app with `yarn build:webpack` or `npm run build:webpack`
1. Using the `.env.sample` as a reference, set the required variables in your production environment. The following are required as a minimum:
1. `SECRET_KEY` (follow instructions in the comments at the top of `.env`)
1. `SLACK_KEY` (this is called "Client ID" in Slack admin)
1. `SLACK_SECRET` (this is called "Client Secret" in Slack admin)
1. `DATABASE_URL` (run your own local copy of Postgres, or use a cloud service)
1. `REDIS_URL` (run your own local copy of Redis, or use a cloud service)
1. `URL` (the public facing URL of your installation)
1. `AWS_` (all of the keys beginning with AWS)
1. Migrate database schema with `yarn sequelize:migrate` or `npm run sequelize:migrate `
1. Start the service with any daemon tools you prefer. Take PM2 for example, `NODE_ENV=production pm2 start index.js --name outline `
1. Visit http://you_server_ip:3000 and you should be able to see Outline page
## Debugging
> Port number can be changed in the `.env` file
In development Outline outputs simple logging to the console, prefixed by categories. In production it outputs JSON logs, these can be easily parsed by your preferred log ingestion pipeline.
1. (Optional) You can add an `nginx` reverse proxy to serve your instance of Outline for a clean URL without the port number, support SSL, etc.
## Development
### Server
Outline uses [debug](https://www.npmjs.com/package/debug). To enable debugging output, the following categories are available:
```
DEBUG=sql,cache,presenters,events,logistics,emails,mailer
```
## Migrations
Sequelize is used to create and run migrations, for example:
```
yarn sequelize migration:generate --name my-migration
yarn sequelize db:migrate
```
Or to run migrations on test database:
```
yarn sequelize db:migrate --env test
```
## Structure
Outline is composed of separate backend and frontend application which are both driven by the same Node process. As both are written in Javascript, they share some code but are mostly separate. We utilize the latest language features, including `async`/`await`, and [Flow](https://flow.org/) typing. Prettier and ESLint are enforced by CI.
### Frontend
Outline's frontend is a React application compiled with [Webpack](https://webpack.js.org/). It uses [Mobx](https://mobx.js.org/) for state management and [Styled Components](https://www.styled-components.com/) for component styles. Unless global, state logic and styles are always co-located with React components together with their subcomponents to make the component tree easier to manage.
The editor itself is built on [Prosemirror](https://github.com/prosemirror) and hosted in a separate repository to encourage reuse: [rich-markdown-editor](https://github.com/outline/rich-markdown-editor)
- `app/` - Frontend React application
- `app/scenes` - Full page views
- `app/components` - Reusable React components
- `app/stores` - Global state stores
- `app/models` - State models
- `app/types` - Flow types for non-models
### Backend
Backend is driven by [Koa](http://koajs.com/) (API, web server), [Sequelize](http://docs.sequelizejs.com/) (database) and React for public pages and emails.
- `server/api` - API endpoints
- `server/commands` - Domain logic, currently being refactored from /models
- `server/emails` - React rendered email templates
- `server/models` - Database models
- `server/policies` - Authorization logic
- `server/presenters` - API responses for database models
- `server/test` - Test helps and support
- `server/utils` - Utility methods
- `shared` - Code shared between frontend and backend applications
HTTP logging is disabled by default, but can be enabled by setting the `DEBUG=http` environment variable.
## Tests
@@ -140,28 +61,41 @@ To add new tests, write your tests with [Jest](https://facebook.github.io/jest/)
```shell
# To run all tests
yarn test
make test
# To run backend tests in watch mode
make watch
```
Once the test database is created with `make test` you may individually run
frontend and backend tests directly.
```shell
# To run backend tests
yarn test:server
# To run a specific backend test
yarn test:server myTestFile
# To run frontend tests
yarn test:app
```
## Contributing
## Migrations
Outline is built and maintained by a small team we'd love your help to fix bugs and add features!
Sequelize is used to create and run migrations, for example:
However, before working on a pull request please let the core team know by creating or commenting in an issue on [GitHub](https://www.github.com/outline/outline/issues), and we'd also love to hear from you in the [Discussions](https://www.github.com/outline/outline/discussions). This way we can ensure that an approach is agreed on before code is written and will hopefully help to get your contributions integrated faster!
```shell
yarn sequelize migration:generate --name my-migration
yarn sequelize db:migrate
```
If youre looking for ways to get started, here's a list of ways to help us improve Outline:
Or to run migrations on test database:
* Issues with [`good first issue`](https://github.com/outline/outline/labels/good%20first%20issue) label
* Performance improvements, both on server and frontend
* Developer happiness and documentation
* Bugs and other issues listed on GitHub
```shell
yarn sequelize db:migrate --env test
```
## License
Outline is [BSL 1.1 licensed](https://github.com/outline/outline/blob/master/LICENSE).
Outline is [BSL 1.1 licensed](LICENSE).
+17
View File
@@ -0,0 +1,17 @@
export default class Queue {
name;
constructor(name) {
this.name = name;
}
process = (fn) => {
console.log(`Registered function ${this.name}`);
this.processFn = fn;
};
add = (data) => {
console.log(`Running ${this.name}`);
return this.processFn({ data });
};
}
+74 -4
View File
@@ -30,8 +30,12 @@
"postdeploy": "yarn sequelize db:migrate"
},
"env": {
"NODE_ENV": {
"value": "production",
"required": true
},
"SECRET_KEY": {
"description": "A secret key",
"description": "A 32-character secret key, generate with openssl rand -hex 32",
"generator": "secret",
"required": true
},
@@ -39,8 +43,12 @@
"value": "true",
"required": true
},
"ALLOWED_DOMAINS": {
"description": "Comma separated list of domains to be allowed (optional). If not set, all domains are allowed by default when using Google OAuth to signin. Consider putting {your app name}.herokuapp.com and any domain you are binding on in this list.",
"required": false
},
"URL": {
"description": "https://{your app name}.herokuapp.com",
"description": "https://{your app name}.herokuapp.com, or the domain you are binding to",
"required": true
},
"GOOGLE_CLIENT_ID": {
@@ -51,8 +59,51 @@
"description": "",
"required": false
},
"GOOGLE_ALLOWED_DOMAINS": {
"description": "Comma separated list of domains to be allowed (optional). If not set, all Google apps domains are allowed by default",
"AZURE_CLIENT_ID": {
"description": "To configure Microsoft/Azure auth, you'll need to create an OAuth Client. See the guide for details on setting up your Azure App: https://wiki.generaloutline.com/share/dfa77e56-d4d2-4b51-8ff8-84ea6608faa4",
"required": false
},
"AZURE_CLIENT_SECRET": {
"description": "",
"required": false
},
"AZURE_RESOURCE_APP_ID": {
"description": "",
"required": false
},
"OIDC_CLIENT_ID": {
"description": "To configure generic OIDC auth, you'll need some kind of identity provider. See documentation for whichever IdP you use to acquire the following info; Redirect URI is https://<URL>/auth/oidc.callback",
"required": false
},
"OIDC_CLIENT_SECRET": {
"description": "",
"required": false
},
"OIDC_AUTH_URI": {
"description": "",
"required": false
},
"OIDC_TOKEN_URI": {
"description": "",
"required": false
},
"OIDC_USERINFO_URI": {
"description": "",
"required": false
},
"OIDC_USERNAME_CLAIM": {
"description": "Specify which claims to derive user information from. Supports any valid JSON path with the JWT payload",
"value": "preferred_username",
"required": false
},
"OIDC_DISPLAY_NAME": {
"description": "Display name for OIDC authentication",
"value": "OpenID",
"required": false
},
"OIDC_SCOPES": {
"description": "Space separated auth scopes.",
"value": "openid profile email",
"required": false
},
"SLACK_KEY": {
@@ -92,6 +143,11 @@
"value": "26214400",
"required": false
},
"AWS_S3_FORCE_PATH_STYLE": {
"description": "Use path-style URL's for connecting to S3 instead of subdomain. This is useful for S3-compatible storage.",
"value": "true",
"required": false
},
"AWS_REGION": {
"value": "us-east-1",
"description": "Region in which the above S3 bucket exists",
@@ -126,6 +182,15 @@
"description": "wikireply@example.com (optional)",
"required": false
},
"SMTP_SECURE": {
"value": "true",
"description": "Use a secure SMTP connection (optional)",
"required": false
},
"SMTP_TLS_CIPHERS": {
"description": "Override SMTP cipher configuration (optional)",
"required": false
},
"GOOGLE_ANALYTICS_ID": {
"description": "UA-xxxx (optional)",
"required": false
@@ -137,6 +202,11 @@
"TEAM_LOGO": {
"description": "A logo that will be displayed on the signed out home page",
"required": false
},
"DEFAULT_LANGUAGE": {
"value": "en_US",
"description": "The default interface language. See translate.getoutline.com for a list of available language codes and their rough percentage translated.",
"required": false
}
}
}
+9
View File
@@ -0,0 +1,9 @@
{
"extends": [
"../.eslintrc"
],
"env": {
"jest": true,
"browser": true
}
}
+27
View File
@@ -0,0 +1,27 @@
{
"testURL": "http://localhost",
"verbose": false,
"rootDir": "..",
"roots": [
"<rootDir>/app",
"<rootDir>/shared"
],
"moduleNameMapper": {
"^~/(.*)$": "<rootDir>/app/$1",
"^@shared/(.*)$": "<rootDir>/shared/$1",
"^.*[.](gif|ttf|eot|svg)$": "<rootDir>/__test__/fileMock.js"
},
"moduleDirectories": [
"node_modules"
],
"modulePaths": [
"<rootDir>/app"
],
"setupFiles": [
"<rootDir>/__mocks__/window.js"
],
"setupFilesAfterEnv": [
"./app/test/setup.ts"
],
"testEnvironment": "jsdom"
}
+71
View File
@@ -0,0 +1,71 @@
import { CollectionIcon, EditIcon, PlusIcon } from "outline-icons";
import * as React from "react";
import stores from "~/stores";
import CollectionEdit from "~/scenes/CollectionEdit";
import CollectionNew from "~/scenes/CollectionNew";
import DynamicCollectionIcon from "~/components/CollectionIcon";
import { createAction } from "~/actions";
import { CollectionSection } from "~/actions/sections";
import history from "~/utils/history";
export const openCollection = createAction({
name: ({ t }) => t("Open collection"),
section: CollectionSection,
shortcut: ["o", "c"],
icon: <CollectionIcon />,
children: ({ stores }) => {
const collections = stores.collections.orderedData;
return collections.map((collection) => ({
// Note: using url which includes the slug rather than id here to bust
// cache if the collection is renamed
id: collection.url,
name: collection.name,
icon: <DynamicCollectionIcon collection={collection} />,
section: CollectionSection,
perform: () => history.push(collection.url),
}));
},
});
export const createCollection = createAction({
name: ({ t }) => t("New collection"),
section: CollectionSection,
icon: <PlusIcon />,
keywords: "create",
visible: ({ stores }) =>
stores.policies.abilities(stores.auth.team?.id || "").createCollection,
perform: ({ t, event }) => {
event?.preventDefault();
event?.stopPropagation();
stores.dialogs.openModal({
title: t("Create a collection"),
content: <CollectionNew onSubmit={stores.dialogs.closeAllModals} />,
});
},
});
export const editCollection = createAction({
name: ({ t }) => t("Edit collection"),
section: CollectionSection,
icon: <EditIcon />,
visible: ({ stores, activeCollectionId }) =>
!!activeCollectionId &&
stores.policies.abilities(activeCollectionId).update,
perform: ({ t, activeCollectionId }) => {
if (!activeCollectionId) {
return;
}
stores.dialogs.openModal({
title: t("Edit collection"),
content: (
<CollectionEdit
onSubmit={stores.dialogs.closeAllModals}
collectionId={activeCollectionId}
/>
),
});
},
});
export const rootCollectionActions = [openCollection, createCollection];
+32
View File
@@ -0,0 +1,32 @@
import { ToolsIcon, TrashIcon } from "outline-icons";
import * as React from "react";
import stores from "~/stores";
import { createAction } from "~/actions";
import { DebugSection } from "~/actions/sections";
import env from "~/env";
import { deleteAllDatabases } from "~/utils/developer";
export const clearIndexedDB = createAction({
name: ({ t }) => t("Delete IndexedDB cache"),
icon: <TrashIcon />,
keywords: "cache clear database",
section: DebugSection,
perform: async ({ t }) => {
await deleteAllDatabases();
stores.toasts.showToast(t("IndexedDB cache deleted"));
},
});
export const development = createAction({
name: ({ t }) => t("Development"),
keywords: "debug",
icon: <ToolsIcon />,
iconInContextMenu: false,
section: DebugSection,
visible: ({ event }) =>
env.ENVIRONMENT === "development" ||
(event instanceof KeyboardEvent && event.altKey),
children: [clearIndexedDB],
});
export const rootDebugActions = [development];
+324
View File
@@ -0,0 +1,324 @@
import invariant from "invariant";
import {
DownloadIcon,
DuplicateIcon,
StarredIcon,
PrintIcon,
UnstarredIcon,
DocumentIcon,
NewDocumentIcon,
ShapesIcon,
ImportIcon,
PinIcon,
} from "outline-icons";
import * as React from "react";
import getDataTransferFiles from "@shared/utils/getDataTransferFiles";
import DocumentTemplatize from "~/scenes/DocumentTemplatize";
import { createAction } from "~/actions";
import { DocumentSection } from "~/actions/sections";
import history from "~/utils/history";
import { homePath, newDocumentPath } from "~/utils/routeHelpers";
export const openDocument = createAction({
name: ({ t }) => t("Open document"),
section: DocumentSection,
shortcut: ["o", "d"],
keywords: "go to",
icon: <DocumentIcon />,
children: ({ stores }) => {
const paths = stores.collections.pathsToDocuments;
return paths
.filter((path) => path.type === "document")
.map((path) => ({
// Note: using url which includes the slug rather than id here to bust
// cache if the document is renamed
id: path.url,
name: path.title,
icon: () =>
stores.documents.get(path.id)?.isStarred ? <StarredIcon /> : null,
section: DocumentSection,
perform: () => history.push(path.url),
}));
},
});
export const createDocument = createAction({
name: ({ t }) => t("New document"),
section: DocumentSection,
icon: <NewDocumentIcon />,
keywords: "create",
visible: ({ activeCollectionId, stores }) =>
!!activeCollectionId &&
stores.policies.abilities(activeCollectionId).update,
perform: ({ activeCollectionId }) =>
activeCollectionId && history.push(newDocumentPath(activeCollectionId)),
});
export const starDocument = createAction({
name: ({ t }) => t("Star"),
section: DocumentSection,
icon: <StarredIcon />,
keywords: "favorite bookmark",
visible: ({ activeDocumentId, stores }) => {
if (!activeDocumentId) {
return false;
}
const document = stores.documents.get(activeDocumentId);
return (
!document?.isStarred && stores.policies.abilities(activeDocumentId).star
);
},
perform: ({ activeDocumentId, stores }) => {
if (!activeDocumentId) {
return;
}
const document = stores.documents.get(activeDocumentId);
document?.star();
},
});
export const unstarDocument = createAction({
name: ({ t }) => t("Unstar"),
section: DocumentSection,
icon: <UnstarredIcon />,
keywords: "unfavorite unbookmark",
visible: ({ activeDocumentId, stores }) => {
if (!activeDocumentId) {
return false;
}
const document = stores.documents.get(activeDocumentId);
return (
!!document?.isStarred &&
stores.policies.abilities(activeDocumentId).unstar
);
},
perform: ({ activeDocumentId, stores }) => {
if (!activeDocumentId) {
return;
}
const document = stores.documents.get(activeDocumentId);
document?.unstar();
},
});
export const downloadDocument = createAction({
name: ({ t, isContextMenu }) =>
isContextMenu ? t("Download") : t("Download document"),
section: DocumentSection,
icon: <DownloadIcon />,
keywords: "export",
visible: ({ activeDocumentId, stores }) =>
!!activeDocumentId && stores.policies.abilities(activeDocumentId).download,
perform: ({ activeDocumentId, stores }) => {
if (!activeDocumentId) {
return;
}
const document = stores.documents.get(activeDocumentId);
document?.download();
},
});
export const duplicateDocument = createAction({
name: ({ t, isContextMenu }) =>
isContextMenu ? t("Duplicate") : t("Duplicate document"),
section: DocumentSection,
icon: <DuplicateIcon />,
keywords: "copy",
visible: ({ activeDocumentId, stores }) =>
!!activeDocumentId && stores.policies.abilities(activeDocumentId).update,
perform: async ({ activeDocumentId, t, stores }) => {
if (!activeDocumentId) {
return;
}
const document = stores.documents.get(activeDocumentId);
invariant(document, "Document must exist");
const duped = await document.duplicate();
// when duplicating, go straight to the duplicated document content
history.push(duped.url);
stores.toasts.showToast(t("Document duplicated"), {
type: "success",
});
},
});
/**
* Pin a document to a collection. Pinned documents will be displayed at the top
* of the collection for all collection members to see.
*/
export const pinDocument = createAction({
name: ({ t }) => t("Pin to collection"),
section: DocumentSection,
icon: <PinIcon />,
visible: ({ activeCollectionId, activeDocumentId, stores }) => {
if (!activeDocumentId || !activeCollectionId) {
return false;
}
const document = stores.documents.get(activeDocumentId);
return (
!!stores.policies.abilities(activeDocumentId).pin && !document?.pinned
);
},
perform: async ({ activeDocumentId, activeCollectionId, t, stores }) => {
if (!activeDocumentId || !activeCollectionId) {
return;
}
const document = stores.documents.get(activeDocumentId);
await document?.pin(document.collectionId);
const collection = stores.collections.get(activeCollectionId);
if (!collection || !location.pathname.startsWith(collection?.url)) {
stores.toasts.showToast(t("Pinned to collection"));
}
},
});
/**
* Pin a document to team home. Pinned documents will be displayed at the top
* of the home screen for all team members to see.
*/
export const pinDocumentToHome = createAction({
name: ({ t }) => t("Pin to home"),
section: DocumentSection,
icon: <PinIcon />,
visible: ({ activeDocumentId, currentTeamId, stores }) => {
if (!currentTeamId || !activeDocumentId) {
return false;
}
const document = stores.documents.get(activeDocumentId);
return (
!!stores.policies.abilities(activeDocumentId).pinToHome &&
!document?.pinnedToHome
);
},
perform: async ({ activeDocumentId, location, t, stores }) => {
if (!activeDocumentId) {
return;
}
const document = stores.documents.get(activeDocumentId);
await document?.pin();
if (location.pathname !== homePath()) {
stores.toasts.showToast(t("Pinned to team home"));
}
},
});
export const printDocument = createAction({
name: ({ t, isContextMenu }) =>
isContextMenu ? t("Print") : t("Print document"),
section: DocumentSection,
icon: <PrintIcon />,
visible: ({ activeDocumentId }) => !!activeDocumentId,
perform: async () => {
window.print();
},
});
export const importDocument = createAction({
name: ({ t }) => t("Import document"),
section: DocumentSection,
icon: <ImportIcon />,
keywords: "upload",
visible: ({ activeCollectionId, activeDocumentId, stores }) => {
if (activeDocumentId) {
return !!stores.policies.abilities(activeDocumentId).createChildDocument;
}
if (activeCollectionId) {
return !!stores.policies.abilities(activeCollectionId).update;
}
return false;
},
perform: ({ activeCollectionId, activeDocumentId, stores }) => {
const { documents, toasts } = stores;
const input = document.createElement("input");
input.type = "file";
input.accept = documents.importFileTypes.join(", ");
input.onchange = async (ev: Event) => {
const files = getDataTransferFiles(ev);
try {
const file = files[0];
const document = await documents.import(
file,
activeDocumentId,
activeCollectionId,
{
publish: true,
}
);
history.push(document.url);
} catch (err) {
toasts.showToast(err.message, {
type: "error",
});
throw err;
}
};
input.click();
},
});
export const createTemplate = createAction({
name: ({ t }) => t("Templatize"),
section: DocumentSection,
icon: <ShapesIcon />,
keywords: "new create template",
visible: ({ activeCollectionId, activeDocumentId, stores }) => {
if (!activeDocumentId) {
return false;
}
const document = stores.documents.get(activeDocumentId);
return (
!!activeCollectionId &&
stores.policies.abilities(activeCollectionId).update &&
!document?.isTemplate
);
},
perform: ({ activeDocumentId, stores, t, event }) => {
if (!activeDocumentId) {
return;
}
event?.preventDefault();
event?.stopPropagation();
stores.dialogs.openModal({
title: t("Create template"),
content: (
<DocumentTemplatize
documentId={activeDocumentId}
onSubmit={stores.dialogs.closeAllModals}
/>
),
});
},
});
export const rootDocumentActions = [
openDocument,
createDocument,
createTemplate,
importDocument,
downloadDocument,
starDocument,
unstarDocument,
duplicateDocument,
printDocument,
pinDocument,
pinDocumentToHome,
];
+160
View File
@@ -0,0 +1,160 @@
import {
HomeIcon,
SearchIcon,
ArchiveIcon,
TrashIcon,
EditIcon,
OpenIcon,
SettingsIcon,
ShapesIcon,
KeyboardIcon,
EmailIcon,
LogoutIcon,
} from "outline-icons";
import * as React from "react";
import {
developersUrl,
changelogUrl,
mailToUrl,
githubIssuesUrl,
} from "@shared/utils/urlHelpers";
import stores from "~/stores";
import KeyboardShortcuts from "~/scenes/KeyboardShortcuts";
import { createAction } from "~/actions";
import { NavigationSection } from "~/actions/sections";
import history from "~/utils/history";
import {
settingsPath,
homePath,
searchUrl,
draftsPath,
templatesPath,
archivePath,
trashPath,
} from "~/utils/routeHelpers";
export const navigateToHome = createAction({
name: ({ t }) => t("Home"),
section: NavigationSection,
shortcut: ["d"],
icon: <HomeIcon />,
perform: () => history.push(homePath()),
visible: ({ location }) => location.pathname !== homePath(),
});
export const navigateToSearch = createAction({
name: ({ t }) => t("Search"),
section: NavigationSection,
shortcut: ["/"],
icon: <SearchIcon />,
perform: () => history.push(searchUrl()),
visible: ({ location }) => location.pathname !== searchUrl(),
});
export const navigateToDrafts = createAction({
name: ({ t }) => t("Drafts"),
section: NavigationSection,
icon: <EditIcon />,
perform: () => history.push(draftsPath()),
visible: ({ location }) => location.pathname !== draftsPath(),
});
export const navigateToTemplates = createAction({
name: ({ t }) => t("Templates"),
section: NavigationSection,
icon: <ShapesIcon />,
perform: () => history.push(templatesPath()),
visible: ({ location }) => location.pathname !== templatesPath(),
});
export const navigateToArchive = createAction({
name: ({ t }) => t("Archive"),
section: NavigationSection,
icon: <ArchiveIcon />,
perform: () => history.push(archivePath()),
visible: ({ location }) => location.pathname !== archivePath(),
});
export const navigateToTrash = createAction({
name: ({ t }) => t("Trash"),
section: NavigationSection,
icon: <TrashIcon />,
perform: () => history.push(trashPath()),
visible: ({ location }) => location.pathname !== trashPath(),
});
export const navigateToSettings = createAction({
name: ({ t }) => t("Settings"),
section: NavigationSection,
shortcut: ["g", "s"],
iconInContextMenu: false,
icon: <SettingsIcon />,
perform: () => history.push(settingsPath()),
});
export const openAPIDocumentation = createAction({
name: ({ t }) => t("API documentation"),
section: NavigationSection,
iconInContextMenu: false,
icon: <OpenIcon />,
perform: () => window.open(developersUrl()),
});
export const openFeedbackUrl = createAction({
name: ({ t }) => t("Send us feedback"),
section: NavigationSection,
iconInContextMenu: false,
icon: <EmailIcon />,
perform: () => window.open(mailToUrl()),
});
export const openBugReportUrl = createAction({
name: ({ t }) => t("Report a bug"),
section: NavigationSection,
perform: () => window.open(githubIssuesUrl()),
});
export const openChangelog = createAction({
name: ({ t }) => t("Changelog"),
section: NavigationSection,
iconInContextMenu: false,
icon: <OpenIcon />,
perform: () => window.open(changelogUrl()),
});
export const openKeyboardShortcuts = createAction({
name: ({ t }) => t("Keyboard shortcuts"),
section: NavigationSection,
shortcut: ["?"],
iconInContextMenu: false,
icon: <KeyboardIcon />,
perform: ({ t }) => {
stores.dialogs.openGuide({
title: t("Keyboard shortcuts"),
content: <KeyboardShortcuts />,
});
},
});
export const logout = createAction({
name: ({ t }) => t("Log out"),
section: NavigationSection,
icon: <LogoutIcon />,
perform: () => stores.auth.logout(),
});
export const rootNavigationActions = [
navigateToHome,
navigateToSearch,
navigateToDrafts,
navigateToTemplates,
navigateToArchive,
navigateToTrash,
navigateToSettings,
openAPIDocumentation,
openFeedbackUrl,
openBugReportUrl,
openChangelog,
openKeyboardShortcuts,
logout,
];
+49
View File
@@ -0,0 +1,49 @@
import { SunIcon, MoonIcon, BrowserIcon } from "outline-icons";
import * as React from "react";
import stores from "~/stores";
import { Theme } from "~/stores/UiStore";
import { createAction } from "~/actions";
import { SettingsSection } from "~/actions/sections";
export const changeToDarkTheme = createAction({
name: ({ t }) => t("Dark"),
icon: <MoonIcon />,
iconInContextMenu: false,
keywords: "theme dark night",
section: SettingsSection,
selected: () => stores.ui.theme === "dark",
perform: () => stores.ui.setTheme(Theme.Dark),
});
export const changeToLightTheme = createAction({
name: ({ t }) => t("Light"),
icon: <SunIcon />,
iconInContextMenu: false,
keywords: "theme light day",
section: SettingsSection,
selected: () => stores.ui.theme === "light",
perform: () => stores.ui.setTheme(Theme.Light),
});
export const changeToSystemTheme = createAction({
name: ({ t }) => t("System"),
icon: <BrowserIcon />,
iconInContextMenu: false,
keywords: "theme system default",
section: SettingsSection,
selected: () => stores.ui.theme === "system",
perform: () => stores.ui.setTheme(Theme.System),
});
export const changeTheme = createAction({
name: ({ t, isContextMenu }) =>
isContextMenu ? t("Appearance") : t("Change theme"),
placeholder: ({ t }) => t("Change theme to"),
icon: () =>
stores.ui.resolvedTheme === "light" ? <SunIcon /> : <MoonIcon />,
keywords: "appearance display",
section: SettingsSection,
children: [changeToLightTheme, changeToDarkTheme, changeToSystemTheme],
});
export const rootSettingsActions = [changeTheme];
+23
View File
@@ -0,0 +1,23 @@
import { PlusIcon } from "outline-icons";
import * as React from "react";
import stores from "~/stores";
import Invite from "~/scenes/Invite";
import { createAction } from "~/actions";
import { UserSection } from "~/actions/sections";
export const inviteUser = createAction({
name: ({ t }) => `${t("Invite people")}`,
icon: <PlusIcon />,
keywords: "team member user",
section: UserSection,
visible: ({ stores }) =>
stores.policies.abilities(stores.auth.team?.id || "").inviteUser,
perform: ({ t }) => {
stores.dialogs.openModal({
title: t("Invite people"),
content: <Invite onSubmit={stores.dialogs.closeAllModals} />,
});
},
});
export const rootUserActions = [inviteUser];
+114
View File
@@ -0,0 +1,114 @@
import { flattenDeep } from "lodash";
import * as React from "react";
import { $Diff } from "utility-types";
import { v4 as uuidv4 } from "uuid";
import {
Action,
ActionContext,
CommandBarAction,
MenuItemButton,
MenuItemWithChildren,
} from "~/types";
export function createAction(
definition: $Diff<
Action,
{
id?: string;
}
>
): Action {
return {
id: uuidv4(),
...definition,
};
}
export function actionToMenuItem(
action: Action,
context: ActionContext
): MenuItemButton | MenuItemWithChildren {
function resolve<T>(value: any): T {
if (typeof value === "function") {
return value(context);
}
return value;
}
const resolvedIcon = resolve<React.ReactElement<any>>(action.icon);
const resolvedChildren = resolve<Action[]>(action.children);
const visible = action.visible ? action.visible(context) : true;
const title = resolve<string>(action.name);
const icon =
resolvedIcon && action.iconInContextMenu !== false
? React.cloneElement(resolvedIcon, {
color: "currentColor",
})
: undefined;
if (resolvedChildren) {
return {
type: "submenu",
title,
icon,
items: resolvedChildren
.map((a) => actionToMenuItem(a, context))
.filter((a) => !!a),
visible,
};
}
return {
type: "button",
title,
icon,
visible,
onClick: () => action.perform && action.perform(context),
selected: action.selected ? action.selected(context) : undefined,
};
}
export function actionToKBar(
action: Action,
context: ActionContext
): CommandBarAction[] {
function resolve<T>(value: any): T {
if (typeof value === "function") {
return value(context);
}
return value;
}
if (typeof action.visible === "function" && !action.visible(context)) {
return [];
}
const resolvedIcon = resolve<React.ReactElement<any>>(action.icon);
const resolvedChildren = resolve<Action[]>(action.children);
const resolvedSection = resolve<string>(action.section);
const resolvedName = resolve<string>(action.name);
const resolvedPlaceholder = resolve<string>(action.placeholder);
const children = resolvedChildren
? flattenDeep(resolvedChildren.map((a) => actionToKBar(a, context))).filter(
(a) => !!a
)
: [];
return [
{
id: action.id,
name: resolvedName,
section: resolvedSection,
placeholder: resolvedPlaceholder,
keywords: `${action.keywords}`,
shortcut: action.shortcut || [],
icon: resolvedIcon,
perform: action.perform
? () => action.perform && action.perform(context)
: undefined,
},
// @ts-expect-error ts-migrate(2769) FIXME: No overload matches this call.
].concat(children.map((child) => ({ ...child, parent: action.id })));
}
+15
View File
@@ -0,0 +1,15 @@
import { rootCollectionActions } from "./definitions/collections";
import { rootDebugActions } from "./definitions/debug";
import { rootDocumentActions } from "./definitions/documents";
import { rootNavigationActions } from "./definitions/navigation";
import { rootSettingsActions } from "./definitions/settings";
import { rootUserActions } from "./definitions/users";
export default [
...rootCollectionActions,
...rootDocumentActions,
...rootUserActions,
...rootNavigationActions,
...rootSettingsActions,
...rootDebugActions,
];
+13
View File
@@ -0,0 +1,13 @@
import { ActionContext } from "~/types";
export const CollectionSection = ({ t }: ActionContext) => t("Collection");
export const DebugSection = ({ t }: ActionContext) => t("Debug");
export const DocumentSection = ({ t }: ActionContext) => t("Document");
export const SettingsSection = ({ t }: ActionContext) => t("Settings");
export const NavigationSection = ({ t }: ActionContext) => t("Navigation");
export const UserSection = ({ t }: ActionContext) => t("People");
@@ -1,7 +1,6 @@
// @flow
import styled from "styled-components";
import breakpoint from "styled-components-breakpoint";
import Flex from "components/Flex";
import Flex from "~/components/Flex";
export const Action = styled(Flex)`
justify-content: center;
@@ -11,11 +10,6 @@ export const Action = styled(Flex)`
font-size: 15px;
flex-shrink: 0;
a {
color: ${(props) => props.theme.text};
height: 24px;
}
&:empty {
display: none;
}
@@ -38,7 +32,7 @@ const Actions = styled(Flex)`
background: ${(props) => props.theme.background};
transition: ${(props) => props.theme.backgroundTransition};
padding: 12px;
-webkit-backdrop-filter: blur(20px);
backdrop-filter: blur(20px);
@media print {
display: none;
@@ -1,34 +1,39 @@
// @flow
/* global ga */
import * as React from "react";
import env from "env";
import env from "~/env";
type Props = {
children?: React.Node,
children?: React.ReactNode;
};
export default class Analytics extends React.Component<Props> {
componentDidMount() {
if (!env.GOOGLE_ANALYTICS_ID) return;
if (!env.GOOGLE_ANALYTICS_ID) {
return;
}
// standard Google Analytics script
window.ga =
window.ga ||
function () {
// $FlowIssue
(ga.q = ga.q || []).push(arguments);
function (...args) {
(ga.q = ga.q || []).push(args);
};
// $FlowIssue
ga.l = +new Date();
ga("create", env.GOOGLE_ANALYTICS_ID, "auto");
ga("set", { dimension1: "true" });
ga("set", {
dimension1: "true",
});
ga("send", "pageview");
const script = document.createElement("script");
script.src = "https://www.google-analytics.com/analytics.js";
script.async = true;
// Track PWA install event
window.addEventListener("appinstalled", () => {
ga("send", "event", "pwa", "install");
});
if (document.body) {
document.body.appendChild(script);
}
+22
View File
@@ -0,0 +1,22 @@
import * as React from "react";
export default function Arrow() {
return (
<svg
width="13"
height="30"
viewBox="0 0 13 30"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill="currentColor"
d="M7.40242 1.48635C8.23085 0.0650039 10.0656 -0.421985 11.5005 0.39863C12.9354 1.21924 13.427 3.03671 12.5986 4.45806L5.59858 16.4681C4.77015 17.8894 2.93538 18.3764 1.5005 17.5558C0.065623 16.7352 -0.426002 14.9177 0.402425 13.4964L7.40242 1.48635Z"
/>
<path
fill="currentColor"
d="M12.5986 25.5419C13.427 26.9633 12.9354 28.7808 11.5005 29.6014C10.0656 30.422 8.23087 29.935 7.40244 28.5136L0.402438 16.5036C-0.425989 15.0823 0.0656365 13.2648 1.50051 12.4442C2.93539 11.6236 4.77016 12.1106 5.59859 13.5319L12.5986 25.5419Z"
/>
</svg>
);
}
@@ -1,10 +1,9 @@
// @flow
import * as React from "react";
type Props = {
size?: number,
fill?: string,
className?: string,
size?: number;
fill?: string;
className?: string;
};
function GoogleLogo({ size = 34, fill = "#FFF", className }: Props) {
+43
View File
@@ -0,0 +1,43 @@
import * as React from "react";
type Props = {
size?: number;
fill?: string;
className?: string;
};
function MicrosoftLogo({ size = 34, fill = "#FFF", className }: Props) {
return (
<svg
fill={fill}
width={size}
height={size}
viewBox="0 0 34 34"
xmlns="http://www.w3.org/2000/svg"
className={className}
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M18.0002 1H33.9998C33.9998 5.8172 34.0007 10.6344 33.9988 15.4516C28.6666 15.4508 23.3334 15.4516 18.0012 15.4516C17.9993 10.6344 18.0002 5.8172 18.0002 1Z"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M18.0009 17.5173C23.3333 17.5155 28.6667 17.5164 34 17.5164V33H18C18.0009 27.8388 17.9991 22.6776 18.0009 17.5173V17.5173Z"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M0 1H16L15.9988 15.4516H0V1Z"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M0 17.5161C5.3332 17.5179 10.6664 17.5155 15.9996 17.5179C16.0005 22.6789 15.9996 27.839 15.9996 33H0V17.5161Z"
/>
</svg>
);
}
export default MicrosoftLogo;
@@ -1,10 +1,9 @@
// @flow
import * as React from "react";
type Props = {
size?: number,
fill?: string,
className?: string,
size?: number;
fill?: string;
className?: string;
};
function SlackLogo({ size = 34, fill = "#FFF", className }: Props) {
+48
View File
@@ -0,0 +1,48 @@
import * as React from "react";
import styled from "styled-components";
import GoogleLogo from "./GoogleLogo";
import MicrosoftLogo from "./MicrosoftLogo";
import SlackLogo from "./SlackLogo";
type Props = {
providerName: string;
size?: number;
};
function AuthLogo({ providerName, size = 16 }: Props) {
switch (providerName) {
case "slack":
return (
<Logo>
<SlackLogo size={size} />
</Logo>
);
case "google":
return (
<Logo>
<GoogleLogo size={size} />
</Logo>
);
case "azure":
return (
<Logo>
<MicrosoftLogo size={size} />
</Logo>
);
default:
return null;
}
}
const Logo = styled.div`
display: flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
`;
export default AuthLogo;
-43
View File
@@ -1,43 +0,0 @@
// @flow
import { observer, inject } from "mobx-react";
import * as React from "react";
import { Redirect } from "react-router-dom";
import { isCustomSubdomain } from "shared/utils/domains";
import AuthStore from "stores/AuthStore";
import LoadingIndicator from "components/LoadingIndicator";
import env from "env";
type Props = {
auth: AuthStore,
children?: React.Node,
};
const Authenticated = observer(({ auth, children }: Props) => {
if (auth.authenticated) {
const { user, team } = auth;
const { hostname } = window.location;
if (!team || !user) {
return <LoadingIndicator />;
}
// If we're authenticated but viewing a subdomain that doesn't match the
// currently authenticated team then kick the user to the teams subdomain.
if (
env.SUBDOMAINS_ENABLED &&
team.subdomain &&
isCustomSubdomain(hostname) &&
!hostname.startsWith(`${team.subdomain}.`)
) {
window.location.href = `${team.url}${window.location.pathname}`;
return <LoadingIndicator />;
}
return children;
}
auth.logout(true);
return <Redirect to="/" />;
});
export default inject("auth")(Authenticated);
+58
View File
@@ -0,0 +1,58 @@
import { observer } from "mobx-react";
import * as React from "react";
import { useTranslation } from "react-i18next";
import { Redirect } from "react-router-dom";
import { isCustomSubdomain } from "@shared/utils/domains";
import LoadingIndicator from "~/components/LoadingIndicator";
import env from "~/env";
import useStores from "~/hooks/useStores";
import { changeLanguage } from "~/utils/language";
type Props = {
children: JSX.Element;
};
const Authenticated = ({ children }: Props) => {
const { auth } = useStores();
const { i18n } = useTranslation();
const language = auth.user && auth.user.language;
// Watching for language changes here as this is the earliest point we have
// the user available and means we can start loading translations faster
React.useEffect(() => {
changeLanguage(language, i18n);
}, [i18n, language]);
if (auth.authenticated) {
const { user, team } = auth;
const { hostname } = window.location;
if (!team || !user) {
return <LoadingIndicator />;
}
// If we're authenticated but viewing a domain that doesn't match the
// current team then kick the user to the teams correct domain.
if (team.domain) {
if (team.domain !== hostname) {
window.location.href = `${team.url}${window.location.pathname}`;
return <LoadingIndicator />;
}
} else if (
env.SUBDOMAINS_ENABLED &&
team.subdomain &&
isCustomSubdomain(hostname) &&
!hostname.startsWith(`${team.subdomain}.`)
) {
window.location.href = `${team.url}${window.location.pathname}`;
return <LoadingIndicator />;
}
return children;
}
auth.logout(true);
return <Redirect to="/" />;
};
export default observer(Authenticated);
+106
View File
@@ -0,0 +1,106 @@
import { observable } from "mobx";
import { observer } from "mobx-react";
import * as React from "react";
import { withTranslation, WithTranslation } from "react-i18next";
import { Switch, Route } from "react-router-dom";
import RootStore from "~/stores/RootStore";
import ErrorSuspended from "~/scenes/ErrorSuspended";
import Layout from "~/components/Layout";
import RegisterKeyDown from "~/components/RegisterKeyDown";
import Sidebar from "~/components/Sidebar";
import SettingsSidebar from "~/components/Sidebar/Settings";
import history from "~/utils/history";
import {
searchUrl,
matchDocumentSlug as slug,
newDocumentPath,
settingsPath,
} from "~/utils/routeHelpers";
import withStores from "./withStores";
const DocumentHistory = React.lazy(
() =>
import(
/* webpackChunkName: "document-history" */
"~/components/DocumentHistory"
)
);
const CommandBar = React.lazy(
() =>
import(
/* webpackChunkName: "command-bar" */
"~/components/CommandBar"
)
);
type Props = WithTranslation &
RootStore & {
children?: React.ReactNode;
};
@observer
class AuthenticatedLayout extends React.Component<Props> {
scrollable: HTMLDivElement | null | undefined;
@observable
keyboardShortcutsOpen = false;
goToSearch = (ev: KeyboardEvent) => {
if (!ev.metaKey && !ev.ctrlKey) {
ev.preventDefault();
ev.stopPropagation();
history.push(searchUrl());
}
};
goToNewDocument = () => {
const { activeCollectionId } = this.props.ui;
if (!activeCollectionId) {
return;
}
const can = this.props.policies.abilities(activeCollectionId);
if (!can.update) {
return;
}
history.push(newDocumentPath(activeCollectionId));
};
render() {
const { auth } = this.props;
const { user, team } = auth;
const showSidebar = auth.authenticated && user && team;
if (auth.isSuspended) {
return <ErrorSuspended />;
}
const sidebar = showSidebar ? (
<Switch>
<Route path={settingsPath()} component={SettingsSidebar} />
<Route component={Sidebar} />
</Switch>
) : undefined;
const rightRail = (
<React.Suspense fallback={null}>
<Switch>
<Route
path={`/doc/${slug}/history/:revisionId?`}
component={DocumentHistory}
/>
</Switch>
</React.Suspense>
);
return (
<Layout title={team?.name} sidebar={sidebar} rightRail={rightRail}>
<RegisterKeyDown trigger="n" handler={this.goToNewDocument} />
<RegisterKeyDown trigger="t" handler={this.goToSearch} />
<RegisterKeyDown trigger="/" handler={this.goToSearch} />
{this.props.children}
<CommandBar />
</Layout>
);
}
}
export default withTranslation()(withStores(AuthenticatedLayout));
@@ -1,19 +1,24 @@
// @flow
import { observable } from "mobx";
import { observer } from "mobx-react";
import * as React from "react";
import styled from "styled-components";
import User from "~/models/User";
import placeholder from "./placeholder.png";
type Props = {
src: string,
size: number,
icon?: React.Node,
src: string;
size: number;
icon?: React.ReactNode;
user?: User;
alt?: string;
onClick?: React.MouseEventHandler<HTMLImageElement>;
className?: string;
};
@observer
class Avatar extends React.Component<Props> {
@observable error: boolean;
@observable
error: boolean;
static defaultProps = {
size: 24,
@@ -25,7 +30,6 @@ class Avatar extends React.Component<Props> {
render() {
const { src, icon, ...rest } = this.props;
return (
<AvatarWrapper>
<CircleImg
@@ -55,7 +59,7 @@ const IconWrapper = styled.div`
height: 20px;
`;
const CircleImg = styled.img`
const CircleImg = styled.img<{ size: number }>`
display: block;
width: ${(props) => props.size}px;
height: ${(props) => props.size}px;
@@ -1,86 +0,0 @@
// @flow
import distanceInWordsToNow from "date-fns/distance_in_words_to_now";
import { observable } from "mobx";
import { observer } from "mobx-react";
import { EditIcon } from "outline-icons";
import * as React from "react";
import styled from "styled-components";
import User from "models/User";
import UserProfile from "scenes/UserProfile";
import Avatar from "components/Avatar";
import Tooltip from "components/Tooltip";
type Props = {
user: User,
isPresent: boolean,
isEditing: boolean,
isCurrentUser: boolean,
lastViewedAt: string,
};
@observer
class AvatarWithPresence extends React.Component<Props> {
@observable isOpen: boolean = false;
handleOpenProfile = () => {
this.isOpen = true;
};
handleCloseProfile = () => {
this.isOpen = false;
};
render() {
const {
user,
lastViewedAt,
isPresent,
isEditing,
isCurrentUser,
} = this.props;
return (
<>
<Tooltip
tooltip={
<Centered>
<strong>{user.name}</strong> {isCurrentUser && "(You)"}
<br />
{isPresent
? isEditing
? "currently editing"
: "currently viewing"
: `viewed ${distanceInWordsToNow(new Date(lastViewedAt))} ago`}
</Centered>
}
placement="bottom"
>
<AvatarWrapper isPresent={isPresent}>
<Avatar
src={user.avatarUrl}
onClick={this.handleOpenProfile}
size={32}
icon={isEditing ? <EditIcon size={16} color="#FFF" /> : undefined}
/>
</AvatarWrapper>
</Tooltip>
<UserProfile
user={user}
isOpen={this.isOpen}
onRequestClose={this.handleCloseProfile}
/>
</>
);
}
}
const Centered = styled.div`
text-align: center;
`;
const AvatarWrapper = styled.div`
opacity: ${(props) => (props.isPresent ? 1 : 0.5)};
transition: opacity 250ms ease-in-out;
`;
export default AvatarWithPresence;
@@ -0,0 +1,141 @@
import { observable } from "mobx";
import { observer } from "mobx-react";
import * as React from "react";
import { WithTranslation, withTranslation } from "react-i18next";
import styled, { css } from "styled-components";
import User from "~/models/User";
import UserProfile from "~/scenes/UserProfile";
import Avatar from "~/components/Avatar";
import Tooltip from "~/components/Tooltip";
type Props = WithTranslation & {
user: User;
isPresent: boolean;
isEditing: boolean;
isObserving: boolean;
isCurrentUser: boolean;
profileOnClick: boolean;
onClick?: React.MouseEventHandler<HTMLImageElement>;
};
@observer
class AvatarWithPresence extends React.Component<Props> {
@observable
isOpen = false;
handleOpenProfile = () => {
this.isOpen = true;
};
handleCloseProfile = () => {
this.isOpen = false;
};
render() {
const {
onClick,
user,
isPresent,
isEditing,
isObserving,
isCurrentUser,
t,
} = this.props;
const status = isPresent
? isEditing
? t("currently editing")
: t("currently viewing")
: t("previously edited");
return (
<>
<Tooltip
tooltip={
<Centered>
<strong>{user.name}</strong> {isCurrentUser && `(${t("You")})`}
{status && (
<>
<br />
{status}
</>
)}
</Centered>
}
placement="bottom"
>
<AvatarWrapper
$isPresent={isPresent}
$isObserving={isObserving}
$color={user.color}
>
<Avatar
src={user.avatarUrl}
onClick={
this.props.profileOnClick === false
? onClick
: this.handleOpenProfile
}
size={32}
/>
</AvatarWrapper>
</Tooltip>
{this.props.profileOnClick && (
<UserProfile
user={user}
isOpen={this.isOpen}
onRequestClose={this.handleCloseProfile}
/>
)}
</>
);
}
}
const Centered = styled.div`
text-align: center;
`;
const AvatarWrapper = styled.div<{
$isPresent: boolean;
$isObserving: boolean;
$color: string;
}>`
opacity: ${(props) => (props.$isPresent ? 1 : 0.5)};
transition: opacity 250ms ease-in-out;
border-radius: 50%;
position: relative;
&:after {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
border-radius: 50%;
transition: border-color 100ms ease-in-out;
border: 2px solid transparent;
pointer-events: none;
${(props) =>
props.$isObserving &&
css`
border: 2px solid ${props.$color};
box-shadow: inset 0 0 0 2px ${props.theme.background};
&:hover {
top: -1px;
left: -1px;
right: -1px;
bottom: -1px;
}
`}
}
&:hover:after {
border: 2px solid ${(props) => props.$color};
box-shadow: inset 0 0 0 2px ${(props) => props.theme.background};
}
`;
export default withTranslation()(AvatarWithPresence);
@@ -1,6 +1,6 @@
// @flow
import Avatar from "./Avatar";
import AvatarWithPresence from "./AvatarWithPresence";
export { AvatarWithPresence };
export default Avatar;
-17
View File
@@ -1,17 +0,0 @@
// @flow
import styled from "styled-components";
const Badge = styled.span`
margin-left: 10px;
padding: 2px 6px 3px;
background-color: ${({ primary, theme }) =>
primary ? theme.primary : theme.textTertiary};
color: ${({ primary, theme }) => (primary ? theme.white : theme.background)};
border-radius: 4px;
font-size: 11px;
font-weight: 500;
text-transform: uppercase;
user-select: none;
`;
export default Badge;
+19
View File
@@ -0,0 +1,19 @@
import styled from "styled-components";
const Badge = styled.span<{ yellow?: boolean; primary?: boolean }>`
margin-left: 10px;
padding: 1px 5px 2px;
background-color: ${({ yellow, primary, theme }) =>
yellow ? theme.yellow : primary ? theme.primary : "transparent"};
color: ${({ primary, yellow, theme }) =>
primary ? theme.white : yellow ? theme.almostBlack : theme.textTertiary};
border: 1px solid
${({ primary, yellow, theme }) =>
primary || yellow ? "transparent" : theme.textTertiary};
border-radius: 8px;
font-size: 12px;
font-weight: 500;
user-select: none;
`;
export default Badge;
@@ -1,11 +1,11 @@
// @flow
import * as React from "react";
import styled from "styled-components";
import breakpoint from "styled-components-breakpoint";
import env from "~/env";
import OutlineLogo from "./OutlineLogo";
import env from "env";
type Props = {
href?: string,
href?: string;
};
function Branding({ href = env.URL }: Props) {
@@ -18,9 +18,8 @@ function Branding({ href = env.URL }: Props) {
}
const Link = styled.a`
position: fixed;
bottom: 0;
left: 0;
justify-content: center;
padding-bottom: 16px;
font-weight: 600;
font-size: 14px;
@@ -29,7 +28,6 @@ const Link = styled.a`
color: ${(props) => props.theme.text};
display: flex;
align-items: center;
padding: 16px;
svg {
fill: ${(props) => props.theme.text};
@@ -38,6 +36,14 @@ const Link = styled.a`
&:hover {
background: ${(props) => props.theme.sidebarBackground};
}
${breakpoint("tablet")`
z-index: ${(props: any) => props.theme.depths.sidebar + 1};
position: fixed;
bottom: 0;
left: 0;
padding: 16px;
`};
`;
export default Branding;
-162
View File
@@ -1,162 +0,0 @@
// @flow
import { observer, inject } from "mobx-react";
import {
PadlockIcon,
GoToIcon,
MoreIcon,
ShapesIcon,
EditIcon,
} from "outline-icons";
import * as React from "react";
import { Link } from "react-router-dom";
import styled from "styled-components";
import breakpoint from "styled-components-breakpoint";
import CollectionsStore from "stores/CollectionsStore";
import Document from "models/Document";
import CollectionIcon from "components/CollectionIcon";
import Flex from "components/Flex";
import BreadcrumbMenu from "./BreadcrumbMenu";
import { collectionUrl } from "utils/routeHelpers";
type Props = {
document: Document,
collections: CollectionsStore,
onlyText: boolean,
};
const Breadcrumb = observer(({ document, collections, onlyText }: Props) => {
const collection = collections.get(document.collectionId);
if (!collection) return <div />;
const path = collection.pathToDocument(document).slice(0, -1);
if (onlyText === true) {
return (
<>
{collection.private && (
<>
<SmallPadlockIcon color="currentColor" size={16} />{" "}
</>
)}
{collection.name}
{path.map((n) => (
<React.Fragment key={n.id}>
<SmallSlash />
{n.title}
</React.Fragment>
))}
</>
);
}
const isTemplate = document.isTemplate;
const isDraft = !document.publishedAt && !isTemplate;
const isNestedDocument = path.length > 1;
const lastPath = path.length ? path[path.length - 1] : undefined;
const menuPath = isNestedDocument ? path.slice(0, -1) : [];
return (
<Wrapper justify="flex-start" align="center">
{isTemplate && (
<>
<CollectionName to="/templates">
<ShapesIcon color="currentColor" />
&nbsp;
<span>Templates</span>
</CollectionName>
<Slash />
</>
)}
{isDraft && (
<>
<CollectionName to="/drafts">
<EditIcon color="currentColor" />
&nbsp;
<span>Drafts</span>
</CollectionName>
<Slash />
</>
)}
<CollectionName to={collectionUrl(collection.id)}>
<CollectionIcon collection={collection} expanded />
&nbsp;
<span>{collection.name}</span>
</CollectionName>
{isNestedDocument && (
<>
<Slash /> <BreadcrumbMenu label={<Overflow />} path={menuPath} />
</>
)}
{lastPath && (
<>
<Slash />{" "}
<Crumb to={lastPath.url} title={lastPath.title}>
{lastPath.title}
</Crumb>
</>
)}
</Wrapper>
);
});
const Wrapper = styled(Flex)`
display: none;
${breakpoint("tablet")`
display: flex;
`};
`;
const SmallPadlockIcon = styled(PadlockIcon)`
display: inline-block;
vertical-align: sub;
`;
const SmallSlash = styled(GoToIcon)`
width: 15px;
height: 10px;
flex-shrink: 0;
opacity: 0.25;
`;
export const Slash = styled(GoToIcon)`
flex-shrink: 0;
fill: ${(props) => props.theme.divider};
`;
const Overflow = styled(MoreIcon)`
flex-shrink: 0;
opacity: 0.25;
transition: opacity 100ms ease-in-out;
&:hover,
&:active {
opacity: 1;
}
`;
const Crumb = styled(Link)`
color: ${(props) => props.theme.text};
font-size: 15px;
height: 24px;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
&:hover {
text-decoration: underline;
}
`;
const CollectionName = styled(Link)`
display: flex;
flex-shrink: 0;
color: ${(props) => props.theme.text};
font-size: 15px;
font-weight: 500;
white-space: nowrap;
overflow: hidden;
`;
export default inject("collections")(Breadcrumb);
+84
View File
@@ -0,0 +1,84 @@
import { GoToIcon } from "outline-icons";
import * as React from "react";
import { Link } from "react-router-dom";
import styled from "styled-components";
import Flex from "~/components/Flex";
import BreadcrumbMenu from "~/menus/BreadcrumbMenu";
import { MenuInternalLink } from "~/types";
type Props = {
items: MenuInternalLink[];
max?: number;
children?: React.ReactNode;
highlightFirstItem?: boolean;
};
function Breadcrumb({ items, highlightFirstItem, children, max = 2 }: Props) {
const totalItems = items.length;
const topLevelItems: MenuInternalLink[] = [...items];
let overflowItems;
// chop middle breadcrumbs and present a "..." menu instead
if (totalItems > max) {
const halfMax = Math.floor(max / 2);
overflowItems = topLevelItems.splice(halfMax, totalItems - max);
topLevelItems.splice(halfMax, 0, {
to: "",
type: "route",
title: <BreadcrumbMenu items={overflowItems as MenuInternalLink[]} />,
});
}
return (
<Flex justify="flex-start" align="center">
{topLevelItems.map((item, index) => (
<React.Fragment key={item.to || index}>
{item.icon}
{item.to ? (
<Item
to={item.to}
$withIcon={!!item.icon}
$highlight={!!highlightFirstItem && index === 0}
>
{item.title}
</Item>
) : (
item.title
)}
{index !== topLevelItems.length - 1 || !!children ? <Slash /> : null}
</React.Fragment>
))}
{children}
</Flex>
);
}
const Slash = styled(GoToIcon)`
flex-shrink: 0;
fill: ${(props) => props.theme.divider};
`;
const Item = styled(Link)<{ $highlight: boolean; $withIcon: boolean }>`
display: flex;
flex-shrink: 1;
min-width: 0;
color: ${(props) => props.theme.text};
font-size: 15px;
height: 24px;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
font-weight: ${(props) => (props.$highlight ? "500" : "inherit")};
margin-left: ${(props) => (props.$withIcon ? "4px" : "0")};
svg {
flex-shrink: 0;
}
&:hover {
text-decoration: underline;
}
`;
export default Breadcrumb;
-25
View File
@@ -1,25 +0,0 @@
// @flow
import * as React from "react";
import { Link } from "react-router-dom";
import { DropdownMenu, DropdownMenuItem } from "components/DropdownMenu";
type Props = {
label: React.Node,
path: Array<any>,
};
export default class BreadcrumbMenu extends React.Component<Props> {
render() {
const { path } = this.props;
return (
<DropdownMenu label={this.props.label} position="center">
{path.map((item) => (
<DropdownMenuItem as={Link} to={item.url} key={item.id}>
{item.title}
</DropdownMenuItem>
))}
</DropdownMenu>
);
}
}
@@ -1,13 +1,16 @@
// @flow
import * as React from "react";
import styled from "styled-components";
import { bounceIn } from "shared/styles/animations";
import { bounceIn } from "~/styles/animations";
type Props = {
count: number,
count: number;
};
const Bubble = ({ count }: Props) => {
if (!count) {
return null;
}
return <Count>{count}</Count>;
};
-160
View File
@@ -1,160 +0,0 @@
// @flow
import { ExpandedIcon } from "outline-icons";
import { darken, lighten } from "polished";
import * as React from "react";
import styled from "styled-components";
const RealButton = styled.button`
display: ${(props) => (props.fullwidth ? "block" : "inline-block")};
width: ${(props) => (props.fullwidth ? "100%" : "auto")};
margin: 0;
padding: 0;
border: 0;
background: ${(props) => props.theme.buttonBackground};
color: ${(props) => props.theme.buttonText};
box-shadow: rgba(0, 0, 0, 0.2) 0px 1px 2px;
border-radius: 4px;
font-size: 14px;
font-weight: 500;
height: 32px;
text-decoration: none;
flex-shrink: 0;
outline: none;
cursor: pointer;
user-select: none;
svg {
fill: ${(props) => props.iconColor || props.theme.buttonText};
}
&::-moz-focus-inner {
padding: 0;
border: 0;
}
&:hover {
background: ${(props) => darken(0.05, props.theme.buttonBackground)};
}
&:focus {
transition-duration: 0.05s;
box-shadow: ${(props) => lighten(0.4, props.theme.buttonBackground)} 0px 0px
0px 3px;
outline: none;
}
&:disabled {
cursor: default;
pointer-events: none;
color: ${(props) => props.theme.white50};
}
${(props) =>
props.neutral &&
`
background: ${props.theme.buttonNeutralBackground};
color: ${props.theme.buttonNeutralText};
box-shadow: ${
props.borderOnHover ? "none" : "rgba(0, 0, 0, 0.07) 0px 1px 2px"
};
border: 1px solid ${
props.borderOnHover ? "transparent" : props.theme.buttonNeutralBorder
};
svg {
fill: ${props.iconColor || props.theme.buttonNeutralText};
}
&:hover {
background: ${darken(0.05, props.theme.buttonNeutralBackground)};
border: 1px solid ${props.theme.buttonNeutralBorder};
}
&:focus {
transition-duration: 0.05s;
border: 1px solid ${lighten(0.4, props.theme.buttonBackground)};
box-shadow: ${lighten(0.4, props.theme.buttonBackground)} 0px 0px
0px 2px;
}
&:disabled {
color: ${props.theme.textTertiary};
}
`} ${(props) =>
props.danger &&
`
background: ${props.theme.danger};
color: ${props.theme.white};
&:hover {
background: ${darken(0.05, props.theme.danger)};
}
&:focus {
transition-duration: 0.05s;
box-shadow: ${lighten(0.4, props.theme.danger)} 0px 0px
0px 3px;
}
`};
`;
const Label = styled.span`
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
${(props) => props.hasIcon && "padding-left: 4px;"};
`;
export const Inner = styled.span`
display: flex;
padding: 0 8px;
padding-right: ${(props) => (props.disclosure ? 2 : 8)}px;
line-height: ${(props) => (props.hasIcon ? 24 : 32)}px;
justify-content: center;
align-items: center;
min-height: 30px;
${(props) => props.hasIcon && props.hasText && "padding-left: 4px;"};
${(props) => props.hasIcon && !props.hasText && "padding: 0 4px;"};
`;
export type Props = {
type?: string,
value?: string,
icon?: React.Node,
iconColor?: string,
className?: string,
children?: React.Node,
innerRef?: React.ElementRef<any>,
disclosure?: boolean,
fullwidth?: boolean,
borderOnHover?: boolean,
};
function Button({
type = "text",
icon,
children,
value,
disclosure,
innerRef,
...rest
}: Props) {
const hasText = children !== undefined || value !== undefined;
const hasIcon = icon !== undefined;
return (
<RealButton type={type} ref={innerRef} {...rest}>
<Inner hasIcon={hasIcon} hasText={hasText} disclosure={disclosure}>
{hasIcon && icon}
{hasText && <Label hasIcon={hasIcon}>{children || value}</Label>}
{disclosure && <ExpandedIcon />}
</Inner>
</RealButton>
);
}
export default React.forwardRef<Props, typeof Button>((props, ref) => (
<Button {...props} innerRef={ref} />
));
+171
View File
@@ -0,0 +1,171 @@
import { ExpandedIcon } from "outline-icons";
import { darken } from "polished";
import * as React from "react";
import styled from "styled-components";
const RealButton = styled.button<{
fullwidth?: boolean;
borderOnHover?: boolean;
$neutral?: boolean;
danger?: boolean;
iconColor?: string;
}>`
display: ${(props) => (props.fullwidth ? "block" : "inline-block")};
width: ${(props) => (props.fullwidth ? "100%" : "auto")};
margin: 0;
padding: 0;
border: 0;
background: ${(props) => props.theme.buttonBackground};
color: ${(props) => props.theme.buttonText};
box-shadow: rgba(0, 0, 0, 0.2) 0px 1px 2px;
border-radius: 4px;
font-size: 14px;
font-weight: 500;
height: 32px;
text-decoration: none;
flex-shrink: 0;
cursor: pointer;
user-select: none;
${(props) =>
!props.borderOnHover &&
`
svg {
fill: ${props.iconColor || "currentColor"};
}
`}
&::-moz-focus-inner {
padding: 0;
border: 0;
}
&:hover:not(:disabled) {
background: ${(props) => darken(0.05, props.theme.buttonBackground)};
}
&:disabled {
cursor: default;
pointer-events: none;
color: ${(props) => props.theme.white50};
svg {
fill: ${(props) => props.theme.white50};
}
}
${(props) =>
props.$neutral &&
`
background: ${props.theme.buttonNeutralBackground};
color: ${props.theme.buttonNeutralText};
box-shadow: ${
props.borderOnHover
? "none"
: `rgba(0, 0, 0, 0.07) 0px 1px 2px, ${props.theme.buttonNeutralBorder} 0 0 0 1px inset`
};
${
props.borderOnHover
? ""
: `svg {
fill: ${props.iconColor || "currentColor"};
}`
}
&:hover:not(:disabled) {
background: ${
props.borderOnHover
? props.theme.buttonNeutralBackground
: darken(0.05, props.theme.buttonNeutralBackground)
};
box-shadow: rgba(0, 0, 0, 0.07) 0px 1px 2px, ${
props.theme.buttonNeutralBorder
} 0 0 0 1px inset;
}
&:disabled {
color: ${props.theme.textTertiary};
svg {
fill: currentColor;
}
}
`}
${(props) =>
props.danger &&
`
background: ${props.theme.danger};
color: ${props.theme.white};
&:hover:not(:disabled) {
background: ${darken(0.05, props.theme.danger)};
}
`};
`;
const Label = styled.span<{ hasIcon?: boolean }>`
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
${(props) => props.hasIcon && "padding-left: 4px;"};
`;
export const Inner = styled.span<{
disclosure?: boolean;
hasIcon?: boolean;
hasText?: boolean;
}>`
display: flex;
padding: 0 8px;
padding-right: ${(props) => (props.disclosure ? 2 : 8)}px;
line-height: ${(props) => (props.hasIcon ? 24 : 32)}px;
justify-content: center;
align-items: center;
min-height: 32px;
${(props) => props.hasIcon && props.hasText && "padding-left: 4px;"};
${(props) => props.hasIcon && !props.hasText && "padding: 0 4px;"};
`;
export type Props<T> = {
icon?: React.ReactNode;
iconColor?: string;
children?: React.ReactNode;
disclosure?: boolean;
neutral?: boolean;
danger?: boolean;
primary?: boolean;
fullwidth?: boolean;
as?: T;
to?: string;
borderOnHover?: boolean;
href?: string;
"data-on"?: string;
"data-event-category"?: string;
"data-event-action"?: string;
};
const Button = <T extends React.ElementType = "button">(
props: Props<T> & React.ComponentPropsWithoutRef<T>,
ref: React.Ref<HTMLButtonElement>
) => {
const { type, icon, children, value, disclosure, neutral, ...rest } = props;
const hasText = children !== undefined || value !== undefined;
const hasIcon = icon !== undefined;
return (
<RealButton type={type || "button"} ref={ref} $neutral={neutral} {...rest}>
<Inner hasIcon={hasIcon} hasText={hasText} disclosure={disclosure}>
{hasIcon && icon}
{hasText && <Label hasIcon={hasIcon}>{children || value}</Label>}
{disclosure && <ExpandedIcon color="currentColor" />}
</Inner>
</RealButton>
);
};
export default React.forwardRef(Button);
@@ -1,4 +1,3 @@
// @flow
import styled from "styled-components";
import Button, { Inner } from "./Button";
+26
View File
@@ -0,0 +1,26 @@
import * as React from "react";
import styled from "styled-components";
type Props = {
onClick?: React.MouseEventHandler<HTMLButtonElement>;
children: React.ReactNode;
};
const ButtonLink = React.forwardRef(
(props: Props, ref: React.Ref<HTMLButtonElement>) => {
return <Button {...props} ref={ref} />;
}
);
const Button = styled.button`
margin: 0;
padding: 0;
border: 0;
color: ${(props) => props.theme.link};
line-height: inherit;
background: none;
text-decoration: none;
cursor: pointer;
`;
export default ButtonLink;
@@ -1,25 +1,29 @@
// @flow
import * as React from "react";
import styled from "styled-components";
import breakpoint from "styled-components-breakpoint";
type Props = {
children?: React.Node,
children?: React.ReactNode;
withStickyHeader?: boolean;
};
const Container = styled.div`
const Container = styled.div<{ withStickyHeader?: boolean }>`
width: 100%;
max-width: 100vw;
padding: 60px 20px;
padding: ${(props) => (props.withStickyHeader ? "4px 12px" : "60px 12px")};
${breakpoint("tablet")`
padding: 60px;
padding: ${(props: any) => (props.withStickyHeader ? "4px 60px" : "60px")};
`};
`;
const Content = styled.div`
max-width: 46em;
margin: 0 auto;
${breakpoint("desktopLarge")`
max-width: 52em;
`};
`;
const CenteredContent = ({ children, ...rest }: Props) => {
-61
View File
@@ -1,61 +0,0 @@
// @flow
import * as React from "react";
import styled from "styled-components";
import HelpText from "components/HelpText";
import VisuallyHidden from "components/VisuallyHidden";
export type Props = {
checked?: boolean,
label?: string,
labelHidden?: boolean,
className?: string,
note?: string,
short?: boolean,
small?: boolean,
};
const LabelText = styled.span`
font-weight: 500;
margin-left: ${(props) => (props.small ? "6px" : "10px")};
${(props) => (props.small ? `color: ${props.theme.textSecondary}` : "")};
`;
const Wrapper = styled.div`
padding-bottom: 8px;
${(props) => (props.small ? "font-size: 14px" : "")};
`;
const Label = styled.label`
display: flex;
align-items: center;
user-select: none;
`;
export default function Checkbox({
label,
labelHidden,
note,
className,
small,
short,
...rest
}: Props) {
const wrappedLabel = <LabelText small={small}>{label}</LabelText>;
return (
<>
<Wrapper small={small}>
<Label>
<input type="checkbox" {...rest} />
{label &&
(labelHidden ? (
<VisuallyHidden>{wrappedLabel}</VisuallyHidden>
) : (
wrappedLabel
))}
</Label>
{note && <HelpText small>{note}</HelpText>}
</Wrapper>
</>
);
}
+80
View File
@@ -0,0 +1,80 @@
import React from "react";
import styled, { useTheme } from "styled-components";
const cleanPercentage = (percentage: number) => {
const tooLow = !Number.isFinite(+percentage) || percentage < 0;
const tooHigh = percentage > 100;
return tooLow ? 0 : tooHigh ? 100 : +percentage;
};
const Circle = ({
color,
percentage,
offset,
}: {
color: string;
percentage?: number;
offset: number;
}) => {
const radius = offset * 0.7;
const circumference = 2 * Math.PI * radius;
let strokePercentage;
if (percentage) {
// because the circle is so small, anything greater than 85% appears like 100%
percentage = percentage > 85 && percentage < 100 ? 85 : percentage;
strokePercentage = percentage
? ((100 - percentage) * circumference) / 100
: 0;
}
return (
<circle
r={radius}
cx={offset}
cy={offset}
fill="none"
stroke={strokePercentage !== circumference ? color : ""}
strokeWidth={2.5}
strokeDasharray={circumference}
strokeDashoffset={percentage ? strokePercentage : 0}
strokeLinecap="round"
style={{
transition: "stroke-dashoffset 0.6s ease 0s",
}}
></circle>
);
};
const CircularProgressBar = ({
percentage,
size = 16,
}: {
percentage: number;
size?: number;
}) => {
const theme = useTheme();
percentage = cleanPercentage(percentage);
const offset = Math.floor(size / 2);
return (
<SVG width={size} height={size}>
<g transform={`rotate(-90 ${offset} ${offset})`}>
<Circle color={theme.progressBarBackground} offset={offset} />
{percentage > 0 && (
<Circle
color={theme.primary}
percentage={percentage}
offset={offset}
/>
)}
</g>
</SVG>
);
};
const SVG = styled.svg`
flex-shrink: 0;
`;
export default CircularProgressBar;
@@ -1,7 +1,6 @@
// @flow
import styled from "styled-components";
const ClickablePadding = styled.div`
const ClickablePadding = styled.div<{ grow?: boolean }>`
min-height: 10em;
cursor: ${({ onClick }) => (onClick ? "text" : "default")};
${({ grow }) => grow && `flex-grow: 100;`};
-76
View File
@@ -1,76 +0,0 @@
// @flow
import { sortBy, keyBy } from "lodash";
import { observer, inject } from "mobx-react";
import * as React from "react";
import { MAX_AVATAR_DISPLAY } from "shared/constants";
import DocumentPresenceStore from "stores/DocumentPresenceStore";
import ViewsStore from "stores/ViewsStore";
import Document from "models/Document";
import { AvatarWithPresence } from "components/Avatar";
import Facepile from "components/Facepile";
type Props = {
views: ViewsStore,
presence: DocumentPresenceStore,
document: Document,
currentUserId: string,
};
@observer
class Collaborators extends React.Component<Props> {
componentDidMount() {
this.props.views.fetchPage({ documentId: this.props.document.id });
}
render() {
const { document, presence, views, currentUserId } = this.props;
let documentPresence = presence.get(document.id);
documentPresence = documentPresence
? Array.from(documentPresence.values())
: [];
const documentViews = views.inDocument(document.id);
const presentIds = documentPresence.map((p) => p.userId);
const editingIds = documentPresence
.filter((p) => p.isEditing)
.map((p) => p.userId);
// ensure currently present via websocket are always ordered first
const mostRecentViewers = sortBy(
documentViews.slice(0, MAX_AVATAR_DISPLAY),
(view) => {
return presentIds.includes(view.user.id);
}
);
const viewersKeyedByUserId = keyBy(mostRecentViewers, (v) => v.user.id);
const overflow = documentViews.length - mostRecentViewers.length;
return (
<Facepile
users={mostRecentViewers.map((v) => v.user)}
overflow={overflow}
renderAvatar={(user) => {
const isPresent = presentIds.includes(user.id);
const isEditing = editingIds.includes(user.id);
const { lastViewedAt } = viewersKeyedByUserId[user.id];
return (
<AvatarWithPresence
key={user.id}
user={user}
lastViewedAt={lastViewedAt}
isPresent={isPresent}
isEditing={isEditing}
isCurrentUser={currentUserId === user.id}
/>
);
}}
/>
);
}
}
export default inject("views", "presence")(Collaborators);
+124
View File
@@ -0,0 +1,124 @@
import { sortBy, filter, uniq, isEqual } from "lodash";
import { observer } from "mobx-react";
import * as React from "react";
import { useTranslation } from "react-i18next";
import { usePopoverState, PopoverDisclosure } from "reakit/Popover";
import Document from "~/models/Document";
import { AvatarWithPresence } from "~/components/Avatar";
import DocumentViews from "~/components/DocumentViews";
import Facepile from "~/components/Facepile";
import NudeButton from "~/components/NudeButton";
import Popover from "~/components/Popover";
import useCurrentTeam from "~/hooks/useCurrentTeam";
import useCurrentUser from "~/hooks/useCurrentUser";
import useStores from "~/hooks/useStores";
type Props = {
document: Document;
};
function Collaborators(props: Props) {
const { t } = useTranslation();
const user = useCurrentUser();
const team = useCurrentTeam();
const currentUserId = user?.id;
const [requestedUserIds, setRequestedUserIds] = React.useState<string[]>([]);
const { users, presence, ui } = useStores();
const { document } = props;
const documentPresence = presence.get(document.id);
const documentPresenceArray = documentPresence
? Array.from(documentPresence.values())
: [];
const presentIds = documentPresenceArray.map((p) => p.userId);
const editingIds = documentPresenceArray
.filter((p) => p.isEditing)
.map((p) => p.userId);
// ensure currently present via websocket are always ordered first
const collaborators = React.useMemo(
() =>
sortBy(
filter(
users.orderedData,
(user) =>
presentIds.includes(user.id) ||
document.collaboratorIds.includes(user.id)
),
(user) => presentIds.includes(user.id)
),
[document.collaboratorIds, users.orderedData, presentIds]
);
// load any users we don't yet have in memory
React.useEffect(() => {
const userIdsToFetch = uniq([
...document.collaboratorIds,
...presentIds,
]).filter((userId) => !users.get(userId));
if (!isEqual(requestedUserIds, userIdsToFetch)) {
setRequestedUserIds(userIdsToFetch);
}
userIdsToFetch
.filter((userId) => requestedUserIds.includes(userId))
.forEach((userId) => users.fetch(userId));
}, [document, users, presentIds, document.collaboratorIds, requestedUserIds]);
const popover = usePopoverState({
gutter: 0,
placement: "bottom-end",
});
return (
<>
<PopoverDisclosure {...popover}>
{(props) => (
<NudeButton width={collaborators.length * 32} height={32} {...props}>
<Facepile
users={collaborators}
renderAvatar={(collaborator) => {
const isPresent = presentIds.includes(collaborator.id);
const isEditing = editingIds.includes(collaborator.id);
const isObserving = ui.observingUserId === collaborator.id;
const isObservable =
team.collaborativeEditing && collaborator.id !== user.id;
return (
<AvatarWithPresence
key={collaborator.id}
user={collaborator}
isPresent={isPresent}
isEditing={isEditing}
isObserving={isObserving}
isCurrentUser={currentUserId === collaborator.id}
profileOnClick={false}
onClick={
isObservable
? (ev) => {
if (isPresent) {
ev.preventDefault();
ev.stopPropagation();
ui.setObservingUser(
isObserving ? undefined : collaborator.id
);
}
}
: undefined
}
/>
);
}}
/>
</NudeButton>
)}
</PopoverDisclosure>
<Popover {...popover} width={300} aria-label={t("Viewers")} tabIndex={0}>
<DocumentViews document={document} isOpen={popover.visible} />
</Popover>
</>
);
}
export default observer(Collaborators);
+226
View File
@@ -0,0 +1,226 @@
import { observer } from "mobx-react";
import { transparentize } from "polished";
import * as React from "react";
import { useTranslation } from "react-i18next";
import styled from "styled-components";
import Collection from "~/models/Collection";
import Arrow from "~/components/Arrow";
import ButtonLink from "~/components/ButtonLink";
import Editor from "~/components/Editor";
import LoadingIndicator from "~/components/LoadingIndicator";
import NudeButton from "~/components/NudeButton";
import useDebouncedCallback from "~/hooks/useDebouncedCallback";
import useStores from "~/hooks/useStores";
import useToasts from "~/hooks/useToasts";
type Props = {
collection: Collection;
};
function CollectionDescription({ collection }: Props) {
const { collections, policies } = useStores();
const { showToast } = useToasts();
const { t } = useTranslation();
const [isExpanded, setExpanded] = React.useState(false);
const [isEditing, setEditing] = React.useState(false);
const [isDirty, setDirty] = React.useState(false);
const can = policies.abilities(collection.id);
const handleStartEditing = React.useCallback(() => {
setEditing(true);
}, []);
const handleStopEditing = React.useCallback(() => {
setEditing(false);
}, []);
const handleClickDisclosure = React.useCallback(
(event) => {
event.preventDefault();
if (isExpanded && document.activeElement) {
// @ts-expect-error ts-migrate(2339) FIXME: Property 'blur' does not exist on type 'Element'.
document.activeElement.blur();
}
setExpanded(!isExpanded);
},
[isExpanded]
);
const handleSave = useDebouncedCallback(async (getValue) => {
try {
await collection.save({
description: getValue(),
});
setDirty(false);
} catch (err) {
showToast(
t("Sorry, an error occurred saving the collection", {
type: "error",
})
);
throw err;
}
}, 1000);
const handleChange = React.useCallback(
(getValue) => {
setDirty(true);
handleSave(getValue);
},
[handleSave]
);
React.useEffect(() => {
setEditing(false);
}, [collection.id]);
const placeholder = `${t("Add a description")}`;
const key = isEditing || isDirty ? "draft" : collection.updatedAt;
return (
<MaxHeight data-editing={isEditing} data-expanded={isExpanded}>
<Input data-editing={isEditing} data-expanded={isExpanded}>
<span onClick={can.update ? handleStartEditing : undefined}>
{collections.isSaving && <LoadingIndicator />}
{collection.hasDescription || isEditing || isDirty ? (
<React.Suspense
fallback={
<Placeholder
onClick={() => {
//
}}
>
Loading
</Placeholder>
}
>
<Editor
key={key}
defaultValue={collection.description || ""}
onChange={handleChange}
placeholder={placeholder}
readOnly={!isEditing}
autoFocus={isEditing}
onBlur={handleStopEditing}
maxLength={1000}
embedsDisabled
readOnlyWriteCheckboxes
grow
/>
</React.Suspense>
) : (
can.update && (
<Placeholder
onClick={() => {
//
}}
>
{placeholder}
</Placeholder>
)
)}
</span>
</Input>
{!isEditing && (
<Disclosure
onClick={handleClickDisclosure}
aria-label={isExpanded ? t("Collapse") : t("Expand")}
size={30}
>
<Arrow />
</Disclosure>
)}
</MaxHeight>
);
}
const Disclosure = styled(NudeButton)`
opacity: 0;
color: ${(props) => props.theme.divider};
position: absolute;
top: calc(25vh - 50px);
left: 50%;
z-index: 1;
transform: rotate(-90deg) translateX(-50%);
transition: opacity 100ms ease-in-out;
&:focus,
&:hover {
opacity: 1;
}
&:active {
color: ${(props) => props.theme.sidebarText};
}
`;
const Placeholder = styled(ButtonLink)`
color: ${(props) => props.theme.placeholder};
cursor: text;
min-height: 27px;
`;
const MaxHeight = styled.div`
position: relative;
max-height: 25vh;
overflow: hidden;
margin: -12px -8px -8px;
padding: 8px;
&[data-editing="true"],
&[data-expanded="true"] {
max-height: initial;
overflow: initial;
${Disclosure} {
top: initial;
bottom: 0;
transform: rotate(90deg) translateX(-50%);
}
}
&:hover ${Disclosure} {
opacity: 1;
}
`;
const Input = styled.div`
margin: -8px;
padding: 8px;
border-radius: 8px;
transition: ${(props) => props.theme.backgroundTransition};
&:after {
content: "";
position: absolute;
top: calc(25vh - 50px);
left: 0;
right: 0;
height: 50px;
pointer-events: none;
background: linear-gradient(
180deg,
${(props) => transparentize(1, props.theme.background)} 0%,
${(props) => props.theme.background} 100%
);
}
&[data-editing="true"],
&[data-expanded="true"] {
&:after {
background: transparent;
}
}
&[data-editing="true"] {
background: ${(props) => props.theme.secondaryBackground};
}
.block-menu-trigger,
.heading-anchor {
display: none !important;
}
`;
export default observer(CollectionDescription);
-45
View File
@@ -1,45 +0,0 @@
// @flow
import { inject, observer } from "mobx-react";
import { PrivateCollectionIcon, CollectionIcon } from "outline-icons";
import { getLuminance } from "polished";
import * as React from "react";
import UiStore from "stores/UiStore";
import Collection from "models/Collection";
import { icons } from "components/IconPicker";
type Props = {
collection: Collection,
expanded?: boolean,
size?: number,
ui: UiStore,
};
function ResolvedCollectionIcon({ collection, expanded, size, ui }: Props) {
// If the chosen icon color is very dark then we invert it in dark mode
// otherwise it will be impossible to see against the dark background.
const color =
ui.resolvedTheme === "dark"
? getLuminance(collection.color) > 0.12
? collection.color
: "currentColor"
: collection.color;
if (collection.icon && collection.icon !== "collection") {
try {
const Component = icons[collection.icon].component;
return <Component color={color} size={size} />;
} catch (error) {
console.warn("Failed to render custom icon " + collection.icon);
}
}
if (collection.private) {
return (
<PrivateCollectionIcon color={color} expanded={expanded} size={size} />
);
}
return <CollectionIcon color={color} expanded={expanded} size={size} />;
}
export default inject("ui")(observer(ResolvedCollectionIcon));
+46
View File
@@ -0,0 +1,46 @@
import { observer } from "mobx-react";
import { CollectionIcon } from "outline-icons";
import { getLuminance } from "polished";
import * as React from "react";
import Collection from "~/models/Collection";
import { icons } from "~/components/IconPicker";
import useStores from "~/hooks/useStores";
type Props = {
collection: Collection;
expanded?: boolean;
size?: number;
color?: string;
};
function ResolvedCollectionIcon({
collection,
color: inputColor,
expanded,
size,
}: Props) {
const { ui } = useStores();
// If the chosen icon color is very dark then we invert it in dark mode
// otherwise it will be impossible to see against the dark background.
const color =
inputColor ||
(ui.resolvedTheme === "dark" && collection.color !== "currentColor"
? getLuminance(collection.color) > 0.09
? collection.color
: "currentColor"
: collection.color);
if (collection.icon && collection.icon !== "collection") {
try {
const Component = icons[collection.icon].component;
return <Component color={color} size={size} />;
} catch (error) {
console.warn("Failed to render custom icon " + collection.icon);
}
}
return <CollectionIcon color={color} expanded={expanded} size={size} />;
}
export default observer(ResolvedCollectionIcon);
+100
View File
@@ -0,0 +1,100 @@
import { useKBar, KBarPositioner, KBarAnimator, KBarSearch } from "kbar";
import { observer } from "mobx-react";
import * as React from "react";
import { useTranslation } from "react-i18next";
import { Portal } from "react-portal";
import styled from "styled-components";
import breakpoint from "styled-components-breakpoint";
import CommandBarResults from "~/components/CommandBarResults";
import rootActions from "~/actions/root";
import useCommandBarActions from "~/hooks/useCommandBarActions";
import { CommandBarAction } from "~/types";
export const CommandBarOptions = {
animations: {
enterMs: 250,
exitMs: 200,
},
};
function CommandBar() {
const { t } = useTranslation();
useCommandBarActions(rootActions);
const { rootAction } = useKBar((state) => ({
rootAction: state.currentRootActionId
? ((state.actions[
state.currentRootActionId
] as unknown) as CommandBarAction)
: undefined,
}));
return (
<KBarPortal>
<Positioner>
<Animator>
<SearchInput
placeholder={`${
rootAction?.placeholder ||
rootAction?.name ||
t("Type a command or search")
}`}
/>
<CommandBarResults />
</Animator>
</Positioner>
</KBarPortal>
);
}
function KBarPortal({ children }: { children: React.ReactNode }) {
const { showing } = useKBar((state) => ({
showing: state.visualState !== "hidden",
}));
if (!showing) {
return null;
}
return <Portal>{children}</Portal>;
}
const Positioner = styled(KBarPositioner)`
z-index: ${(props) => props.theme.depths.commandBar};
`;
const SearchInput = styled(KBarSearch)`
padding: 16px 20px;
width: 100%;
outline: none;
border: none;
background: ${(props) => props.theme.menuBackground};
color: ${(props) => props.theme.text};
&:disabled,
&::placeholder {
color: ${(props) => props.theme.placeholder};
}
`;
const Animator = styled(KBarAnimator)`
max-width: 600px;
max-height: 75vh;
width: 90vw;
background: ${(props) => props.theme.menuBackground};
color: ${(props) => props.theme.text};
border-radius: 8px;
overflow: hidden;
box-shadow: rgb(0 0 0 / 40%) 0px 16px 60px;
transition: max-width 0.2s ease-in-out;
${breakpoint("desktopLarge")`
max-width: 740px;
`};
@media print {
display: none;
}
`;
export default observer(CommandBar);
+122
View File
@@ -0,0 +1,122 @@
import { ActionImpl } from "kbar";
import { ArrowIcon, BackIcon } from "outline-icons";
import * as React from "react";
import styled, { css, useTheme } from "styled-components";
import Flex from "~/components/Flex";
import Key from "~/components/Key";
type Props = {
action: ActionImpl;
active: boolean;
currentRootActionId: string | null | undefined;
};
function CommandBarItem(
{ action, active, currentRootActionId }: Props,
ref: React.RefObject<HTMLDivElement>
) {
const theme = useTheme();
const ancestors = React.useMemo(() => {
if (!currentRootActionId) {
return action.ancestors;
}
const index = action.ancestors.findIndex(
(ancestor) => ancestor.id === currentRootActionId
);
// +1 removes the currentRootAction; e.g. if we are on the "Set theme"
// parent action, the UI should not display "Set theme… > Dark" but rather
// just "Dark"
return action.ancestors.slice(index + 1);
}, [action.ancestors, currentRootActionId]);
return (
<Item active={active} ref={ref}>
<Text align="center" gap={8}>
<Icon>
{action.icon ? (
// @ts-expect-error no icon on ActionImpl
React.cloneElement(action.icon, {
size: 22,
color: "currentColor",
})
) : (
<ArrowIcon color="currentColor" />
)}
</Icon>
{ancestors.map((ancestor) => (
<React.Fragment key={ancestor.id}>
<Ancestor>{ancestor.name}</Ancestor>
<ForwardIcon color={theme.textSecondary} size={22} />
</React.Fragment>
))}
{action.name}
{action.children?.length ? "…" : ""}
</Text>
{action.shortcut?.length ? (
<div
style={{
display: "grid",
gridAutoFlow: "column",
gap: "4px",
}}
>
{action.shortcut.map((sc: string) => (
<Key key={sc}>{sc}</Key>
))}
</div>
) : null}
</Item>
);
}
const Icon = styled(Flex)`
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
color: ${(props) => props.theme.textSecondary};
flex-shrink: 0;
`;
const Ancestor = styled.span`
color: ${(props) => props.theme.textSecondary};
`;
const Text = styled(Flex)`
overflow: hidden;
text-overflow: ellipsis;
flex-shrink: 1;
`;
const Item = styled.div<{ active?: boolean }>`
font-size: 15px;
padding: 10px 16px;
background: ${(props) =>
props.active ? props.theme.menuItemSelected : "none"};
display: flex;
align-items: center;
justify-content: space-between;
cursor: pointer;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
min-width: 0;
${(props) =>
props.active &&
css`
${Icon} {
color: ${props.theme.text};
}
`}
`;
const ForwardIcon = styled(BackIcon)`
transform: rotate(180deg);
flex-shrink: 0;
`;
export default React.forwardRef<HTMLDivElement, Props>(CommandBarItem);
+35
View File
@@ -0,0 +1,35 @@
import { useMatches, KBarResults } from "kbar";
import * as React from "react";
import styled from "styled-components";
import CommandBarItem from "~/components/CommandBarItem";
export default function CommandBarResults() {
const { results, rootActionId } = useMatches();
return (
<KBarResults
items={results}
maxHeight={400}
onRender={({ item, active }) =>
typeof item === "string" ? (
<Header>{item}</Header>
) : (
<CommandBarItem
action={item}
active={active}
currentRootActionId={rootActionId}
/>
)
}
/>
);
}
const Header = styled.h3`
font-size: 13px;
letter-spacing: 0.04em;
margin: 0;
padding: 16px 0 4px 20px;
color: ${(props) => props.theme.textTertiary};
height: 36px;
`;
+58
View File
@@ -0,0 +1,58 @@
import { observer } from "mobx-react";
import { DisconnectedIcon } from "outline-icons";
import * as React from "react";
import { useTranslation } from "react-i18next";
import styled, { useTheme } from "styled-components";
import breakpoint from "styled-components-breakpoint";
import Fade from "~/components/Fade";
import NudeButton from "~/components/NudeButton";
import Tooltip from "~/components/Tooltip";
import useStores from "~/hooks/useStores";
function ConnectionStatus() {
const { ui } = useStores();
const theme = useTheme();
const { t } = useTranslation();
return ui.multiplayerStatus === "connecting" ||
ui.multiplayerStatus === "disconnected" ? (
<Tooltip
tooltip={
<Centered>
<strong>{t("Server connection lost")}</strong>
<br />
{t("Edits you make will sync once youre online")}
</Centered>
}
placement="bottom"
>
<Button>
<Fade>
<DisconnectedIcon color={theme.sidebarText} />
</Fade>
</Button>
</Tooltip>
) : null;
}
const Button = styled(NudeButton)`
display: none;
position: fixed;
bottom: 0;
right: 32px;
margin: 24px;
${breakpoint("tablet")`
display: block;
`};
@media print {
display: none;
}
`;
const Centered = styled.div`
text-align: center;
`;
export default observer(ConnectionStatus);
+120
View File
@@ -0,0 +1,120 @@
import isPrintableKeyEvent from "is-printable-key-event";
import * as React from "react";
import styled from "styled-components";
type Props = Omit<React.HTMLAttributes<HTMLSpanElement>, "ref" | "onChange"> & {
disabled?: boolean;
readOnly?: boolean;
onClick?: React.MouseEventHandler<HTMLDivElement>;
onChange?: (text: string) => void;
onBlur?: React.FocusEventHandler<HTMLSpanElement> | undefined;
onInput?: React.FormEventHandler<HTMLSpanElement> | undefined;
onKeyDown?: React.KeyboardEventHandler<HTMLSpanElement> | undefined;
placeholder?: string;
maxLength?: number;
autoFocus?: boolean;
children?: React.ReactNode;
value: string;
};
/**
* Defines a content editable component with the same interface as a native
* HTMLInputElement (or, as close as we can get).
*/
const ContentEditable = React.forwardRef(
(
{
disabled,
onChange,
onInput,
onBlur,
onKeyDown,
value,
children,
className,
maxLength,
autoFocus,
placeholder,
readOnly,
dir,
onClick,
...rest
}: Props,
forwardedRef: React.RefObject<HTMLSpanElement>
) => {
const innerRef = React.useRef<HTMLSpanElement>(null);
const ref = forwardedRef || innerRef;
const [innerValue, setInnerValue] = React.useState<string>(value);
const lastValue = React.useRef("");
const wrappedEvent = (
callback:
| React.FocusEventHandler<HTMLSpanElement>
| React.FormEventHandler<HTMLSpanElement>
| React.KeyboardEventHandler<HTMLSpanElement>
| undefined
) => (event: any) => {
const text = ref.current?.innerText || "";
if (maxLength && isPrintableKeyEvent(event) && text.length >= maxLength) {
event?.preventDefault();
return;
}
if (text !== lastValue.current) {
lastValue.current = text;
onChange && onChange(text);
}
callback?.(event);
};
React.useLayoutEffect(() => {
if (autoFocus) {
ref.current?.focus();
}
});
React.useEffect(() => {
if (value !== ref.current?.innerText) {
setInnerValue(value);
}
}, [value, ref]);
return (
<div className={className} dir={dir} onClick={onClick}>
<Content
ref={ref}
contentEditable={!disabled && !readOnly}
onInput={wrappedEvent(onInput)}
onBlur={wrappedEvent(onBlur)}
onKeyDown={wrappedEvent(onKeyDown)}
data-placeholder={placeholder}
suppressContentEditableWarning
role="textbox"
{...rest}
>
{innerValue}
</Content>
{children}
</div>
);
}
);
const Content = styled.span`
&:empty {
display: inline-block;
}
&:empty::before {
display: inline-block;
color: ${(props) => props.theme.placeholder};
-webkit-text-fill-color: ${(props) => props.theme.placeholder};
content: attr(data-placeholder);
pointer-events: none;
height: 0;
}
`;
export default ContentEditable;
+12
View File
@@ -0,0 +1,12 @@
import styled from "styled-components";
const Header = styled.h3`
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
color: ${(props) => props.theme.sidebarText};
letter-spacing: 0.04em;
margin: 1em 12px 0.5em;
`;
export default Header;
+151
View File
@@ -0,0 +1,151 @@
import { CheckmarkIcon } from "outline-icons";
import * as React from "react";
import { MenuItem as BaseMenuItem } from "reakit/Menu";
import styled, { css } from "styled-components";
import breakpoint from "styled-components-breakpoint";
import { hover } from "~/styles";
import MenuIconWrapper from "../MenuIconWrapper";
type Props = {
onClick?: (arg0: React.SyntheticEvent) => void | Promise<void>;
children?: React.ReactNode;
selected?: boolean;
disabled?: boolean;
to?: string;
href?: string;
target?: "_blank";
as?: string | React.ComponentType<any>;
hide?: () => void;
level?: number;
icon?: React.ReactElement;
};
const MenuItem = ({
onClick,
children,
selected,
disabled,
as,
hide,
icon,
...rest
}: Props) => {
const handleClick = React.useCallback(
(ev) => {
if (onClick) {
ev.preventDefault();
ev.stopPropagation();
onClick(ev);
}
if (hide) {
hide();
}
},
[onClick, hide]
);
// Preventing default mousedown otherwise menu items do not work in Firefox,
// which triggers the hideOnClickOutside handler first via mousedown hiding
// and un-rendering the menu contents.
const handleMouseDown = React.useCallback((ev) => {
ev.preventDefault();
ev.stopPropagation();
}, []);
return (
<BaseMenuItem
onClick={disabled ? undefined : onClick}
disabled={disabled}
hide={hide}
{...rest}
>
{(props) => (
<MenuAnchor
{...props}
$toggleable={selected !== undefined}
as={onClick ? "button" : as}
onClick={handleClick}
onMouseDown={handleMouseDown}
>
{selected !== undefined && (
<>
{selected ? <CheckmarkIcon color="currentColor" /> : <Spacer />}
&nbsp;
</>
)}
{icon && (
<MenuIconWrapper>
{React.cloneElement(icon, { color: "currentColor" })}
</MenuIconWrapper>
)}
{children}
</MenuAnchor>
)}
</BaseMenuItem>
);
};
const Spacer = styled.svg`
width: 24px;
height: 24px;
flex-shrink: 0;
`;
export const MenuAnchorCSS = css<{ level?: number; disabled?: boolean }>`
display: flex;
margin: 0;
border: 0;
padding: 12px;
padding-left: ${(props) => 12 + (props.level || 0) * 10}px;
width: 100%;
min-height: 32px;
background: none;
color: ${(props) =>
props.disabled ? props.theme.textTertiary : props.theme.textSecondary};
justify-content: left;
align-items: center;
font-size: 16px;
cursor: default;
user-select: none;
white-space: nowrap;
svg:not(:last-child) {
margin-right: 4px;
}
svg {
flex-shrink: 0;
opacity: ${(props) => (props.disabled ? ".5" : 1)};
}
${(props) =>
props.disabled
? "pointer-events: none;"
: `
&:${hover},
&:focus,
&.focus-visible {
color: ${props.theme.white};
background: ${props.theme.primary};
box-shadow: none;
cursor: pointer;
svg {
fill: ${props.theme.white};
}
}
`};
${breakpoint("tablet")`
padding: 4px 12px;
font-size: 14px;
`};
`;
export const MenuAnchor = styled.a`
${MenuAnchorCSS}
`;
export default MenuItem;
@@ -0,0 +1,25 @@
import { MoreIcon } from "outline-icons";
import * as React from "react";
import { MenuButton } from "reakit/Menu";
import NudeButton from "~/components/NudeButton";
type Props = React.ComponentProps<typeof MenuButton> & {
className?: string;
iconColor?: string;
};
export default function OverflowMenuButton({
iconColor,
className,
...rest
}: Props) {
return (
<MenuButton {...rest}>
{(props) => (
<NudeButton className={className} {...props}>
<MoreIcon color={iconColor} />
</NudeButton>
)}
</MenuButton>
);
}
+15
View File
@@ -0,0 +1,15 @@
import * as React from "react";
import { MenuSeparator } from "reakit/Menu";
import styled from "styled-components";
export default function Separator(rest: any) {
return (
<MenuSeparator {...rest}>
{(props) => <HorizontalRule {...props} />}
</MenuSeparator>
);
}
const HorizontalRule = styled.hr`
margin: 0.5em 12px;
`;
+217
View File
@@ -0,0 +1,217 @@
import { ExpandedIcon } from "outline-icons";
import * as React from "react";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import {
useMenuState,
MenuButton,
MenuItem as BaseMenuItem,
} from "reakit/Menu";
import styled, { useTheme } from "styled-components";
import Flex from "~/components/Flex";
import MenuIconWrapper from "~/components/MenuIconWrapper";
import { actionToMenuItem } from "~/actions";
import useActionContext from "~/hooks/useActionContext";
import {
Action,
ActionContext,
MenuSeparator,
MenuHeading,
MenuItem as TMenuItem,
} from "~/types";
import Header from "./Header";
import MenuItem, { MenuAnchor } from "./MenuItem";
import Separator from "./Separator";
import ContextMenu from ".";
type Props = {
actions?: (Action | MenuSeparator | MenuHeading)[];
context?: Partial<ActionContext>;
items?: TMenuItem[];
};
const Disclosure = styled(ExpandedIcon)`
transform: rotate(270deg);
position: absolute;
right: 8px;
`;
const Submenu = React.forwardRef(
(
{
templateItems,
title,
...rest
}: { templateItems: TMenuItem[]; title: React.ReactNode },
ref: React.LegacyRef<HTMLButtonElement>
) => {
const { t } = useTranslation();
const theme = useTheme();
const menu = useMenuState();
return (
<>
<MenuButton ref={ref} {...menu} {...rest}>
{(props) => (
<MenuAnchor {...props}>
{title} <Disclosure color={theme.textTertiary} />
</MenuAnchor>
)}
</MenuButton>
<ContextMenu {...menu} aria-label={t("Submenu")}>
<Template {...menu} items={templateItems} />
</ContextMenu>
</>
);
}
);
export function filterTemplateItems(items: TMenuItem[]): TMenuItem[] {
let filtered = items.filter((item) => item.visible !== false);
// this block literally just trims unnecessary separators
filtered = filtered.reduce((acc, item, index) => {
// trim separators from start / end
if (item.type === "separator" && index === 0) {
return acc;
}
if (item.type === "separator" && index === filtered.length - 1) {
return acc;
}
// trim double separators looking ahead / behind
const prev = filtered[index - 1];
if (prev && prev.type === "separator" && item.type === "separator") {
return acc;
}
// otherwise, continue
return [...acc, item];
}, []);
return filtered;
}
function Template({ items, actions, context, ...menu }: Props) {
const ctx = useActionContext({
isContextMenu: true,
});
const templateItems = actions
? actions.map((item) =>
item.type === "separator" || item.type === "heading"
? item
: actionToMenuItem(item, ctx)
)
: items || [];
const filteredTemplates = filterTemplateItems(templateItems);
const iconIsPresentInAnyMenuItem = filteredTemplates.find(
(item) =>
item.type !== "separator" && item.type !== "heading" && !!item.icon
);
return (
<>
{filteredTemplates.map((item, index) => {
if (
iconIsPresentInAnyMenuItem &&
item.type !== "separator" &&
item.type !== "heading"
) {
item.icon = item.icon || <MenuIconWrapper />;
}
if (item.type === "route") {
return (
<MenuItem
as={Link}
to={item.to}
key={index}
disabled={item.disabled}
selected={item.selected}
icon={item.icon}
{...menu}
>
{item.title}
</MenuItem>
);
}
if (item.type === "link") {
return (
<MenuItem
href={item.href}
key={index}
disabled={item.disabled}
selected={item.selected}
level={item.level}
target={item.href.startsWith("#") ? undefined : "_blank"}
icon={item.icon}
{...menu}
>
{item.title}
</MenuItem>
);
}
if (item.type === "button") {
return (
<MenuItem
as="button"
onClick={item.onClick}
disabled={item.disabled}
selected={item.selected}
key={index}
icon={item.icon}
{...menu}
>
{item.title}
</MenuItem>
);
}
if (item.type === "submenu") {
return (
<BaseMenuItem
key={index}
as={Submenu}
templateItems={item.items}
title={<Title title={item.title} icon={item.icon} />}
{...menu}
/>
);
}
if (item.type === "separator") {
return <Separator key={index} />;
}
if (item.type === "heading") {
return <Header>{item.title}</Header>;
}
const _exhaustiveCheck: never = item;
return _exhaustiveCheck;
})}
</>
);
}
function Title({
title,
icon,
}: {
title: React.ReactNode;
icon?: React.ReactNode;
}) {
return (
<Flex align="center">
{icon && <MenuIconWrapper>{icon}</MenuIconWrapper>}
{title}
</Flex>
);
}
export default React.memo<Props>(Template);
+177
View File
@@ -0,0 +1,177 @@
import * as React from "react";
import { Portal } from "react-portal";
import { Menu } from "reakit/Menu";
import styled from "styled-components";
import breakpoint from "styled-components-breakpoint";
import useMenuHeight from "~/hooks/useMenuHeight";
import usePrevious from "~/hooks/usePrevious";
import {
fadeIn,
fadeAndSlideUp,
fadeAndSlideDown,
mobileContextMenu,
} from "~/styles/animations";
export type Placement =
| "auto-start"
| "auto"
| "auto-end"
| "top-start"
| "top"
| "top-end"
| "right-start"
| "right"
| "right-end"
| "bottom-end"
| "bottom"
| "bottom-start"
| "left-end"
| "left"
| "left-start";
type Props = {
"aria-label": string;
visible?: boolean;
placement?: Placement;
animating?: boolean;
children: React.ReactNode;
unstable_disclosureRef?: React.RefObject<HTMLElement | null>;
onOpen?: () => void;
onClose?: () => void;
hide?: () => void;
};
export default function ContextMenu({
children,
onOpen,
onClose,
...rest
}: Props) {
const previousVisible = usePrevious(rest.visible);
const maxHeight = useMenuHeight(rest.visible, rest.unstable_disclosureRef);
const backgroundRef = React.useRef<HTMLDivElement>(null);
React.useEffect(() => {
if (rest.visible && !previousVisible) {
if (onOpen) {
onOpen();
}
}
if (!rest.visible && previousVisible) {
if (onClose) {
onClose();
}
}
}, [onOpen, onClose, previousVisible, rest.visible]);
// Perf win don't render anything until the menu has been opened
if (!rest.visible && !previousVisible) {
return null;
}
// sets the menu height based on the available space between the disclosure/
// trigger and the bottom of the window
return (
<>
<Menu hideOnClickOutside preventBodyScroll {...rest}>
{(props) => {
// kind of hacky, but this is an effective way of telling which way
// the menu will _actually_ be placed when taking into account screen
// positioning.
const topAnchor = props.style?.top === "0";
// @ts-expect-error ts-migrate(2339) FIXME: Property 'placement' does not exist on type 'Extra... Remove this comment to see the full error message
const rightAnchor = props.placement === "bottom-end";
return (
<Position {...props}>
<Background
dir="auto"
topAnchor={topAnchor}
rightAnchor={rightAnchor}
ref={backgroundRef}
style={
maxHeight && topAnchor
? {
maxHeight,
}
: undefined
}
>
{rest.visible || rest.animating ? children : null}
</Background>
</Position>
);
}}
</Menu>
{(rest.visible || rest.animating) && (
<Portal>
<Backdrop onClick={rest.hide} />
</Portal>
)}
</>
);
}
export const Backdrop = styled.div`
animation: ${fadeIn} 200ms ease-in-out;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: ${(props) => props.theme.backdrop};
z-index: ${(props) => props.theme.depths.menu - 1};
${breakpoint("tablet")`
display: none;
`};
`;
export const Position = styled.div`
position: absolute;
z-index: ${(props) => props.theme.depths.menu};
// overrides make mobile-first coding style challenging
// so we explicitly define mobile breakpoint here
${breakpoint("mobile", "tablet")`
position: fixed !important;
transform: none !important;
top: auto !important;
right: 8px !important;
bottom: 16px !important;
left: 8px !important;
`};
`;
export const Background = styled.div<{
topAnchor?: boolean;
rightAnchor?: boolean;
}>`
animation: ${mobileContextMenu} 200ms ease;
transform-origin: 50% 100%;
max-width: 100%;
background: ${(props) => props.theme.menuBackground};
border-radius: 6px;
padding: 6px 0;
min-width: 180px;
min-height: 44px;
overflow: hidden;
overflow-y: auto;
max-height: 75vh;
pointer-events: all;
font-weight: normal;
@media print {
display: none;
}
${breakpoint("tablet")`
animation: ${(props: any) =>
props.topAnchor ? fadeAndSlideDown : fadeAndSlideUp} 200ms ease;
transform-origin: ${(props: any) => (props.rightAnchor ? "75%" : "25%")} 0;
max-width: 276px;
background: ${(props: any) => props.theme.menuBackground};
box-shadow: ${(props: any) => props.theme.menuShadow};
`};
`;
@@ -1,23 +1,25 @@
// @flow
import copy from "copy-to-clipboard";
import * as React from "react";
type Props = {
text: string,
children?: React.Node,
onClick?: () => void,
onCopy: () => void,
text: string;
children?: React.ReactElement;
onClick?: React.MouseEventHandler<HTMLButtonElement>;
onCopy: () => void;
};
class CopyToClipboard extends React.PureComponent<Props> {
onClick = (ev: SyntheticEvent<>) => {
onClick = (ev: React.SyntheticEvent) => {
const { text, onCopy, children } = this.props;
const elem = React.Children.only(children);
copy(text, {
debug: process.env.NODE_ENV !== "production",
format: "text/plain",
});
if (onCopy) onCopy();
if (onCopy) {
onCopy();
}
if (elem && elem.props && typeof elem.props.onClick === "function") {
elem.props.onClick(ev);
@@ -25,8 +27,12 @@ class CopyToClipboard extends React.PureComponent<Props> {
};
render() {
const { text: _text, onCopy: _onCopy, children, ...rest } = this.props;
const { text, onCopy, children, ...rest } = this.props;
const elem = React.Children.only(children);
if (!elem) {
return null;
}
return React.cloneElement(elem, { ...rest, onClick: this.onClick });
}
}
@@ -0,0 +1,103 @@
import { HomeIcon } from "outline-icons";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import CollectionIcon from "~/components/CollectionIcon";
import Flex from "~/components/Flex";
import InputSelect from "~/components/InputSelect";
import { IconWrapper } from "~/components/Sidebar/components/SidebarLink";
import useStores from "~/hooks/useStores";
import useToasts from "~/hooks/useToasts";
type DefaultCollectionInputSelectProps = {
onSelectCollection: (collection: string) => void;
defaultCollectionId: string | null;
};
const DefaultCollectionInputSelect = ({
onSelectCollection,
defaultCollectionId,
}: DefaultCollectionInputSelectProps) => {
const { t } = useTranslation();
const { collections } = useStores();
const [fetching, setFetching] = useState(false);
const [fetchError, setFetchError] = useState();
const { showToast } = useToasts();
React.useEffect(() => {
async function load() {
if (!collections.isLoaded && !fetching && !fetchError) {
try {
setFetching(true);
await collections.fetchPage({
limit: 100,
});
} catch (error) {
showToast(
t("Collections could not be loaded, please reload the app"),
{
type: "error",
}
);
setFetchError(error);
} finally {
setFetching(false);
}
}
}
load();
}, [showToast, fetchError, t, fetching, collections]);
const options = React.useMemo(
() =>
collections.publicCollections.reduce(
(acc, collection) => [
...acc,
{
label: (
<Flex align="center">
<IconWrapper>
<CollectionIcon collection={collection} />
</IconWrapper>
{collection.name}
</Flex>
),
value: collection.id,
},
],
[
{
label: (
<Flex align="center">
<IconWrapper>
<HomeIcon color="currentColor" />
</IconWrapper>
{t("Home")}
</Flex>
),
value: "home",
},
]
),
[collections.publicCollections, t]
);
if (fetching) {
return null;
}
return (
<InputSelect
value={defaultCollectionId ?? "home"}
label={t("Start view")}
options={options}
onChange={onSelectCollection}
ariaLabel={t("Default collection")}
note={t(
"This is the screen that team members will first see when they sign in."
)}
short
/>
);
};
export default DefaultCollectionInputSelect;
@@ -1,9 +1,8 @@
// @flow
import * as React from "react";
type Props = {
delay?: number,
children: React.Node,
delay?: number;
children: JSX.Element;
};
export default function DelayedMount({ delay = 250, children }: Props) {
@@ -14,7 +13,7 @@ export default function DelayedMount({ delay = 250, children }: Props) {
return () => {
clearTimeout(timeout);
};
}, []);
}, [delay]);
if (!isShowing) {
return null;
+35
View File
@@ -0,0 +1,35 @@
import { observer } from "mobx-react-lite";
import * as React from "react";
import Guide from "~/components/Guide";
import Modal from "~/components/Modal";
import useStores from "~/hooks/useStores";
function Dialogs() {
const { dialogs } = useStores();
const { guide, modalStack } = dialogs;
return (
<>
{guide ? (
<Guide
isOpen={guide.isOpen}
onRequestClose={dialogs.closeGuide}
title={guide.title}
>
{guide.content}
</Guide>
) : undefined}
{[...modalStack].map(([id, modal]) => (
<Modal
key={id}
isOpen={modal.isOpen}
onRequestClose={() => dialogs.closeModal(id)}
title={modal.title}
>
{modal.content}
</Modal>
))}
</>
);
}
export default observer(Dialogs);
+10
View File
@@ -0,0 +1,10 @@
import styled from "styled-components";
const Divider = styled.hr`
border: 0;
border-bottom: 1px solid ${(props) => props.theme.divider};
margin: 0;
padding: 0;
`;
export default Divider;
+131
View File
@@ -0,0 +1,131 @@
import { observer } from "mobx-react";
import { ArchiveIcon, GoToIcon, ShapesIcon, TrashIcon } from "outline-icons";
import * as React from "react";
import { useTranslation } from "react-i18next";
import styled from "styled-components";
import Document from "~/models/Document";
import Breadcrumb from "~/components/Breadcrumb";
import CollectionIcon from "~/components/CollectionIcon";
import useStores from "~/hooks/useStores";
import { MenuInternalLink, NavigationNode } from "~/types";
import { collectionUrl } from "~/utils/routeHelpers";
type Props = {
document: Document;
children?: React.ReactNode;
onlyText?: boolean;
};
function useCategory(document: Document): MenuInternalLink | null {
const { t } = useTranslation();
if (document.isDeleted) {
return {
type: "route",
icon: <TrashIcon color="currentColor" />,
title: t("Trash"),
to: "/trash",
};
}
if (document.isArchived) {
return {
type: "route",
icon: <ArchiveIcon color="currentColor" />,
title: t("Archive"),
to: "/archive",
};
}
if (document.isTemplate) {
return {
type: "route",
icon: <ShapesIcon color="currentColor" />,
title: t("Templates"),
to: "/templates",
};
}
return null;
}
const DocumentBreadcrumb = ({ document, children, onlyText }: Props) => {
const { collections } = useStores();
const { t } = useTranslation();
const category = useCategory(document);
const collection = collections.get(document.collectionId);
let collectionNode: MenuInternalLink;
if (collection) {
collectionNode = {
type: "route",
title: collection.name,
icon: <CollectionIcon collection={collection} expanded />,
to: collectionUrl(collection.url),
};
} else {
collectionNode = {
type: "route",
title: t("Deleted Collection"),
icon: undefined,
to: collectionUrl("deleted-collection"),
};
}
const path = React.useMemo(
() => collection?.pathToDocument?.(document.id).slice(0, -1) || [],
[collection, document]
);
const items = React.useMemo(() => {
const output = [];
if (category) {
output.push(category);
}
output.push(collectionNode);
path.forEach((node: NavigationNode) => {
output.push({
type: "route",
title: node.title,
to: node.url,
});
});
return output;
}, [path, category, collectionNode]);
if (!collections.isLoaded) {
return null;
}
if (onlyText === true) {
return (
<>
{collection?.name}
{path.map((node: NavigationNode) => (
<React.Fragment key={node.id}>
<SmallSlash />
{node.title}
</React.Fragment>
))}
</>
);
}
return <Breadcrumb items={items} children={children} highlightFirstItem />;
};
const SmallSlash = styled(GoToIcon)`
width: 12px;
height: 12px;
vertical-align: middle;
flex-shrink: 0;
fill: ${(props) => props.theme.slate};
opacity: 0.5;
`;
export default observer(DocumentBreadcrumb);
+260
View File
@@ -0,0 +1,260 @@
import { useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import { m } from "framer-motion";
import { observer } from "mobx-react";
import { CloseIcon, DocumentIcon } from "outline-icons";
import { getLuminance, transparentize } from "polished";
import * as React from "react";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import styled, { css } from "styled-components";
import Document from "~/models/Document";
import Pin from "~/models/Pin";
import DocumentMeta from "~/components/DocumentMeta";
import Flex from "~/components/Flex";
import NudeButton from "~/components/NudeButton";
import useStores from "~/hooks/useStores";
import CollectionIcon from "./CollectionIcon";
import Tooltip from "./Tooltip";
type Props = {
/** The pin record */
pin: Pin | undefined;
/** The document related to the pin */
document: Document;
/** Whether the user has permission to delete or reorder the pin */
canUpdatePin?: boolean;
/** Whether this pin can be reordered by dragging */
isDraggable?: boolean;
};
function DocumentCard(props: Props) {
const { t } = useTranslation();
const { collections } = useStores();
const { document, pin, canUpdatePin, isDraggable } = props;
const collection = collections.get(document.collectionId);
const {
attributes,
listeners,
setNodeRef,
transform,
transition,
isDragging,
} = useSortable({ id: props.document.id });
const style = {
transform: CSS.Transform.toString(transform),
transition,
};
const handleUnpin = React.useCallback(() => {
pin?.delete();
}, [pin]);
return (
<Reorderable
ref={setNodeRef}
style={style}
$isDragging={isDragging}
{...attributes}
>
<AnimatePresence
initial={{ opacity: 0, scale: 0.95 }}
animate={{
opacity: 1,
scale: 1,
transition: {
type: "spring",
bounce: 0.6,
},
}}
exit={{ opacity: 0, scale: 0.95 }}
>
<DocumentLink
dir={document.dir}
style={{
background:
collection?.color && getLuminance(collection.color) < 0.6
? collection.color
: undefined,
}}
$isDragging={isDragging}
to={{
pathname: document.url,
state: {
title: document.titleWithDefault,
},
}}
>
<Content justify="space-between" column>
{collection?.icon &&
collection?.icon !== "collection" &&
!pin?.collectionId ? (
<CollectionIcon collection={collection} color="white" />
) : (
<DocumentIcon color="white" />
)}
<div>
<Heading dir={document.dir}>{document.titleWithDefault}</Heading>
<StyledDocumentMeta document={document} />
</div>
</Content>
</DocumentLink>
{canUpdatePin && (
<Actions dir={document.dir} gap={4}>
{!isDragging && pin && (
<Tooltip tooltip={t("Unpin")}>
<PinButton onClick={handleUnpin}>
<CloseIcon color="currentColor" />
</PinButton>
</Tooltip>
)}
{isDraggable && (
<DragHandle $isDragging={isDragging} {...listeners}>
:::
</DragHandle>
)}
</Actions>
)}
</AnimatePresence>
</Reorderable>
);
}
const PinButton = styled(NudeButton)`
color: ${(props) => props.theme.white75};
&:hover,
&:active {
color: ${(props) => props.theme.white};
}
`;
const Actions = styled(Flex)`
position: absolute;
top: 12px;
right: ${(props) => (props.dir === "rtl" ? "auto" : "12px")};
left: ${(props) => (props.dir === "rtl" ? "12px" : "auto")};
opacity: 0;
transition: opacity 100ms ease-in-out;
// move actions above content
z-index: 2;
`;
const DragHandle = styled.div<{ $isDragging: boolean }>`
cursor: ${(props) => (props.$isDragging ? "grabbing" : "grab")};
padding: 0 4px;
font-weight: bold;
color: ${(props) => props.theme.white75};
line-height: 1.35;
&:hover,
&:active {
color: ${(props) => props.theme.white};
}
`;
const AnimatePresence = m.div;
const Reorderable = styled.div<{ $isDragging: boolean }>`
position: relative;
user-select: none;
border-radius: 8px;
// move above other cards when dragging
z-index: ${(props) => (props.$isDragging ? 1 : "inherit")};
transform: scale(${(props) => (props.$isDragging ? "1.025" : "1")});
box-shadow: ${(props) =>
props.$isDragging ? "0 0 20px rgba(0,0,0,0.3);" : "0 0 0 rgba(0,0,0,0)"};
&:hover ${Actions} {
opacity: 1;
}
`;
const Content = styled(Flex)`
min-width: 0;
height: 100%;
// move content above ::after
position: relative;
z-index: 1;
`;
const StyledDocumentMeta = styled(DocumentMeta)`
color: ${(props) => transparentize(0.25, props.theme.white)} !important;
`;
const DocumentLink = styled(Link)<{
$menuOpen?: boolean;
$isDragging?: boolean;
}>`
position: relative;
display: block;
padding: 12px;
border-radius: 8px;
height: 160px;
background: ${(props) => props.theme.slate};
color: ${(props) => props.theme.white};
transition: transform 50ms ease-in-out;
&:after {
content: "";
display: block;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(transparent, rgba(0, 0, 0, 0.1));
border-radius: 8px;
pointer-events: none;
}
${Actions} {
opacity: 0;
}
&:hover,
&:active,
&:focus,
&:focus-within {
${Actions} {
opacity: 1;
}
${(props) =>
!props.$isDragging &&
css`
&:after {
background: rgba(0, 0, 0, 0.1);
}
`}
}
${(props) =>
props.$menuOpen &&
css`
background: ${(props) => props.theme.listItemHoverBackground};
${Actions} {
opacity: 1;
}
`}
`;
const Heading = styled.h3`
margin-top: 0;
margin-bottom: 0.35em;
line-height: 22px;
max-height: 66px; // 3*line-height
overflow: hidden;
color: ${(props) => props.theme.white};
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen,
Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
`;
export default observer(DocumentCard);
+132
View File
@@ -0,0 +1,132 @@
import { observer } from "mobx-react";
import { CloseIcon } from "outline-icons";
import * as React from "react";
import { useTranslation } from "react-i18next";
import { useHistory, useRouteMatch } from "react-router-dom";
import styled from "styled-components";
import breakpoint from "styled-components-breakpoint";
import Event from "~/models/Event";
import Button from "~/components/Button";
import Empty from "~/components/Empty";
import Flex from "~/components/Flex";
import PaginatedEventList from "~/components/PaginatedEventList";
import Scrollable from "~/components/Scrollable";
import useStores from "~/hooks/useStores";
import { documentUrl } from "~/utils/routeHelpers";
const EMPTY_ARRAY: Event[] = [];
function DocumentHistory() {
const { events, documents } = useStores();
const { t } = useTranslation();
const match = useRouteMatch<{ documentSlug: string }>();
const history = useHistory();
const document = documents.getByUrl(match.params.documentSlug);
const eventsInDocument = document
? events.inDocument(document.id)
: EMPTY_ARRAY;
const onCloseHistory = () => {
if (document) {
history.push(documentUrl(document));
} else {
history.goBack();
}
};
const items = React.useMemo(() => {
if (
eventsInDocument[0] &&
document &&
eventsInDocument[0].createdAt !== document.updatedAt
) {
eventsInDocument.unshift(
new Event(
{
name: "documents.latest_version",
documentId: document.id,
createdAt: document.updatedAt,
actor: document.updatedBy,
},
events
)
);
}
return eventsInDocument;
}, [eventsInDocument, events, document]);
return (
<Sidebar>
{document ? (
<Position column>
<Header>
<Title>{t("History")}</Title>
<Button
icon={<CloseIcon />}
onClick={onCloseHistory}
borderOnHover
neutral
/>
</Header>
<Scrollable topShadow>
<PaginatedEventList
fetch={events.fetchPage}
events={items}
options={{
documentId: document.id,
}}
document={document}
empty={<Empty>{t("Oh weird, there's nothing here")}</Empty>}
/>
</Scrollable>
</Position>
) : null}
</Sidebar>
);
}
const Position = styled(Flex)`
position: fixed;
top: 0;
bottom: 0;
width: ${(props) => props.theme.sidebarWidth}px;
`;
const Sidebar = styled(Flex)`
display: none;
position: relative;
flex-shrink: 0;
background: ${(props) => props.theme.background};
width: ${(props) => props.theme.sidebarWidth}px;
border-left: 1px solid ${(props) => props.theme.divider};
z-index: 1;
${breakpoint("tablet")`
display: flex;
`};
`;
const Title = styled(Flex)`
font-size: 16px;
font-weight: 600;
text-align: center;
align-items: center;
justify-content: flex-start;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
width: 0;
flex-grow: 1;
`;
const Header = styled(Flex)`
align-items: center;
position: relative;
padding: 12px;
color: ${(props) => props.theme.text};
flex-shrink: 0;
`;
export default observer(DocumentHistory);
@@ -1,149 +0,0 @@
// @flow
import ArrowKeyNavigation from "boundless-arrow-key-navigation";
import { observable, action } from "mobx";
import { observer, inject } from "mobx-react";
import * as React from "react";
import { type RouterHistory, type Match } from "react-router-dom";
import { Waypoint } from "react-waypoint";
import styled from "styled-components";
import { DEFAULT_PAGINATION_LIMIT } from "stores/BaseStore";
import DocumentsStore from "stores/DocumentsStore";
import RevisionsStore from "stores/RevisionsStore";
import Flex from "components/Flex";
import { ListPlaceholder } from "components/LoadingPlaceholder";
import Revision from "./components/Revision";
import { documentHistoryUrl } from "utils/routeHelpers";
type Props = {
match: Match,
documents: DocumentsStore,
revisions: RevisionsStore,
history: RouterHistory,
};
@observer
class DocumentHistory extends React.Component<Props> {
@observable isLoaded: boolean = false;
@observable isFetching: boolean = false;
@observable offset: number = 0;
@observable allowLoadMore: boolean = true;
async componentDidMount() {
await this.loadMoreResults();
this.selectFirstRevision();
}
fetchResults = async () => {
this.isFetching = true;
const limit = DEFAULT_PAGINATION_LIMIT;
const results = await this.props.revisions.fetchPage({
limit,
offset: this.offset,
documentId: this.props.match.params.documentSlug,
});
if (
results &&
(results.length === 0 || results.length < DEFAULT_PAGINATION_LIMIT)
) {
this.allowLoadMore = false;
} else {
this.offset += DEFAULT_PAGINATION_LIMIT;
}
this.isLoaded = true;
this.isFetching = false;
};
selectFirstRevision = () => {
if (this.revisions.length) {
const document = this.props.documents.getByUrl(
this.props.match.params.documentSlug
);
if (!document) return;
this.props.history.replace(
documentHistoryUrl(document, this.revisions[0].id)
);
}
};
@action
loadMoreResults = async () => {
// Don't paginate if there aren't more results or were in the middle of fetching
if (!this.allowLoadMore || this.isFetching) return;
await this.fetchResults();
};
get revisions() {
const document = this.props.documents.getByUrl(
this.props.match.params.documentSlug
);
if (!document) return [];
return this.props.revisions.getDocumentRevisions(document.id);
}
render() {
const document = this.props.documents.getByUrl(
this.props.match.params.documentSlug
);
const showLoading = (!this.isLoaded && this.isFetching) || !document;
return (
<Sidebar>
<Wrapper column>
{showLoading ? (
<Loading>
<ListPlaceholder count={5} />
</Loading>
) : (
<ArrowKeyNavigation
mode={ArrowKeyNavigation.mode.VERTICAL}
defaultActiveChildIndex={0}
>
{this.revisions.map((revision, index) => (
<Revision
key={revision.id}
revision={revision}
document={document}
showMenu={index !== 0}
selected={this.props.match.params.revisionId === revision.id}
/>
))}
</ArrowKeyNavigation>
)}
{this.allowLoadMore && (
<Waypoint key={this.offset} onEnter={this.loadMoreResults} />
)}
</Wrapper>
</Sidebar>
);
}
}
const Loading = styled.div`
margin: 0 16px;
`;
const Wrapper = styled(Flex)`
position: fixed;
top: 0;
right: 0;
z-index: 1;
min-width: ${(props) => props.theme.sidebarWidth};
height: 100%;
overflow-y: auto;
overscroll-behavior: none;
`;
const Sidebar = styled(Flex)`
background: ${(props) => props.theme.background};
min-width: ${(props) => props.theme.sidebarWidth};
border-left: 1px solid ${(props) => props.theme.divider};
z-index: 1;
`;
export default inject("documents", "revisions")(DocumentHistory);
@@ -1,89 +0,0 @@
// @flow
import format from "date-fns/format";
import { MoreIcon } from "outline-icons";
import * as React from "react";
import { NavLink } from "react-router-dom";
import styled, { withTheme } from "styled-components";
import Document from "models/Document";
import Revision from "models/Revision";
import Avatar from "components/Avatar";
import Flex from "components/Flex";
import Time from "components/Time";
import RevisionMenu from "menus/RevisionMenu";
import { documentHistoryUrl } from "utils/routeHelpers";
type Props = {
theme: Object,
showMenu: boolean,
selected: boolean,
document: Document,
revision: Revision,
};
class RevisionListItem extends React.Component<Props> {
render() {
const { revision, document, showMenu, selected, theme } = this.props;
return (
<StyledNavLink
to={documentHistoryUrl(document, revision.id)}
activeStyle={{ background: theme.primary, color: theme.white }}
>
<Author>
<StyledAvatar src={revision.createdBy.avatarUrl} />{" "}
{revision.createdBy.name}
</Author>
<Meta>
<Time dateTime={revision.createdAt}>
{format(revision.createdAt, "MMMM Do, YYYY h:mm a")}
</Time>
</Meta>
{showMenu && (
<StyledRevisionMenu
document={document}
revision={revision}
label={
<MoreIcon color={selected ? theme.white : theme.textTertiary} />
}
/>
)}
</StyledNavLink>
);
}
}
const StyledAvatar = styled(Avatar)`
border-color: transparent;
margin-right: 4px;
`;
const StyledRevisionMenu = styled(RevisionMenu)`
position: absolute;
right: 16px;
top: 20px;
`;
const StyledNavLink = styled(NavLink)`
color: ${(props) => props.theme.text};
display: block;
padding: 8px 16px;
font-size: 15px;
position: relative;
`;
const Author = styled(Flex)`
font-weight: 500;
padding: 0;
margin: 0;
`;
const Meta = styled.p`
font-size: 14px;
opacity: 0.75;
margin: 0 0 2px;
padding: 0;
`;
export default withTheme(RevisionListItem);
-3
View File
@@ -1,3 +0,0 @@
// @flow
import DocumentHistory from "./DocumentHistory";
export default DocumentHistory;
@@ -1,24 +1,27 @@
// @flow
import ArrowKeyNavigation from "boundless-arrow-key-navigation";
import * as React from "react";
import Document from "models/Document";
import DocumentPreview from "components/DocumentPreview";
import Document from "~/models/Document";
import DocumentListItem from "~/components/DocumentListItem";
type Props = {
documents: Document[],
limit?: number,
documents: Document[];
limit?: number;
showCollection?: boolean;
showPublished?: boolean;
showPin?: boolean;
showDraft?: boolean;
showTemplate?: boolean;
};
export default function DocumentList({ limit, documents, ...rest }: Props) {
const items = limit ? documents.splice(0, limit) : documents;
return (
<ArrowKeyNavigation
mode={ArrowKeyNavigation.mode.VERTICAL}
defaultActiveChildIndex={0}
>
{items.map((document) => (
<DocumentPreview key={document.id} document={document} {...rest} />
<DocumentListItem key={document.id} document={document} {...rest} />
))}
</ArrowKeyNavigation>
);
+271
View File
@@ -0,0 +1,271 @@
import { observer } from "mobx-react";
import { PlusIcon } from "outline-icons";
import * as React from "react";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import styled, { css } from "styled-components";
import breakpoint from "styled-components-breakpoint";
import Document from "~/models/Document";
import Badge from "~/components/Badge";
import Button from "~/components/Button";
import DocumentMeta from "~/components/DocumentMeta";
import EventBoundary from "~/components/EventBoundary";
import Flex from "~/components/Flex";
import Highlight from "~/components/Highlight";
import StarButton, { AnimatedStar } from "~/components/Star";
import Tooltip from "~/components/Tooltip";
import useBoolean from "~/hooks/useBoolean";
import useCurrentTeam from "~/hooks/useCurrentTeam";
import useCurrentUser from "~/hooks/useCurrentUser";
import useStores from "~/hooks/useStores";
import DocumentMenu from "~/menus/DocumentMenu";
import { hover } from "~/styles";
import { newDocumentPath } from "~/utils/routeHelpers";
type Props = {
document: Document;
highlight?: string | undefined;
context?: string | undefined;
showParentDocuments?: boolean;
showCollection?: boolean;
showPublished?: boolean;
showPin?: boolean;
showDraft?: boolean;
showTemplate?: boolean;
};
const SEARCH_RESULT_REGEX = /<b\b[^>]*>(.*?)<\/b>/gi;
function replaceResultMarks(tag: string) {
// don't use SEARCH_RESULT_REGEX here as it causes
// an infinite loop to trigger a regex inside it's own callback
return tag.replace(/<b\b[^>]*>(.*?)<\/b>/gi, "$1");
}
function DocumentListItem(
props: Props,
ref: React.RefObject<HTMLAnchorElement>
) {
const { t } = useTranslation();
const { policies } = useStores();
const currentUser = useCurrentUser();
const currentTeam = useCurrentTeam();
const [menuOpen, handleMenuOpen, handleMenuClose] = useBoolean();
const {
document,
showParentDocuments,
showCollection,
showPublished,
showPin,
showDraft = true,
showTemplate,
highlight,
context,
} = props;
const queryIsInTitle =
!!highlight &&
!!document.title.toLowerCase().includes(highlight.toLowerCase());
const canStar =
!document.isDraft && !document.isArchived && !document.isTemplate;
const can = policies.abilities(currentTeam.id);
const canCollection = policies.abilities(document.collectionId);
return (
<DocumentLink
ref={ref}
dir={document.dir}
$isStarred={document.isStarred}
$menuOpen={menuOpen}
to={{
pathname: document.url,
state: {
title: document.titleWithDefault,
},
}}
>
<Content>
<Heading dir={document.dir}>
<Title
text={document.titleWithDefault}
highlight={highlight}
dir={document.dir}
/>
{document.isBadgedNew && document.createdBy.id !== currentUser.id && (
<Badge yellow>{t("New")}</Badge>
)}
{canStar && (
<StarPositioner>
<StarButton document={document} />
</StarPositioner>
)}
{document.isDraft && showDraft && (
<Tooltip
tooltip={t("Only visible to you")}
delay={500}
placement="top"
>
<Badge>{t("Draft")}</Badge>
</Tooltip>
)}
{document.isTemplate && showTemplate && (
<Badge primary>{t("Template")}</Badge>
)}
</Heading>
{!queryIsInTitle && (
<ResultContext
text={context}
highlight={highlight ? SEARCH_RESULT_REGEX : undefined}
processResult={replaceResultMarks}
/>
)}
<DocumentMeta
document={document}
showCollection={showCollection}
showPublished={showPublished}
showParentDocuments={showParentDocuments}
showLastViewed
/>
</Content>
<Actions>
{document.isTemplate &&
!document.isArchived &&
!document.isDeleted &&
can.createDocument &&
canCollection.update && (
<>
<Button
as={Link}
to={newDocumentPath(document.collectionId, {
templateId: document.id,
})}
icon={<PlusIcon />}
neutral
>
{t("New doc")}
</Button>
&nbsp;
</>
)}
<DocumentMenu
document={document}
showPin={showPin}
onOpen={handleMenuOpen}
onClose={handleMenuClose}
modal={false}
/>
</Actions>
</DocumentLink>
);
}
const Content = styled.div`
flex-grow: 1;
flex-shrink: 1;
min-width: 0;
`;
const Actions = styled(EventBoundary)`
display: none;
align-items: center;
margin: 8px;
flex-shrink: 0;
flex-grow: 0;
${breakpoint("tablet")`
display: flex;
`};
`;
const DocumentLink = styled(Link)<{
$isStarred?: boolean;
$menuOpen?: boolean;
}>`
display: flex;
align-items: center;
margin: 10px -8px;
padding: 6px 8px;
border-radius: 8px;
max-height: 50vh;
width: calc(100vw - 8px);
${breakpoint("tablet")`
width: auto;
`};
${Actions} {
opacity: 0;
}
${AnimatedStar} {
opacity: ${(props) => (props.$isStarred ? "1 !important" : 0)};
}
&:${hover},
&:active,
&:focus,
&:focus-within {
background: ${(props) => props.theme.listItemHoverBackground};
${Actions} {
opacity: 1;
}
${AnimatedStar} {
opacity: 0.5;
&:hover {
opacity: 1;
}
}
}
${(props) =>
props.$menuOpen &&
css`
background: ${(props) => props.theme.listItemHoverBackground};
${Actions} {
opacity: 1;
}
${AnimatedStar} {
opacity: 0.5;
}
`}
`;
const Heading = styled.h3<{ rtl?: boolean }>`
display: flex;
justify-content: ${(props) => (props.rtl ? "flex-end" : "flex-start")};
align-items: center;
height: 24px;
margin-top: 0;
margin-bottom: 0.25em;
overflow: hidden;
white-space: nowrap;
color: ${(props) => props.theme.text};
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen,
Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
`;
const StarPositioner = styled(Flex)`
margin-left: 4px;
align-items: center;
`;
const Title = styled(Highlight)`
max-width: 90%;
overflow: hidden;
text-overflow: ellipsis;
`;
const ResultContext = styled(Highlight)`
display: block;
color: ${(props) => props.theme.textTertiary};
font-size: 14px;
margin-top: -0.25em;
margin-bottom: 0.25em;
`;
export default observer(React.forwardRef(DocumentListItem));
-123
View File
@@ -1,123 +0,0 @@
// @flow
import { inject, observer } from "mobx-react";
import * as React from "react";
import { Link } from "react-router-dom";
import styled from "styled-components";
import AuthStore from "stores/AuthStore";
import CollectionsStore from "stores/CollectionsStore";
import Document from "models/Document";
import Breadcrumb from "components/Breadcrumb";
import Flex from "components/Flex";
import Time from "components/Time";
const Container = styled(Flex)`
color: ${(props) => props.theme.textTertiary};
font-size: 13px;
white-space: nowrap;
overflow: hidden;
`;
const Modified = styled.span`
color: ${(props) =>
props.highlight ? props.theme.text : props.theme.textTertiary};
font-weight: ${(props) => (props.highlight ? "600" : "400")};
`;
type Props = {
collections: CollectionsStore,
auth: AuthStore,
showCollection?: boolean,
showPublished?: boolean,
document: Document,
children: React.Node,
to?: string,
};
function DocumentMeta({
auth,
collections,
showPublished,
showCollection,
document,
children,
to,
...rest
}: Props) {
const {
modifiedSinceViewed,
updatedAt,
updatedBy,
createdAt,
publishedAt,
archivedAt,
deletedAt,
isDraft,
} = document;
// Prevent meta information from displaying if updatedBy is not available.
// Currently the situation where this is true is rendering share links.
if (!updatedBy) {
return null;
}
let content;
if (deletedAt) {
content = (
<span>
deleted <Time dateTime={deletedAt} /> ago
</span>
);
} else if (archivedAt) {
content = (
<span>
archived <Time dateTime={archivedAt} /> ago
</span>
);
} else if (createdAt === updatedAt) {
content = (
<span>
created <Time dateTime={updatedAt} /> ago
</span>
);
} else if (publishedAt && (publishedAt === updatedAt || showPublished)) {
content = (
<span>
published <Time dateTime={publishedAt} /> ago
</span>
);
} else if (isDraft) {
content = (
<span>
saved <Time dateTime={updatedAt} /> ago
</span>
);
} else {
content = (
<Modified highlight={modifiedSinceViewed}>
updated <Time dateTime={updatedAt} /> ago
</Modified>
);
}
const collection = collections.get(document.collectionId);
const updatedByMe = auth.user && auth.user.id === updatedBy.id;
return (
<Container align="center" {...rest}>
{updatedByMe ? "You" : updatedBy.name}&nbsp;
{to ? <Link to={to}>{content}</Link> : content}
{showCollection && collection && (
<span>
&nbsp;in&nbsp;
<strong>
<Breadcrumb document={document} onlyText />
</strong>
</span>
)}
{children}
</Container>
);
}
export default inject("collections", "auth")(observer(DocumentMeta));
+174
View File
@@ -0,0 +1,174 @@
import { observer } from "mobx-react";
import * as React from "react";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import styled from "styled-components";
import Document from "~/models/Document";
import DocumentBreadcrumb from "~/components/DocumentBreadcrumb";
import DocumentTasks from "~/components/DocumentTasks";
import Flex from "~/components/Flex";
import Time from "~/components/Time";
import useCurrentUser from "~/hooks/useCurrentUser";
import useStores from "~/hooks/useStores";
const Container = styled(Flex)<{ rtl?: boolean }>`
justify-content: ${(props) => (props.rtl ? "flex-end" : "flex-start")};
color: ${(props) => props.theme.textTertiary};
font-size: 13px;
white-space: nowrap;
overflow: hidden;
min-width: 0;
`;
const Viewed = styled.span`
text-overflow: ellipsis;
overflow: hidden;
`;
const Modified = styled.span<{ highlight?: boolean }>`
font-weight: ${(props) => (props.highlight ? "600" : "400")};
`;
type Props = {
showCollection?: boolean;
showPublished?: boolean;
showLastViewed?: boolean;
showParentDocuments?: boolean;
document: Document;
children?: React.ReactNode;
to?: string;
};
function DocumentMeta({
showPublished,
showCollection,
showLastViewed,
showParentDocuments,
document,
children,
to,
...rest
}: Props) {
const { t } = useTranslation();
const { collections } = useStores();
const user = useCurrentUser();
const {
modifiedSinceViewed,
updatedAt,
updatedBy,
createdAt,
publishedAt,
archivedAt,
deletedAt,
isDraft,
lastViewedAt,
isTasks,
isTemplate,
} = document;
// Prevent meta information from displaying if updatedBy is not available.
// Currently the situation where this is true is rendering share links.
if (!updatedBy) {
return null;
}
const collection = collections.get(document.collectionId);
const lastUpdatedByCurrentUser = user.id === updatedBy.id;
let content;
if (deletedAt) {
content = (
<span>
{t("deleted")} <Time dateTime={deletedAt} addSuffix />
</span>
);
} else if (archivedAt) {
content = (
<span>
{t("archived")} <Time dateTime={archivedAt} addSuffix />
</span>
);
} else if (createdAt === updatedAt) {
content = (
<span>
{t("created")} <Time dateTime={updatedAt} addSuffix />
</span>
);
} else if (publishedAt && (publishedAt === updatedAt || showPublished)) {
content = (
<span>
{t("published")} <Time dateTime={publishedAt} addSuffix />
</span>
);
} else if (isDraft) {
content = (
<span>
{t("saved")} <Time dateTime={updatedAt} addSuffix />
</span>
);
} else {
content = (
<Modified highlight={modifiedSinceViewed && !lastUpdatedByCurrentUser}>
{t("updated")} <Time dateTime={updatedAt} addSuffix />
</Modified>
);
}
const nestedDocumentsCount = collection
? collection.getDocumentChildren(document.id).length
: 0;
const canShowProgressBar = isTasks && !isTemplate;
const timeSinceNow = () => {
if (isDraft || !showLastViewed) {
return null;
}
if (!lastViewedAt) {
return (
<Viewed>
&nbsp;<Modified highlight>{t("Never viewed")}</Modified>
</Viewed>
);
}
return (
<Viewed>
&nbsp;{t("Viewed")} <Time dateTime={lastViewedAt} addSuffix shorten />
</Viewed>
);
};
return (
<Container align="center" rtl={document.dir === "rtl"} {...rest} dir="ltr">
{lastUpdatedByCurrentUser ? t("You") : updatedBy.name}&nbsp;
{to ? <Link to={to}>{content}</Link> : content}
{showCollection && collection && (
<span>
&nbsp;{t("in")}&nbsp;
<strong>
<DocumentBreadcrumb document={document} onlyText />
</strong>
</span>
)}
{showParentDocuments && nestedDocumentsCount > 0 && (
<span>
&nbsp; {nestedDocumentsCount}{" "}
{t("nested document", {
count: nestedDocumentsCount,
})}
</span>
)}
&nbsp;{timeSinceNow()}
{canShowProgressBar && (
<>
&nbsp;&nbsp;
<DocumentTasks document={document} />
</>
)}
{children}
</Container>
);
}
export default observer(DocumentMeta);
-48
View File
@@ -1,48 +0,0 @@
// @flow
import { inject } from "mobx-react";
import * as React from "react";
import styled from "styled-components";
import ViewsStore from "stores/ViewsStore";
import Document from "models/Document";
import DocumentMeta from "components/DocumentMeta";
type Props = {|
views: ViewsStore,
document: Document,
isDraft: boolean,
to?: string,
|};
function DocumentMetaWithViews({ views, to, isDraft, document }: Props) {
const totalViews = views.countForDocument(document.id);
return (
<Meta document={document} to={to}>
{totalViews && !isDraft ? (
<>
&nbsp;&middot; Viewed{" "}
{totalViews === 1 ? "once" : `${totalViews} times`}
</>
) : null}
</Meta>
);
}
const Meta = styled(DocumentMeta)`
margin: -12px 0 2em 0;
font-size: 14px;
a {
color: inherit;
&:hover {
text-decoration: underline;
}
}
@media print {
display: none;
}
`;
export default inject("views")(DocumentMetaWithViews);
+86
View File
@@ -0,0 +1,86 @@
import { useObserver } from "mobx-react";
import * as React from "react";
import { useTranslation } from "react-i18next";
import { usePopoverState, PopoverDisclosure } from "reakit/Popover";
import styled from "styled-components";
import Document from "~/models/Document";
import DocumentMeta from "~/components/DocumentMeta";
import DocumentViews from "~/components/DocumentViews";
import Popover from "~/components/Popover";
import useStores from "~/hooks/useStores";
type Props = {
document: Document;
isDraft: boolean;
to?: string;
rtl?: boolean;
};
function DocumentMetaWithViews({ to, isDraft, document, ...rest }: Props) {
const { views } = useStores();
const { t } = useTranslation();
const documentViews = useObserver(() => views.inDocument(document.id));
const totalViewers = documentViews.length;
const onlyYou = totalViewers === 1 && documentViews[0].user.id;
React.useEffect(() => {
if (!document.isDeleted) {
views.fetchPage({
documentId: document.id,
});
}
}, [views, document.id, document.isDeleted]);
const popover = usePopoverState({
gutter: 8,
placement: "bottom",
modal: true,
});
return (
<Meta document={document} to={to} {...rest}>
{totalViewers && !isDraft ? (
<PopoverDisclosure {...popover}>
{(props) => (
<>
&nbsp;&nbsp;
<a {...props}>
{t("Viewed by")}{" "}
{onlyYou
? t("only you")
: `${totalViewers} ${
totalViewers === 1 ? t("person") : t("people")
}`}
</a>
</>
)}
</PopoverDisclosure>
) : null}
<Popover {...popover} width={300} aria-label={t("Viewers")} tabIndex={0}>
<DocumentViews document={document} isOpen={popover.visible} />
</Popover>
</Meta>
);
}
const Meta = styled(DocumentMeta)<{ rtl?: boolean }>`
justify-content: ${(props) => (props.rtl ? "flex-end" : "flex-start")};
margin: -12px 0 2em 0;
font-size: 14px;
position: relative;
z-index: 1;
a {
color: inherit;
&:hover {
text-decoration: underline;
}
}
@media print {
display: none;
}
`;
export default DocumentMetaWithViews;
@@ -1,232 +0,0 @@
// @flow
import { observer } from "mobx-react";
import { StarredIcon, PlusIcon } from "outline-icons";
import * as React from "react";
import { Link, withRouter, type RouterHistory } from "react-router-dom";
import styled, { withTheme } from "styled-components";
import Document from "models/Document";
import Badge from "components/Badge";
import Button from "components/Button";
import DocumentMeta from "components/DocumentMeta";
import Flex from "components/Flex";
import Highlight from "components/Highlight";
import Tooltip from "components/Tooltip";
import DocumentMenu from "menus/DocumentMenu";
import { newDocumentUrl } from "utils/routeHelpers";
type Props = {
history: RouterHistory,
document: Document,
highlight?: ?string,
context?: ?string,
showCollection?: boolean,
showPublished?: boolean,
showPin?: boolean,
showDraft?: boolean,
showTemplate?: boolean,
};
const SEARCH_RESULT_REGEX = /<b\b[^>]*>(.*?)<\/b>/gi;
@observer
class DocumentPreview extends React.Component<Props> {
handleStar = (ev: SyntheticEvent<>) => {
ev.preventDefault();
ev.stopPropagation();
this.props.document.star();
};
handleUnstar = (ev: SyntheticEvent<>) => {
ev.preventDefault();
ev.stopPropagation();
this.props.document.unstar();
};
replaceResultMarks = (tag: string) => {
// don't use SEARCH_RESULT_REGEX here as it causes
// an infinite loop to trigger a regex inside it's own callback
return tag.replace(/<b\b[^>]*>(.*?)<\/b>/gi, "$1");
};
handleNewFromTemplate = (event) => {
event.preventDefault();
event.stopPropagation();
const { document } = this.props;
this.props.history.push(
newDocumentUrl(document.collectionId, {
templateId: document.id,
})
);
};
render() {
const {
document,
showCollection,
showPublished,
showPin,
showDraft = true,
showTemplate,
highlight,
context,
} = this.props;
const queryIsInTitle =
!!highlight &&
!!document.title.toLowerCase().includes(highlight.toLowerCase());
return (
<DocumentLink
to={{
pathname: document.url,
state: { title: document.titleWithDefault },
}}
>
<Heading>
<Title text={document.titleWithDefault} highlight={highlight} />
{!document.isDraft &&
!document.isArchived &&
!document.isTemplate && (
<Actions>
{document.isStarred ? (
<StyledStar onClick={this.handleUnstar} solid />
) : (
<StyledStar onClick={this.handleStar} />
)}
</Actions>
)}
{document.isDraft && showDraft && (
<Tooltip tooltip="Only visible to you" delay={500} placement="top">
<Badge>Draft</Badge>
</Tooltip>
)}
{document.isTemplate && showTemplate && (
<Badge primary>Template</Badge>
)}
<SecondaryActions>
{document.isTemplate &&
!document.isArchived &&
!document.isDeleted && (
<Button
onClick={this.handleNewFromTemplate}
icon={<PlusIcon />}
neutral
>
New doc
</Button>
)}
&nbsp;
<DocumentMenu document={document} showPin={showPin} />
</SecondaryActions>
</Heading>
{!queryIsInTitle && (
<ResultContext
text={context}
highlight={highlight ? SEARCH_RESULT_REGEX : undefined}
processResult={this.replaceResultMarks}
/>
)}
<DocumentMeta
document={document}
showCollection={showCollection}
showPublished={showPublished}
/>
</DocumentLink>
);
}
}
const StyledStar = withTheme(styled(({ solid, theme, ...props }) => (
<StarredIcon color={theme.text} {...props} />
))`
flex-shrink: 0;
opacity: ${(props) => (props.solid ? "1 !important" : 0)};
transition: all 100ms ease-in-out;
&:hover {
transform: scale(1.1);
}
&:active {
transform: scale(0.95);
}
`);
const SecondaryActions = styled(Flex)`
align-items: center;
position: absolute;
right: 16px;
top: 50%;
transform: translateY(-50%);
`;
const DocumentLink = styled(Link)`
display: block;
margin: 10px -8px;
padding: 6px 8px;
border-radius: 8px;
max-height: 50vh;
min-width: 100%;
max-width: calc(100vw - 40px);
overflow: hidden;
position: relative;
${SecondaryActions} {
opacity: 0;
}
&:hover,
&:active,
&:focus {
background: ${(props) => props.theme.listItemHoverBackground};
outline: none;
${SecondaryActions} {
opacity: 1;
}
${StyledStar} {
opacity: 0.5;
&:hover {
opacity: 1;
}
}
}
`;
const Heading = styled.h3`
display: flex;
align-items: center;
height: 24px;
margin-top: 0;
margin-bottom: 0.25em;
overflow: hidden;
white-space: nowrap;
color: ${(props) => props.theme.text};
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen,
Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
`;
const Actions = styled(Flex)`
margin-left: 4px;
align-items: center;
`;
const Title = styled(Highlight)`
max-width: 90%;
overflow: hidden;
text-overflow: ellipsis;
`;
const ResultContext = styled(Highlight)`
display: block;
color: ${(props) => props.theme.textTertiary};
font-size: 14px;
margin-top: -0.25em;
margin-bottom: 0.25em;
`;
export default withRouter(DocumentPreview);
-3
View File
@@ -1,3 +0,0 @@
// @flow
import DocumentPreview from "./DocumentPreview";
export default DocumentPreview;
+63
View File
@@ -0,0 +1,63 @@
import { DoneIcon } from "outline-icons";
import * as React from "react";
import { useTranslation, TFunction } from "react-i18next";
import styled, { useTheme } from "styled-components";
import Document from "~/models/Document";
import CircularProgressBar from "~/components/CircularProgressBar";
import usePrevious from "~/hooks/usePrevious";
import { bounceIn } from "~/styles/animations";
type Props = {
document: Document;
};
function getMessage(t: TFunction, total: number, completed: number): string {
if (completed === 0) {
return t(`{{ total }} task`, {
total,
count: total,
});
} else if (completed === total) {
return t(`{{ completed }} task done`, {
completed,
count: completed,
});
} else {
return t(`{{ completed }} of {{ total }} tasks`, {
total,
completed,
});
}
}
function DocumentTasks({ document }: Props) {
const { tasks, tasksPercentage } = document;
const { t } = useTranslation();
const theme = useTheme();
const { completed, total } = tasks;
const done = completed === total;
const previousDone = usePrevious(done);
const message = getMessage(t, total, completed);
return (
<>
{completed === total ? (
<Done
color={theme.primary}
size={20}
$animated={done && previousDone === false}
/>
) : (
<CircularProgressBar percentage={tasksPercentage} />
)}
&nbsp;{message}
</>
);
}
const Done = styled(DoneIcon)<{ $animated: boolean }>`
margin: -1px;
animation: ${(props) => (props.$animated ? bounceIn : "none")} 600ms;
transform-origin: center center;
`;
export default DocumentTasks;
+75
View File
@@ -0,0 +1,75 @@
import { formatDistanceToNow } from "date-fns";
import { sortBy } from "lodash";
import { observer } from "mobx-react";
import * as React from "react";
import { useTranslation } from "react-i18next";
import Document from "~/models/Document";
import Avatar from "~/components/Avatar";
import ListItem from "~/components/List/Item";
import PaginatedList from "~/components/PaginatedList";
import useStores from "~/hooks/useStores";
type Props = {
document: Document;
isOpen?: boolean;
};
function DocumentViews({ document, isOpen }: Props) {
const { t } = useTranslation();
const { views, presence } = useStores();
const documentPresence = presence.get(document.id);
const documentPresenceArray = documentPresence
? Array.from(documentPresence.values())
: [];
const presentIds = documentPresenceArray.map((p) => p.userId);
const editingIds = documentPresenceArray
.filter((p) => p.isEditing)
.map((p) => p.userId);
// ensure currently present via websocket are always ordered first
const documentViews = views.inDocument(document.id);
const sortedViews = sortBy(
documentViews,
(view) => !presentIds.includes(view.user.id)
);
const users = React.useMemo(() => sortedViews.map((v) => v.user), [
sortedViews,
]);
return (
<>
{isOpen && (
<PaginatedList
items={users}
renderItem={(item) => {
const view = documentViews.find((v) => v.user.id === item.id);
const isPresent = presentIds.includes(item.id);
const isEditing = editingIds.includes(item.id);
const subtitle = isPresent
? isEditing
? t("Currently editing")
: t("Currently viewing")
: t("Viewed {{ timeAgo }} ago", {
timeAgo: formatDistanceToNow(
view ? Date.parse(view.lastViewedAt) : new Date()
),
});
return (
<ListItem
key={item.id}
title={item.name}
subtitle={subtitle}
image={<Avatar key={item.id} src={item.avatarUrl} size={32} />}
border={false}
compact
small
/>
);
}}
/>
)}
</>
);
}
export default observer(DocumentViews);
-113
View File
@@ -1,113 +0,0 @@
// @flow
import invariant from "invariant";
import { observable } from "mobx";
import { observer, inject } from "mobx-react";
import * as React from "react";
import Dropzone from "react-dropzone";
import { withRouter, type RouterHistory, type Match } from "react-router-dom";
import { createGlobalStyle } from "styled-components";
import DocumentsStore from "stores/DocumentsStore";
import LoadingIndicator from "components/LoadingIndicator";
import importFile from "utils/importFile";
const EMPTY_OBJECT = {};
let importingLock = false;
type Props = {
children: React.Node,
collectionId: string,
documentId?: string,
activeClassName?: string,
rejectClassName?: string,
documents: DocumentsStore,
disabled: boolean,
location: Object,
match: Match,
history: RouterHistory,
staticContext: Object,
};
export const GlobalStyles = createGlobalStyle`
.activeDropZone {
border-radius: 4px;
background: ${(props) => props.theme.slateDark};
svg { fill: ${(props) => props.theme.white}; }
}
.activeDropZone a {
color: ${(props) => props.theme.white} !important;
}
`;
@observer
class DropToImport extends React.Component<Props> {
@observable isImporting: boolean = false;
onDropAccepted = async (files = []) => {
if (importingLock) return;
this.isImporting = true;
importingLock = true;
try {
let collectionId = this.props.collectionId;
const documentId = this.props.documentId;
const redirect = files.length === 1;
if (documentId && !collectionId) {
const document = await this.props.documents.fetch(documentId);
invariant(document, "Document not available");
collectionId = document.collectionId;
}
for (const file of files) {
const doc = await importFile({
documents: this.props.documents,
file,
documentId,
collectionId,
});
if (redirect) {
this.props.history.push(doc.url);
}
}
} finally {
this.isImporting = false;
importingLock = false;
}
};
render() {
const {
documentId,
collectionId,
documents,
disabled,
location,
match,
history,
staticContext,
...rest
} = this.props;
if (this.props.disabled) return this.props.children;
return (
<Dropzone
accept="text/markdown, text/plain"
onDropAccepted={this.onDropAccepted}
style={EMPTY_OBJECT}
disableClick
disablePreview
multiple
{...rest}
>
{this.isImporting && <LoadingIndicator />}
{this.props.children}
</Dropzone>
);
}
}
export default inject("documents")(withRouter(DropToImport));
-286
View File
@@ -1,286 +0,0 @@
// @flow
import invariant from "invariant";
import { observable } from "mobx";
import { observer } from "mobx-react";
import { MoreIcon } from "outline-icons";
import { rgba } from "polished";
import * as React from "react";
import { PortalWithState } from "react-portal";
import styled from "styled-components";
import { fadeAndScaleIn } from "shared/styles/animations";
import Flex from "components/Flex";
import NudeButton from "components/NudeButton";
let previousClosePortal;
let counter = 0;
type Children =
| React.Node
| ((options: { closePortal: () => void }) => React.Node);
type Props = {
label?: React.Node,
onOpen?: () => void,
onClose?: () => void,
children?: Children,
className?: string,
hover?: boolean,
style?: Object,
position?: "left" | "right" | "center",
};
@observer
class DropdownMenu extends React.Component<Props> {
id: string = `menu${counter++}`;
closeTimeout: TimeoutID;
@observable top: ?number;
@observable bottom: ?number;
@observable right: ?number;
@observable left: ?number;
@observable position: "left" | "right" | "center";
@observable fixed: ?boolean;
@observable bodyRect: ClientRect;
@observable labelRect: ClientRect;
@observable dropdownRef: { current: null | HTMLElement } = React.createRef();
@observable menuRef: { current: null | HTMLElement } = React.createRef();
handleOpen = (
openPortal: (SyntheticEvent<>) => void,
closePortal: () => void
) => {
return (ev: SyntheticMouseEvent<HTMLElement>) => {
ev.preventDefault();
const currentTarget = ev.currentTarget;
invariant(document.body, "why you not here");
if (currentTarget instanceof HTMLDivElement) {
this.bodyRect = document.body.getBoundingClientRect();
this.labelRect = currentTarget.getBoundingClientRect();
this.top = this.labelRect.bottom - this.bodyRect.top;
this.bottom = undefined;
this.position = this.props.position || "left";
if (currentTarget.parentElement) {
const triggerParentStyle = getComputedStyle(
currentTarget.parentElement
);
if (triggerParentStyle.position === "static") {
this.fixed = true;
this.top = this.labelRect.bottom;
}
}
this.initPosition();
// attempt to keep only one flyout menu open at once
if (previousClosePortal && !this.props.hover) {
previousClosePortal();
}
previousClosePortal = closePortal;
openPortal(ev);
}
};
};
initPosition() {
if (this.position === "left") {
this.right =
this.bodyRect.width - this.labelRect.left - this.labelRect.width;
} else if (this.position === "center") {
this.left = this.labelRect.left + this.labelRect.width / 2;
} else {
this.left = this.labelRect.left;
}
}
onOpen = () => {
if (typeof this.props.onOpen === "function") {
this.props.onOpen();
}
this.fitOnTheScreen();
};
fitOnTheScreen() {
if (!this.dropdownRef || !this.dropdownRef.current) return;
const el = this.dropdownRef.current;
const sticksOutPastBottomEdge =
el.clientHeight + this.top > window.innerHeight;
if (sticksOutPastBottomEdge) {
this.top = undefined;
this.bottom = this.fixed ? 0 : -1 * window.pageYOffset;
} else {
this.bottom = undefined;
}
if (this.position === "left" || this.position === "right") {
const totalWidth =
Math.sign(this.position === "left" ? -1 : 1) * el.offsetLeft +
el.scrollWidth;
const isVisible = totalWidth < window.innerWidth;
if (!isVisible) {
if (this.position === "right") {
this.position = "left";
this.left = undefined;
} else if (this.position === "left") {
this.position = "right";
this.right = undefined;
}
}
}
this.initPosition();
this.forceUpdate();
}
closeAfterTimeout = (closePortal: () => void) => () => {
if (this.closeTimeout) {
clearTimeout(this.closeTimeout);
}
this.closeTimeout = setTimeout(closePortal, 500);
};
clearCloseTimeout = () => {
if (this.closeTimeout) {
clearTimeout(this.closeTimeout);
}
};
render() {
const { className, hover, label, children } = this.props;
return (
<div className={className}>
<PortalWithState
onOpen={this.onOpen}
onClose={this.props.onClose}
closeOnOutsideClick
closeOnEsc
>
{({ closePortal, openPortal, isOpen, portal }) => (
<>
<Label
onMouseMove={hover ? this.clearCloseTimeout : undefined}
onMouseOut={
hover ? this.closeAfterTimeout(closePortal) : undefined
}
onMouseEnter={
hover ? this.handleOpen(openPortal, closePortal) : undefined
}
onClick={
hover ? undefined : this.handleOpen(openPortal, closePortal)
}
>
{label || (
<NudeButton
id={`${this.id}button`}
aria-haspopup="true"
aria-expanded={isOpen ? "true" : "false"}
aria-controls={this.id}
>
<MoreIcon />
</NudeButton>
)}
</Label>
{portal(
<Position
ref={this.dropdownRef}
position={this.position}
fixed={this.fixed}
top={this.top}
bottom={this.bottom}
left={this.left}
right={this.right}
>
<Menu
ref={this.menuRef}
onMouseMove={hover ? this.clearCloseTimeout : undefined}
onMouseOut={
hover ? this.closeAfterTimeout(closePortal) : undefined
}
onClick={
typeof children === "function"
? undefined
: (ev) => {
ev.stopPropagation();
closePortal();
}
}
style={this.props.style}
id={this.id}
aria-labelledby={`${this.id}button`}
role="menu"
>
{typeof children === "function"
? children({ closePortal })
: children}
</Menu>
</Position>
)}
</>
)}
</PortalWithState>
</div>
);
}
}
const Label = styled(Flex).attrs({
justify: "center",
align: "center",
})`
z-index: ${(props) => props.theme.depths.menu};
cursor: pointer;
`;
const Position = styled.div`
position: ${({ fixed }) => (fixed ? "fixed" : "absolute")};
display: flex;
${({ left }) => (left !== undefined ? `left: ${left}px` : "")};
${({ right }) => (right !== undefined ? `right: ${right}px` : "")};
${({ top }) => (top !== undefined ? `top: ${top}px` : "")};
${({ bottom }) => (bottom !== undefined ? `bottom: ${bottom}px` : "")};
max-height: 75%;
z-index: ${(props) => props.theme.depths.menu};
transform: ${(props) =>
props.position === "center" ? "translateX(-50%)" : "initial"};
pointer-events: none;
`;
const Menu = styled.div`
animation: ${fadeAndScaleIn} 200ms ease;
transform-origin: ${(props) => (props.left !== undefined ? "25%" : "75%")} 0;
backdrop-filter: blur(10px);
background: ${(props) => rgba(props.theme.menuBackground, 0.8)};
border: ${(props) =>
props.theme.menuBorder ? `1px solid ${props.theme.menuBorder}` : "none"};
border-radius: 2px;
padding: 0.5em 0;
min-width: 180px;
overflow: hidden;
overflow-y: auto;
box-shadow: ${(props) => props.theme.menuShadow};
pointer-events: all;
hr {
margin: 0.5em 12px;
}
@media print {
display: none;
}
`;
export const Header = styled.h3`
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
color: ${(props) => props.theme.sidebarText};
letter-spacing: 0.04em;
margin: 1em 12px 0.5em;
`;
export default DropdownMenu;
@@ -1,88 +0,0 @@
// @flow
import { CheckmarkIcon } from "outline-icons";
import * as React from "react";
import styled from "styled-components";
type Props = {
onClick?: (SyntheticEvent<>) => void | Promise<void>,
children?: React.Node,
selected?: boolean,
disabled?: boolean,
};
const DropdownMenuItem = ({
onClick,
children,
selected,
disabled,
...rest
}: Props) => {
return (
<MenuItem
onClick={disabled ? undefined : onClick}
disabled={disabled}
role="menuitem"
tabIndex="-1"
{...rest}
>
{selected !== undefined && (
<>
<CheckmarkIcon
color={selected === false ? "transparent" : undefined}
/>
&nbsp;
</>
)}
{children}
</MenuItem>
);
};
const MenuItem = styled.a`
display: flex;
margin: 0;
padding: 6px 12px;
width: 100%;
min-height: 32px;
color: ${(props) =>
props.disabled ? props.theme.textTertiary : props.theme.textSecondary};
justify-content: left;
align-items: center;
font-size: 15px;
cursor: default;
user-select: none;
svg:not(:last-child) {
margin-right: 8px;
}
svg {
opacity: ${(props) => (props.disabled ? ".5" : 1)};
}
${(props) =>
props.disabled
? "pointer-events: none;"
: `
&:hover {
color: ${props.theme.white};
background: ${props.theme.primary};
box-shadow: none;
cursor: pointer;
svg {
fill: ${props.theme.white};
}
}
&:focus {
color: ${props.theme.white};
background: ${props.theme.primary};
outline: none;
}
`};
`;
export default DropdownMenuItem;
-3
View File
@@ -1,3 +0,0 @@
// @flow
export { default as DropdownMenu, Header } from "./DropdownMenu";
export { default as DropdownMenuItem } from "./DropdownMenuItem";

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