Skip to content
4 changes: 2 additions & 2 deletions css/At.scss
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,8 @@
}

.atwho-cur {
background: var(--color-primary);
color: var(--color-primary-text);
background: var(--color-primary-light);
color: var(--color-main-text);
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions docs/chat.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ Base endpoint is: `/ocs/v2.php/apps/spreed/api/v1`
------|------|------------
`search` | string | Search term for name suggestions (should at least be 1 character)
`limit` | int | Number of suggestions to receive (20 by default)
`includeStatus` | bool | Whether the user status information also needs to be loaded

* Response:
- Status code:
Expand All @@ -132,6 +133,9 @@ Base endpoint is: `/ocs/v2.php/apps/spreed/api/v1`
`id` | string | The user id which should be sent as `@<id>` in the message (user ids that contain spaces as well as guest ids need to be wrapped in double-quotes when sending in a message: `@"space user"` and `@"guest/random-string"`)
`label` | string | The displayname of the user
`source` | string | The type of the user, currently only `users`, `guests` or `calls` (for mentioning the whole conversation
`status` | string | Optional: Only available with `includeStatus=true` and for users with a set status
`statusIcon` | string | Optional: Only available with `includeStatus=true` and for users with a set status
`statusMessage` | string | Optional: Only available with `includeStatus=true` and for users with a set status

## System messages

Expand Down
8 changes: 8 additions & 0 deletions docs/participant.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ Base endpoint is: `/ocs/v2.php/apps/spreed/api/v1`

* Method: `GET`
* Endpoint: `/room/{token}/participants`
* Data:

field | type | Description
------|------|------------
`includeStatus` | bool | Whether the user status information also needs to be loaded

* Response:
- Status code:
Expand All @@ -24,6 +29,9 @@ Base endpoint is: `/ocs/v2.php/apps/spreed/api/v1`
`participantType` | int | Permissions level of the participant
`lastPing` | int | Timestamp of the last ping of the user (should be used for sorting)
`sessionId` | string | `'0'` if not connected, otherwise a 512 character long string
`status` | string | Optional: Only available with `includeStatus=true` and for users with a set status
`statusIcon` | string | Optional: Only available with `includeStatus=true` and for users with a set status
`statusMessage` | string | Optional: Only available with `includeStatus=true` and for users with a set status

## Add a participant to a conversation

Expand Down
38 changes: 34 additions & 4 deletions lib/Controller/ChatController.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
use OCP\IL10N;
use OCP\IRequest;
use OCP\IUserManager;
use OCP\UserStatus\IManager as IUserStatusManager;
use OCP\UserStatus\IUserStatus;

class ChatController extends AEnvironmentAwareController {

Expand Down Expand Up @@ -71,6 +73,9 @@ class ChatController extends AEnvironmentAwareController {
/** @var IManager */
private $autoCompleteManager;

/** @var IUserStatusManager */
private $statusManager;

/** @var SearchPlugin */
private $searchPlugin;

Expand All @@ -91,6 +96,7 @@ public function __construct(string $appName,
GuestManager $guestManager,
MessageParser $messageParser,
IManager $autoCompleteManager,
IUserStatusManager $statusManager,
SearchPlugin $searchPlugin,
ISearchResult $searchResult,
ITimeFactory $timeFactory,
Expand All @@ -104,6 +110,7 @@ public function __construct(string $appName,
$this->guestManager = $guestManager;
$this->messageParser = $messageParser;
$this->autoCompleteManager = $autoCompleteManager;
$this->statusManager = $statusManager;
$this->searchPlugin = $searchPlugin;
$this->searchResult = $searchResult;
$this->timeFactory = $timeFactory;
Expand Down Expand Up @@ -397,9 +404,10 @@ public function setReadMarker(int $lastReadMessage): DataResponse {
*
* @param string $search
* @param int $limit
* @param bool $includeStatus
* @return DataResponse
*/
public function mentions(string $search, int $limit = 20): DataResponse {
public function mentions(string $search, int $limit = 20, bool $includeStatus = false): DataResponse {
$this->searchPlugin->setContext([
'itemType' => 'chat',
'itemId' => $this->room->getId(),
Expand All @@ -419,7 +427,16 @@ public function mentions(string $search, int $limit = 20): DataResponse {
'search' => $search,
]);

$results = $this->prepareResultArray($results);
$statuses = [];
if ($includeStatus) {
$userIds = array_filter(array_map(static function (array $userResult) {
return $userResult['value']['shareWith'];
}, $results['users']));

$statuses = $this->statusManager->getUserStatuses($userIds);
}

$results = $this->prepareResultArray($results, $statuses);

$roomDisplayName = $this->room->getDisplayName($this->participant->getUser());
if (($search === '' || strpos('all', $search) !== false || stripos($roomDisplayName, $search) !== false) && $this->room->getType() !== Room::ONE_TO_ONE_CALL) {
Expand All @@ -444,15 +461,28 @@ public function mentions(string $search, int $limit = 20): DataResponse {
}


protected function prepareResultArray(array $results): array {
/**
* @param array $results
* @param IUserStatus[] $statuses
* @return array
*/
protected function prepareResultArray(array $results, array $statuses): array {
$output = [];
foreach ($results as $type => $subResult) {
foreach ($subResult as $result) {
$output[] = [
$data = [
'id' => $result['value']['shareWith'],
'label' => $result['label'],
'source' => $type,
];

if ($type === 'users' && isset($statuses[$data['id']])) {
$data['status'] = $statuses[$data['id']]->getStatus();
$data['statusIcon'] = $statuses[$data['id']]->getIcon();
$data['statusMessage'] = $statuses[$data['id']]->getMessage();
}

$output[] = $data;
}
}
return $output;
Expand Down
28 changes: 23 additions & 5 deletions lib/Controller/RoomController.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
use OCP\IGroup;
use OCP\IGroupManager;
use OCP\IConfig;
use OCP\UserStatus\IManager as IUserStatusManager;

class RoomController extends AEnvironmentAwareController {
public const EVENT_BEFORE_ROOMS_GET = self::class . '::preGetRooms';
Expand All @@ -78,6 +79,8 @@ class RoomController extends AEnvironmentAwareController {
protected $roomService;
/** @var GuestManager */
protected $guestManager;
/** @var IUserStatusManager */
protected $statusManager;
/** @var ChatManager */
protected $chatManager;
/** @var IEventDispatcher */
Expand All @@ -103,6 +106,7 @@ public function __construct(string $appName,
Manager $manager,
RoomService $roomService,
GuestManager $guestManager,
IUserStatusManager $statusManager,
ChatManager $chatManager,
IEventDispatcher $dispatcher,
MessageParser $messageParser,
Expand All @@ -119,6 +123,7 @@ public function __construct(string $appName,
$this->manager = $manager;
$this->roomService = $roomService;
$this->guestManager = $guestManager;
$this->statusManager = $statusManager;
$this->chatManager = $chatManager;
$this->dispatcher = $dispatcher;
$this->messageParser = $messageParser;
Expand Down Expand Up @@ -824,9 +829,10 @@ public function deleteRoom(): DataResponse {
* @RequireParticipant
* @RequireModeratorOrNoLobby
*
* @param bool $includeStatus
* @return DataResponse
*/
public function getParticipants(): DataResponse {
public function getParticipants(bool $includeStatus = false): DataResponse {
if ($this->participant->getParticipantType() === Participant::GUEST) {
return new DataResponse([], Http::STATUS_FORBIDDEN);
}
Expand All @@ -835,6 +841,12 @@ public function getParticipants(): DataResponse {
$participants = $this->room->getParticipantsLegacy();
$results = [];

$statuses = [];
if ($includeStatus && count($participants['users']) < 100) {
$userIds = array_map('strval', array_keys($participants['users']));
$statuses = $this->statusManager->getUserStatuses($userIds);
}

foreach ($participants['users'] as $userId => $participant) {
$userId = (string) $userId;
if ($participant['sessionId'] !== '0' && $participant['lastPing'] <= $maxPingAge) {
Expand All @@ -846,10 +858,16 @@ public function getParticipants(): DataResponse {
continue;
}

$results[] = array_merge($participant, [
'userId' => $userId,
'displayName' => (string) $user->getDisplayName(),
]);
$participant['userId'] = $userId;
$participant['displayName'] = (string) $user->getDisplayName();

if (isset($statuses[$userId])) {
$participant['status'] = $statuses[$userId]->getStatus();
$participant['statusIcon'] = $statuses[$userId]->getIcon();
$participant['statusMessage'] = $statuses[$userId]->getMessage();
}

$results[] = $participant;
}

$guestSessions = [];
Expand Down
12 changes: 12 additions & 0 deletions src/components/NewMessageForm/AdvancedInput/AdvancedInput.vue
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@
:disable-menu="true" />
&nbsp;
<span>{{ scope.item.label }}</span>
<em v-if="isNotAvailable(scope.item)">&nbsp;{{ getStatus(scope.item) }}</em>

</template>
<template v-slot:embeddedItem="scope">
<!-- The root element itself is ignored, only its contents are taken
Expand Down Expand Up @@ -76,6 +78,7 @@

<script>
import At from 'vue-at'
import UserStatus from '../../../mixins/userStatus'
import VueAtReparenter from '../../../mixins/vueAtReparenter'
import { EventBus } from '../../../services/EventBus'
import { searchPossibleMentions } from '../../../services/mentionsService'
Expand Down Expand Up @@ -153,6 +156,7 @@ export default {
},
mixins: [
VueAtReparenter,
UserStatus,
],
props: {
/**
Expand Down Expand Up @@ -350,6 +354,14 @@ export default {

return 'user'
},

getStatus(candidate) {
const status = this.getStatusMessage(candidate)
if (status) {
return '(' + status + ')'
}
return ''
},
},
}
</script>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

import ParticipantsList from '../ParticipantsList/ParticipantsList'
import { PARTICIPANT } from '../../../../constants'
import UserStatus from '../../../../mixins/userStatus'

export default {
name: 'CurrentParticipants',
Expand All @@ -37,6 +38,10 @@ export default {
ParticipantsList,
},

mixins: [
UserStatus,
],

props: {
searchText: {
type: String,
Expand Down Expand Up @@ -76,6 +81,7 @@ export default {
* Sort two participants by:
* - type (moderators before normal participants)
* - online status
* - user status (away + dnd at the end)
* - display name
*
* @param {object} participant1 First participant
Expand Down Expand Up @@ -105,6 +111,12 @@ export default {
return -1
}

const participant1Away = this.isNotAvailable(participant1)
const participant2Away = this.isNotAvailable(participant2)
if (participant1Away !== participant2Away) {
return participant1Away ? 1 : -1
}

return participant1.displayName.localeCompare(participant2.displayName)
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,18 @@
:name="computedName"
:source="participant.source"
:offline="isOffline" />
<span class="participant-row__user-name">{{ computedName }}</span>
<span v-if="showModeratorLabel" class="participant-row__moderator-indicator">({{ t('spreed', 'moderator') }})</span>
<span v-if="isGuest" class="participant-row__guest-indicator">({{ t('spreed', 'guest') }})</span>
<span v-if="callIconClass" class="icon callstate-icon" :class="callIconClass" />
<div class="participant-row__user-wrapper">
<div class="participant-row__user-descriptor">
<span class="participant-row__user-name">{{ computedName }}</span>
<span v-if="showModeratorLabel" class="participant-row__moderator-indicator">({{ t('spreed', 'moderator') }})</span>
<span v-if="isGuest" class="participant-row__guest-indicator">({{ t('spreed', 'guest') }})</span>
<span v-if="callIconClass" class="icon callstate-icon" :class="callIconClass" />
</div>
<div v-if="isNotAvailable(participant)"
class="participant-row__status">
<span>{{ getStatusMessage(participant) }}</span>
</div>
</div>
<Actions
v-if="canModerate && !isSearched"
:aria-label="t('spreed', 'Participant settings')"
Expand Down Expand Up @@ -66,6 +74,7 @@
import ActionButton from '@nextcloud/vue/dist/Components/ActionButton'
import Actions from '@nextcloud/vue/dist/Components/Actions'
import { CONVERSATION, PARTICIPANT } from '../../../../../constants'
import UserStatus from '../../../../../mixins/userStatus'
import isEqual from 'lodash/isEqual'
import AvatarWrapper from '../../../../AvatarWrapper/AvatarWrapper'

Expand All @@ -78,6 +87,10 @@ export default {
AvatarWrapper,
},

mixins: [
UserStatus,
],

props: {
participant: {
type: Object,
Expand Down Expand Up @@ -277,11 +290,13 @@ export default {
height: 44px;
cursor: pointer;
padding: 0 5px;
margin: 5px 0;
margin: 8px 0;
border-radius: 22px;

&__user-wrapper {
padding-left: 8px;
}
&__user-name {
margin-left: 6px;
display: inline-block;
vertical-align: middle;
line-height: normal;
Expand All @@ -293,6 +308,10 @@ export default {
font-weight: 300;
padding-left: 5px;
}
&__status {
color: var(--color-text-maxcontrast);
line-height: 1.3em;
}
&__icon {
width: 32px;
height: 44px;
Expand All @@ -317,7 +336,7 @@ export default {

.offline {

& > span {
.participant-row__user-descriptor > span {
color: var(--color-text-maxcontrast);
}
}
Expand Down
Loading