Browse Source

Highlight the unread count only for mentions

Signed-off-by: Joas Schilling <coding@schilljs.com>
pull/1086/head
Joas Schilling 7 years ago
parent
commit
36ae3a3e55
No known key found for this signature in database GPG Key ID: 7076EA9751AACDDA
  1. 2
      appinfo/info.xml
  2. 12
      css/style.scss
  3. 2
      docs/api-v1.md
  4. 3
      js/views/roomlistview.js
  5. 1
      lib/Capabilities.php
  6. 22
      lib/Chat/ChatManager.php
  7. 9
      lib/Chat/Notifier.php
  8. 10
      lib/Controller/RoomController.php
  9. 15
      lib/Manager.php
  10. 52
      lib/Migration/Version3003Date20180730080327.php
  11. 13
      lib/Participant.php
  12. 9
      lib/Room.php
  13. 1
      tests/php/CapabilitiesTest.php

2
appinfo/info.xml

@ -17,7 +17,7 @@ And in the works for the [coming versions](https://github.com/nextcloud/spreed/m
]]></description>
<version>3.99.6</version>
<version>3.99.7</version>
<licence>agpl</licence>
<author>Daniel Calviño Sánchez</author>

12
css/style.scss

@ -149,13 +149,19 @@
line-height: 44px;
padding: 0 12px; /* Same padding as all li > a in the app-navigation */
span {
padding: 2px 5px;
border-radius: 10px;
background-color: nc-lighten($color-main-text, 90%);
font-weight: bold;
}
&.highlighted {
padding-right: 0;
text-align: center;
width: 22px !important;
span {
padding: 2px 5px;
border-radius: 10px;
background-color: $color-primary;
color: $color-primary-text;
}
@ -926,7 +932,7 @@ video {
}
.participantWithList .participant-entry-utils-menu-button button:hover,
.participantWithList .participant-entry-utils-menu-button button:focus, {
.participantWithList .participant-entry-utils-menu-button button:focus {
opacity: 1;
}

2
docs/api-v1.md

@ -71,6 +71,7 @@ Base endpoint is: `/ocs/v2.php/apps/spreed/api/v1`
* `last-room-activity` - Rooms have the `lastActivity` attribute and should be sorted by that instead of the last ping of the user.
* `no-ping` - The ping endpoint has been removed. Ping is updated with a call to fetch the signaling or chat messages instead.
* `system-messages` - Chat messages have a `systemMessage` attribute and can be generated by the system
* `mention-flag` - The room list populates the boolean `unreadMention` when the user was mentioned since their last visit
## Room management
@ -132,6 +133,7 @@ Base endpoint is: `/ocs/v2.php/apps/spreed/api/v1`
`lastActivity` | int | Timestamp of the last activity in the room, in seconds and UTC time zone
`isFavorite` | bool | Flag if the room is favorited by the user
`unreadMessages` | int | Number of unread chat messages in the room (only available with `chat-v2` capability)
`unreadMention` | bool | Flag if the user was mentioned since their last visit
`lastMessage` | message | Last message in a room if available, otherwise empty
### Get single room (also for guests)

3
js/views/roomlistview.js

@ -37,7 +37,8 @@
var ITEM_TEMPLATE = '<a class="app-navigation-entry-link" href="#{{id}}" data-token="{{token}}"><div class="avatar" data-user="{{name}}" data-user-display-name="{{displayName}}"></div> {{displayName}}</a>'+
'<div class="app-navigation-entry-utils">'+
'<ul>'+
'{{#if unreadMessages}}<li class="app-navigation-entry-utils-counter highlighted"><span>{{numUnreadMessages}}</span></li>{{/if}}'+
'{{#if unreadMention}}<li class="app-navigation-entry-utils-counter highlighted"><span>@</span></li>{{/if}}'+
'{{#if unreadMessages}}<li class="app-navigation-entry-utils-counter"><span>{{numUnreadMessages}}</span></li>{{/if}}'+
'<li class="app-navigation-entry-utils-menu-button"><button></button></li>'+
'</ul>'+
'</div>'+

1
lib/Capabilities.php

@ -43,6 +43,7 @@ class Capabilities implements IPublicCapability {
'last-room-activity',
'no-ping',
'system-messages',
'mention-flag',
],
],
];

