Răsfoiți Sursa

Merge pull request #385 from sharelatex/pr-style-announcements

Announcements styling
Paulo Jorge Reis 10 ani în urmă
părinte
comite
a772b268aa

+ 9 - 2
services/web/app/coffee/Features/Announcements/AnnouncementsHandler.coffee

@@ -25,8 +25,15 @@ module.exports =
 			announcementIndex = _.findIndex announcements, (announcement)-> 
 				announcement.id == lastSeenBlogId
 
-			if announcementIndex != -1
-				announcements = announcements.slice(0, announcementIndex)
+			announcements = _.map announcements, (announcement, index)->
+				if announcementIndex == -1
+					read = false
+				else if index >= announcementIndex
+					read = true
+				else
+					read = false
+				announcement.read = read
+				return announcement
 
 			logger.log announcementsLength:announcements?.length, user_id:user_id, "returning announcements"
 

+ 39 - 5
services/web/app/views/project/list.jade

@@ -19,12 +19,46 @@ block content
 			}
 		};
 
-	.content.content-alt(ng-controller="ProjectPageController")
+	.content.content-alt.project-list-page(ng-controller="ProjectPageController")
 		.container
-			
-			//- div(ng-controller="AnnouncementsController", ng-cloak)
-			//- 	.alert.alert-success(ng-show="dataRecived")
-			//- 		a(href, ng-click="openLink()") {{title}} and {{totalAnnouncements}} others
+			.announcements(
+				ng-controller="AnnouncementsController"
+				ng-class="{ 'announcements-open': ui.isOpen }"
+				ng-cloak
+			)
+				.announcements-backdrop(
+					ng-if="ui.isOpen"
+					ng-click="toggleAnnouncementsUI();"
+				)
+				a.announcements-btn(
+					href
+					ng-if="announcements.length"
+					ng-click="toggleAnnouncementsUI();"
+					ng-class="{ 'announcements-btn-open': ui.isOpen, 'announcements-btn-has-new': ui.newItems }"
+				)
+					span.announcements-badge(ng-if="ui.newItems") {{ ui.newItems }}
+				.announcements-body(
+					ng-if="ui.isOpen"
+				)
+					.announcements-scroller
+						.announcement(
+							ng-repeat="announcement in announcements | filter:(ui.newItems ? { read: false } : '') track by announcement.id"
+						)
+							h2.announcement-header {{ announcement.title }}
+							p.announcement-description(ng-bind-html="announcement.excerpt")
+							.announcement-meta
+								p.announcement-date {{ announcement.date | date:"longDate" }}
+								a.announcement-link(
+									ng-href="{{ announcement.url }}"
+									target="_blank"
+								) Read more
+						div.text-center(
+							ng-if="ui.newItems > 0 && ui.newItems < announcements.length"
+						)
+							a.btn.btn-default.btn-sm(
+								href
+								ng-click="showAll();"
+							) Show all
 
 			.row(ng-cloak)
 				span(ng-if="projects.length > 0")

+ 23 - 14
services/web/public/coffee/main/announcements.coffee

@@ -1,20 +1,29 @@
 define [
 	"base"
 ], (App) ->
-	App.controller "AnnouncementsController", ($scope, $http, event_tracking, $window) ->
+	App.controller "AnnouncementsController", ($scope, $http, event_tracking, $window, _) ->
+		$scope.announcements = []
+		$scope.ui =
+			isOpen: false
+			newItems: 0
+		
+		refreshAnnouncements = ->
+			$http.get("/announcements").success (announcements) ->
+				$scope.announcements = announcements
+				$scope.ui.newItems = _.filter(announcements, (announcement) -> !announcement.read).length
+				
+		markAnnouncementsAsRead = ->
+			event_tracking.sendMB "announcement-alert-dismissed", { blogPostId: $scope.announcements[0].id }
 
-		$scope.dataRecived = false
-		announcement = null
-		$http.get("/announcements").success (announcements) ->
-			if announcements?[0]?
-				announcement = announcements[0]
-				$scope.title = announcement.title
-				$scope.totalAnnouncements =  announcements.length
-				$scope.dataRecived = true
+		refreshAnnouncements()
 
-		dismissannouncement = ->
-			event_tracking.sendMB "announcement-alert-dismissed", {blogPostId:announcement.id}
+		$scope.toggleAnnouncementsUI = ->
+			$scope.ui.isOpen = !$scope.ui.isOpen
+
+			if !$scope.ui.isOpen and $scope.ui.newItems
+				$scope.ui.newItems = 0
+				markAnnouncementsAsRead()
+
+		$scope.showAll = -> 
+			$scope.ui.newItems = 0
 
-		$scope.openLink = ->
-			dismissannouncement()
-			$window.location.href = announcement.url

+ 166 - 0
services/web/public/stylesheets/app/project-list.less

