Add chat.default_join_voice_chat_as_chat_id.
This commit is contained in:
parent
f4ce6c9de9
commit
4eab57cd7b
@ -915,10 +915,11 @@ chatPosition list:ChatList order:int64 is_pinned:Bool source:ChatSource = ChatPo
|
||||
//@action_bar Describes actions which should be possible to do through a chat action bar; may be null
|
||||
//@voice_chat_group_call_id Group call identifier of an active voice chat; 0 if none or unknown. The voice chat can be received through the method getGroupCall
|
||||
//@is_voice_chat_empty True, if an active voice chat is empty
|
||||
//@default_join_voice_chat_as_chat_id Chat identifier of a chat, under which name to join voice chat by default; 0 if none or unknown
|
||||
//@reply_markup_message_id Identifier of the message from which reply markup needs to be used; 0 if there is no default custom reply markup in the chat
|
||||
//@draft_message A draft of a message in the chat; may be null
|
||||
//@client_data Contains application-specific data associated with the chat. (For example, the chat scroll position or local chat notification settings can be stored here.) Persistent if the message database is used
|
||||
chat id:int53 type:ChatType title:string photo:chatPhotoInfo permissions:chatPermissions last_message:message positions:vector<chatPosition> is_marked_as_unread:Bool is_blocked:Bool has_scheduled_messages:Bool can_be_deleted_only_for_self:Bool can_be_deleted_for_all_users:Bool can_be_reported:Bool default_disable_notification:Bool unread_count:int32 last_read_inbox_message_id:int53 last_read_outbox_message_id:int53 unread_mention_count:int32 notification_settings:chatNotificationSettings message_ttl_setting:int32 action_bar:ChatActionBar voice_chat_group_call_id:int32 is_voice_chat_empty:Bool reply_markup_message_id:int53 draft_message:draftMessage client_data:string = Chat;
|
||||
chat id:int53 type:ChatType title:string photo:chatPhotoInfo permissions:chatPermissions last_message:message positions:vector<chatPosition> is_marked_as_unread:Bool is_blocked:Bool has_scheduled_messages:Bool can_be_deleted_only_for_self:Bool can_be_deleted_for_all_users:Bool can_be_reported:Bool default_disable_notification:Bool unread_count:int32 last_read_inbox_message_id:int53 last_read_outbox_message_id:int53 unread_mention_count:int32 notification_settings:chatNotificationSettings message_ttl_setting:int32 action_bar:ChatActionBar voice_chat_group_call_id:int32 is_voice_chat_empty:Bool default_join_voice_chat_as_chat_id:int53 reply_markup_message_id:int53 draft_message:draftMessage client_data:string = Chat;
|
||||
|
||||
//@description Represents a list of chats @total_count Approximate total count of chats found @chat_ids List of chat identifiers
|
||||
chats total_count:int32 chat_ids:vector<int53> = Chats;
|
||||
@ -3387,8 +3388,8 @@ updateChatIsBlocked chat_id:int53 is_blocked:Bool = Update;
|
||||
//@description A chat's has_scheduled_messages field has changed @chat_id Chat identifier @has_scheduled_messages New value of has_scheduled_messages
|
||||
updateChatHasScheduledMessages chat_id:int53 has_scheduled_messages:Bool = Update;
|
||||
|
||||
//@description A chat voice chat state has changed @chat_id Chat identifier @voice_chat_group_call_id New value of voice_chat_group_call_id @is_voice_chat_empty New value of is_voice_chat_empty
|
||||
updateChatVoiceChat chat_id:int53 voice_chat_group_call_id:int32 is_voice_chat_empty:Bool = Update;
|
||||
//@description A chat voice chat state has changed @chat_id Chat identifier @voice_chat_group_call_id New value of voice_chat_group_call_id @is_voice_chat_empty New value of is_voice_chat_empty @default_join_voice_chat_as_chat_id Chat identifier of a chat, under which name to join voice chat by default; 0 if none or unknown
|
||||
updateChatVoiceChat chat_id:int53 voice_chat_group_call_id:int32 is_voice_chat_empty:Bool default_join_voice_chat_as_chat_id:int53 = Update;
|
||||
|
||||
//@description The value of the default disable_notification parameter, used when a message is sent to the chat, was changed @chat_id Chat identifier @default_disable_notification The new default_disable_notification value
|
||||
updateChatDefaultDisableNotification chat_id:int53 default_disable_notification:Bool = Update;
|
||||
|
@ -10326,6 +10326,16 @@ void ContactsManager::on_get_chat_full(tl_object_ptr<telegram_api::ChatFull> &&c
|
||||
}
|
||||
td_->messages_manager_->on_update_dialog_group_call_id(DialogId(chat_id), input_group_call_id);
|
||||
}
|
||||
{
|
||||
DialogId default_join_group_call_as_dialog_id;
|
||||
if (chat->groupcall_default_join_as_ != nullptr) {
|
||||
default_join_group_call_as_dialog_id = DialogId(chat->groupcall_default_join_as_);
|
||||
}
|
||||
// use send closure later to not crete synchronously default_join_group_call_as_dialog_id
|
||||
send_closure_later(G()->messages_manager(),
|
||||
&MessagesManager::on_update_dialog_default_join_group_call_as_dialog_id, DialogId(chat_id),
|
||||
default_join_group_call_as_dialog_id);
|
||||
}
|
||||
{
|
||||
MessageTtlSetting message_ttl_setting;
|
||||
if ((chat->flags_ & CHAT_FULL_FLAG_HAS_MESSAGE_TTL) != 0) {
|
||||
@ -10525,6 +10535,16 @@ void ContactsManager::on_get_chat_full(tl_object_ptr<telegram_api::ChatFull> &&c
|
||||
}
|
||||
td_->messages_manager_->on_update_dialog_group_call_id(DialogId(channel_id), input_group_call_id);
|
||||
}
|
||||
{
|
||||
DialogId default_join_group_call_as_dialog_id;
|
||||
if (channel->groupcall_default_join_as_ != nullptr) {
|
||||
default_join_group_call_as_dialog_id = DialogId(channel->groupcall_default_join_as_);
|
||||
}
|
||||
// use send closure later to not crete synchronously default_join_group_call_as_dialog_id
|
||||
send_closure_later(G()->messages_manager(),
|
||||
&MessagesManager::on_update_dialog_default_join_group_call_as_dialog_id, DialogId(channel_id),
|
||||
default_join_group_call_as_dialog_id);
|
||||
}
|
||||
|
||||
if (participant_count >= 190) {
|
||||
int32 online_member_count = 0;
|
||||
|
@ -1676,18 +1676,18 @@ void GroupCallManager::join_group_call(GroupCallId group_call_id, DialogId as_di
|
||||
request->query_ref = td_->create_handler<JoinGroupCallQuery>(std::move(query_promise))
|
||||
->send(input_group_call_id, as_dialog_id, json_payload, is_muted, generation);
|
||||
|
||||
if (!as_dialog_id.is_valid()) {
|
||||
as_dialog_id = DialogId(td_->contacts_manager_->get_my_id());
|
||||
}
|
||||
if (group_call->dialog_id.is_valid()) {
|
||||
td_->messages_manager_->on_update_dialog_default_join_group_call_as_dialog_id(group_call->dialog_id, as_dialog_id);
|
||||
} else {
|
||||
td_->messages_manager_->force_create_dialog(as_dialog_id, "join_group_call");
|
||||
}
|
||||
if (group_call->is_inited) {
|
||||
GroupCallParticipant group_call_participant;
|
||||
group_call_participant.is_self = true;
|
||||
if (as_dialog_id.is_valid()) {
|
||||
// dialog already exists
|
||||
group_call_participant.dialog_id = as_dialog_id;
|
||||
} else {
|
||||
// create dialog with self
|
||||
DialogId my_dialog_id(td_->contacts_manager_->get_my_id());
|
||||
td_->messages_manager_->force_create_dialog(my_dialog_id, "join_group_call");
|
||||
group_call_participant.dialog_id = my_dialog_id;
|
||||
}
|
||||
group_call_participant.dialog_id = as_dialog_id;
|
||||
group_call_participant.about = td_->contacts_manager_->get_dialog_about(group_call_participant.dialog_id);
|
||||
group_call_participant.audio_source = audio_source;
|
||||
group_call_participant.joined_date = G()->unix_time();
|
||||
@ -1893,6 +1893,12 @@ void GroupCallManager::finish_join_group_call(InputGroupCallId input_group_call_
|
||||
pending_join_requests_.erase(it);
|
||||
try_clear_group_call_participants(input_group_call_id);
|
||||
process_group_call_after_join_requests(input_group_call_id);
|
||||
|
||||
GroupCall *group_call = get_group_call(input_group_call_id);
|
||||
CHECK(group_call != nullptr);
|
||||
if (group_call->dialog_id.is_valid()) {
|
||||
td_->messages_manager_->reload_dialog_info_full(group_call->dialog_id);
|
||||
}
|
||||
}
|
||||
|
||||
void GroupCallManager::process_group_call_after_join_requests(InputGroupCallId input_group_call_id) {
|
||||
|
@ -5442,6 +5442,7 @@ void MessagesManager::Dialog::store(StorerT &storer) const {
|
||||
bool has_last_yet_unsent_message = last_message_id.is_valid() && last_message_id.is_yet_unsent();
|
||||
bool has_active_group_call_id = active_group_call_id.is_valid();
|
||||
bool has_message_ttl_setting = !message_ttl_setting.is_empty();
|
||||
bool has_default_join_group_call_as_dialog_id = default_join_group_call_as_dialog_id.is_valid();
|
||||
BEGIN_STORE_FLAGS();
|
||||
STORE_FLAG(has_draft_message);
|
||||
STORE_FLAG(has_last_database_message);
|
||||
@ -5503,6 +5504,7 @@ void MessagesManager::Dialog::store(StorerT &storer) const {
|
||||
STORE_FLAG(can_invite_members);
|
||||
STORE_FLAG(has_message_ttl_setting);
|
||||
STORE_FLAG(is_message_ttl_setting_inited);
|
||||
STORE_FLAG(has_default_join_group_call_as_dialog_id);
|
||||
END_STORE_FLAGS();
|
||||
}
|
||||
|
||||
@ -5593,6 +5595,9 @@ void MessagesManager::Dialog::store(StorerT &storer) const {
|
||||
if (has_message_ttl_setting) {
|
||||
store(message_ttl_setting, storer);
|
||||
}
|
||||
if (has_default_join_group_call_as_dialog_id) {
|
||||
store(default_join_group_call_as_dialog_id, storer);
|
||||
}
|
||||
}
|
||||
|
||||
// do not forget to resolve dialog dependencies including dependencies of last_message
|
||||
@ -5624,6 +5629,7 @@ void MessagesManager::Dialog::parse(ParserT &parser) {
|
||||
bool has_distance = false;
|
||||
bool has_active_group_call_id = false;
|
||||
bool has_message_ttl_setting = false;
|
||||
bool has_default_join_group_call_as_dialog_id = false;
|
||||
BEGIN_PARSE_FLAGS();
|
||||
PARSE_FLAG(has_draft_message);
|
||||
PARSE_FLAG(has_last_database_message);
|
||||
@ -5685,6 +5691,7 @@ void MessagesManager::Dialog::parse(ParserT &parser) {
|
||||
PARSE_FLAG(can_invite_members);
|
||||
PARSE_FLAG(has_message_ttl_setting);
|
||||
PARSE_FLAG(is_message_ttl_setting_inited);
|
||||
PARSE_FLAG(has_default_join_group_call_as_dialog_id);
|
||||
END_PARSE_FLAGS();
|
||||
} else {
|
||||
is_folder_id_inited = false;
|
||||
@ -5827,6 +5834,9 @@ void MessagesManager::Dialog::parse(ParserT &parser) {
|
||||
if (has_message_ttl_setting) {
|
||||
parse(message_ttl_setting, parser);
|
||||
}
|
||||
if (has_default_join_group_call_as_dialog_id) {
|
||||
parse(default_join_group_call_as_dialog_id, parser);
|
||||
}
|
||||
}
|
||||
|
||||
template <class StorerT>
|
||||
@ -19918,7 +19928,8 @@ td_api::object_ptr<td_api::chat> MessagesManager::get_chat_object(const Dialog *
|
||||
get_chat_notification_settings_object(&d->notification_settings),
|
||||
d->message_ttl_setting.get_message_ttl_setting_object(), get_chat_action_bar_object(d),
|
||||
active_group_call_id.get(), active_group_call_id.is_valid() ? d->is_group_call_empty : true,
|
||||
d->reply_markup_message_id.get(), std::move(draft_message), d->client_data);
|
||||
d->default_join_group_call_as_dialog_id.get(), d->reply_markup_message_id.get(), std::move(draft_message),
|
||||
d->client_data);
|
||||
}
|
||||
|
||||
tl_object_ptr<td_api::chat> MessagesManager::get_chat_object(DialogId dialog_id) const {
|
||||
@ -28547,9 +28558,10 @@ void MessagesManager::send_update_chat_voice_chat(const Dialog *d) {
|
||||
LOG_CHECK(d->is_update_new_chat_sent) << "Wrong " << d->dialog_id << " in send_update_chat_voice_chat";
|
||||
on_dialog_updated(d->dialog_id, "send_update_chat_voice_chat");
|
||||
auto group_call_id = td_->group_call_manager_->get_group_call_id(d->active_group_call_id, d->dialog_id);
|
||||
send_closure(G()->td(), &Td::send_update,
|
||||
td_api::make_object<td_api::updateChatVoiceChat>(d->dialog_id.get(), group_call_id.get(),
|
||||
d->is_group_call_empty));
|
||||
send_closure(
|
||||
G()->td(), &Td::send_update,
|
||||
td_api::make_object<td_api::updateChatVoiceChat>(d->dialog_id.get(), group_call_id.get(), d->is_group_call_empty,
|
||||
d->default_join_group_call_as_dialog_id.get()));
|
||||
}
|
||||
|
||||
void MessagesManager::send_update_chat_message_ttl_setting(const Dialog *d) {
|
||||
@ -29778,6 +29790,23 @@ void MessagesManager::on_update_dialog_group_call_id(DialogId dialog_id, InputGr
|
||||
}
|
||||
}
|
||||
|
||||
void MessagesManager::on_update_dialog_default_join_group_call_as_dialog_id(DialogId dialog_id,
|
||||
DialogId default_join_as_dialog_id) {
|
||||
auto d = get_dialog_force(dialog_id);
|
||||
if (d == nullptr) {
|
||||
// nothing to do
|
||||
return;
|
||||
}
|
||||
if (default_join_as_dialog_id.is_valid()) {
|
||||
force_create_dialog(default_join_as_dialog_id, "on_update_dialog_default_join_group_call_as_dialog_id");
|
||||
}
|
||||
|
||||
if (d->default_join_group_call_as_dialog_id != default_join_as_dialog_id) {
|
||||
d->default_join_group_call_as_dialog_id = default_join_as_dialog_id;
|
||||
send_update_chat_voice_chat(d);
|
||||
}
|
||||
}
|
||||
|
||||
void MessagesManager::on_update_dialog_message_ttl_setting(DialogId dialog_id, MessageTtlSetting message_ttl_setting) {
|
||||
auto d = get_dialog_force(dialog_id);
|
||||
if (d == nullptr) {
|
||||
@ -33810,6 +33839,10 @@ MessagesManager::Dialog *MessagesManager::add_new_dialog(unique_ptr<Dialog> &&d,
|
||||
MessageId last_clear_history_message_id = d->last_clear_history_message_id;
|
||||
d->last_clear_history_date = 0;
|
||||
d->last_clear_history_message_id = MessageId();
|
||||
DialogId default_join_group_call_as_dialog_id = d->default_join_group_call_as_dialog_id;
|
||||
if (default_join_group_call_as_dialog_id != dialog_id && !have_dialog(default_join_group_call_as_dialog_id)) {
|
||||
d->default_join_group_call_as_dialog_id = DialogId();
|
||||
}
|
||||
|
||||
if (d->message_notification_group.group_id.is_valid()) {
|
||||
notification_group_id_to_dialog_id_.emplace(d->message_notification_group.group_id, d->dialog_id);
|
||||
@ -33848,14 +33881,15 @@ MessagesManager::Dialog *MessagesManager::add_new_dialog(unique_ptr<Dialog> &&d,
|
||||
send_update_new_chat(dialog);
|
||||
|
||||
fix_new_dialog(dialog, std::move(last_database_message), last_database_message_id, order, last_clear_history_date,
|
||||
last_clear_history_message_id, is_loaded_from_database);
|
||||
last_clear_history_message_id, default_join_group_call_as_dialog_id, is_loaded_from_database);
|
||||
|
||||
return dialog;
|
||||
}
|
||||
|
||||
void MessagesManager::fix_new_dialog(Dialog *d, unique_ptr<Message> &&last_database_message,
|
||||
MessageId last_database_message_id, int64 order, int32 last_clear_history_date,
|
||||
MessageId last_clear_history_message_id, bool is_loaded_from_database) {
|
||||
MessageId last_clear_history_message_id,
|
||||
DialogId default_join_group_call_as_dialog_id, bool is_loaded_from_database) {
|
||||
CHECK(d != nullptr);
|
||||
auto dialog_id = d->dialog_id;
|
||||
auto dialog_type = dialog_id.get_type();
|
||||
@ -33959,18 +33993,32 @@ void MessagesManager::fix_new_dialog(Dialog *d, unique_ptr<Message> &&last_datab
|
||||
}
|
||||
}
|
||||
|
||||
auto pending_it = pending_add_dialog_last_database_message_dependent_dialogs_.find(dialog_id);
|
||||
if (pending_it != pending_add_dialog_last_database_message_dependent_dialogs_.end()) {
|
||||
auto pending_dialogs = std::move(pending_it->second);
|
||||
pending_add_dialog_last_database_message_dependent_dialogs_.erase(pending_it);
|
||||
{
|
||||
auto it = pending_add_dialog_last_database_message_dependent_dialogs_.find(dialog_id);
|
||||
if (it != pending_add_dialog_last_database_message_dependent_dialogs_.end()) {
|
||||
auto pending_dialog_ids = std::move(it->second);
|
||||
pending_add_dialog_last_database_message_dependent_dialogs_.erase(it);
|
||||
|
||||
for (auto &pending_dialog_id : pending_dialogs) {
|
||||
auto &counter_message = pending_add_dialog_last_database_message_[pending_dialog_id];
|
||||
CHECK(counter_message.first > 0);
|
||||
counter_message.first--;
|
||||
if (counter_message.first == 0) {
|
||||
add_dialog_last_database_message(get_dialog(pending_dialog_id), std::move(counter_message.second));
|
||||
pending_add_dialog_last_database_message_.erase(pending_dialog_id);
|
||||
for (auto &pending_dialog_id : pending_dialog_ids) {
|
||||
auto &counter_message = pending_add_dialog_last_database_message_[pending_dialog_id];
|
||||
CHECK(counter_message.first > 0);
|
||||
counter_message.first--;
|
||||
if (counter_message.first == 0) {
|
||||
add_dialog_last_database_message(get_dialog(pending_dialog_id), std::move(counter_message.second));
|
||||
pending_add_dialog_last_database_message_.erase(pending_dialog_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto it = pending_add_default_join_group_call_as_dialog_id_.find(dialog_id);
|
||||
if (it != pending_add_default_join_group_call_as_dialog_id_.end()) {
|
||||
auto pending_dialog_ids = std::move(it->second);
|
||||
pending_add_default_join_group_call_as_dialog_id_.erase(it);
|
||||
|
||||
for (auto &pending_dialog_id : pending_dialog_ids) {
|
||||
on_update_dialog_default_join_group_call_as_dialog_id(pending_dialog_id, dialog_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -34069,6 +34117,17 @@ void MessagesManager::fix_new_dialog(Dialog *d, unique_ptr<Message> &&last_datab
|
||||
}
|
||||
}
|
||||
|
||||
if (default_join_group_call_as_dialog_id != d->default_join_group_call_as_dialog_id) {
|
||||
CHECK(default_join_group_call_as_dialog_id.is_valid());
|
||||
if (!have_dialog(default_join_group_call_as_dialog_id)) {
|
||||
LOG(INFO) << "Postpone adding of default join voice chat as " << default_join_group_call_as_dialog_id << " in "
|
||||
<< dialog_id;
|
||||
pending_add_default_join_group_call_as_dialog_id_[default_join_group_call_as_dialog_id].push_back(dialog_id);
|
||||
} else {
|
||||
on_update_dialog_default_join_group_call_as_dialog_id(dialog_id, default_join_group_call_as_dialog_id);
|
||||
}
|
||||
}
|
||||
|
||||
switch (dialog_type) {
|
||||
case DialogType::User:
|
||||
break;
|
||||
@ -34842,6 +34901,9 @@ unique_ptr<MessagesManager::Dialog> MessagesManager::parse_dialog(DialogId dialo
|
||||
|
||||
Dependencies dependencies;
|
||||
add_dialog_dependencies(dependencies, dialog_id);
|
||||
if (d->default_join_group_call_as_dialog_id != dialog_id) {
|
||||
add_dialog_and_dependencies(dependencies, d->default_join_group_call_as_dialog_id);
|
||||
}
|
||||
if (d->messages != nullptr) {
|
||||
add_message_dependencies(dependencies, dialog_id, d->messages.get());
|
||||
}
|
||||
|
@ -290,6 +290,8 @@ class MessagesManager : public Actor {
|
||||
|
||||
void on_update_dialog_group_call_id(DialogId dialog_id, InputGroupCallId input_group_call_id);
|
||||
|
||||
void on_update_dialog_default_join_group_call_as_dialog_id(DialogId dialog_id, DialogId default_join_as_dialog_id);
|
||||
|
||||
void on_update_dialog_message_ttl_setting(DialogId dialog_id, MessageTtlSetting message_ttl_setting);
|
||||
|
||||
void on_update_dialog_filters();
|
||||
@ -502,6 +504,8 @@ class MessagesManager : public Actor {
|
||||
bool have_dialog_info(DialogId dialog_id) const;
|
||||
bool have_dialog_info_force(DialogId dialog_id) const;
|
||||
|
||||
void reload_dialog_info_full(DialogId dialog_id);
|
||||
|
||||
bool load_dialog(DialogId dialog_id, int left_tries, Promise<Unit> &&promise);
|
||||
|
||||
void load_dialogs(vector<DialogId> dialog_ids, Promise<Unit> &&promise);
|
||||
@ -1165,6 +1169,7 @@ class MessagesManager : public Actor {
|
||||
std::unordered_set<MessageId, MessageIdHash> updated_read_history_message_ids;
|
||||
LogEventIdWithGeneration set_folder_id_log_event_id;
|
||||
InputGroupCallId active_group_call_id;
|
||||
DialogId default_join_group_call_as_dialog_id;
|
||||
|
||||
FolderId folder_id;
|
||||
vector<DialogListId> dialog_list_ids; // TODO replace with mask
|
||||
@ -2401,7 +2406,7 @@ class MessagesManager : public Actor {
|
||||
|
||||
void fix_new_dialog(Dialog *d, unique_ptr<Message> &&last_database_message, MessageId last_database_message_id,
|
||||
int64 order, int32 last_clear_history_date, MessageId last_clear_history_message_id,
|
||||
bool is_loaded_from_database);
|
||||
DialogId default_join_group_call_as_dialog_id, bool is_loaded_from_database);
|
||||
|
||||
void add_dialog_last_database_message(Dialog *d, unique_ptr<Message> &&last_database_message);
|
||||
|
||||
@ -2428,8 +2433,6 @@ class MessagesManager : public Actor {
|
||||
|
||||
void send_search_public_dialogs_query(const string &query, Promise<Unit> &&promise);
|
||||
|
||||
void reload_dialog_info_full(DialogId dialog_id);
|
||||
|
||||
vector<DialogId> get_pinned_dialog_ids(DialogListId dialog_list_id) const;
|
||||
|
||||
void reload_pinned_dialogs(DialogListId dialog_list_id, Promise<Unit> &&promise);
|
||||
@ -3285,6 +3288,9 @@ class MessagesManager : public Actor {
|
||||
std::unordered_map<DialogId, std::pair<int32, unique_ptr<Message>>, DialogIdHash>
|
||||
pending_add_dialog_last_database_message_; // dialog -> dependency counter + message
|
||||
|
||||
std::unordered_map<DialogId, vector<DialogId>, DialogIdHash>
|
||||
pending_add_default_join_group_call_as_dialog_id_; // dialog_id -> dependent dialogs
|
||||
|
||||
struct CallsDbState {
|
||||
std::array<MessageId, 2> first_calls_database_message_id_by_index;
|
||||
std::array<int32, 2> message_count_by_index;
|
||||
|
Loading…
Reference in New Issue
Block a user