mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-07 20:31:06 -05:00
Merge branch 'master' into node-6.9
This commit is contained in:
commit
2d0a7b2bdb
27 changed files with 328 additions and 161 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
| #{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
|
||||
|
|
|
@ -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" : {
|
||||
|
|
|
@ -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
|
||||
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();"
|
||||
) ×
|
||||
.feat-onboard-wrapper
|
||||
h1.feat-onboard-title
|
||||
| Introducing
|
||||
span.feat-onboard-highlight Commenting
|
||||
| &
|
||||
span.feat-onboard-highlight Track Changes
|
||||
p.feat-onboard-description
|
||||
span.feat-onboard-description-name Code check
|
||||
| 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
|
||||
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
|
||||
span.feat-onboard-adv-title-highlight environments
|
||||
p
|
||||
| Know when you are missing an
|
||||
code \end{...}
|
||||
| command.
|
||||
.col-xs-4
|
||||
h2.feat-onboard-adv-title
|
||||
| Incorrect
|
||||
span.feat-onboard-adv-title-highlight nesting
|
||||
p
|
||||
| Order matters. Get notified when you use an
|
||||
code \end{...}
|
||||
| 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
|
||||
span.feat-onboard-description-name Code check
|
||||
em on
|
||||
| or
|
||||
em off
|
||||
|, 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 “Add comment”
|
||||
| .
|
||||
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 “Track Changes”
|
||||
| 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
|
||||
span.feat-onboard-highlight “Accept”
|
||||
| or
|
||||
span.feat-onboard-highlight “Reject”
|
||||
| to incorporate or discard an individual change.
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
services/web/public/coffee/directives/videoPlayState.coffee
Normal file
15
services/web/public/coffee/directives/videoPlayState.coffee
Normal file
|
@ -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()
|
||||
}
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
navToInnerStep2 = () ->
|
||||
$scope.innerStep = 2
|
||||
$scope.ui.leftMenuShown = true
|
||||
$scope.gotoPrevStep = () ->
|
||||
if $scope.onboarding.innerStep > 1
|
||||
$scope.$applyAsync(() -> $scope.onboarding.innerStep--)
|
||||
|
||||
handleKeypress = (e) ->
|
||||
if e.keyCode == 13
|
||||
if $scope.innerStep == 1
|
||||
$scope.turnCodeCheckOn()
|
||||
else
|
||||
$scope.dismiss()
|
||||
$scope.gotoNextStep = () ->
|
||||
if $scope.onboarding.innerStep < 4
|
||||
$scope.$applyAsync(() -> $scope.onboarding.innerStep++)
|
||||
|
||||
$(document).on "keypress", handleKeypress
|
||||
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 "keydown", handleKeydown
|
||||
$(document).on "click", $scope.dismiss
|
||||
|
||||
$scope.$on "$destroy", () ->
|
||||
$(document).off "keypress", handleKeypress
|
||||
$(document).off "keydown", handleKeydown
|
||||
$(document).off "click", $scope.dismiss
|
|
@ -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: () ->
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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 = ""
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 557 KiB |
Binary file not shown.
BIN
services/web/public/img/onboarding/review-panel/add-changes.gif
Normal file
BIN
services/web/public/img/onboarding/review-panel/add-changes.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 491 KiB |
BIN
services/web/public/img/onboarding/review-panel/add-changes.mp4
Normal file
BIN
services/web/public/img/onboarding/review-panel/add-changes.mp4
Normal file
Binary file not shown.
BIN
services/web/public/img/onboarding/review-panel/commenting.gif
Normal file
BIN
services/web/public/img/onboarding/review-panel/commenting.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 103 KiB |
BIN
services/web/public/img/onboarding/review-panel/commenting.mp4
Normal file
BIN
services/web/public/img/onboarding/review-panel/commenting.mp4
Normal file
Binary file not shown.
BIN
services/web/public/img/onboarding/review-panel/open-review.gif
Normal file
BIN
services/web/public/img/onboarding/review-panel/open-review.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 119 KiB |
BIN
services/web/public/img/onboarding/review-panel/open-review.mp4
Normal file
BIN
services/web/public/img/onboarding/review-panel/open-review.mp4
Normal file
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 83 KiB |
|
@ -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-adv-title-highlight {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.feat-onboard-btn-wrapper {
|
||||
margin-bottom: 10px;
|
||||
|
||||
> .btn {
|
||||
.feat-onboard-video {
|
||||
width: 616px;
|
||||
margin: 0 30px;
|
||||
box-shadow: 0 0 70px 0 rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 ->
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue