Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/spreed.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVincent Petry <vincent@nextcloud.com>2020-12-02 14:22:01 +0300
committerVincent Petry <vincent@nextcloud.com>2020-12-11 20:15:48 +0300
commit353206e1e695d531f2a51003ac93cfc3cd669bce (patch)
tree3b29ffeabce403a08033bf86517af956a278af22
parentf4d26223b3d22ecf2435c67adccf05629102be7f (diff)
Adjust approach for search results for listable rooms
Added "/listable-room" endpoint for listing listable rooms with a similar result format like the "/room" endpoint. Switched left sidebar to use Conversation components for displaying results. Signed-off-by: Vincent Petry <vincent@nextcloud.com>
-rw-r--r--appinfo/routes.php8
-rw-r--r--docs/conversation.md57
-rw-r--r--docs/events.md5
-rw-r--r--lib/Controller/RoomController.php71
-rw-r--r--lib/Manager.php2
-rw-r--r--src/components/LeftSidebar/ConversationsList/Conversation.vue6
-rw-r--r--src/components/LeftSidebar/LeftSidebar.vue30
-rw-r--r--src/services/conversationsService.js24
8 files changed, 162 insertions, 41 deletions
diff --git a/appinfo/routes.php b/appinfo/routes.php
index f702840f1..1c6aed1ad 100644
--- a/appinfo/routes.php
+++ b/appinfo/routes.php
@@ -184,6 +184,14 @@ return [
],
],
[
+ 'name' => 'Room#getListedRooms',
+ 'url' => '/api/{apiVersion}/listed-room',
+ 'verb' => 'GET',
+ 'requirements' => [
+ 'apiVersion' => 'v3',
+ ],
+ ],
+ [
'name' => 'Room#createRoom',
'url' => '/api/{apiVersion}/room',
'verb' => 'POST',
diff --git a/docs/conversation.md b/docs/conversation.md
index 10899e69b..1c57556b6 100644
--- a/docs/conversation.md
+++ b/docs/conversation.md
@@ -4,6 +4,63 @@
* Base endpoint for API v2 is: `/ocs/v2.php/apps/spreed/api/v2`
* Base endpoint for API v3 is: `/ocs/v2.php/apps/spreed/api/v3`
+## Get listed conversations
+
+* Method: `GET`
+* Endpoint: `/listed-room`
+
+* Response:
+ - Status code:
+ + `200 OK`
+ + `401 Unauthorized` when the user is not logged in
+
+ - Header:
+
+ field | type | Description
+ ------|------|------------
+ `searchTerm` | string | search term
+
+ - Data:
+ Array of conversations, each conversation has at least:
+
+ field | type | API | Description
+ ------|------|-----|------------
+ `token` | string | * | Token identifier of the conversation which is used for further interaction
+ `type` | int | * | See list of conversation types in the [constants list](constants.md#Conversation-types)
+ `name` | string | * | Name of the conversation (can also be empty)
+ `displayName` | string | * | `name` if non empty, otherwise it falls back to a list of participants
+ `participantType` | int | * | Permissions level of the current user
+ `attendeeId` | int | v3 | Unique attendee id
+ `attendeePin` | string | v3 | Unique dial-in authentication code for this user, when the conversation has SIP enabled (see `sipEnabled` attribute)
+ `actorType` | string | v3 | Currently known `users|guests|emails|groups`
+ `actorId` | string | v3 | The unique identifier for the given actor type
+ `participantInCall` | bool | 🏴 v1 | Flag if the current user is in the call (deprecated, use `participantFlags` instead)
+ `participantFlags` | int | * | Flags of the current user (only available with `in-call-flags` capability)
+ `readOnly` | int | * | Read-only state for the current user (only available with `read-only-rooms` capability)
+ `listable` | int | * | Listable scope for the room (only available with `listable-rooms` capability)
+ `count` | int | 🏴 v1 | **Deprecated:** ~~Number of active users~~ - always returns `0`
+ `numGuests` | int | 🏴 v1 | Number of active guests
+ `lastPing` | int | * | Timestamp of the last ping of the current user (should be used for sorting)
+ `sessionId` | string | * | `'0'` if not connected, otherwise a 512 character long string
+ `hasPassword` | bool | * | Flag if the conversation has a password
+ `hasCall` | bool | * | Flag if the conversation has an active call
+ `canStartCall` | bool | * | Flag if the user can start a new call in this conversation (joining is always possible) (only available with `start-call-flag` capability)
+ `canDeleteConversation` | bool | πŸ†• v2 | Flag if the user can delete the conversation for everyone (not possible without moderator permissions or in one-to-one conversations)
+ `canLeaveConversation` | bool | πŸ†• v2 | Flag if the user can leave the conversation (not possible for the last user with moderator permissions)
+ `lastActivity` | int | * | Timestamp of the last activity in the conversation, in seconds and UTC time zone
+ `isFavorite` | bool | * | Flag if the conversation is favorited by the user
+ `notificationLevel` | int | * | The notification level for the user (one of `Participant::NOTIFY_*` (1-3))
+ `lobbyState` | int | * | Webinary lobby restriction (0-1), if the participant is a moderator they can always join the conversation (only available with `webinary-lobby` capability)
+ `lobbyTimer` | int | * | Timestamp when the lobby will be automatically disabled (only available with `webinary-lobby` capability)
+ `sipEnabled` | int | v3 | SIP enable status (0-1)
+ `canEnableSIP` | int | v3 | Whether the given user can enable SIP for this conversation. Note that when the token is not-numeric only, SIP can not be enabled even if the user is permitted and a moderator of the conversation
+ `unreadMessages` | int | * | Number of unread chat messages in the conversation (only available with `chat-v2` capability)
+ `unreadMention` | bool | * | Flag if the user was mentioned since their last visit
+ `lastReadMessage` | int | * | ID of the last read message in a room (only available with `chat-read-marker` capability)
+ `lastMessage` | message | * | Last message in a conversation if available, otherwise empty
+ `objectType` | string | * | The type of object that the conversation is associated with; "share:password" if the conversation is used to request a password for a share, otherwise empty
+ `objectId` | string | * | Share token if "objectType" is "share:password", otherwise empty
+
## Get userΒ΄s conversations
* Method: `GET`
diff --git a/docs/events.md b/docs/events.md
index 227326a85..5b7558646 100644
--- a/docs/events.md
+++ b/docs/events.md
@@ -12,6 +12,11 @@ Explanations:
* Event name: `OCA\Talk\Controller\RoomController::EVENT_BEFORE_ROOMS_GET`
* Since: 8.0.0
+### Search listed conversations
+
+* Event class: `OCA\Talk\Events\UserEvent`
+* Event name: `OCA\Talk\Controller\RoomController::EVENT_BEFORE_LISTED_ROOMS_GET`
+* Since: 11.0.0
### Create conversation
diff --git a/lib/Controller/RoomController.php b/lib/Controller/RoomController.php
index c05c98bdd..047fd72f7 100644
--- a/lib/Controller/RoomController.php
+++ b/lib/Controller/RoomController.php
@@ -68,6 +68,7 @@ use OCP\UserStatus\IUserStatus;
class RoomController extends AEnvironmentAwareController {
public const EVENT_BEFORE_ROOMS_GET = self::class . '::preGetRooms';
+ public const EVENT_BEFORE_LISTED_ROOMS_GET = self::class . '::preGetListedRooms';
/** @var string|null */
protected $userId;
@@ -220,6 +221,37 @@ class RoomController extends AEnvironmentAwareController {
}
/**
+ * Search listed rooms
+ *
+ * @NoAdminRequired
+ *
+ * @param string $searchTerm search term
+ * @return DataResponse
+ */
+ public function getListedRooms(string $searchTerm = ''): DataResponse {
+ $event = new UserEvent($this->userId);
+ $this->dispatcher->dispatch(self::EVENT_BEFORE_LISTED_ROOMS_GET, $event);
+
+ $rooms = $this->manager->getListedRoomsForUser($this->userId, $searchTerm);
+
+ $return = [];
+ foreach ($rooms as $room) {
+ try {
+ $roomData = $this->formatRoom($room, null);
+ // since formatRoom will break early due to having no participant,
+ // we populate the remaining base attributes here
+ $return[] = $this->populateBaseRoomData($roomData, $room, $this->userId);
+ // TODO: should we populate more ?
+ } catch (RoomNotFoundException $e) {
+ } catch (\RuntimeException $e) {
+ }
+ }
+
+ return new DataResponse($return, Http::STATUS_OK);
+ }
+
+
+ /**
* @PublicPage
*
* @param string $token
@@ -599,20 +631,12 @@ class RoomController extends AEnvironmentAwareController {
$attendee = $currentParticipant->getAttendee();
$userId = $attendee->getActorType() === Attendee::ACTOR_USERS ? $attendee->getActorId() : '';
+ $roomData = $this->populateBaseRoomData($roomData, $room, $userId);
+
$roomData = array_merge($roomData, [
- 'name' => $room->getName(),
- 'displayName' => $room->getDisplayName($userId),
- 'objectType' => $room->getObjectType(),
- 'objectId' => $room->getObjectId(),
'participantType' => $attendee->getParticipantType(),
- 'readOnly' => $room->getReadOnly(),
- 'listable' => $room->getListable(),
- 'hasCall' => $room->getActiveSince() instanceof \DateTimeInterface,
- 'lastActivity' => $lastActivity,
'isFavorite' => $attendee->isFavorite(),
'notificationLevel' => $attendee->getNotificationLevel(),
- 'lobbyState' => $room->getLobbyState(),
- 'lobbyTimer' => $lobbyTimer,
]);
if ($this->getAPIVersion() >= 3) {
if ($this->talkConfig->isSIPConfigured()) {
@@ -732,6 +756,33 @@ class RoomController extends AEnvironmentAwareController {
return $roomData;
}
+ protected function populateBaseRoomData(array $roomData, Room $room, $userId) {
+ $lastActivity = $room->getLastActivity();
+ if ($lastActivity instanceof \DateTimeInterface) {
+ $lastActivity = $lastActivity->getTimestamp();
+ } else {
+ $lastActivity = 0;
+ }
+
+ $lobbyTimer = $room->getLobbyTimer();
+ if ($lobbyTimer instanceof \DateTimeInterface) {
+ $lobbyTimer = $lobbyTimer->getTimestamp();
+ } else {
+ $lobbyTimer = 0;
+ }
+
+ return array_merge($roomData, [
+ 'name' => $room->getName(),
+ 'displayName' => $room->getDisplayName($userId),
+ 'objectType' => $room->getObjectType(),
+ 'objectId' => $room->getObjectId(),
+ 'readOnly' => $room->getReadOnly(),
+ 'listable' => $room->getListable(),
+ 'hasCall' => $room->getActiveSince() instanceof \DateTimeInterface,
+ 'lobbyState' => $room->getLobbyState(),
+ ]);
+ }
+
/**
* @param Room $room
* @param Participant $participant
diff --git a/lib/Manager.php b/lib/Manager.php
index 4777142ee..e6efc5345 100644
--- a/lib/Manager.php
+++ b/lib/Manager.php
@@ -347,7 +347,7 @@ class Manager {
$allowedRoomTypes = [Room::GROUP_CALL, Room::PUBLIC_CALL];
$allowedListedTypes = [Room::LISTABLE_ALL];
if (!$this->isGuestUser($userId)) {
- $listedType[] = Room::LISTABLE_USERS;
+ $allowedListedTypes[] = Room::LISTABLE_USERS;
}
$query = $this->db->getQueryBuilder();
$query->select('r.*')
diff --git a/src/components/LeftSidebar/ConversationsList/Conversation.vue b/src/components/LeftSidebar/ConversationsList/Conversation.vue
index e34360c71..bb1013b18 100644
--- a/src/components/LeftSidebar/ConversationsList/Conversation.vue
+++ b/src/components/LeftSidebar/ConversationsList/Conversation.vue
@@ -45,7 +45,7 @@
:highlighted="counterShouldBePrimary">
<strong>{{ item.unreadMessages }}</strong>
</AppNavigationCounter>
- <template slot="actions">
+ <template v-if="!isSearchResult" slot="actions">
<ActionButton v-if="canFavorite"
:icon="iconFavorite"
@click.prevent.exact="toggleFavoriteConversation">
@@ -124,6 +124,10 @@ export default {
ConversationIcon,
},
props: {
+ isSearchResult: {
+ type: Boolean,
+ default: false,
+ },
item: {
type: Object,
default: function() {
diff --git a/src/components/LeftSidebar/LeftSidebar.vue b/src/components/LeftSidebar/LeftSidebar.vue
index 5af424057..4b12bcca4 100644
--- a/src/components/LeftSidebar/LeftSidebar.vue
+++ b/src/components/LeftSidebar/LeftSidebar.vue
@@ -47,12 +47,11 @@
<template v-if="searchResultsListedConversations.length !== 0">
<Caption
:title="t('spreed', 'Listed conversations')" />
- <li role="presentation">
- <!-- FIXME: use ConversationsList instead ? -->
- <ConversationsOptionsList
- :items="searchResultsListedConversations"
- @click="joinConversation" />
- </li>
+ <Conversation
+ v-for="item of searchResultsListedConversations"
+ :key="item.id"
+ :item="item"
+ :is-search-result="true" />
</template>
<template v-if="searchResultsUsers.length !== 0">
<Caption
@@ -114,6 +113,7 @@
import AppNavigation from '@nextcloud/vue/dist/Components/AppNavigation'
import Caption from '../Caption'
import ConversationsList from './ConversationsList/ConversationsList'
+import Conversation from './ConversationsList/Conversation'
import ConversationsOptionsList from '../ConversationsOptionsList'
import Hint from '../Hint'
import SearchBox from './SearchBox/SearchBox'
@@ -144,6 +144,7 @@ export default {
Hint,
SearchBox,
NewGroupConversation,
+ Conversation,
},
mixins: [
@@ -286,18 +287,7 @@ export default {
async fetchListedConversations() {
this.listedConversationsLoading = true
const response = await searchListedConversations(this.searchText)
- if (response.data.ocs.data?.entries?.length) {
- this.searchResultsListedConversations = response.data.ocs.data.entries.map((result) => {
- return {
- // TODO: extract token ?
- id: result.resourceUrl,
- label: result.title,
- icon: result.icon,
- }
- })
- } else {
- this.searchResultsListedConversations = []
- }
+ this.searchResultsListedConversations = response.data.ocs.data
this.listedConversationsLoading = false
},
@@ -308,10 +298,6 @@ export default {
this.focusInitialise()
},
- async joinConversation(item) {
- console.log('TODO: Implement join conversation: ', item)
- },
-
/**
* Create a new conversation with the selected group/user/circle
* @param {Object} item The autocomplete suggestion to start a conversation with
diff --git a/src/services/conversationsService.js b/src/services/conversationsService.js
index a4b8e41b2..b88a5bc18 100644
--- a/src/services/conversationsService.js
+++ b/src/services/conversationsService.js
@@ -90,19 +90,29 @@ const checkTalkVersionHash = function(response) {
/**
* Fetch listed conversations
- * @param {string} searchText The string that will be used in the search query.
+ * @param {string} searchTerm The string that will be used in the search query.
*/
-const searchListedConversations = async function(searchText) {
+const searchListedConversations = async function(searchTerm) {
try {
- // use search provider to find listed conversations
- return axios.get(generateOcsUrl('search/providers/talk-listed-conversations', 2) + 'search', {
+ const response = await axios.get(generateOcsUrl('apps/spreed/api/v3', 2) + 'listed-room', {
params: {
- term: searchText,
- format: 'json',
+ searchTerm,
},
})
+
+ if (maintenanceWarning) {
+ maintenanceWarning.hideToast()
+ maintenanceWarning = null
+ }
+
+ return response
} catch (error) {
- console.debug('Error while searching listedk conversations: ', error)
+ if (error.response && error.response.status === 503 && !maintenanceWarning) {
+ maintenanceWarning = showError(t('spreed', 'Nextcloud is in maintenance mode, please reload the page'), {
+ timeout: TOAST_PERMANENT_TIMEOUT,
+ })
+ }
+ throw error
}
}
/**