2021-03-11 05:43:45 +01:00
|
|
|
import $ from "jquery";
|
2021-02-28 01:13:42 +01:00
|
|
|
import _ from "lodash";
|
|
|
|
|
2021-03-16 23:38:59 +01:00
|
|
|
import * as blueslip from "./blueslip";
|
2021-02-28 01:13:42 +01:00
|
|
|
import * as buddy_data from "./buddy_data";
|
|
|
|
import {buddy_list} from "./buddy_list";
|
|
|
|
import * as channel from "./channel";
|
|
|
|
import * as keydown_util from "./keydown_util";
|
|
|
|
import {ListCursor} from "./list_cursor";
|
2021-02-28 21:31:57 +01:00
|
|
|
import * as narrow from "./narrow";
|
2021-03-25 22:35:45 +01:00
|
|
|
import {page_params} from "./page_params";
|
2021-02-28 01:13:42 +01:00
|
|
|
import * as people from "./people";
|
|
|
|
import * as pm_list from "./pm_list";
|
|
|
|
import * as popovers from "./popovers";
|
|
|
|
import * as presence from "./presence";
|
2021-04-15 08:43:09 +02:00
|
|
|
import * as ui_util from "./ui_util";
|
2021-02-28 01:13:42 +01:00
|
|
|
import {UserSearch} from "./user_search";
|
2022-11-15 15:41:56 +01:00
|
|
|
import * as util from "./util";
|
2021-03-22 13:59:50 +01:00
|
|
|
import * as watchdog from "./watchdog";
|
2021-02-28 01:13:42 +01:00
|
|
|
|
|
|
|
export let user_cursor;
|
|
|
|
export let user_filter;
|
2020-08-20 21:24:06 +02:00
|
|
|
|
2013-02-07 19:57:45 +01:00
|
|
|
/*
|
|
|
|
Helpers for detecting user activity and managing user idle states
|
|
|
|
*/
|
|
|
|
|
2013-08-20 20:57:26 +02:00
|
|
|
/* Broadcast "idle" to server after 5 minutes of local inactivity */
|
2019-11-02 00:06:25 +01:00
|
|
|
const DEFAULT_IDLE_TIMEOUT_MS = 5 * 60 * 1000;
|
2013-02-12 20:40:28 +01:00
|
|
|
|
2016-04-03 08:01:10 +02:00
|
|
|
/* Keep in sync with views.py:update_active_status_backend() */
|
2021-02-28 01:13:42 +01:00
|
|
|
export const ACTIVE = "active";
|
|
|
|
|
|
|
|
export const IDLE = "idle";
|
2013-02-07 19:57:45 +01:00
|
|
|
|
2019-06-29 02:52:15 +02:00
|
|
|
// When you open Zulip in a new browser window, client_is_active
|
|
|
|
// should be true. When a server-initiated reload happens, however,
|
|
|
|
// it should be initialized to false. We handle this with a check for
|
|
|
|
// whether the window is focused at initialization time.
|
2021-02-28 01:13:42 +01:00
|
|
|
export let client_is_active = document.hasFocus && document.hasFocus();
|
2019-06-29 02:52:15 +02:00
|
|
|
|
|
|
|
// new_user_input is a more strict version of client_is_active used
|
|
|
|
// primarily for analytics. We initialize this to true, to count new
|
|
|
|
// page loads, but set it to false in the onload function in reload.js
|
|
|
|
// if this was a server-initiated-reload to avoid counting a
|
|
|
|
// server-initiated reload as user activity.
|
2021-02-28 01:13:42 +01:00
|
|
|
export let new_user_input = true;
|
|
|
|
|
|
|
|
export function set_new_user_input(value) {
|
|
|
|
new_user_input = value;
|
|
|
|
}
|
2013-09-06 21:52:12 +02:00
|
|
|
|
2017-05-24 20:00:04 +02:00
|
|
|
function get_pm_list_item(user_id) {
|
2018-04-19 14:17:22 +02:00
|
|
|
return buddy_list.find_li({
|
|
|
|
key: user_id,
|
|
|
|
});
|
2017-03-17 19:38:24 +01:00
|
|
|
}
|
|
|
|
|
2017-05-24 20:00:04 +02:00
|
|
|
function set_pm_count(user_ids_string, count) {
|
2022-01-25 11:36:19 +01:00
|
|
|
const $pm_li = get_pm_list_item(user_ids_string);
|
|
|
|
ui_util.update_unread_count_in_dom($pm_li, count);
|
2017-05-24 20:00:04 +02:00
|
|
|
}
|
|
|
|
|
2021-02-28 01:13:42 +01:00
|
|
|
export function update_dom_with_unread_counts(counts) {
|
2016-11-11 20:48:13 +01:00
|
|
|
// counts is just a data object that gets calculated elsewhere
|
|
|
|
// Our job is to update some DOM elements.
|
|
|
|
|
2020-02-03 09:26:53 +01:00
|
|
|
for (const [user_ids_string, count] of counts.pm_count) {
|
2016-11-16 00:09:09 +01:00
|
|
|
// TODO: just use user_ids_string in our markup
|
2020-07-15 01:29:15 +02:00
|
|
|
const is_pm = !user_ids_string.includes(",");
|
2017-05-24 20:00:04 +02:00
|
|
|
if (is_pm) {
|
|
|
|
set_pm_count(user_ids_string, count);
|
|
|
|
}
|
2020-02-03 09:26:53 +01:00
|
|
|
}
|
2021-02-28 01:13:42 +01:00
|
|
|
}
|
2014-02-27 01:46:17 +01:00
|
|
|
|
2021-05-18 22:47:01 +02:00
|
|
|
export function clear_for_testing() {
|
|
|
|
user_cursor = undefined;
|
|
|
|
user_filter = undefined;
|
|
|
|
client_is_active = false;
|
|
|
|
}
|
|
|
|
|
2021-05-18 21:29:06 +02:00
|
|
|
export function mark_client_idle() {
|
2017-03-06 15:42:58 +01:00
|
|
|
// When we become idle, we don't immediately send anything to the
|
|
|
|
// server; instead, we wait for our next periodic update, since
|
|
|
|
// this data is fundamentally not timely.
|
2021-02-28 01:13:42 +01:00
|
|
|
client_is_active = false;
|
2013-02-07 19:57:45 +01:00
|
|
|
}
|
|
|
|
|
2021-02-28 01:13:42 +01:00
|
|
|
export function redraw_user(user_id) {
|
2017-02-28 23:34:14 +01:00
|
|
|
if (page_params.realm_presence_disabled) {
|
2016-07-27 02:09:10 +02:00
|
|
|
return;
|
2016-01-19 21:03:56 +01:00
|
|
|
}
|
|
|
|
|
2021-02-28 01:13:42 +01:00
|
|
|
const filter_text = get_filter_text();
|
2018-04-19 15:46:56 +02:00
|
|
|
|
|
|
|
if (!buddy_data.matches_filter(filter_text, user_id)) {
|
2017-02-21 00:53:08 +01:00
|
|
|
return;
|
2016-01-19 21:03:56 +01:00
|
|
|
}
|
2017-02-21 00:53:08 +01:00
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const info = buddy_data.get_item(user_id);
|
2018-04-19 15:46:56 +02:00
|
|
|
|
2018-04-19 14:17:22 +02:00
|
|
|
buddy_list.insert_or_move({
|
|
|
|
key: user_id,
|
|
|
|
item: info,
|
|
|
|
});
|
2021-02-28 01:13:42 +01:00
|
|
|
}
|
2017-02-21 00:53:08 +01:00
|
|
|
|
2021-02-28 01:13:42 +01:00
|
|
|
export function searching() {
|
|
|
|
return user_filter && user_filter.searching();
|
|
|
|
}
|
2018-04-19 17:47:41 +02:00
|
|
|
|
2021-02-28 01:13:42 +01:00
|
|
|
export function build_user_sidebar() {
|
2017-02-28 23:34:14 +01:00
|
|
|
if (page_params.realm_presence_disabled) {
|
2020-09-24 07:50:36 +02:00
|
|
|
return undefined;
|
2016-01-19 21:03:56 +01:00
|
|
|
}
|
2013-10-23 19:02:18 +02:00
|
|
|
|
2021-02-28 01:13:42 +01:00
|
|
|
const filter_text = get_filter_text();
|
2017-10-03 03:18:49 +02:00
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const user_ids = buddy_data.get_filtered_and_sorted_user_ids(filter_text);
|
2018-04-19 14:17:22 +02:00
|
|
|
|
2023-03-27 23:29:53 +02:00
|
|
|
buddy_list.populate({keys: user_ids});
|
2017-02-21 00:53:08 +01:00
|
|
|
|
2018-07-14 14:06:30 +02:00
|
|
|
return user_ids; // for testing
|
2021-02-28 01:13:42 +01:00
|
|
|
}
|
2014-01-13 19:34:24 +01:00
|
|
|
|
2018-04-21 14:59:03 +02:00
|
|
|
function do_update_users_for_search() {
|
2019-02-08 14:37:04 +01:00
|
|
|
// Hide all the popovers but not userlist sidebar
|
|
|
|
// when the user is searching.
|
2019-07-10 08:03:41 +02:00
|
|
|
popovers.hide_all_except_sidebars();
|
2021-02-28 01:13:42 +01:00
|
|
|
build_user_sidebar();
|
|
|
|
user_cursor.reset();
|
2018-04-21 14:59:03 +02:00
|
|
|
}
|
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const update_users_for_search = _.throttle(do_update_users_for_search, 50);
|
2014-01-13 19:34:24 +01:00
|
|
|
|
2021-02-28 01:13:42 +01:00
|
|
|
export function compute_active_status() {
|
2019-03-29 00:13:07 +01:00
|
|
|
// The overall algorithm intent for the `status` field is to send
|
|
|
|
// `ACTIVE` (aka green circle) if we know the user is at their
|
|
|
|
// computer, and IDLE (aka orange circle) if the user might not
|
|
|
|
// be:
|
|
|
|
//
|
2021-05-14 00:16:30 +02:00
|
|
|
// * For the web app, we just know whether this window has focus.
|
2019-03-29 00:13:07 +01:00
|
|
|
// * For the electron desktop app, we also know whether the
|
|
|
|
// user is active or idle elsewhere on their system.
|
|
|
|
//
|
2020-04-26 23:13:25 +02:00
|
|
|
// The check for `get_idle_on_system === undefined` is feature
|
2019-03-29 00:13:07 +01:00
|
|
|
// detection; older desktop app releases never set that property.
|
2020-07-15 00:34:28 +02:00
|
|
|
if (
|
|
|
|
window.electron_bridge !== undefined &&
|
|
|
|
window.electron_bridge.get_idle_on_system !== undefined
|
|
|
|
) {
|
2020-04-26 23:13:25 +02:00
|
|
|
if (window.electron_bridge.get_idle_on_system()) {
|
2021-02-28 01:13:42 +01:00
|
|
|
return IDLE;
|
2019-03-29 00:13:07 +01:00
|
|
|
}
|
2021-02-28 01:13:42 +01:00
|
|
|
return ACTIVE;
|
2019-03-29 00:13:07 +01:00
|
|
|
}
|
|
|
|
|
2021-02-28 01:13:42 +01:00
|
|
|
if (client_is_active) {
|
|
|
|
return ACTIVE;
|
2019-03-29 00:13:07 +01:00
|
|
|
}
|
2021-02-28 01:13:42 +01:00
|
|
|
return IDLE;
|
|
|
|
}
|
2019-03-29 00:13:07 +01:00
|
|
|
|
2021-02-28 01:13:42 +01:00
|
|
|
export function send_presence_to_server(want_redraw) {
|
2020-02-13 21:22:05 +01:00
|
|
|
// Zulip has 2 data feeds coming from the server to the client:
|
|
|
|
// The server_events data, and this presence feed. Data from
|
|
|
|
// server_events is nicely serialized, but if we've been offline
|
|
|
|
// and not running for a while (e.g. due to suspend), we can end
|
|
|
|
// up with inconsistent state where users appear in presence that
|
|
|
|
// don't appear in people.js. We handle this in 2 stages. First,
|
|
|
|
// here, we trigger an extra run of the clock-jump check that
|
|
|
|
// detects whether this device just resumed from suspend. This
|
2021-03-23 14:24:49 +01:00
|
|
|
// ensures that watchdog.suspect_offline is always up-to-date
|
2020-02-13 21:22:05 +01:00
|
|
|
// before we initiate a presence request.
|
|
|
|
//
|
|
|
|
// If we did just resume, it will also trigger an immediate
|
|
|
|
// server_events request to the server (the success handler to
|
|
|
|
// which will clear suspect_offline and potentially trigger a
|
|
|
|
// reload if the device was offline for more than
|
|
|
|
// DEFAULT_EVENT_QUEUE_TIMEOUT_SECS).
|
2021-06-15 18:03:32 +02:00
|
|
|
if (page_params.is_spectator) {
|
2020-09-24 07:50:36 +02:00
|
|
|
return;
|
2020-08-31 09:35:39 +02:00
|
|
|
}
|
|
|
|
|
2021-03-22 13:59:50 +01:00
|
|
|
watchdog.check_for_unsuspend();
|
2020-02-13 21:22:05 +01:00
|
|
|
|
2014-01-07 23:41:55 +01:00
|
|
|
channel.post({
|
2020-07-15 01:29:15 +02:00
|
|
|
url: "/json/users/me/presence",
|
2019-06-29 02:55:26 +02:00
|
|
|
data: {
|
2021-02-28 01:13:42 +01:00
|
|
|
status: compute_active_status(),
|
2019-06-29 02:55:26 +02:00
|
|
|
ping_only: !want_redraw,
|
2021-02-28 01:13:42 +01:00
|
|
|
new_user_input,
|
2020-02-02 17:29:05 +01:00
|
|
|
slim_presence: true,
|
2019-06-29 02:55:26 +02:00
|
|
|
},
|
2020-07-20 22:18:43 +02:00
|
|
|
success(data) {
|
2014-01-07 23:41:55 +01:00
|
|
|
// Update Zephyr mirror activity warning
|
|
|
|
if (data.zephyr_mirror_active === false) {
|
2020-07-15 01:29:15 +02:00
|
|
|
$("#zephyr-mirror-error").addClass("show");
|
2014-01-07 23:41:55 +01:00
|
|
|
} else {
|
2020-07-15 01:29:15 +02:00
|
|
|
$("#zephyr-mirror-error").removeClass("show");
|
2014-01-07 23:41:55 +01:00
|
|
|
}
|
2013-06-12 19:58:25 +02:00
|
|
|
|
2021-02-28 01:13:42 +01:00
|
|
|
new_user_input = false;
|
2013-02-07 19:57:45 +01:00
|
|
|
|
2017-03-06 15:24:43 +01:00
|
|
|
if (want_redraw) {
|
2017-03-30 20:04:01 +02:00
|
|
|
presence.set_info(data.presences, data.server_timestamp);
|
2021-02-28 01:13:42 +01:00
|
|
|
redraw();
|
2017-03-06 15:24:43 +01:00
|
|
|
}
|
2017-01-12 00:17:43 +01:00
|
|
|
},
|
2013-02-07 19:57:45 +01:00
|
|
|
});
|
2021-02-28 01:13:42 +01:00
|
|
|
}
|
2013-02-07 19:57:45 +01:00
|
|
|
|
2021-05-18 21:29:06 +02:00
|
|
|
export function mark_client_active() {
|
|
|
|
// exported for testing
|
2021-02-28 01:13:42 +01:00
|
|
|
if (!client_is_active) {
|
|
|
|
client_is_active = true;
|
|
|
|
send_presence_to_server(false);
|
2013-02-07 19:57:45 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-28 01:13:42 +01:00
|
|
|
export function initialize() {
|
2020-07-02 01:45:54 +02:00
|
|
|
$("html").on("mousemove", () => {
|
2021-02-28 01:13:42 +01:00
|
|
|
new_user_input = true;
|
2017-05-24 22:32:17 +02:00
|
|
|
});
|
|
|
|
|
2020-07-20 21:26:58 +02:00
|
|
|
$(window).on("focus", mark_client_active);
|
2020-07-15 00:34:28 +02:00
|
|
|
$(window).idle({
|
|
|
|
idle: DEFAULT_IDLE_TIMEOUT_MS,
|
|
|
|
onIdle: mark_client_idle,
|
|
|
|
onActive: mark_client_active,
|
|
|
|
keepTracking: true,
|
|
|
|
});
|
2013-02-07 19:57:45 +01:00
|
|
|
|
2021-02-28 01:13:42 +01:00
|
|
|
set_cursor_and_filter();
|
2017-10-07 18:57:44 +02:00
|
|
|
|
2021-02-28 01:13:42 +01:00
|
|
|
build_user_sidebar();
|
2017-03-06 15:24:43 +01:00
|
|
|
|
2018-07-16 15:16:33 +02:00
|
|
|
buddy_list.start_scroll_handler();
|
|
|
|
|
2017-03-06 15:24:43 +01:00
|
|
|
function get_full_presence_list_update() {
|
2021-02-28 01:13:42 +01:00
|
|
|
send_presence_to_server(true);
|
2017-03-06 15:24:43 +01:00
|
|
|
}
|
|
|
|
|
2023-02-21 11:52:30 +01:00
|
|
|
/* Time between keep-alive pings */
|
|
|
|
const active_ping_interval_ms = page_params.server_presence_ping_interval_seconds * 1000;
|
|
|
|
util.call_function_periodically(get_full_presence_list_update, active_ping_interval_ms);
|
2022-11-09 11:09:28 +01:00
|
|
|
|
|
|
|
// Let the server know we're here, but pass "false" for
|
|
|
|
// want_redraw, since we just got all this info in page_params.
|
|
|
|
send_presence_to_server(false);
|
2021-02-28 01:13:42 +01:00
|
|
|
}
|
2013-04-03 22:00:02 +02:00
|
|
|
|
2021-02-28 01:13:42 +01:00
|
|
|
export function update_presence_info(user_id, info, server_time) {
|
2020-02-07 17:19:03 +01:00
|
|
|
presence.update_info_from_event(user_id, info, server_time);
|
2021-02-28 01:13:42 +01:00
|
|
|
redraw_user(user_id);
|
2019-02-18 16:32:27 +01:00
|
|
|
pm_list.update_private_messages();
|
2021-02-28 01:13:42 +01:00
|
|
|
}
|
2013-02-07 19:57:45 +01:00
|
|
|
|
2021-02-28 01:13:42 +01:00
|
|
|
export function redraw() {
|
|
|
|
build_user_sidebar();
|
|
|
|
user_cursor.redraw();
|
2019-02-18 16:32:27 +01:00
|
|
|
pm_list.update_private_messages();
|
2021-02-28 01:13:42 +01:00
|
|
|
}
|
2017-01-04 23:54:59 +01:00
|
|
|
|
2021-02-28 01:13:42 +01:00
|
|
|
export function reset_users() {
|
2018-04-21 14:59:03 +02:00
|
|
|
// Call this when we're leaving the search widget.
|
2021-02-28 01:13:42 +01:00
|
|
|
build_user_sidebar();
|
|
|
|
user_cursor.clear();
|
|
|
|
}
|
2014-01-14 17:57:34 +01:00
|
|
|
|
2021-02-28 01:13:42 +01:00
|
|
|
export function narrow_for_user(opts) {
|
2022-01-25 11:36:19 +01:00
|
|
|
const user_id = buddy_list.get_key_from_li({$li: opts.$li});
|
2021-02-28 01:13:42 +01:00
|
|
|
return narrow_for_user_id({user_id});
|
|
|
|
}
|
2018-04-21 14:59:03 +02:00
|
|
|
|
2021-02-28 01:13:42 +01:00
|
|
|
export function narrow_for_user_id(opts) {
|
2020-02-05 14:30:59 +01:00
|
|
|
const person = people.get_by_user_id(opts.user_id);
|
2019-11-02 00:06:25 +01:00
|
|
|
const email = person.email;
|
2018-04-19 23:14:58 +02:00
|
|
|
|
2023-04-11 21:04:33 +02:00
|
|
|
narrow.by("dm", email, {trigger: "sidebar"});
|
2021-02-28 01:13:42 +01:00
|
|
|
user_filter.clear_and_hide_search();
|
|
|
|
}
|
2018-04-19 23:14:58 +02:00
|
|
|
|
2018-02-12 23:31:17 +01:00
|
|
|
function keydown_enter_key() {
|
2021-02-28 01:13:42 +01:00
|
|
|
const user_id = user_cursor.get_key();
|
2018-04-21 14:59:03 +02:00
|
|
|
if (user_id === undefined) {
|
|
|
|
return;
|
2014-02-13 23:08:41 +01:00
|
|
|
}
|
|
|
|
|
2021-02-28 01:13:42 +01:00
|
|
|
narrow_for_user_id({user_id});
|
2018-04-21 14:59:03 +02:00
|
|
|
popovers.hide_all();
|
2018-02-12 23:31:17 +01:00
|
|
|
}
|
|
|
|
|
2021-02-28 01:13:42 +01:00
|
|
|
export function set_cursor_and_filter() {
|
|
|
|
user_cursor = new ListCursor({
|
2018-04-21 14:59:03 +02:00
|
|
|
list: buddy_list,
|
2020-07-15 01:29:15 +02:00
|
|
|
highlight_class: "highlighted_user",
|
2018-04-21 14:59:03 +02:00
|
|
|
});
|
2018-02-12 23:31:17 +01:00
|
|
|
|
2021-02-28 01:13:42 +01:00
|
|
|
user_filter = new UserSearch({
|
2018-04-19 17:47:41 +02:00
|
|
|
update_list: update_users_for_search,
|
2021-02-28 01:13:42 +01:00
|
|
|
reset_items: reset_users,
|
|
|
|
on_focus: () => user_cursor.reset(),
|
2018-04-19 17:47:41 +02:00
|
|
|
});
|
|
|
|
|
2021-02-28 01:13:42 +01:00
|
|
|
const $input = user_filter.input_field();
|
2018-04-21 14:59:03 +02:00
|
|
|
|
2021-02-28 01:13:42 +01:00
|
|
|
$input.on("blur", () => user_cursor.clear());
|
2018-04-21 14:59:03 +02:00
|
|
|
|
|
|
|
keydown_util.handle({
|
2022-01-25 11:36:19 +01:00
|
|
|
$elem: $input,
|
2018-04-21 14:59:03 +02:00
|
|
|
handlers: {
|
2021-05-26 19:51:07 +02:00
|
|
|
Enter() {
|
2018-04-21 14:59:03 +02:00
|
|
|
keydown_enter_key();
|
|
|
|
return true;
|
|
|
|
},
|
2021-05-26 19:51:07 +02:00
|
|
|
ArrowUp() {
|
2021-02-28 01:13:42 +01:00
|
|
|
user_cursor.prev();
|
2018-04-21 14:59:03 +02:00
|
|
|
return true;
|
|
|
|
},
|
2021-05-26 19:51:07 +02:00
|
|
|
ArrowDown() {
|
2021-02-28 01:13:42 +01:00
|
|
|
user_cursor.next();
|
2018-04-21 14:59:03 +02:00
|
|
|
return true;
|
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
2021-02-28 01:13:42 +01:00
|
|
|
}
|
2014-01-13 19:34:24 +01:00
|
|
|
|
2021-02-28 01:13:42 +01:00
|
|
|
export function initiate_search() {
|
|
|
|
if (user_filter) {
|
|
|
|
user_filter.initiate_search();
|
2018-04-19 17:47:41 +02:00
|
|
|
}
|
2021-02-28 01:13:42 +01:00
|
|
|
}
|
2018-04-19 17:47:41 +02:00
|
|
|
|
2021-02-28 01:13:42 +01:00
|
|
|
export function escape_search() {
|
|
|
|
if (user_filter) {
|
|
|
|
user_filter.escape_search();
|
2018-04-19 17:47:41 +02:00
|
|
|
}
|
2021-02-28 01:13:42 +01:00
|
|
|
}
|
2018-04-19 17:47:41 +02:00
|
|
|
|
2021-02-28 01:13:42 +01:00
|
|
|
export function get_filter_text() {
|
|
|
|
if (!user_filter) {
|
2017-10-07 19:55:44 +02:00
|
|
|
// This may be overly defensive, but there may be
|
|
|
|
// situations where get called before everything is
|
2017-11-09 16:26:38 +01:00
|
|
|
// fully initialized. The empty string is a fine
|
2017-10-07 19:55:44 +02:00
|
|
|
// default here.
|
2020-07-15 01:29:15 +02:00
|
|
|
blueslip.warn("get_filter_text() is called before initialization");
|
|
|
|
return "";
|
2017-10-07 19:55:44 +02:00
|
|
|
}
|
|
|
|
|
2021-02-28 01:13:42 +01:00
|
|
|
return user_filter.text();
|
|
|
|
}
|