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

Merge branch 'master-redesign' of https://github.com/sharelatex/web-sharelatex into master-redesign

Henry Oswald 12 лет назад
Родитель
Сommit
c8c0030b7c

+ 1 - 1
services/web/app/coffee/Features/Chat/ChatController.coffee

@@ -18,7 +18,7 @@ module.exports =
 
 	getMessages: (req, res)->
 		project_id = req.params.Project_id
-		query = req.body
+		query = req.query
 		logger.log project_id:project_id, query:query, "getting messages"
 		ChatHandler.getMessages project_id, query, (err, messages)->
 			if err?

+ 0 - 29
services/web/app/views/layout/navbar.jade

@@ -31,33 +31,4 @@ nav.navbar.navbar-default
 								else
 									| !{item.text}
 
-				//- li.dropdown
-				//- 	a.dropdown-toggle(href='#',data-toggle="dropdown")
-				//- 		| Help 
-				//- 		b.caret
-				//- 	ul.dropdown-menu
-				//- 		li
-				//- 			a(href='/learn') Documentation
-				//- 		-if(typeof(tenderUrl) != "undefined")
-				//- 			li
-				//- 				a.js-tender-widget(href='#') Contact Support
-
-				//- -if(session && session.user)
-				//- 	li.dropdown
-				//- 		a.dropdown-toggle(href="#",data-toggle="dropdown")
-				//- 			| Account 
-				//- 			b.caret
-				//- 		ul.dropdown-menu
-				//- 			li
-				//- 				a(href='/user/settings').userSettingsLink Account Settings
-				//- 			- if (settings.enableSubscriptions)
-				//- 				li
-				//- 					a(href='/user/subscription').subscriptionLink Subscription
-				//- 			li
-				//- 				a(href='/logout').logoutLink Logout
-				//- -else
-				//- 	li
-				//- 		a(href='/register') Register
-				//- 	li 
-				//- 		a#loginLink(href='/login'+getRedirAsQueryString()) Login
 

+ 2 - 1
services/web/app/views/project/editor.jade

@@ -62,7 +62,7 @@ block content
 						include ./editor/publish-template
 
 			.ui-layout-east
-				//- include ./editor/chat
+				include ./editor/chat
 
 	script(type="text/ng-template", id="genericMessageModalTemplate")
 		.modal-header
@@ -120,4 +120,5 @@ block content
 		data-ace-base=jsPath+'ace',
 		src=jsPath+'libs/require.js?fingerprint='+fingerprint(jsPath + 'libs/require.js')
 	)
+		
 

+ 10 - 9
services/web/app/views/project/editor/chat.jade

@@ -1,24 +1,25 @@
 aside.chat(
 	ng-controller="ChatController"
-	infinite-scroll="loadMore()"
-	infinite-scroll-disabled="chat.loading || chat.atEnd"
-	infinite-scroll-initialize="ui.chatOpen"
 )
 	.messages(
-		infinite-scroll="loadMore()"
+		infinite-scroll="loadMoreMessages()"
 		infinite-scroll-disabled="chat.loading || chat.atEnd"
 		infinite-scroll-initialize="ui.chatOpen"
-		scroll-glue
+		infinite-scroll-upwards="true"
+		update-scroll-bottom-on="updateScrollPosition"
 	)
 		.infinite-scroll-inner
+			.loading(ng-show="chat.loading")
+				i.fa.fa-fw.fa-spin.fa-refresh
+				| Loading...
 			ul.list-unstyled
 				li.message(
-					ng-repeat="message in chat.groupedMessages | orderBy:'timestamp':false"
+					ng-repeat="message in chat.messages"
 					ng-controller="ChatMessageController"
 					ng-class="{'self': message.user.id == user.id }"
 				)
-					div.date(ng-if="$index == 0 || (message.timestamp - chat.groupedMessages[$index - 1].timestamp) > 5 * 60 * 1000")
-						{{ message.timestamp | formatDate:'h:MMa' }} {{ message.timestamp | relativeDate }}
+					div.date(ng-if="$index == 0 || (message.timestamp - chat.messages[$index - 1].timestamp) > 5 * 60 * 1000")
+						{{ message.timestamp | formatDate:'h:mm a' }} {{ message.timestamp | relativeDate }}
 					span.avatar
 						img(ng-src="{{message.user.gravatar_url}}")
 					div.message-wrapper