@@ -1,3 +1,26 @@
+@announcements-shadow: 0 2px 20px rgba(0, 0, 0, 0.5);
+
+@keyframes pulse {
+	0% {
+		opacity: .7;
+	}
+	100% {
+		opacity: .9;
+	}
+}
+@keyframes fade-in {
+	0% {
+		opacity: 0;
+	}
+	100% {
+		opacity: 1;
+	}
+}
+
+.project-list-page {
+	position: relative;
+}
+
 .project-header {
 	.btn-group > .btn {
 		padding-left: @line-height-base / 2;
@@ -293,3 +316,146 @@ ul.project-list {
 		margin-left:-100px;
 	}
 }
+
+.announcements {
+	position: absolute;
+	bottom: 0;
+	right: 0;
+	height: 150px;
+	width: 100%;
+	pointer-events: none;
+	overflow: hidden;
+
+	&-open {
+		top: -100%;
+		height: auto;
+		pointer-events: all;
+	}
+}
+
+.announcements-backdrop {
+	position: fixed;
+	top: 0;
+	right: 0;
+	bottom: 0;
+	left: 0;
+	background-color: rgba(0, 0, 0, 0.35);
+	opacity: 0;
+	animation: fade-in 0.35s forwards;
+	z-index: 1;
+}
+
+.announcements-btn {
+	position: absolute;
+	bottom: -50px;
+	right: 3%;
+	width: 80px;
+	height: 80px;
+	background: url(/img/lion-128.png) no-repeat center/80% transparent;
+	border-radius: 50%;
+	box-shadow: none;
+	z-index: 1;
+	pointer-events: all;
+	transition: bottom 0.25s cubic-bezier(0.68, -0.55, 0.265, 1.55),
+				background 0.25s ease,
+				box-shadow 0.25s ease;
+
+	&:hover {
+		bottom: -45px;
+	}
+
+	&-open, &-open:hover,
+	&-has-new, &-has-new:hover {
+		background-color: #FFF;
+		box-shadow: @announcements-shadow;
+		bottom: 30px;
+	}
+}
+	.announcements-badge {
+		display: inline-block;
+		position: absolute;
+		font-size: 11px;
+		height: 1.8em;
+		min-width: 1.8em;
+		border-radius: 0.9em;
+		line-height: 1.8;
+		padding: 0 2px;
+		top: 1px;
+		right: 1px;
+		font-weight: bold;
+		color: #FFF;
+		background-color: @red;
+		vertical-align: baseline;
+		white-space: nowrap;
+		text-align: center;
+		z-index: 1;
+		animation: pulse 1s alternate infinite;
+	}
+
+.announcements-body {
+	display: flex;
+	flex-direction: column;
+	align-items: stretch;
+	position: absolute;
+	right: 3%;
+	margin-right: 95px;
+	bottom: 30px;
+	width: 700px;
+	max-height: 52%;
+	min-height: 100px;
+	background: #FFF;
+	z-index: 1;
+	box-shadow: @announcements-shadow;
+	border-radius: @border-radius-base;
+	animation: fade-in 0.35s forwards;
+
+	&::after {
+		content: "\25b8";
+		position: absolute;
+		left: 100%;
+		bottom: 17px;
+		width: 30px;
+		color: #FFF;
+		text-shadow: @announcements-shadow;
+		font-size: 2em;
+		overflow: hidden;
+		text-indent: -7px;
+	}
+}
+
+	.announcements-scroller {
+		padding: @line-height-computed;
+		flex-grow: 0;
+		overflow-x: hidden;
+		overflow-y: auto;
+	}
+		.announcement {
+			margin-bottom: @line-height-computed * 1.5;
+			&:last-child {
+				margin-bottom: 0;
+			}
+		}	
+			.announcement-header {
+				.page-header;
+				margin: 0;
+			}
+
+			.announcement-description {
+				margin: (@line-height-computed / 4) 0 (@line-height-computed / 2);
+			}
+
+			.announcement-meta {
+				.clearfix;
+				font-size: 0.9em;
+			}
+
+				.announcement-date {
+					float: left;
+					color: @gray;
+					margin: 0;
+				}
+
+				.announcement-link {
+					float: right;
+					margin: 0;
+				}

+ 23 - 6
services/web/test/UnitTests/coffee/Announcement/AnnouncementsHandlerTests.coffee

@@ -42,10 +42,13 @@ describe 'AnnouncementsHandler', ->
 			@BlogHandler.getLatestAnnouncements.callsArgWith(0, null, @stubbedAnnouncements)
 
 
-		it "should return all announcements if there are no getLastOccurance", (done)->
+		it "should mark all announcements as read is false", (done)->
 			@AnalyticsManager.getLastOccurance.callsArgWith(2, null, [])
 			@handler.getUnreadAnnouncements @user_id, (err, announcements)=>
-				announcements.length.should.equal 4
+				announcements[0].read.should.equal false
+				announcements[1].read.should.equal false
+				announcements[2].read.should.equal false
+				announcements[3].read.should.equal false
 				done()
 
 		it "should should be sorted again to ensure correct order", (done)->
@@ -57,16 +60,30 @@ describe 'AnnouncementsHandler', ->
 				announcements[0].should.equal @stubbedAnnouncements[0]
 				done()
 
-		it "should return ones older than the last blog id", (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"}})
 			@handler.getUnreadAnnouncements @user_id, (err, announcements)=>
-				announcements.length.should.equal 2
 				announcements[0].id.should.equal @stubbedAnnouncements[0].id
+				announcements[0].read.should.equal false
+
 				announcements[1].id.should.equal @stubbedAnnouncements[1].id
+				announcements[1].read.should.equal false
+
+				announcements[2].id.should.equal @stubbedAnnouncements[3].id
+				announcements[2].read.should.equal true
+
+				announcements[3].id.should.equal @stubbedAnnouncements[2].id
+				announcements[3].read.should.equal true
+
 				done()
 
-		it "should return none when the latest id is the first element", (done)->
+		it "should return all of them marked as read", (done)->
 			@AnalyticsManager.getLastOccurance.callsArgWith(2, null, {segmentation:{blogPostId:"/2016/11/01/introducting-latex-code-checker"}})
 			@handler.getUnreadAnnouncements @user_id, (err, announcements)=>
-				announcements.length.should.equal 0
+				announcements[0].read.should.equal true
+				announcements[1].read.should.equal true
+				announcements[2].read.should.equal true
+				announcements[3].read.should.equal true
 				done()
+
+