Просмотр исходного кода

Merge branch 'master' into node-6.9

Shane Kilkelly 9 лет назад
Родитель
Сommit
2d0a7b2bdb
27 измененных файлов с 326 добавлено и 159 удалено
  1. 6 1
      services/web/app/coffee/Features/Announcements/AnnouncementsHandler.coffee
  2. 17 2
      services/web/app/coffee/Features/Project/ProjectController.coffee
  3. 1 1
      services/web/app/coffee/Features/Project/ProjectEditorHandler.coffee
  4. 3 5
      services/web/app/views/beta_program/opt_in.pug
  5. 1 0
      services/web/app/views/project/editor.pug
  6. 111 53
      services/web/app/views/project/editor/feature-onboarding.pug
  7. 3 1
      services/web/app/views/project/editor/header.pug
  8. 5 2
      services/web/app/views/project/editor/review-panel.pug
  9. 15 0
      services/web/public/coffee/directives/videoPlayState.coffee
  10. 1 1
      services/web/public/coffee/ide.coffee
  11. 26 25
      services/web/public/coffee/ide/FeatureOnboardingController.coffee
  12. 10 8
      services/web/public/coffee/ide/editor/directives/aceEditor/track-changes/TrackChangesManager.coffee
  13. 7 1
      services/web/public/coffee/ide/review-panel/controllers/ReviewPanelController.coffee
  14. 1 2
      services/web/public/coffee/ide/review-panel/directives/addCommentEntry.coffee
  15. BIN
      services/web/public/img/onboarding/review-panel/accept-changes.gif
  16. BIN
      services/web/public/img/onboarding/review-panel/accept-changes.mp4
  17. BIN
      services/web/public/img/onboarding/review-panel/add-changes.gif
  18. BIN
      services/web/public/img/onboarding/review-panel/add-changes.mp4
  19. BIN
      services/web/public/img/onboarding/review-panel/commenting.gif
  20. BIN
      services/web/public/img/onboarding/review-panel/commenting.mp4
  21. BIN
      services/web/public/img/onboarding/review-panel/open-review.gif
  22. BIN
      services/web/public/img/onboarding/review-panel/open-review.mp4
  23. BIN
      services/web/public/img/teasers/track-changes/track-changes-beta.png
  24. 74 51
      services/web/public/stylesheets/app/editor/feature-onboarding.less
  25. 1 1
      services/web/public/stylesheets/app/editor/review-panel.less
  26. 19 4
      services/web/test/UnitTests/coffee/Announcement/AnnouncementsHandlerTests.coffee
  27. 25 1
      services/web/test/UnitTests/coffee/Project/ProjectControllerTests.coffee

+ 6 - 1
services/web/app/coffee/Features/Announcements/AnnouncementsHandler.coffee

@@ -19,6 +19,9 @@ module.exports = AnnouncementsHandler =
 		if !user? and !user._id?
 			return callback("user not supplied")
 
+		timestamp = user._id.toString().substring(0,8)
+		userSignupDate = new Date( parseInt( timestamp, 16 ) * 1000 )
+
 		async.parallel {
 			lastEvent: (cb)->
 				AnalyticsManager.getLastOccurance user._id, "announcement-alert-dismissed", cb
@@ -48,7 +51,9 @@ module.exports = AnnouncementsHandler =
 				announcement.id == lastSeenBlogId
 
 			announcements = _.map announcements, (announcement, index)->
-				if announcementIndex == -1
+				if announcement.date < userSignupDate
+					read = true
+				else if announcementIndex == -1
 					read = false
 				else if index >= announcementIndex
 					read = true

+ 17 - 2
services/web/app/coffee/Features/Project/ProjectController.coffee

@@ -10,7 +10,7 @@ TagsHandler = require("../Tags/TagsHandler")
 SubscriptionLocator = require("../Subscription/SubscriptionLocator")
 NotificationsHandler = require("../Notifications/NotificationsHandler")
 LimitationsManager = require("../Subscription/LimitationsManager")
-_ = require("underscore")
+underscore = require("underscore")
 Settings = require("settings-sharelatex")
 AuthorizationManager = require("../Authorization/AuthorizationManager")
 fs = require "fs"
@@ -20,6 +20,7 @@ ProjectGetter = require("./ProjectGetter")
 PrivilegeLevels = require("../Authorization/PrivilegeLevels")
 AuthenticationController = require("../Authentication/AuthenticationController")
 PackageVersions = require("../../infrastructure/PackageVersions")
+AnalyticsManager = require "../Analytics/AnalyticsManager"
 
 module.exports = ProjectController =
 
@@ -219,6 +220,19 @@ module.exports = ProjectController =
 				#don't need to wait for this to complete
 				ProjectUpdateHandler.markAsOpened project_id, ->
 				cb()
+			showTrackChangesOnboarding: (cb) ->
+				cb = underscore.once(cb)
+				if !user_id?
+					return cb()
+				timeout = setTimeout cb, 500
+				AnalyticsManager.getLastOccurance user_id, "shown-track-changes-onboarding", (error, event) ->
+					clearTimeout timeout
+					if error?
+						return cb(null, false)
+					else if event?
+						return cb(null, false)
+					else
+						return cb(null, true)
 		}, (err, results)->
 			if err?
 				logger.err err:err, "error getting details for project page"