@@ -30,7 +31,7 @@ aside.chat(
 							}"
 						)
 							.arrow(ng-style="{'border-color': 'hsl({{ hue(message.user) }}, 60%, 80%)'}")
-							p(ng-repeat="content in message.contents") {{ content }}
+							p(ng-repeat="content in message.contents track by $index") {{ content }}
 
 	.new-message
 		textarea(placeholder="Your message...", on-enter="sendMessage()", ng-model="newMessageContent")

+ 15 - 9
services/web/app/views/project/editor/header.jade

@@ -6,6 +6,12 @@ header.toolbar.toolbar-header(ng-cloak, ng-hide="state.loading")
 		tooltip-placement="bottom"
 	)
 		i.fa.fa-fw.fa-bars
+	a(
+		href="/project"
+		tooltip="Back to projects",
+		tooltip-placement="bottom"
+	)
+		i.fa.fa-fw.fa-level-up
 
 	.toolbar-center.project-name(ng-controller="ProjectNameController")
 		span.name(
@@ -50,12 +56,12 @@ header.toolbar.toolbar-header(ng-cloak, ng-hide="state.loading")
 			tooltip-placement="bottom"
 		)
 			i.fa.fa-fw.fa-history
-		//- a.btn.btn-full-height(
-		//- 	href,
-		//- 	tooltip="Chat",
-		//- 	tooltip-placement="bottom",
-		//- 	ng-class="{ active: ui.chatOpen }",
-		//- 	ng-click="toggleChat()",
-		//- 	ng-controller="ChatButtonController"
-		//- )
-		//- 	i.fa.fa-fw.fa-comment
+		a.btn.btn-full-height(
+			href,
+			tooltip="Chat",
+			tooltip-placement="bottom",
+			ng-class="{ active: ui.chatOpen }",
+			ng-click="toggleChat()",
+			ng-controller="ChatButtonController"
+		)
+			i.fa.fa-fw.fa-comment

+ 0 - 8
services/web/app/views/project/editor/left-menu.jade

@@ -2,14 +2,6 @@ aside#left-menu.full-size(
 	ng-class="{ 'shown': ui.leftMenuShown }"
 	ng-cloak
 )
-	ul.list-unstyled.nav
-		li
-			a(
-				href="/project"
-			)
-				i.fa.fa-list-alt.fa-fw 
-				|   View All Projects
-
 	h4 Download
 
 	ul.list-unstyled.nav.nav-downloads

+ 30 - 28
services/web/public/coffee/directives/scroll.coffee

@@ -1,34 +1,36 @@
 define [
 	"base"
 ], (App) ->
-
-	fakeNgModel = (initValue) ->
-		$setViewValue: (value) ->
-			@$viewValue = value
-			return
-
-		$viewValue: initValue
-
-	App.directive "scrollGlue", ->
+	App.directive "updateScrollBottomOn", ($timeout) ->
 		return {
-
-			priority: 1
-			require: ["?ngModel"]
 			restrict: "A"
-
-			link: (scope, $el, attrs, ctrls) ->
-				scrollToBottom = ->
-					el.scrollTop = el.scrollHeight
-
-				shouldActivateAutoScroll = ->
-					el.scrollTop + el.clientHeight + 10 >= el.scrollHeight
-
-				el = $el[0]
-				ngModel = ctrls[0] or fakeNgModel(true)
-				scope.$watch ->
-					scrollToBottom()  if ngModel.$viewValue
-
-				$el.bind "scroll", ->
-					activate = shouldActivateAutoScroll()
-					scope.$apply ngModel.$setViewValue.bind(ngModel, activate)  if activate isnt ngModel.$viewValue
+			link: (scope, element, attrs, ctrls) ->
+				# We keep the offset from the bottom fixed whenever the event fires
+				#
+				# ^   | ^
+				# |   | | scrollTop
+				# |   | v
+				# |   |-----------
+				# |   | ^
+				# |   | |
+				# |   | | clientHeight (viewable area)
+				# |   | |
+				# |   | |
+				# |   | v
+				# |   |-----------
+				# |   | ^
+				# |   | | scrollBottom
+				# v   | v
+				#  \
+				#   scrollHeight
+				
+				scrollBottom = 0
+				element.on "scroll", (e) ->
+					scrollBottom = element[0].scrollHeight - element[0].scrollTop - element[0].clientHeight
+					
+				scope.$on attrs.updateScrollBottomOn, () ->
+					$timeout () ->
+						element.scrollTop(element[0].scrollHeight - element[0].clientHeight - scrollBottom)
+					, 0
 		}