22
lib/Chat/ChatManager.php

@ -27,6 +27,7 @@ use OCA\Spreed\Room;
use OCP\Comments\IComment;
use OCP\Comments\ICommentsManager;
use OCP\Comments\NotFoundException;
use OCP\IDBConnection;
use OCP\IUser;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\GenericEvent;
@ -114,7 +115,10 @@ class ChatManager {
// Update last_message
$chat->setLastMessage($comment);
$this->notifier->notifyMentionedUsers($chat, $comment);
$notifiedUsers = $this->notifier->notifyMentionedUsers($chat, $comment);
if (!empty($notifiedUsers)) {
$chat->markUsersAsMentioned($notifiedUsers, $creationDateTime);
}
$this->dispatcher->dispatch(self::class . '::sendMessage', new GenericEvent($chat, [
'comment' => $comment,
@ -125,13 +129,15 @@ class ChatManager {
return $comment;
}
/**
* @param Room $chat
* @param IUser $user
* @return int
*/
public function getUnreadCount(Room $chat, IUser $user) {
$unreadSince = $this->commentsManager->getReadMark('chat', $chat->getId(), $user);
public function getUnreadMarker(Room $chat, IUser $user): \DateTime {
$marker = $this->commentsManager->getReadMark('chat', $chat->getId(), $user);
if ($marker === null) {
$marker = new \DateTime('2000-01-01');
}
return $marker;
}
public function getUnreadCount(Room $chat, \DateTime $unreadSince): int {
return $this->commentsManager->getNumberOfCommentsForObject('chat', $chat->getId(), $unreadSince);
}

9
lib/Chat/Notifier.php

@ -74,20 +74,25 @@ class Notifier {
*
* @param Room $chat
* @param IComment $comment
* @return string[] The notified users
*/
public function notifyMentionedUsers(Room $chat, IComment $comment) {
public function notifyMentionedUsers(Room $chat, IComment $comment): array {
$mentionedUserIds = $this->getMentionedUserIds($comment);
if (empty($mentionedUserIds)) {
return;
return [];
}
$notifiedUsers = [];
foreach ($mentionedUserIds as $mentionedUserId) {
if ($this->shouldUserBeNotified($mentionedUserId, $comment)) {
$notification = $this->createNotification($chat, $comment, $mentionedUserId);
$this->notificationManager->notify($notification);
$notifiedUsers[] = $mentionedUserId;
}
}
return $notifiedUsers;
}
/**

10
lib/Controller/RoomController.php

@ -162,7 +162,7 @@ class RoomController extends OCSController {
* @return array
* @throws RoomNotFoundException
*/
protected function formatRoom(Room $room, Participant $participant = null) {
protected function formatRoom(Room $room, Participant $participant = null): array {
if ($participant instanceof Participant) {
$participantType = $participant->getParticipantType();
@ -193,6 +193,7 @@ class RoomController extends OCSController {
'hasCall' => $room->getActiveSince() instanceof \DateTimeInterface,
'lastActivity' => $lastActivity,
'unreadMessages' => 0,
'unreadMention' => false,
'isFavorite' => $favorite,
];
@ -213,7 +214,12 @@ class RoomController extends OCSController {
$currentUser = $this->userManager->get($this->userId);
if ($currentUser instanceof IUser) {
$roomData['unreadMessages'] = $this->chatManager->getUnreadCount($room, $currentUser);
$unreadSince = $this->chatManager->getUnreadMarker($room, $currentUser);
if ($participant instanceof Participant) {
$lastMention = $participant->getLastMention();
$roomData['unreadMention'] = $lastMention !== null && $unreadSince < $lastMention;
}
$roomData['unreadMessages'] = $this->chatManager->getUnreadCount($room, $unreadSince);
}
// Sort by lastPing

15
lib/Manager.php

@ -96,7 +96,13 @@ class Manager {
$lastMessage = null;
if (!empty($row['comment_id'])) {
$lastMessage = $this->commentsManager->getCommentFromData(array_merge($row, ['id' => $row['comment_id']]));
$lastMessage = $this->commentsManager->getCommentFromData(array_merge($row, [
'id' => $row['comment_id'],
'parent_id' => '',
'topmost_parent_id' => '',
'latest_child_timestamp' => null,
'children_count' => 0,
]));
}
return new Room($this, $this->db, $this->secureRandom, $this->dispatcher, $this->hasher, (int) $row['id'], (int) $row['type'], $row['token'], $row['name'], $row['password'], (int) $row['active_guests'], $activeSince, $lastActivity, $lastMessage);
@ -108,7 +114,12 @@ class Manager {
* @return Participant
*/
public function createParticipantObject(Room $room, array $row) {
return new Participant($this->db, $room, $row['user_id'], (int) $row['participant_type'], (int) $row['last_ping'], $row['session_id'], (bool) $row['in_call'], (bool) $row['favorite']);
$lastMention = null;
if (!empty($row['last_mention'])) {
$lastMention = new \DateTime($row['last_mention']);
}
return new Participant($this->db, $room, $row['user_id'], (int) $row['participant_type'], (int) $row['last_ping'], $row['session_id'], (bool) $row['in_call'], (bool) $row['favorite'], $lastMention);
}
/**

52
lib/Migration/Version3003Date20180730080327.php

@ -0,0 +1,52 @@
<?php
/**
* @copyright Copyright (c) 2018 Joas Schilling <coding@schilljs.com>
*
* @author Joas Schilling <coding@schilljs.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Spreed\Migration;
use Doctrine\DBAL\Types\Type;
use OCP\DB\ISchemaWrapper;
use OCP\Migration\SimpleMigrationStep;
use OCP\Migration\IOutput;
class Version3003Date20180730080327 extends SimpleMigrationStep {
/**
* @param IOutput $output
* @param \Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
* @param array $options
* @return null|ISchemaWrapper
* @since 13.0.0
*/
public function changeSchema(IOutput $output, \Closure $schemaClosure, array $options) {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();
$table = $schema->getTable('talk_participants');
if (!$table->hasColumn('last_mention')) {
$table->addColumn('last_mention', Type::DATETIME, [
'notnull' => false,
]);
}
return $schema;
}
}

13
lib/Participant.php

@ -49,6 +49,8 @@ class Participant {
protected $inCall;
/** @var bool */
private $isFavorite;
/** @var \DateTime|null */
private $lastMention;
/**
* @param IDBConnection $db
@ -59,8 +61,9 @@ class Participant {
* @param string $sessionId
* @param bool $inCall
* @param bool $isFavorite
* @param \DateTime|null $lastMention
*/
public function __construct(IDBConnection $db, Room $room, $user, $participantType, $lastPing, $sessionId, $inCall, $isFavorite) {
public function __construct(IDBConnection $db, Room $room, $user, $participantType, $lastPing, $sessionId, $inCall, $isFavorite, \DateTime $lastMention = null) {
$this->db = $db;
$this->room = $room;
$this->user = $user;
@ -69,6 +72,7 @@ class Participant {
$this->sessionId = $sessionId;
$this->inCall = $inCall;
$this->isFavorite = $isFavorite;
$this->lastMention = $lastMention;
}
public function getUser() {
@ -91,6 +95,13 @@ class Participant {
return $this->inCall;
}
/**
* @return \DateTime|null
*/
public function getLastMention() {
return $this->lastMention;
}
/**
* @return bool
*/

9
lib/Room.php

@ -863,6 +863,15 @@ class Room {
return isset($row['num_participants']) ? (int) $row['num_participants'] : 0;
}
public function markUsersAsMentioned(array $userIds, \DateTime $time) {
$query = $this->db->getQueryBuilder();
$query->update('talk_participants')
->set('last_mention', $query->createNamedParameter($time, 'datetime'))
->where($query->expr()->eq('room_id', $query->createNamedParameter($this->getId(), IQueryBuilder::PARAM_INT)))
->andWhere($query->expr()->in('user_id', $query->createNamedParameter($userIds, IQueryBuilder::PARAM_STR_ARRAY)));
$query->execute();
}
/**
* @param string $userId
* @param string $sessionId

1
tests/php/CapabilitiesTest.php

@ -47,6 +47,7 @@ class CapabilitiesTest extends TestCase {
'last-room-activity',
'no-ping',
'system-messages',
'mention-flag',
],
],
], $capabilities->getCapabilities());

Loading…
Cancel
Save