@@ -226,7 +240,7 @@ module.exports = ProjectController =
 			project = results.project
 			user = results.user
 			subscription = results.subscription
-
+			showTrackChangesOnboarding = results.showTrackChangesOnboarding
 
 			daysSinceLastUpdated =  (new Date() - project.lastUpdated) /86400000
 			logger.log project_id:project_id, daysSinceLastUpdated:daysSinceLastUpdated, "got db results for loading editor"
@@ -268,6 +282,7 @@ module.exports = ProjectController =
 						syntaxValidation: user.ace.syntaxValidation
 					}
 					trackChangesEnabled: !!project.track_changes
+					showTrackChangesOnboarding: !!showTrackChangesOnboarding
 					privilegeLevel: privilegeLevel
 					chatUrl: Settings.apis.chat.url
 					anonymous: anonymous

+ 1 - 1
services/web/app/coffee/Features/Project/ProjectEditorHandler.coffee

@@ -22,7 +22,7 @@ module.exports = ProjectEditorHandler =
 			
 		trackChangesVisible = false
 		for member in members
-			if member.privilegeLevel == "owner" and member.user?.featureSwitches?.track_changes
+			if member.privilegeLevel == "owner" and (member.user?.featureSwitches?.track_changes or member.user?.betaProgram)
 				trackChangesVisible = true
 
 		{owner, ownerFeatures, members} = @buildOwnerAndMembersViews(members)

+ 3 - 5
services/web/app/views/beta_program/opt_in.pug

@@ -18,11 +18,9 @@ block content
 											| #{translate("beta_program_badge_description")}
 											span.beta-feature-badge
 										p.text-centered
-											| #{translate("beta_program_current_beta_features_description")}
-											ul.list-unstyled.text-center
-												li 
-													i.fa.fa-fw.fa-book
-													| &nbsp;#{translate("syntax_checking")}
+											strong We're currently testing track changes and commenting:
+										p.text-centered
+											img(src="/img/teasers/track-changes/track-changes-beta.png", style="max-width: 100%; border-bottom: 1px solid #ddd")
 								.row.text-centered
 									.col-md-12
 										if user.betaProgram

+ 1 - 0
services/web/app/views/project/editor.pug

@@ -108,6 +108,7 @@ block requirejs
 		window.anonymous = #{anonymous};
 		window.maxDocLength = #{maxDocLength};
 		window.trackChangesEnabled = #{trackChangesEnabled};
