2024-08-30 00:55:09 +02:00
|
|
|
import assert from "minimalistic-assert";
|
|
|
|
|
2024-11-12 03:59:37 +01:00
|
|
|
import * as hash_util from "./hash_util.ts";
|
|
|
|
import {$t} from "./i18n.ts";
|
|
|
|
import * as message_lists from "./message_lists.ts";
|
|
|
|
import * as muted_users from "./muted_users.ts";
|
|
|
|
import * as narrow_state from "./narrow_state.ts";
|
|
|
|
import {page_params} from "./page_params.ts";
|
|
|
|
import * as peer_data from "./peer_data.ts";
|
|
|
|
import * as people from "./people.ts";
|
|
|
|
import * as presence from "./presence.ts";
|
|
|
|
import {realm} from "./state_data.ts";
|
|
|
|
import * as stream_data from "./stream_data.ts";
|
|
|
|
import type {StreamSubscription} from "./sub_store.ts";
|
|
|
|
import * as timerender from "./timerender.ts";
|
|
|
|
import * as unread from "./unread.ts";
|
|
|
|
import {user_settings} from "./user_settings.ts";
|
|
|
|
import * as user_status from "./user_status.ts";
|
|
|
|
import * as util from "./util.ts";
|
2021-02-28 01:12:35 +01:00
|
|
|
|
2018-04-19 15:46:56 +02:00
|
|
|
/*
|
|
|
|
|
|
|
|
This is the main model code for building the buddy list.
|
|
|
|
We also rely on presence.js to compute the actual presence
|
|
|
|
for users. We glue in other "people" data and do
|
|
|
|
filtering/sorting of the data that we'll send into the view.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
2024-11-08 11:33:34 +01:00
|
|
|
export let max_size_before_shrinking = 600;
|
|
|
|
|
|
|
|
export function rewire_max_size_before_shrinking(value: typeof max_size_before_shrinking): void {
|
|
|
|
max_size_before_shrinking = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
export let max_channel_size_to_show_all_subscribers = 75;
|
|
|
|
|
|
|
|
export function rewire_max_channel_size_to_show_all_subscribers(
|
|
|
|
value: typeof max_channel_size_to_show_all_subscribers,
|
|
|
|
): void {
|
|
|
|
max_channel_size_to_show_all_subscribers = value;
|
|
|
|
}
|
2018-04-20 18:22:28 +02:00
|
|
|
|
2023-08-24 01:33:39 +02:00
|
|
|
let is_searching_users = false;
|
2023-08-23 02:18:53 +02:00
|
|
|
|
|
|
|
export function get_is_searching_users(): boolean {
|
|
|
|
return is_searching_users;
|
|
|
|
}
|
|
|
|
|
2023-12-24 01:20:16 +01:00
|
|
|
export function set_is_searching_users(val: boolean): void {
|
2023-08-24 01:33:39 +02:00
|
|
|
is_searching_users = val;
|
|
|
|
}
|
|
|
|
|
2023-12-24 01:20:16 +01:00
|
|
|
export function get_user_circle_class(user_id: number): string {
|
2022-09-21 15:49:36 +02:00
|
|
|
const status = presence.get_status(user_id);
|
2019-02-17 02:10:42 +01:00
|
|
|
|
|
|
|
switch (status) {
|
2020-07-15 02:14:03 +02:00
|
|
|
case "active":
|
|
|
|
return "user_circle_green";
|
|
|
|
case "idle":
|
2022-01-27 22:22:59 +01:00
|
|
|
return "user_circle_idle";
|
2020-07-15 02:14:03 +02:00
|
|
|
default:
|
|
|
|
return "user_circle_empty";
|
2019-02-17 02:10:42 +01:00
|
|
|
}
|
2021-02-28 01:12:35 +01:00
|
|
|
}
|
2019-02-17 02:10:42 +01:00
|
|
|
|
2023-12-24 01:20:16 +01:00
|
|
|
export function level(user_id: number): number {
|
2023-08-24 01:33:39 +02:00
|
|
|
// Put current user at the top, unless we're in a user search view.
|
|
|
|
if (people.is_my_user_id(user_id) && !is_searching_users) {
|
2018-12-18 18:50:58 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-09-21 15:49:36 +02:00
|
|
|
const status = presence.get_status(user_id);
|
2018-09-08 15:54:52 +02:00
|
|
|
|
2018-04-20 18:22:28 +02:00
|
|
|
switch (status) {
|
2020-07-15 02:14:03 +02:00
|
|
|
case "active":
|
|
|
|
return 1;
|
|
|
|
case "idle":
|
|
|
|
return 2;
|
|
|
|
default:
|
|
|
|
return 3;
|
2018-04-19 15:46:56 +02:00
|
|
|
}
|
2021-02-28 01:12:35 +01:00
|
|
|
}
|
2018-04-19 15:46:56 +02:00
|
|
|
|
2024-11-08 11:33:34 +01:00
|
|
|
export let user_matches_narrow = (
|
2023-08-23 02:18:53 +02:00
|
|
|
user_id: number,
|
|
|
|
pm_ids: Set<number>,
|
|
|
|
stream_id?: number | null,
|
2024-11-08 11:33:34 +01:00
|
|
|
): boolean => {
|
2023-08-23 02:18:53 +02:00
|
|
|
if (stream_id) {
|
|
|
|
return stream_data.is_user_subscribed(stream_id, user_id);
|
|
|
|
}
|
|
|
|
if (pm_ids.size > 0) {
|
|
|
|
return pm_ids.has(user_id) || people.is_my_user_id(user_id);
|
|
|
|
}
|
|
|
|
return false;
|
2024-11-08 11:33:34 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
export function rewire_user_matches_narrow(value: typeof user_matches_narrow): void {
|
|
|
|
user_matches_narrow = value;
|
2023-08-23 02:18:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
export function compare_function(
|
|
|
|
a: number,
|
|
|
|
b: number,
|
|
|
|
current_sub: StreamSubscription | undefined,
|
|
|
|
pm_ids: Set<number>,
|
2024-10-02 07:09:29 +02:00
|
|
|
conversation_participants: Set<number>,
|
2023-08-23 02:18:53 +02:00
|
|
|
): number {
|
2024-10-02 07:09:29 +02:00
|
|
|
const a_is_participant = conversation_participants.has(a);
|
|
|
|
const b_is_participant = conversation_participants.has(b);
|
|
|
|
if (a_is_participant && !b_is_participant) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (!a_is_participant && b_is_participant) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2023-08-23 02:18:53 +02:00
|
|
|
const a_would_receive_message = user_matches_narrow(a, pm_ids, current_sub?.stream_id);
|
|
|
|
const b_would_receive_message = user_matches_narrow(b, pm_ids, current_sub?.stream_id);
|
|
|
|
if (a_would_receive_message && !b_would_receive_message) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (!a_would_receive_message && b_would_receive_message) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2021-02-28 01:12:35 +01:00
|
|
|
const level_a = level(a);
|
|
|
|
const level_b = level(b);
|
2019-11-02 00:06:25 +01:00
|
|
|
const diff = level_a - level_b;
|
2018-04-19 15:46:56 +02:00
|
|
|
if (diff !== 0) {
|
|
|
|
return diff;
|
|
|
|
}
|
|
|
|
|
2023-06-16 13:23:39 +02:00
|
|
|
// Sort equivalent direct message names alphabetically
|
2023-06-16 15:23:45 +02:00
|
|
|
const person_a = people.maybe_get_user_by_id(a);
|
|
|
|
const person_b = people.maybe_get_user_by_id(b);
|
2018-04-19 15:46:56 +02:00
|
|
|
|
2020-07-15 01:29:15 +02:00
|
|
|
const full_name_a = person_a ? person_a.full_name : "";
|
|
|
|
const full_name_b = person_b ? person_b.full_name : "";
|
2018-04-19 15:46:56 +02:00
|
|
|
|
|
|
|
return util.strcmp(full_name_a, full_name_b);
|
2021-02-28 01:12:35 +01:00
|
|
|
}
|
2018-04-19 15:46:56 +02:00
|
|
|
|
2024-10-02 07:09:29 +02:00
|
|
|
export function sort_users(user_ids: number[], conversation_participants: Set<number>): number[] {
|
2018-04-19 15:46:56 +02:00
|
|
|
// TODO sort by unread count first, once we support that
|
2023-08-23 02:18:53 +02:00
|
|
|
const current_sub = narrow_state.stream_sub();
|
|
|
|
const pm_ids_set = narrow_state.pm_ids_set();
|
2024-10-02 07:09:29 +02:00
|
|
|
user_ids.sort((a, b) =>
|
|
|
|
compare_function(a, b, current_sub, pm_ids_set, conversation_participants),
|
|
|
|
);
|
2018-04-19 15:46:56 +02:00
|
|
|
return user_ids;
|
2021-02-28 01:12:35 +01:00
|
|
|
}
|
2018-04-19 15:46:56 +02:00
|
|
|
|
2023-12-24 01:20:16 +01:00
|
|
|
function get_num_unread(user_id: number): number {
|
2022-10-22 07:15:44 +02:00
|
|
|
return unread.num_unread_for_user_ids_string(user_id.toString());
|
2018-04-19 15:46:56 +02:00
|
|
|
}
|
|
|
|
|
2023-12-24 01:20:16 +01:00
|
|
|
export function user_last_seen_time_status(user_id: number): string {
|
2019-11-02 00:06:25 +01:00
|
|
|
const status = presence.get_status(user_id);
|
2019-02-28 16:09:03 +01:00
|
|
|
if (status === "active") {
|
2021-04-13 06:51:54 +02:00
|
|
|
return $t({defaultMessage: "Active now"});
|
2019-02-28 16:09:03 +01:00
|
|
|
}
|
|
|
|
|
2021-06-03 20:45:59 +02:00
|
|
|
if (status === "idle") {
|
|
|
|
// When we complete our presence API rewrite to have the data
|
|
|
|
// plumbed, we may want to change this to also mention when
|
|
|
|
// they were last active.
|
|
|
|
return $t({defaultMessage: "Idle"});
|
|
|
|
}
|
|
|
|
|
2021-06-03 20:43:19 +02:00
|
|
|
const last_active_date = presence.last_active_date(user_id);
|
2024-02-13 02:08:24 +01:00
|
|
|
if (realm.realm_is_zephyr_mirror_realm) {
|
2019-02-28 16:09:03 +01:00
|
|
|
// We don't send presence data to clients in Zephyr mirroring realms
|
2023-04-07 18:24:15 +02:00
|
|
|
return $t({defaultMessage: "Activity unknown"});
|
2021-06-03 20:43:19 +02:00
|
|
|
} else if (last_active_date === undefined) {
|
|
|
|
// There are situations where the client has incomplete presence
|
2024-08-30 00:55:09 +02:00
|
|
|
// history on a user. This can happen when users are deactivated,
|
|
|
|
// or when the user's last activity is older than what we fetch.
|
|
|
|
assert(page_params.presence_history_limit_days_for_web_app === 365);
|
|
|
|
return $t({defaultMessage: "Not active in the last year"});
|
2019-02-28 16:09:03 +01:00
|
|
|
}
|
2023-04-07 18:24:15 +02:00
|
|
|
return timerender.last_seen_status_from_date(last_active_date);
|
2021-02-28 01:12:35 +01:00
|
|
|
}
|
2019-02-28 16:09:03 +01:00
|
|
|
|
2023-12-24 01:20:16 +01:00
|
|
|
export type BuddyUserInfo = {
|
|
|
|
href: string;
|
|
|
|
name: string;
|
|
|
|
user_id: number;
|
2024-10-24 08:10:56 +02:00
|
|
|
profile_picture: string;
|
2023-12-24 01:20:16 +01:00
|
|
|
status_emoji_info: user_status.UserStatusEmojiInfo | undefined;
|
|
|
|
is_current_user: boolean;
|
|
|
|
num_unread: number;
|
|
|
|
user_circle_class: string;
|
|
|
|
status_text: string | undefined;
|
2024-11-05 22:45:51 +01:00
|
|
|
has_status_text: boolean;
|
2023-12-24 01:20:16 +01:00
|
|
|
user_list_style: {
|
|
|
|
COMPACT: boolean;
|
|
|
|
WITH_STATUS: boolean;
|
|
|
|
WITH_AVATAR: boolean;
|
|
|
|
};
|
|
|
|
should_add_guest_user_indicator: boolean;
|
|
|
|
faded?: boolean;
|
|
|
|
};
|
|
|
|
|
|
|
|
export function info_for(user_id: number): BuddyUserInfo {
|
2021-02-28 01:12:35 +01:00
|
|
|
const user_circle_class = get_user_circle_class(user_id);
|
2020-02-05 14:30:59 +01:00
|
|
|
const person = people.get_by_user_id(user_id);
|
2021-06-27 19:04:17 +02:00
|
|
|
|
|
|
|
const status_emoji_info = user_status.get_status_emoji(user_id);
|
2022-08-12 22:41:06 +02:00
|
|
|
const status_text = user_status.get_status_text(user_id);
|
|
|
|
const user_list_style_value = user_settings.user_list_style;
|
|
|
|
const user_list_style = {
|
|
|
|
COMPACT: user_list_style_value === 1,
|
|
|
|
WITH_STATUS: user_list_style_value === 2,
|
|
|
|
WITH_AVATAR: user_list_style_value === 3,
|
|
|
|
};
|
2018-04-19 15:46:56 +02:00
|
|
|
|
|
|
|
return {
|
2022-03-01 19:14:26 +01:00
|
|
|
href: hash_util.pm_with_url(person.email),
|
2018-04-19 15:46:56 +02:00
|
|
|
name: person.full_name,
|
2020-07-20 22:18:43 +02:00
|
|
|
user_id,
|
2021-06-27 19:04:17 +02:00
|
|
|
status_emoji_info,
|
2024-10-24 08:10:56 +02:00
|
|
|
profile_picture: people.small_avatar_url_for_person(person),
|
2018-11-28 00:07:01 +01:00
|
|
|
is_current_user: people.is_my_user_id(user_id),
|
2018-04-19 15:46:56 +02:00
|
|
|
num_unread: get_num_unread(user_id),
|
2020-07-20 22:18:43 +02:00
|
|
|
user_circle_class,
|
2022-08-12 22:41:06 +02:00
|
|
|
status_text,
|
2024-11-05 22:45:51 +01:00
|
|
|
has_status_text: Boolean(status_text),
|
2022-08-12 22:41:06 +02:00
|
|
|
user_list_style,
|
2023-09-13 19:30:52 +02:00
|
|
|
should_add_guest_user_indicator: people.should_add_guest_user_indicator(user_id),
|
2019-08-04 14:57:32 +02:00
|
|
|
};
|
2021-02-28 01:12:35 +01:00
|
|
|
}
|
2019-08-04 14:57:32 +02:00
|
|
|
|
2023-12-24 01:20:16 +01:00
|
|
|
export function get_title_data(
|
|
|
|
user_ids_string: string,
|
|
|
|
is_group: boolean,
|
|
|
|
): {
|
|
|
|
first_line: string;
|
2024-05-16 05:05:29 +02:00
|
|
|
second_line: string | undefined;
|
2023-12-24 01:20:16 +01:00
|
|
|
third_line: string;
|
|
|
|
show_you?: boolean;
|
2024-11-13 18:42:04 +01:00
|
|
|
is_deactivated?: boolean;
|
2023-12-24 01:20:16 +01:00
|
|
|
} {
|
|
|
|
if (is_group) {
|
2019-08-04 14:57:32 +02:00
|
|
|
// For groups, just return a string with recipient names.
|
|
|
|
return {
|
2019-11-20 23:39:10 +01:00
|
|
|
first_line: people.get_recipients(user_ids_string),
|
2020-07-15 01:29:15 +02:00
|
|
|
second_line: "",
|
|
|
|
third_line: "",
|
2019-08-04 14:57:32 +02:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
// Since it's not a group, user_ids_string is a single user ID.
|
2020-10-07 09:17:30 +02:00
|
|
|
const user_id = Number.parseInt(user_ids_string, 10);
|
2020-02-05 14:30:59 +01:00
|
|
|
const person = people.get_by_user_id(user_id);
|
2024-11-13 18:42:04 +01:00
|
|
|
const is_deactivated = !people.is_person_active(user_id);
|
2019-08-04 14:57:32 +02:00
|
|
|
|
|
|
|
if (person.is_bot) {
|
2020-03-24 22:42:58 +01:00
|
|
|
const bot_owner = people.get_bot_owner_user(person);
|
2019-11-20 23:39:10 +01:00
|
|
|
|
2020-03-24 22:42:58 +01:00
|
|
|
if (bot_owner) {
|
2021-04-13 06:51:54 +02:00
|
|
|
const bot_owner_name = $t(
|
|
|
|
{defaultMessage: "Owner: {name}"},
|
|
|
|
{name: bot_owner.full_name},
|
|
|
|
);
|
2019-11-20 23:39:10 +01:00
|
|
|
|
|
|
|
return {
|
|
|
|
first_line: person.full_name,
|
|
|
|
second_line: bot_owner_name,
|
2024-11-13 18:42:04 +01:00
|
|
|
third_line: is_deactivated
|
|
|
|
? $t({defaultMessage: "This bot has been deactivated."})
|
|
|
|
: "",
|
|
|
|
is_deactivated,
|
2019-11-20 23:39:10 +01:00
|
|
|
};
|
2019-08-04 14:57:32 +02:00
|
|
|
}
|
|
|
|
|
2019-11-20 23:39:10 +01:00
|
|
|
// Bot does not have an owner.
|
2019-08-04 14:57:32 +02:00
|
|
|
return {
|
2019-11-20 23:39:10 +01:00
|
|
|
first_line: person.full_name,
|
2020-07-15 01:29:15 +02:00
|
|
|
second_line: "",
|
|
|
|
third_line: "",
|
2019-08-04 14:57:32 +02:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2023-06-16 13:23:39 +02:00
|
|
|
// For buddy list and individual direct messages.
|
|
|
|
// Since is_group=False, it's a single, human user.
|
2021-02-28 01:12:35 +01:00
|
|
|
const last_seen = user_last_seen_time_status(user_id);
|
2021-04-02 13:05:26 +02:00
|
|
|
const is_my_user = people.is_my_user_id(user_id);
|
2019-08-04 14:57:32 +02:00
|
|
|
|
2024-11-13 18:42:04 +01:00
|
|
|
if (is_deactivated) {
|
|
|
|
return {
|
|
|
|
first_line: person.full_name,
|
|
|
|
second_line: $t({defaultMessage: "This user has been deactivated."}),
|
|
|
|
third_line: "",
|
|
|
|
show_you: is_my_user,
|
|
|
|
is_deactivated,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2019-11-20 23:39:10 +01:00
|
|
|
// Users has a status.
|
|
|
|
if (user_status.get_status_text(user_id)) {
|
|
|
|
return {
|
|
|
|
first_line: person.full_name,
|
|
|
|
second_line: user_status.get_status_text(user_id),
|
2021-06-03 20:43:19 +02:00
|
|
|
third_line: last_seen,
|
2021-04-02 13:05:26 +02:00
|
|
|
show_you: is_my_user,
|
2019-11-20 23:39:10 +01:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
// Users does not have a status.
|
2019-08-04 14:57:32 +02:00
|
|
|
return {
|
2019-11-20 23:39:10 +01:00
|
|
|
first_line: person.full_name,
|
2021-06-03 20:43:19 +02:00
|
|
|
second_line: last_seen,
|
2020-07-15 01:29:15 +02:00
|
|
|
third_line: "",
|
2021-04-02 13:05:26 +02:00
|
|
|
show_you: is_my_user,
|
2018-04-19 15:46:56 +02:00
|
|
|
};
|
2021-02-28 01:12:35 +01:00
|
|
|
}
|
2018-04-19 15:46:56 +02:00
|
|
|
|
2023-12-24 01:20:16 +01:00
|
|
|
export function get_item(user_id: number): BuddyUserInfo {
|
2021-02-28 01:12:35 +01:00
|
|
|
const info = info_for(user_id);
|
2018-04-22 17:46:20 +02:00
|
|
|
return info;
|
2021-02-28 01:12:35 +01:00
|
|
|
}
|
2018-04-22 17:46:20 +02:00
|
|
|
|
2023-12-24 01:20:16 +01:00
|
|
|
export function get_items_for_users(user_ids: number[]): BuddyUserInfo[] {
|
2021-04-23 15:54:05 +02:00
|
|
|
const user_info = user_ids.map((user_id) => info_for(user_id));
|
|
|
|
return user_info;
|
|
|
|
}
|
|
|
|
|
2023-12-24 01:20:16 +01:00
|
|
|
function user_is_recently_active(user_id: number): boolean {
|
2020-03-28 01:25:56 +01:00
|
|
|
// return true if the user has a green/orange circle
|
2021-02-28 01:12:35 +01:00
|
|
|
return level(user_id) <= 2;
|
2018-04-20 18:22:28 +02:00
|
|
|
}
|
|
|
|
|
2024-09-25 00:55:31 +02:00
|
|
|
function maybe_shrink_list(
|
|
|
|
user_ids: number[],
|
|
|
|
user_filter_text: string,
|
|
|
|
conversation_participants: Set<number>,
|
|
|
|
): number[] {
|
2021-02-28 01:12:35 +01:00
|
|
|
if (user_ids.length <= max_size_before_shrinking) {
|
2018-04-23 23:13:52 +02:00
|
|
|
return user_ids;
|
|
|
|
}
|
|
|
|
|
2019-08-01 12:49:18 +02:00
|
|
|
if (user_filter_text) {
|
2018-04-23 23:13:52 +02:00
|
|
|
// If the user types something, we want to show all
|
|
|
|
// users matching the text, even if they have not been
|
|
|
|
// online recently.
|
|
|
|
// For super common letters like "s", we may
|
|
|
|
// eventually want to filter down to only users that
|
|
|
|
// are in presence.get_user_ids().
|
2018-04-20 18:22:28 +02:00
|
|
|
return user_ids;
|
|
|
|
}
|
|
|
|
|
2023-08-23 02:18:53 +02:00
|
|
|
// We want to always show PM recipients even if they're inactive.
|
|
|
|
const pm_ids_set = narrow_state.pm_ids_set();
|
2024-09-18 21:45:52 +02:00
|
|
|
const stream_id = narrow_state.stream_id();
|
|
|
|
const filter_by_stream_id =
|
|
|
|
stream_id &&
|
|
|
|
peer_data.get_subscriber_count(stream_id) <= max_channel_size_to_show_all_subscribers;
|
|
|
|
|
2023-08-23 02:18:53 +02:00
|
|
|
user_ids = user_ids.filter(
|
2024-09-18 21:45:52 +02:00
|
|
|
(user_id) =>
|
|
|
|
user_is_recently_active(user_id) ||
|
2024-09-25 00:55:31 +02:00
|
|
|
user_matches_narrow(user_id, pm_ids_set, filter_by_stream_id ? stream_id : undefined) ||
|
|
|
|
conversation_participants.has(user_id),
|
2023-08-23 02:18:53 +02:00
|
|
|
);
|
2018-04-20 18:22:28 +02:00
|
|
|
|
|
|
|
return user_ids;
|
|
|
|
}
|
|
|
|
|
2023-12-24 01:20:16 +01:00
|
|
|
function filter_user_ids(user_filter_text: string, user_ids: number[]): number[] {
|
2021-04-23 16:55:26 +02:00
|
|
|
// This first filter is for whether the user is eligible to be
|
|
|
|
// displayed in the right sidebar at all.
|
|
|
|
user_ids = user_ids.filter((user_id) => {
|
2024-06-05 22:22:17 +02:00
|
|
|
const person = people.maybe_get_user_by_id(user_id, true);
|
2021-04-23 16:55:26 +02:00
|
|
|
|
|
|
|
if (!person) {
|
2024-06-05 22:22:17 +02:00
|
|
|
// See the comments in presence.set_info for details, but this is an expected race.
|
|
|
|
// User IDs for whom we have presence but no user metadata should be skipped.
|
2021-04-23 16:55:26 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-04-23 17:30:00 +02:00
|
|
|
if (person.is_bot) {
|
|
|
|
// Bots should never appear in the right sidebar. This
|
|
|
|
// case should never happen, since bots cannot have
|
|
|
|
// presence data.
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2024-08-09 20:38:24 +02:00
|
|
|
if (!people.is_person_active(user_id)) {
|
|
|
|
// Deactivated users are hidden from the right sidebar entirely.
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-06-27 21:38:26 +02:00
|
|
|
if (muted_users.is_user_muted(user_id)) {
|
2021-04-23 17:30:00 +02:00
|
|
|
// Muted users are hidden from the right sidebar entirely.
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2021-04-23 16:55:26 +02:00
|
|
|
});
|
|
|
|
|
2021-04-23 16:12:16 +02:00
|
|
|
if (!user_filter_text) {
|
2021-04-23 15:54:05 +02:00
|
|
|
return user_ids;
|
|
|
|
}
|
|
|
|
|
2024-10-16 01:38:50 +02:00
|
|
|
// If a query is present in "Filter users", we return matches.
|
2021-04-23 15:54:05 +02:00
|
|
|
const persons = user_ids.map((user_id) => people.get_by_user_id(user_id));
|
2024-05-16 07:41:44 +02:00
|
|
|
return [...people.filter_people_by_search_terms(persons, user_filter_text)];
|
2021-04-23 15:54:05 +02:00
|
|
|
}
|
|
|
|
|
2024-09-25 00:55:31 +02:00
|
|
|
function get_filtered_user_id_list(
|
|
|
|
user_filter_text: string,
|
|
|
|
conversation_participants: Set<number>,
|
|
|
|
): number[] {
|
2024-11-01 00:04:44 +01:00
|
|
|
let base_user_id_list = [];
|
2018-04-19 15:46:56 +02:00
|
|
|
|
2019-08-01 12:49:18 +02:00
|
|
|
if (user_filter_text) {
|
2018-04-19 15:46:56 +02:00
|
|
|
// If there's a filter, select from all users, not just those
|
|
|
|
// recently active.
|
2021-04-23 16:12:16 +02:00
|
|
|
base_user_id_list = people.get_active_user_ids();
|
2018-04-19 15:46:56 +02:00
|
|
|
} else {
|
|
|
|
// From large realms, the user_ids in presence may exclude
|
|
|
|
// users who have been idle more than three weeks. When the
|
|
|
|
// filter text is blank, we show only those recently active users.
|
2021-04-23 16:12:16 +02:00
|
|
|
base_user_id_list = presence.get_user_ids();
|
2021-06-28 13:20:16 +02:00
|
|
|
|
|
|
|
// Always include ourselves, even if we're "unavailable".
|
|
|
|
const my_user_id = people.my_current_user_id();
|
|
|
|
if (!base_user_id_list.includes(my_user_id)) {
|
|
|
|
base_user_id_list = [my_user_id, ...base_user_id_list];
|
|
|
|
}
|
2023-08-23 02:18:53 +02:00
|
|
|
|
|
|
|
// We want to always show PM recipients even if they're inactive.
|
|
|
|
const pm_ids_set = narrow_state.pm_ids_set();
|
|
|
|
if (pm_ids_set.size) {
|
|
|
|
const base_user_id_set = new Set([...base_user_id_list, ...pm_ids_set]);
|
|
|
|
base_user_id_list = [...base_user_id_set];
|
|
|
|
}
|
2024-09-21 23:55:49 +02:00
|
|
|
|
|
|
|
// We want to show subscribers even if they're inactive, if there are few
|
|
|
|
// enough subscribers in the channel.
|
|
|
|
const stream_id = narrow_state.stream_id();
|
|
|
|
if (stream_id) {
|
|
|
|
const subscribers = peer_data.get_subscribers(stream_id);
|
|
|
|
if (subscribers.length <= max_channel_size_to_show_all_subscribers) {
|
|
|
|
const base_user_id_set = new Set([...base_user_id_list, ...subscribers]);
|
|
|
|
base_user_id_list = [...base_user_id_set];
|
|
|
|
}
|
|
|
|
}
|
2018-04-19 15:46:56 +02:00
|
|
|
}
|
|
|
|
|
2021-04-23 16:55:26 +02:00
|
|
|
const user_ids = filter_user_ids(user_filter_text, base_user_id_list);
|
2024-11-01 00:04:44 +01:00
|
|
|
// Make sure all the participants are in the list, even if they're inactive.
|
|
|
|
const user_ids_set = new Set([...user_ids, ...conversation_participants]);
|
|
|
|
return [...user_ids_set];
|
2019-08-01 12:51:20 +02:00
|
|
|
}
|
2018-04-20 18:22:28 +02:00
|
|
|
|
2024-09-25 00:55:31 +02:00
|
|
|
export function get_conversation_participants(): Set<number> {
|
|
|
|
const participant_ids_set = new Set<number>();
|
|
|
|
if (!narrow_state.stream_id() || !narrow_state.topic() || !message_lists.current) {
|
|
|
|
return participant_ids_set;
|
|
|
|
}
|
|
|
|
for (const message of message_lists.current.all_messages()) {
|
|
|
|
if (
|
|
|
|
!people.is_valid_bot_user(message.sender_id) &&
|
|
|
|
people.is_person_active(message.sender_id)
|
|
|
|
) {
|
|
|
|
participant_ids_set.add(message.sender_id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return participant_ids_set;
|
|
|
|
}
|
|
|
|
|
2023-12-24 01:20:16 +01:00
|
|
|
export function get_filtered_and_sorted_user_ids(user_filter_text: string): number[] {
|
2019-08-01 12:51:20 +02:00
|
|
|
let user_ids;
|
2024-09-25 00:55:31 +02:00
|
|
|
const conversation_participants = get_conversation_participants();
|
|
|
|
user_ids = get_filtered_user_id_list(user_filter_text, conversation_participants);
|
|
|
|
user_ids = maybe_shrink_list(user_ids, user_filter_text, conversation_participants);
|
2024-10-02 07:09:29 +02:00
|
|
|
return sort_users(user_ids, conversation_participants);
|
2021-02-28 01:12:35 +01:00
|
|
|
}
|
2018-07-16 15:25:11 +02:00
|
|
|
|
2023-12-24 01:20:16 +01:00
|
|
|
export function matches_filter(user_filter_text: string, user_id: number): boolean {
|
2021-04-23 15:54:05 +02:00
|
|
|
// This is a roundabout way of checking a user if you look
|
|
|
|
// too hard at it, but it should be fine for now.
|
|
|
|
return filter_user_ids(user_filter_text, [user_id]).length === 1;
|
2021-02-28 01:12:35 +01:00
|
|
|
}
|