/**
* @file Definition of 'fnd' topic.
*
* @copyright 2015-2026 Tinode LLC.
*/
'use strict';
import * as Const from './config.js';
import Topic from './topic.js';
import {
mergeToCache
} from './utils.js';
/**
* Special case of {@link Tinode.Topic} for searching for contacts and group topics.
* @class TopicFnd
* @extends Tinode.Topic
* @memberof Tinode
*/
export default class TopicFnd extends Topic {
// List of users and topics uid or topic_name -> Contact object)
_contacts = {};
/**
* Create TopicFnd.
*
* @param {TopicFnd.Callbacks} callbacks - Callbacks to receive various events.
*/
constructor(callbacks) {
super(Const.TOPIC_FND, callbacks);
}
// Override the original Topic._processMetaSubs
_processMetaSubs(subs) {
let updateCount = Object.getOwnPropertyNames(this._contacts).length;
// Reset contact list.
this._contacts = {};
for (let idx in subs) {
let sub = subs[idx];
const indexBy = sub.topic ? sub.topic : sub.user;
sub = mergeToCache(this._contacts, indexBy, sub);
updateCount++;
if (this.onMetaSub) {
this.onMetaSub(sub);
}
}
if (updateCount > 0 && this.onSubsUpdated) {
this.onSubsUpdated(Object.keys(this._contacts));
}
}
/**
* Publishing to TopicFnd is not supported. {@link Topic#publish} is overriden and thows an {Error} if called.
* @memberof Tinode.TopicFnd#
* @throws {Error} Always throws an error.
*/
publish() {
return Promise.reject(new Error("Publishing to 'fnd' is not supported"));
}
/**
* setMeta to TopicFnd resets contact list in addition to sending the message.
* @memberof Tinode.TopicFnd#
* @param {Tinode.SetParams} params - parameters to update.
* @returns {Promise} Promise to be resolved/rejected when the server responds to request.
*/
setMeta(params) {
return Object.getPrototypeOf(TopicFnd.prototype).setMeta.call(this, params).then(_ => {
if (Object.keys(this._contacts).length > 0) {
this._contacts = {};
if (this.onSubsUpdated) {
this.onSubsUpdated([]);
}
}
});
}
/**
* Check if the given tag is unique by asking the server.
* @param {string} tag - tag to check.
* @param {string} caller - identifier of the caller.
* @returns {Promise} promise to be resolved with true if the tag is unique, false otherwise.
*/
checkTagUniqueness(tag, caller) {
return new Promise((resolve, reject) => {
this.subscribe()
.then(_ => this.setMeta({
desc: {
public: tag
}
}))
.then(_ => this.getMeta(this.startMetaQuery().withTags().build()))
.then(meta => {
if (!meta || !Array.isArray(meta.tags) || meta.tags.length == 0) {
resolve(true);
}
const tags = meta.tags.filter(t => t !== caller);
resolve(tags.length == 0);
})
.catch(err => {
reject(err);
});
});
}
/**
* Iterate over found contacts. If callback is undefined, use {@link this.onMetaSub}.
* @function
* @memberof Tinode.TopicFnd#
* @param {TopicFnd.ContactCallback} callback - Callback to call for each contact.
* @param {Object} context - Context to use for calling the `callback`, i.e. the value of `this` inside the callback.
*/
contacts(callback, context) {
const cb = (callback || this.onMetaSub);
if (cb) {
for (let idx in this._contacts) {
cb.call(context, this._contacts[idx], idx, this._contacts);
}
}
}
}