Skip to content

Commit ab4544f

Browse files
committed
Add watch screen.
1 parent 8ceaf86 commit ab4544f

File tree

7 files changed

+223
-8
lines changed

7 files changed

+223
-8
lines changed

src/main/scala/Plugin.scala

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
import gitbucket.core.controller.Context
2+
import gitbucket.core.model.Issue
13
import gitbucket.core.plugin._
4+
import gitbucket.core.service.RepositoryService.RepositoryInfo
25
import io.github.gitbucket.solidbase.migration.LiquibaseMigration
36
import io.github.gitbucket.solidbase.model.Version
47

@@ -29,5 +32,22 @@ class Plugin extends gitbucket.core.plugin.Plugin {
2932
override val pullRequestHooks: Seq[PullRequestHook] = Seq(
3033
)
3134

35+
override val repositoryMenus = Seq(
36+
(repository: RepositoryInfo, context: Context) =>
37+
Some(Link(
38+
id = "watch",
39+
label = "Watch",
40+
path = "/watch",
41+
icon = Some("menu-icon octicon octicon-eye")
42+
))
43+
)
44+
45+
override val issueSidebars = Seq(
46+
(issue: Issue, repository: RepositoryInfo, context: Context) =>
47+
context.loginAccount map { account =>
48+
// TODO DB access
49+
gitbucket.notifications.html.issue(false, issue, repository)
50+
}
51+
)
3252

3353
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package gitbucket.notifications.controller
2+
3+
import gitbucket.core.controller.ControllerBase
4+
import gitbucket.core.service.{AccountService, RepositoryService}
5+
import gitbucket.core.util.Implicits._
6+
import gitbucket.core.util.ReadableUsersAuthenticator
7+
import gitbucket.core.util.SyntaxSugars._
8+
import gitbucket.notifications.html
9+
import gitbucket.notifications.model.Watch
10+
import gitbucket.notifications.service.NotificationsService
11+
import org.scalatra.Ok
12+
13+
class NotificationsController extends NotificationsControllerBase
14+
with NotificationsService with RepositoryService with AccountService with ReadableUsersAuthenticator
15+
16+
trait NotificationsControllerBase extends ControllerBase {
17+
self: NotificationsService with RepositoryService with AccountService with ReadableUsersAuthenticator =>
18+
19+
get("/:owner/:repository/watch")(readableUsersOnly { repository =>
20+
defining(repository.owner, repository.name) { case (owner, name) =>
21+
html.watch(
22+
// TODO default value
23+
getWatch(owner, name, context.loginAccount.get.userName),
24+
repository
25+
)
26+
}
27+
})
28+
29+
ajaxPost("/:owner/:repository/watch")(readableUsersOnly { repository =>
30+
params.get("notification").flatMap(Watch.Notification.valueOf).map { notification =>
31+
updateWatch(repository.owner, repository.name, context.loginAccount.get.userName, notification)
32+
Ok()
33+
} getOrElse NotFound()
34+
})
35+
36+
ajaxPost("/:owner/:repository/issues/:id/notification")(readableUsersOnly { repository =>
37+
params.getAs[Boolean]("subscribed").map { subscribed =>
38+
updateIssueNotification(repository.owner, repository.name, params("id").toInt, context.loginAccount.get.userName, subscribed)
39+
Ok()
40+
} getOrElse NotFound()
41+
})
42+
43+
}

src/main/scala/gitbucket/notifications/model/Watch.scala

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package gitbucket.notifications.model
33
trait WatchComponent { self: gitbucket.core.model.Profile =>
44
import profile.api._
55

6-
implicit val watchNotificationType = MappedColumnType.base[Watch.Notification, String](_.code, Watch.Notification.valueOf)
6+
implicit val watchNotificationType = MappedColumnType.base[Watch.Notification, String](_.id, Watch.Notification.valueOf(_).get)
77

88
lazy val Watches = TableQuery[Watches]
99

@@ -24,13 +24,13 @@ case class Watch(
2424
)
2525

2626
object Watch {
27-
abstract sealed class Notification(val code: String)
28-
case object Watching extends Notification("watching")
29-
case object NotWatching extends Notification("not_watching")
30-
case object Ignoring extends Notification("ignoring")
27+
abstract sealed class Notification(val id: String, val name: String)
28+
case object Watching extends Notification("watching", "Watching")
29+
case object NotWatching extends Notification("not_watching", "Not watching")
30+
case object Ignoring extends Notification("ignoring", "Ignoring")
3131

32-
private[model] object Notification {
33-
private val values: Seq[Notification] = Seq(Watching, NotWatching, Ignoring)
34-
def valueOf(code: String): Notification = values.find(_.code == code).get
32+
object Notification {
33+
val values: Seq[Notification] = Seq(Watching, NotWatching, Ignoring)
34+
def valueOf(id: String): Option[Notification] = values.find(_.id == id)
3535
}
3636
}

src/main/scala/gitbucket/notifications/service/NotificationsAccountHook.scala

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,38 @@ trait NotificationsAccountHook extends AccountHook {
1111
Watches.filter(_.notificationUserName === userName.bind).delete
1212
}
1313

14+
}
15+
16+
trait NotificationsRepositoryHook extends RepositoryHook {
17+
18+
override def deleted(owner: String, repository: String)(implicit session: Session): Unit = {
19+
IssueNotifications.filter(t => t.userName === owner.bind && t.repositoryName === repository.bind).delete
20+
Watches.filter(t => t.userName === owner.bind && t.repositoryName === repository.bind).delete
21+
}
22+
23+
override def renamed(owner: String, repository: String, newRepository: String)(implicit session: Session): Unit = {
24+
rename(owner, repository, owner, newRepository)
25+
}
26+
27+
override def transferred(owner: String, newOwner: String, repository: String)(implicit session: Session): Unit = {
28+
rename(owner, repository, newOwner, repository)
29+
}
30+
31+
// TODO select - insert
32+
private def rename(owner: String, repository: String, newOwner: String, newRepository: String)(implicit session: Session) = {
33+
val n = IssueNotifications.filter(t => t.userName === owner.bind && t.repositoryName === repository.bind).list
34+
val w = Watches.filter(t => t.userName === owner.bind && t.repositoryName === repository.bind).list
35+
36+
deleted(owner, repository)
37+
38+
IssueNotifications.insertAll(n.map(_.copy(userName = newOwner, repositoryName = newRepository)) :_*)
39+
Watches.insertAll(w.map(_.copy(userName = newOwner, repositoryName = newRepository)) :_*)
40+
}
41+
42+
}
43+
44+
trait NotificationsIssueHook extends IssueHook {
45+
46+
47+
1448
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package gitbucket.notifications.service
2+
3+
import gitbucket.notifications.model._, Profile._
4+
import profile.blockingApi._
5+
6+
trait NotificationsService {
7+
8+
def getWatch(owner: String, repository: String, userName: String)(implicit s: Session): Option[Watch] = {
9+
Watches
10+
.filter(t => t.userName === owner.bind && t.repositoryName === repository.bind && t.notificationUserName === userName.bind)
11+
.firstOption
12+
}
13+
14+
def updateWatch(owner: String, repository: String, userName: String, notification: Watch.Notification)(implicit s: Session): Unit = {
15+
Watches
16+
.filter(t => t.userName === owner.bind && t.repositoryName === repository.bind && t.notificationUserName === userName.bind)
17+
.delete
18+
Watches insert Watch(
19+
userName = owner,
20+
repositoryName = repository,
21+
notificationUserName = userName,
22+
notification = notification
23+
)
24+
}
25+
26+
def getIssueNotification(owner: String, repository: String, issueId: Int, userName: String)(implicit s: Session): Option[IssueNotification] = {
27+
IssueNotifications
28+
.filter { t =>
29+
t.userName === owner.bind && t.repositoryName === repository.bind &&
30+
t.issueId === issueId.bind && t.notificationUserName === userName.bind
31+
}
32+
.firstOption
33+
}
34+
35+
def updateIssueNotification(owner: String, repository: String, issueId: Int, userName: String, subscribed: Boolean)(implicit s: Session): Unit = {
36+
IssueNotifications
37+
.filter { t =>
38+
t.userName === owner.bind && t.repositoryName === repository.bind &&
39+
t.issueId === issueId.bind && t.notificationUserName === userName.bind
40+
}
41+
.delete
42+
IssueNotifications insert IssueNotification(
43+
userName = owner,
44+
repositoryName = repository,
45+
issueId = issueId,
46+
notificationUserName = userName,
47+
subscribed = subscribed
48+
)
49+
}
50+
51+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
@(subscribed: Boolean,
2+
issue: gitbucket.core.model.Issue,
3+
repository: gitbucket.core.service.RepositoryService.RepositoryInfo)
4+
@import gitbucket.core.view.helpers
5+
6+
<hr/>
7+
<div style="margin-bottom: 14px;">
8+
<span class="muted small strong">Notifications</span>
9+
<div class="pull-right">
10+
@if(subscribed){
11+
<input type="button" class="btn btn-default issue-notification" value="Unsubscribe" data-subscribed="false" />
12+
} else {
13+
<input type="button" class="btn btn-default issue-notification" value="Subscribe" data-subscribed="true" />
14+
}
15+
</div>
16+
</div>
17+
<script>
18+
$(function(){
19+
$('.issue-notification').click(function(){
20+
var $this = $(this);
21+
var subscribed = $this.data('subscribed');
22+
$.post('@helpers.url(repository)/issues/@issue.issueId/notification,
23+
{ subscribed : subscribed },
24+
function(){
25+
if(subscribed == 'false'){
26+
$this.val('Subscribe').data('subscribed', 'true');
27+
} else {
28+
$this.val('Unsubscribe').data('subscribed', 'false');
29+
}
30+
}
31+
);
32+
});
33+
});
34+
</script>
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
@(notification: gitbucket.notifications.model.Watch.Notification,
2+
repository: gitbucket.core.service.RepositoryService.RepositoryInfo)(implicit context: gitbucket.core.controller.Context)
3+
@import gitbucket.core.view.helpers
4+
@gitbucket.core.html.main("Watch", Some(repository)){
5+
@gitbucket.core.html.menu("watch", repository){
6+
7+
@gitbucket.core.helper.html.dropdown("Notifications"){
8+
@gitbucket.notifications.model.Watch.Notification.values.map { n =>
9+
<li>
10+
<a href="#" class="watch" data-id="@n.id">
11+
@gitbucket.core.helper.html.checkicon(notification.id == n.id) @n.name
12+
</a>
13+
</li>
14+
}
15+
}
16+
17+
}
18+
}
19+
<script>
20+
$(function(){
21+
$('a.watch').click(function(){
22+
var notification = $(this).data('id');
23+
$.post('@helpers.url(repository)/watch',
24+
{ notification : notification },
25+
function(){
26+
$('a.watch i.octicon-check').removeClass('octicon-check');
27+
$('a.watch[data-id=' + notification + '] i').addClass('octicon-check');
28+
}
29+
);
30+
return false;
31+
});
32+
});
33+
</script>

0 commit comments

Comments
 (0)