+		window.showTrackChangesOnboarding = #{!!showTrackChangesOnboarding};
 		window.wikiEnabled = #{!!(settings.apis.wiki && settings.apis.wiki.url)};
 		window.requirejs = {
 			"paths" : {

+ 111 - 53
services/web/app/views/project/editor/feature-onboarding.pug

@@ -1,54 +1,112 @@
-.feat-onboard(
-	ng-controller="FeatureOnboardingController"
-	ng-class="('feat-onboard-step' + innerStep)"
-	ng-if="!state.loading && ui.showCodeCheckerOnboarding"
-	ng-cloak
-)
-	.feat-onboard-wrapper
-		h1.feat-onboard-title
-			| Introducing&nbsp;
-			span.feat-onboard-title-name Code check
-		div(ng-if="innerStep === 1;")
+div(ng-controller="FeatureOnboardingController")
+	.feat-onboard(
+		ng-class="('feat-onboard-step' + onboarding.innerStep)"
+		ng-if="!state.loading && showCollabFeaturesOnboarding"
+		ng-cloak
+		stop-propagation="click"
+	)
+		a.feat-onboard-dismiss(
+			href
+			ng-click="dismiss();"
+		) &times;
+		.feat-onboard-wrapper
+			h1.feat-onboard-title
+				| Introducing&nbsp;
+				span.feat-onboard-highlight Commenting
+				|  &amp; 
+				span.feat-onboard-highlight Track Changes
 			p.feat-onboard-description
-				span.feat-onboard-description-name Code check&nbsp;
-				| will highlight potential problems in your LaTeX code, allowing you to handle errors earlier and become more productive.
-			.row
-				video.feat-onboard-video(autoplay, loop)
-					source(src="/img/teasers/code-checker/code-checker.mp4", type="video/mp4")
-					img(src="/img/teasers/code-checker/code-checker.gif")
-			.row.feat-onboard-adv-wrapper
-				.col-xs-4
-					h2.feat-onboard-adv-title 
-						| Missing&nbsp;
-						span.feat-onboard-adv-title-highlight brackets
-					p Forgot to place a closing bracket? We'll warn you.
-				.col-xs-4
-					h2.feat-onboard-adv-title 
-						| Unclosed&nbsp;
-						span.feat-onboard-adv-title-highlight environments
-					p 
-						| Know when you are missing an&nbsp;
-						code \end{...}
-						| &nbsp;command.
-				.col-xs-4
-					h2.feat-onboard-adv-title 
-						| Incorrect&nbsp;
-						span.feat-onboard-adv-title-highlight nesting
-					p 
-						| Order matters. Get notified when you use an&nbsp;
-						code \end{...}
-						| &nbsp; too soon.
-			.feat-onboard-btn-wrapper
-				button.btn.btn-primary(ng-click="turnCodeCheckOn();") Yes, turn Code check on
-			.feat-onboard-btn-wrapper
-				button.btn.btn-default(ng-click="turnCodeCheckOff();") No, disable it for now
-		div(ng-if="innerStep === 2;")
-			p.feat-onboard-description
-				| Remember: you can always turn&nbsp;
-				span.feat-onboard-description-name Code check&nbsp;
-				em on&nbsp;
-				| or&nbsp;
-				em off&nbsp;
-				|, in the settings menu.
-			.feat-onboard-btn-wrapper
-				button.btn.btn-primary(ng-click="dismiss();") OK, got it
+				span.feat-onboard-highlight Commenting 
+				| and 
+				span.feat-onboard-highlight Track Changes 
+				| will make it easier for you to work with peers in your documents. 
+
+			.feat-onboard-tutorial-wrapper
+				button.btn.btn-primary.feat-onboard-nav-btn(
+					ng-click="gotoPrevStep();"
+					ng-disabled="onboarding.innerStep === 1;")
+					i.fa.fa-arrow-left
+				div(ng-show="onboarding.innerStep === 1;")
+					video.feat-onboard-video(
+						video-play-state="onboarding.innerStep === 1;"
+						autoplay
+						loop
+					)
+						source(src="/img/onboarding/review-panel/open-review.mp4", type="video/mp4")
+						img(src="/img/onboarding/review-panel/open-review.gif")
+				div(ng-show="onboarding.innerStep === 2;")
+					video.feat-onboard-video(
+						video-play-state="onboarding.innerStep === 2;"
+						autoplay
+						loop
+					)
+						source(src="/img/onboarding/review-panel/commenting.mp4", type="video/mp4")
+						img(src="/img/onboarding/review-panel/commenting.gif")
+				div(ng-show="onboarding.innerStep === 3;")
+					video.feat-onboard-video(
+						video-play-state="onboarding.innerStep === 3;"
+						autoplay
+						loop
+					)
+						source(src="/img/onboarding/review-panel/add-changes.mp4", type="video/mp4")
+						img(src="/img/onboarding/review-panel/add-changes.gif")
+				div(ng-show="onboarding.innerStep === 4;")
+					video.feat-onboard-video(
+						video-play-state="onboarding.innerStep === 4;"
+						autoplay
+						loop
+					)
+						source(src="/img/onboarding/review-panel/accept-changes.mp4", type="video/mp4")
+						img(src="/img/onboarding/review-panel/accept-changes.gif")
+				button.btn.btn-primary.feat-onboard-nav-btn(
+					ng-click="gotoNextStep();"
+					ng-disabled="onboarding.innerStep === onboarding.nSteps;")
+					i.fa.fa-arrow-right
+
+			div(ng-switch="onboarding.innerStep")
+				.row(ng-switch-when="1")
+					.col-xs-6
+						h2.feat-onboard-adv-title Commenting
+						p.feat-onboard-description Want to discuss specific parts of the text?
+						p.feat-onboard-description Use our brand-new commenting system.
+					.col-xs-6
+						h2.feat-onboard-adv-title Track Changes
+						p.feat-onboard-description See changes in your documents, live.
+						p.feat-onboard-description Track, accept and reject changes individually.
+				.row(ng-switch-when="2")
+					.col-xs-12
+						h2.feat-onboard-adv-title Commenting
+						p.feat-onboard-description Just select a span of text and click on 
+							span.feat-onboard-highlight &ldquo;Add comment&rdquo;
+							| .	
+						p.feat-onboard-description 
+							span.feat-onboard-highlight Comments 
+							| can be 
+							span.feat-onboard-highlight replied 
+							| to, 
+							span.feat-onboard-highlight resolved 
+							| and permanently 
+							span.feat-onboard-highlight deleted
+							| .
+				.row(ng-switch-when="3")
+					.col-xs-12
+						h2.feat-onboard-adv-title Track Changes
+						p.feat-onboard-description
+							| Let your peers know what you've been up to.
+						p.feat-onboard-description
+							| Click on the 
+							span.feat-onboard-highlight &ldquo;Track Changes&rdquo; 
+							| toggle to start marking your insertions, as well as your deletions.
+
+				.row(ng-switch-when="4") 
+					.col-xs-12
+						h2.feat-onboard-adv-title Track Changes
+						p.feat-onboard-description Upon reviewing, 
+							span.feat-onboard-highlight changes 
+							| can be accepted or undone.
+						p.feat-onboard-description
+							| Click&nbsp;
+							span.feat-onboard-highlight &ldquo;Accept&rdquo; 
+							| or&nbsp;
+							span.feat-onboard-highlight &ldquo;Reject&rdquo; 
+							| to incorporate or discard an individual change.

+ 3 - 1
services/web/app/views/project/editor/header.pug

@@ -92,7 +92,9 @@ header.toolbar.toolbar-header.toolbar-with-labels(
 			ng-click="toggleReviewPanel()"
 		)
 			i.review-icon
-			p.toolbar-label Review
+			p.toolbar-label
+				| Review
+				span(style="vertical-align: 20%; margin-left: 4px; padding: 2px 4px;").beta-feature-badge
 		a.btn.btn-full-height(
 			href,
 			ng-if="permissions.admin",

+ 5 - 2
services/web/app/views/project/editor/review-panel.pug

@@ -6,7 +6,7 @@
 		ng-class="{ 'rp-track-changes-indicator-on-dark' : darkTheme }"
 	) Track changes is 
 		strong on
-		
+			
 	.review-panel-toolbar
 		resolved-comments-dropdown(
 			class="rp-flex-block"
@@ -332,6 +332,7 @@ script(type='text/ng-template', id='addCommentEntryTemplate')
 						ng-keypress="handleCommentKeyPress($event);"
 						placeholder="Add your comment here"
 						focus-on="comment:new:open"
+						ng-blur="submitNewComment()"
 					)
 				.rp-entry-actions
 					button.rp-entry-button(
@@ -417,10 +418,12 @@ script(type="text/ng-template", id="trackChangesUpgradeModalTemplate")
 			a.btn.btn-success(
 				href
 				ng-click="startFreeTrial('track-changes')"
+				ng-show="project.owner._id == user.id"
 			) Try it for free
+			p(ng-show="project.owner._id != user.id"): strong Please ask the project owner to upgrade to use track changes
 
 	.modal-footer()
 		button.btn.btn-default(
 			ng-click="cancel()"
 		)
-			span #{translate("close")}
+			span #{translate("close")}

+ 15 - 0
services/web/public/coffee/directives/videoPlayState.coffee

@@ -0,0 +1,15 @@
+define [
+	"base"
+], (App) ->
+	App.directive "videoPlayState", ($parse) ->
+		return {
+			restrict: "A",
+			link: (scope, element, attrs) ->
+				videoDOMEl = element[0]
+				scope.$watch (() -> $parse(attrs.videoPlayState)(scope)), (shouldPlay) ->
+					if shouldPlay
+						videoDOMEl.currentTime = 0
+						videoDOMEl.play()
+					else 
+						videoDOMEl.pause()
+		}

+ 1 - 1
services/web/public/coffee/ide.coffee

@@ -29,6 +29,7 @@ define [
 	"directives/stopPropagation"
 	"directives/rightClick"
 	"directives/expandableTextArea"
+	"directives/videoPlayState"
 	"services/queued-http"
 	"filters/formatDate"
 	"main/event"
@@ -69,7 +70,6 @@ define [
 			chatOpen: false
 			pdfLayout: 'sideBySide'
 			reviewPanelOpen: localStorage("ui.reviewPanelOpen.#{window.project_id}")
-			showCodeCheckerOnboarding: !window.userSettings.syntaxValidation?
 		}
 		$scope.user = window.user
 

+ 26 - 25
services/web/public/coffee/ide/FeatureOnboardingController.coffee

@@ -1,35 +1,36 @@
 define [
 	"base"
 ], (App) ->
-	App.controller "FeatureOnboardingController", ($scope, settings) ->
-		$scope.innerStep = 1
-
-		$scope.turnCodeCheckOn = () ->
-			settings.saveSettings({ syntaxValidation: true })
-			$scope.settings.syntaxValidation = true
-			navToInnerStep2()
-			
-		$scope.turnCodeCheckOff = () ->
-			settings.saveSettings({ syntaxValidation: false })
-			$scope.settings.syntaxValidation = false
-			navToInnerStep2()
+	App.controller "FeatureOnboardingController", ($scope, settings, event_tracking) ->
+		$scope.onboarding = 
+			innerStep: 1
+			nSteps: 4
+		
+		$scope.$watch "project.features.trackChangesVisible", (visible) ->
+			return if !visible?
+			$scope.showCollabFeaturesOnboarding = window.showTrackChangesOnboarding and visible
 
 		$scope.dismiss = () ->
-			$scope.ui.leftMenuShown = false
-			$scope.ui.showCodeCheckerOnboarding = false
+			event_tracking.sendMB "shown-track-changes-onboarding"
+			$scope.$applyAsync(() -> $scope.showCollabFeaturesOnboarding = false)
+
+		$scope.gotoPrevStep = () ->
+			if $scope.onboarding.innerStep > 1 
+				$scope.$applyAsync(() -> $scope.onboarding.innerStep--)
 
-		navToInnerStep2 = () ->
-			$scope.innerStep = 2
-			$scope.ui.leftMenuShown = true
+		$scope.gotoNextStep = () ->
+			if $scope.onboarding.innerStep < 4
+				$scope.$applyAsync(() -> $scope.onboarding.innerStep++)
 
-		handleKeypress = (e) ->
-			if e.keyCode == 13
-				if $scope.innerStep == 1
-					$scope.turnCodeCheckOn()
-				else
-					$scope.dismiss()
+		handleKeydown = (e) ->
+			switch e.keyCode
+				when 37 then $scope.gotoPrevStep()     # left directional key
+				when 39, 13 then $scope.gotoNextStep() # right directional key, enter
+				when 27 then $scope.dismiss()          # escape
 
-		$(document).on "keypress", handleKeypress
+		$(document).on "keydown", handleKeydown
+		$(document).on "click", $scope.dismiss
 
 		$scope.$on "$destroy", () -> 
-			$(document).off "keypress", handleKeypress
+			$(document).off "keydown", handleKeydown
+			$(document).off "click", $scope.dismiss

+ 10 - 8
services/web/public/coffee/ide/editor/directives/aceEditor/track-changes/TrackChangesManager.coffee

@@ -20,8 +20,8 @@ define [
 				@rangesTracker = doc.ranges
 				@connectToRangesTracker()
 			
-			@$scope.$on "comment:add", (e, thread_id) =>
-				@addCommentToSelection(thread_id)
+			@$scope.$on "comment:add", (e, thread_id, offset, length) =>
+				@addCommentToSelection(thread_id, offset, length)
 
 			@$scope.$on "comment:select_line", (e) =>
 				@selectLineIfNoSelection()
@@ -45,7 +45,7 @@ define [
 				@recalculateReviewEntriesScreenPositions()
 
 			changingSelection = false
-			onChangeSelection = (args...) =>
+			onChangeSelection = () =>
 				# Deletes can send about 5 changeSelection events, so
 				# just act on the last one.
 				if !changingSelection
@@ -53,7 +53,6 @@ define [
 					@$scope.$evalAsync () =>
 						changingSelection = false
 						@updateFocus()
-						@recalculateReviewEntriesScreenPositions()
 			
 			onResize = () =>
 				@recalculateReviewEntriesScreenPositions()
@@ -64,11 +63,13 @@ define [
 
 			bindToAce = () =>
 				@editor.on "changeSelection", onChangeSelection
+				@editor.on "change", onChangeSelection # Selection also moves with updates elsewhere in the document
 				@editor.on "changeSession", onChangeSession
 				@editor.renderer.on "resize", onResize
 
 			unbindFromAce = () =>
 				@editor.off "changeSelection", onChangeSelection
+				@editor.off "change", onChangeSelection
 				@editor.off "changeSession", onChangeSession
 				@editor.renderer.off "resize", onResize
 
@@ -174,10 +175,11 @@ define [
 			# @rangesTracker.applyOp op # Will apply via sharejs
 			@$scope.sharejsDoc.submitOp op
 		
-		addCommentToSelection: (thread_id) ->
-			range = @editor.getSelectionRange()
-			content = @editor.getSelectedText()
-			offset = @_aceRangeToShareJs(range.start)
+		addCommentToSelection: (thread_id, offset, length) ->
+			start = @_shareJsOffsetToAcePosition(offset)
+			end = @_shareJsOffsetToAcePosition(offset + length)
+			range = new Range(start.row, start.column, end.row, end.column)
+			content = @editor.session.getTextRange(range)
 			@addComment(offset, content, thread_id)
 		
 		selectLineIfNoSelection: () ->

+ 7 - 1
services/web/public/coffee/ide/review-panel/controllers/ReviewPanelController.coffee

@@ -280,6 +280,7 @@ define [
 					entries["add-comment"] = {
 						type: "add-comment"
 						offset: selection_offset_start
+						length: selection_offset_end - selection_offset_start
 					}
 			
 			for id, entry of entries
@@ -310,10 +311,15 @@ define [
 				$scope.$broadcast "review-panel:layout"
 		
 		$scope.submitNewComment = (content) ->
+			return if !content? or content == ""
+			doc_id = $scope.editor.open_doc_id
+			entries = getDocEntries(doc_id)
+			return if !entries["add-comment"]?
+			{offset, length} = entries["add-comment"]
 			thread_id = RangesTracker.generateId()
 			thread = getThread(thread_id)
 			thread.submitting = true
-			$scope.$broadcast "comment:add", thread_id
+			$scope.$broadcast "comment:add", thread_id, offset, length
 			$http.post("/project/#{$scope.project_id}/thread/#{thread_id}/messages", {content, _csrf: window.csrfToken})
 				.error (error) ->
 					ide.showGenericMessageModal("Error submitting comment", "Sorry, there was a problem submitting your comment")

+ 1 - 2
services/web/public/coffee/ide/review-panel/directives/addCommentEntry.coffee

@@ -28,10 +28,9 @@ define [
 				if ev.keyCode == 13 and !ev.shiftKey and !ev.ctrlKey and !ev.metaKey
 					ev.preventDefault()
 					if scope.state.content.length > 0 
-						ev.target.blur()
 						scope.submitNewComment()
 
 			scope.submitNewComment = () ->
 				scope.onSubmit { content: scope.state.content }
 				scope.state.isAdding = false
-				scope.state.content = ""
+				scope.state.content = ""

BIN
services/web/public/img/onboarding/review-panel/accept-changes.gif


BIN
services/web/public/img/onboarding/review-panel/accept-changes.mp4


BIN
services/web/public/img/onboarding/review-panel/add-changes.gif


BIN
services/web/public/img/onboarding/review-panel/add-changes.mp4


BIN
services/web/public/img/onboarding/review-panel/commenting.gif


BIN
services/web/public/img/onboarding/review-panel/commenting.mp4


BIN
services/web/public/img/onboarding/review-panel/open-review.gif


BIN
services/web/public/img/onboarding/review-panel/open-review.mp4


BIN
services/web/public/img/teasers/track-changes/track-changes-beta.png


+ 74 - 51
services/web/public/stylesheets/app/editor/feature-onboarding.less

@@ -1,78 +1,101 @@
-@feat-onboard-wrapper-width: 820px;
-@feat-onboard-max-text-width: 750px;
+@feat-onboard-width: 900px;
 
 .feat-onboard {
 	position: fixed;
-	top: 0;
-	right: 0;
-	bottom: 0;
-	left: 0;
+	top: 50px;
+	bottom: 50px;
+	left: 50%;
+	width: @feat-onboard-width;
+	margin-left: -(@feat-onboard-width / 2);
 	display: flex;
  	justify-content: center;
- 	align-items: center;
-	background-image: linear-gradient(rgba(0, 0, 0, .85), rgba(0, 0, 0, .85));
+ 	align-items: baseline;
+	background-color: rgba(0, 0, 0, .85);
 	background-repeat: no-repeat;
 	background-position-x: 0;
 	color: #FFF;
 	text-align: center;
+	border-radius: 1em;
 	z-index: 102;
-	transition: background-position ease-in-out @left-menu-animation-duration;
 	overflow: auto;
 }
-
-.feat-onboard-step2 {
-	background-position-x: @left-menu-width;
-
-	~ #left-menu {
-		pointer-events: none;
-
-		.code-check-setting {
-			box-shadow: 0 0 300px 0 #000;
-		}
-	}
-}
 	.feat-onboard-wrapper {
-		width: @feat-onboard-wrapper-width;
+		padding: 30px 0;
 	}
-		.feat-onboard-title {
-			color: @brand-primary;
-			margin-bottom: 40px;
 
+		.feat-onboard-title {
+			color: #FFF;
+			margin-bottom: 30px;
 		}
-			.feat-onboard-title-name {
-				color: #FFF;
-				font-weight: bold;
-			}
 
 		.feat-onboard-description {
-			max-width: @feat-onboard-max-text-width;
-			margin: 0 auto 30px;
-			padding: 0 80px;
+			max-width: 35em;
+			margin: 0 auto 5px;
 		}
-			.feat-onboard-description-name {
+		
+		.feat-onboard-highlight {
+			font-weight: bold;
+			white-space: nowrap;
+		}
+
+			.feat-onboard-adv-title {
 				font-weight: bold;
+				white-space: nowrap;
+				color: #FFF;
+				font-size: 23px;
+				margin-top: 0;
 			}
 
-	.feat-onboard-video {
-		box-shadow: 0 0 70px 0 rgba(255, 255, 255, 0.3);
+	.feat-onboard-tutorial-wrapper {
+		display: flex;
+		align-items: center;
+		padding: 30px 0 15px;
 	}
-
-	.feat-onboard-adv-wrapper {
-		text-align: left;
-		margin-bottom: 30px;
-	}
-		.feat-onboard-adv-title {
-			color: #FFF;
-			font-size: 23px;
+		.feat-onboard-video {
+			width: 616px;
+			margin: 0 30px;
+			box-shadow: 0 0 70px 0 rgba(255, 255, 255, 0.3);
 		}
-			.feat-onboard-adv-title-highlight {
-				font-weight: bold;
-			}
-
-	.feat-onboard-btn-wrapper {
-		margin-bottom: 10px;
 
-		> .btn {
+		.feat-onboard-nav-btn {
+			border-radius: 1em;
+			width: 2em;
+			height: 2em;
+			text-align: center;
+			padding: 0;
+			font-size: 1.3em;
+			line-height: 1em;
 			box-shadow: 0 0 70px 0 rgba(255, 255, 255, 0.3);
+
+			&[disabled] {
+				opacity: 0.2;
+			}
+
+			&:focus,
+			&:active:focus {
+				outline: 0;
+				box-shadow: 0 0 70px 0 rgba(255, 255, 255, 0.3);
+			}
 		}
-	}
+
+a.feat-onboard-dismiss {
+	position: absolute;
+	top: 10px;
+	right: 10px;
+	width: 1em;
+	height: 1em;
+	line-height: 1em;
+	font-size: 2.5em;
+	color: #FFF;
+	background-color: rgba(0,0,0, .25);
+	opacity: 0.7;
+	border-radius: 0.5em;
+	transition: opacity .15s ease-in-out;
+	
+	&:hover,
+	&:focus {
+		text-decoration: none;
+		color: #FFF;
+		opacity: 1;
+	}
+}

+ 1 - 1
services/web/public/stylesheets/app/editor/review-panel.less

@@ -391,7 +391,7 @@
 				border-right-width: 0;
 			}
 
-			.rp-layout-left & {
+			.rp-state-current-file-mini.rp-layout-left & {
 				&:first-child {
 					border-bottom-left-radius: 3px;
 				}

+ 19 - 4
services/web/test/UnitTests/coffee/Announcement/AnnouncementsHandlerTests.coffee

@@ -11,7 +11,7 @@ describe 'AnnouncementsHandler', ->
 
 	beforeEach ->
 		@user = 
-			_id:"some_id"
+			_id:"3c6afe000000000000000000" #2002-02-14T00:00:00.000Z
 			email: "someone@gmail.com"
 		@AnalyticsManager =
 			getLastOccurance: sinon.stub()
@@ -36,10 +36,10 @@ describe 'AnnouncementsHandler', ->
 					id: '/2013/08/02/thesis-series-pt1' 
 				}, {
 					date: new Date(1108369600000),
-					id: '/2011/08/04/somethingelse'
+					id: '/2005/08/04/somethingelse'
 				}, {
 					date: new Date(1208369600000),
-					id: '/2014/04/12/title-date-irrelivant'
+					id: '/2008/04/12/title-date-irrelivant'
 				}
 			]
 			@BlogHandler.getLatestAnnouncements.callsArgWith(0, null, @stubbedAnnouncements)
@@ -64,7 +64,7 @@ describe 'AnnouncementsHandler', ->
 				done()
 
 		it "should return older ones marked as read as well", (done)->
-			@AnalyticsManager.getLastOccurance.callsArgWith(2, null, {segmentation:{blogPostId:"/2014/04/12/title-date-irrelivant"}})
+			@AnalyticsManager.getLastOccurance.callsArgWith(2, null, {segmentation:{blogPostId:"/2008/04/12/title-date-irrelivant"}})
 			@handler.getUnreadAnnouncements @user, (err, announcements)=>
 				announcements[0].id.should.equal @stubbedAnnouncements[0].id
 				announcements[0].read.should.equal false
@@ -89,6 +89,21 @@ describe 'AnnouncementsHandler', ->
 				announcements[3].read.should.equal true
 				done()
 
+		it "should return posts older than signup date as read", (done)->
+			@stubbedAnnouncements.push({
+					date: new Date(978836800000),
+					id: '/2001/04/12/title-date-irrelivant'
+			})
+			@AnalyticsManager.getLastOccurance.callsArgWith(2, null, [])
+			@handler.getUnreadAnnouncements @user, (err, announcements)=>
+				announcements[0].read.should.equal false
+				announcements[1].read.should.equal false
+				announcements[2].read.should.equal false
+				announcements[3].read.should.equal false
+				announcements[4].read.should.equal true
+				announcements[4].id.should.equal '/2001/04/12/title-date-irrelivant'
+				done()
+
 
 		describe "with custom domain announcements", ->
 			beforeEach ->

+ 25 - 1
services/web/test/UnitTests/coffee/Project/ProjectControllerTests.coffee

@@ -58,6 +58,8 @@ describe "ProjectController", ->
 			getLoggedInUserId: sinon.stub().returns(@user._id)
 			getSessionUser: sinon.stub().returns(@user)
 			isUserLoggedIn: sinon.stub().returns(true)
+		@AnalyticsManager =
+			getLastOccurance: sinon.stub()
 		@ProjectController = SandboxedModule.require modulePath, requires:
 			"settings-sharelatex":@settings
 			"logger-sharelatex":
@@ -82,6 +84,7 @@ describe "ProjectController", ->
 			"../ReferencesSearch/ReferencesSearchHandler": @ReferencesSearchHandler
 			"./ProjectGetter": @ProjectGetter
 			'../Authentication/AuthenticationController': @AuthenticationController
+			"../Analytics/AnalyticsManager": @AnalyticsManager
 
 		@projectName = "£12321jkj9ujkljds"
 		@req =
@@ -310,9 +313,9 @@ describe "ProjectController", ->
 			@AuthorizationManager.getPrivilegeLevelForProject.callsArgWith 2, null, "owner"
 			@ProjectDeleter.unmarkAsDeletedByExternalSource = sinon.stub()
 			@InactiveProjectManager.reactivateProjectIfRequired.callsArgWith(1)
+			@AnalyticsManager.getLastOccurance.yields(null, {"mock": "event"})
 			@ProjectUpdateHandler.markAsOpened.callsArgWith(1)
 
-
 		it "should render the project/editor page", (done)->
 			@res.render = (pageName, opts)=>
 				pageName.should.equal "project/editor"
@@ -357,3 +360,24 @@ describe "ProjectController", ->
 				@ProjectUpdateHandler.markAsOpened.calledWith(@project_id).should.equal true
 				done()
 			@ProjectController.loadEditor @req, @res
+		
+		it "should set showTrackChangesOnboarding = false if there is an event", (done) ->
+			@AnalyticsManager.getLastOccurance.yields(null, {"mock": "event"})
+			@res.render = (pageName, opts)=>
+				opts.showTrackChangesOnboarding.should.equal false
+				done()
+			@ProjectController.loadEditor @req, @res
+		
+		it "should set showTrackChangesOnboarding = true if there is no event", (done) ->
+			@AnalyticsManager.getLastOccurance.yields(null, null)
+			@res.render = (pageName, opts)=>
+				opts.showTrackChangesOnboarding.should.equal true
+				done()
+			@ProjectController.loadEditor @req, @res
+		
+		it "should set showTrackChangesOnboarding = false if there is an error", (done) ->
+			@AnalyticsManager.getLastOccurance.yields(new Error("oops"), null)
+			@res.render = (pageName, opts)=>
+				opts.showTrackChangesOnboarding.should.equal false
+				done()
+			@ProjectController.loadEditor @req, @res