+

+ 20 - 43
services/web/public/coffee/ide/chat/controllers/ChatController.coffee

@@ -1,48 +1,25 @@
 define [
 	"base"
+	"ide/chat/services/chatMessages"
 ], (App) ->
-	App.controller "ChatController", ["$scope", "$http", "ide", "$location", ($scope, $http, @ide, $location) ->
-		MESSAGES_URL = "/project/#{$scope.project_id}/messages"
-		CONNECTED_USER_URL = "/project/#{$scope.project_id}/connected_users"
-
-		$scope.$on "project:joined", =>
-			@ide.socket.on "new-chat-message", (message) =>
-				$scope.chat.messages.push(message)
-
-		$http.get(MESSAGES_URL).success (data, status, headers, config)->
-			$scope.chat.messages = data
-
-		$http.get(CONNECTED_USER_URL).success (data)->
-			console.log data
-
-		$scope.$watchCollection "chat.messages", (messages) ->
+	App.controller "ChatController", ($scope, chatMessages, @ide, $location) ->
+		$scope.chat = chatMessages.state
+		
+		$scope.$watch "chat.messages", (messages) ->
 			if messages?
-				console.log "grouping messages"
-				$scope.chat.groupedMessages = groupMessages(messages)
-
+				$scope.$emit "updateScrollPosition"
+		, true # Deep watch
+		
+		$scope.$on "layout:chat:resize", () ->
+			$scope.$emit "updateScrollPosition"
+				
 		$scope.sendMessage = ->
-			body = 
-				content:$scope.newMessageContent
-				_csrf : window.csrfToken
-			$http.post(MESSAGES_URL, body).success (data, status, headers, config)->
-				$scope.newMessageContent = ""
-
-		TIMESTAMP_GROUP_SIZE = 5 * 60 * 1000 # 5 minutes
-		groupMessages = (messages) ->
-			previousMessage = null
-			groupedMessages = []
-			for message in messages
-				shouldGroup = previousMessage? and
-					previousMessage.user == message.user and
-					message.timestamp - previousMessage.timestamp < TIMESTAMP_GROUP_SIZE
-				if shouldGroup
-					previousMessage.timestamp = message.timestamp
-					previousMessage.contents.push message.content
-				else
-					groupedMessages.push(previousMessage = {
-						user: message.user
-						timestamp: message.timestamp
-						contents: [message.content]
-					})
-			return groupedMessages
-	]
+			chatMessages
+				.sendMessage $scope.newMessageContent
+				.then () ->
+					$scope.newMessageContent = ""
+				
+		$scope.loadMoreMessages = ->
+			chatMessages.loadMoreMessages()
+			
+			

+ 80 - 0
services/web/public/coffee/ide/chat/services/chatMessages.coffee

@@ -0,0 +1,80 @@
+define [
+	"base"
+], (App) ->
+	App.factory "chatMessages", ($http, ide) ->
+		MESSAGES_URL = "/project/#{ide.project_id}/messages"
+		MESSAGE_LIMIT = 50
+		CONNECTED_USER_URL = "/project/#{ide.project_id}/connected_users"
+
+		chat = {
+			state:
+				messages: []
+				loading: false
+				atEnd: false
+				nextBeforeTimestamp: null
+		}
+			
+		ide.socket.on "new-chat-message", (message) =>
+			appendMessage(message)
+			
+		chat.loadMoreMessages = () ->
+			return if chat.state.atEnd
+			
+			url = MESSAGES_URL + "?limit=#{MESSAGE_LIMIT}"
+			if chat.state.nextBeforeTimestamp?
+				url += "&before=#{chat.state.nextBeforeTimestamp}"
+			
+			chat.state.loading = true
+			return $http
+				.get(url)
+				.success (messages = [])->
+					chat.state.loading = false
+					if messages.length < MESSAGE_LIMIT
+						chat.state.atEnd = true
+					messages.reverse()
+					prependMessages(messages)
+					chat.state.nextBeforeTimestamp = chat.state.messages[0]?.timestamp
+		
+		TIMESTAMP_GROUP_SIZE = 5 * 60 * 1000 # 5 minutes
+		
+		prependMessage = (message) ->
+			firstMessage = chat.state.messages[0]
+			shouldGroup = firstMessage? and
+				firstMessage.user.id == message.user.id and
+				firstMessage.timestamp - message.timestamp < TIMESTAMP_GROUP_SIZE
+			if shouldGroup
+				firstMessage.timestamp = message.timestamp
+				firstMessage.contents.unshift message.content
+			else
+				chat.state.messages.unshift({
+					user: message.user
+					timestamp: message.timestamp
+					contents: [message.content]
+				})
+				
+		prependMessages = (messages) ->
+			for message in messages.slice(0).reverse()
+				prependMessage(message)
+				
+		appendMessage = (message) ->
+			lastMessage = chat.state.messages[chat.state.messages.length - 1]
+			shouldGroup = lastMessage? and
+				lastMessage.user.id == message.user.id and
+				message.timestamp - lastMessage.timestamp < TIMESTAMP_GROUP_SIZE
+			if shouldGroup
+				lastMessage.timestamp = message.timestamp
+				lastMessage.contents.push message.content
+			else
+				chat.state.messages.push({
+					user: message.user
+					timestamp: message.timestamp
+					contents: [message.content]
+				})
+					
+		chat.sendMessage = (message) ->
+			body = 
+				content: message
+				_csrf : window.csrfToken
+			return $http.post(MESSAGES_URL, body)
+					
+		return chat

+ 1 - 1
services/web/public/coffee/ide/track-changes/TrackChangesManager.coffee

@@ -63,7 +63,7 @@ define [
 
 			@$scope.trackChanges.updates[indexOfLastUpdateNotByMe].selectedFrom = true
 
-		BATCH_SIZE: 4
+		BATCH_SIZE: 10
 		fetchNextBatchOfUpdates: () ->
 			url = "/project/#{@ide.project_id}/updates?min_count=#{@BATCH_SIZE}"
 			if @$scope.trackChanges.nextBeforeTimestamp?

+ 10 - 14
services/web/public/coffee/ide/track-changes/directives/infiniteScroll.coffee

@@ -8,29 +8,25 @@ define [
 				element.css 'overflow-y': 'auto'
 
 				atEndOfListView = () ->
+					if attrs.infiniteScrollUpwards?
+						atTopOfListView()
+					else
+						atBottomOfList()
+					
+				atTopOfListView = () ->
+					element.scrollTop() < 30
+					
+				atBottomOfListView = () ->
 					element.scrollTop() + element.height() >= innerElement.height() - 30
 
 				listShorterThanContainer = () ->
-					element.innerHeight() > @$(".change-list").outerHeight()
+					element.height() > innerElement.height()
 
 				loadUntilFull = () ->
 					if (listShorterThanContainer() or atEndOfListView()) and not scope.$eval(attrs.infiniteScrollDisabled)
 						promise = scope.$eval(attrs.infiniteScroll)
 						promise.then () ->
 							loadUntilFull()
-						# @collection.fetchNextBatch
-						# 	error: (error) =>
-						# 		@hideLoading()
-						# 		@showEmptyMessageIfCollectionEmpty()
-						# 		callback(error)
-						# 	success: (collection, response) =>
-						# 		@hideLoading()
-						# 		if @collection.isAtEnd()
-						# 			@atEndOfCollection = true
-						# 			@showEmptyMessageIfCollectionEmpty()
-						# 			callback()
-						# 		else
-						# 			@loadUntilFull(callback)
 
 				element.on "scroll", (event) ->
 					loadUntilFull()

+ 6 - 0
services/web/public/stylesheets/app/editor/chat.less

@@ -1,6 +1,12 @@
 @new-message-height: 80px;
 
 .chat {
+	.loading {
+		font-family: @font-family-serif;
+		padding: @line-height-computed / 2;
+		text-align: center;
+	}
+	
 	.messages {
 		position: absolute;
 		top: 0;

+ 1 - 1
services/web/test/UnitTests/coffee/Chat/ChatControllerTests.coffee

@@ -55,7 +55,7 @@ describe "ChatController", ->
 
 	describe "getMessages", ->
 		beforeEach ->
-			@req.body = @query
+			@req.query = @query
 
 		it "should ask the chat handler about the request", (done)->