mirror of https://github.com/zulip/zulip.git
drafts: Convert module to typescript.
This commit is contained in:
parent
e26b4bbd3e
commit
c60cc07605
|
@ -96,7 +96,7 @@ EXEMPT_FILES = make_set(
|
||||||
"web/src/desktop_integration.js",
|
"web/src/desktop_integration.js",
|
||||||
"web/src/desktop_notifications.js",
|
"web/src/desktop_notifications.js",
|
||||||
"web/src/dialog_widget.ts",
|
"web/src/dialog_widget.ts",
|
||||||
"web/src/drafts.js",
|
"web/src/drafts.ts",
|
||||||
"web/src/drafts_overlay_ui.js",
|
"web/src/drafts_overlay_ui.js",
|
||||||
"web/src/dropdown_widget.ts",
|
"web/src/dropdown_widget.ts",
|
||||||
"web/src/echo.js",
|
"web/src/echo.js",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import {subDays} from "date-fns";
|
import {subDays} from "date-fns";
|
||||||
import Handlebars from "handlebars/runtime";
|
|
||||||
import $ from "jquery";
|
import $ from "jquery";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
|
import assert from "minimalistic-assert";
|
||||||
import tippy from "tippy.js";
|
import tippy from "tippy.js";
|
||||||
import {z} from "zod";
|
import {z} from "zod";
|
||||||
|
|
||||||
|
@ -22,15 +22,40 @@ import * as timerender from "./timerender";
|
||||||
import * as ui_util from "./ui_util";
|
import * as ui_util from "./ui_util";
|
||||||
import * as util from "./util";
|
import * as util from "./util";
|
||||||
|
|
||||||
export function set_count(count) {
|
export function set_count(count: number): void {
|
||||||
const $drafts_li = $(".top_left_drafts");
|
const $drafts_li = $(".top_left_drafts");
|
||||||
ui_util.update_unread_count_in_dom($drafts_li, count);
|
ui_util.update_unread_count_in_dom($drafts_li, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTimestamp() {
|
function getTimestamp(): number {
|
||||||
return Date.now();
|
return Date.now();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const draft_schema = z.intersection(
|
||||||
|
z.object({
|
||||||
|
content: z.string(),
|
||||||
|
updatedAt: z.number(),
|
||||||
|
}),
|
||||||
|
z.union([
|
||||||
|
z.object({
|
||||||
|
type: z.literal("stream"),
|
||||||
|
topic: z.string(),
|
||||||
|
stream_id: z.number().optional(),
|
||||||
|
}),
|
||||||
|
z.object({
|
||||||
|
type: z.literal("private"),
|
||||||
|
reply_to: z.string(),
|
||||||
|
private_message_recipient: z.string(),
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
|
type LocalStorageDraft = z.infer<typeof draft_schema>;
|
||||||
|
|
||||||
|
// The id is added to the draft in format_drafts in drafts_overlay_ui.
|
||||||
|
// We should probably just include it in the draft object itself always?
|
||||||
|
type LocalStorageDraftWithId = LocalStorageDraft & {id: string};
|
||||||
|
|
||||||
const possibly_buggy_draft_schema = z.intersection(
|
const possibly_buggy_draft_schema = z.intersection(
|
||||||
z.object({
|
z.object({
|
||||||
content: z.string(),
|
content: z.string(),
|
||||||
|
@ -51,33 +76,33 @@ const possibly_buggy_draft_schema = z.intersection(
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const drafts_schema = z.record(z.string(), draft_schema);
|
||||||
const possibly_buggy_drafts_schema = z.record(z.string(), possibly_buggy_draft_schema);
|
const possibly_buggy_drafts_schema = z.record(z.string(), possibly_buggy_draft_schema);
|
||||||
|
|
||||||
export const draft_model = (function () {
|
export const draft_model = (function () {
|
||||||
const exports = {};
|
|
||||||
|
|
||||||
// the key that the drafts are stored under.
|
// the key that the drafts are stored under.
|
||||||
const KEY = "drafts";
|
const KEY = "drafts";
|
||||||
const ls = localstorage();
|
const ls = localstorage();
|
||||||
let fixed_buggy_drafts = false;
|
let fixed_buggy_drafts = false;
|
||||||
|
|
||||||
function get() {
|
function get(): Record<string, LocalStorageDraft> {
|
||||||
const drafts = ls.get(KEY);
|
let drafts = ls.get(KEY);
|
||||||
if (ls.get(KEY) === undefined) {
|
if (drafts === undefined) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fixed_buggy_drafts) {
|
if (!fixed_buggy_drafts) {
|
||||||
fix_buggy_drafts();
|
fix_buggy_drafts();
|
||||||
return ls.get(KEY);
|
drafts = ls.get(KEY);
|
||||||
}
|
}
|
||||||
return drafts;
|
|
||||||
}
|
|
||||||
exports.get = get;
|
|
||||||
|
|
||||||
function fix_buggy_drafts() {
|
return drafts_schema.parse(drafts);
|
||||||
|
}
|
||||||
|
|
||||||
|
function fix_buggy_drafts(): void {
|
||||||
const drafts = ls.get(KEY);
|
const drafts = ls.get(KEY);
|
||||||
const parsed_drafts = possibly_buggy_drafts_schema.parse(drafts);
|
const parsed_drafts = possibly_buggy_drafts_schema.parse(drafts);
|
||||||
const valid_drafts = {};
|
const valid_drafts: Record<string, LocalStorageDraft> = {};
|
||||||
for (const [draft_id, draft] of Object.entries(parsed_drafts)) {
|
for (const [draft_id, draft] of Object.entries(parsed_drafts)) {
|
||||||
if (draft.type !== "stream") {
|
if (draft.type !== "stream") {
|
||||||
valid_drafts[draft_id] = draft;
|
valid_drafts[draft_id] = draft;
|
||||||
|
@ -119,23 +144,23 @@ export const draft_model = (function () {
|
||||||
fixed_buggy_drafts = true;
|
fixed_buggy_drafts = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.getDraft = function (id) {
|
function getDraft(id: string): LocalStorageDraft | false {
|
||||||
return get()[id] || false;
|
return get()[id] || false;
|
||||||
};
|
}
|
||||||
|
|
||||||
exports.getDraftCount = function () {
|
function getDraftCount(): number {
|
||||||
const drafts = get();
|
const drafts = get();
|
||||||
return Object.keys(drafts).length;
|
return Object.keys(drafts).length;
|
||||||
};
|
}
|
||||||
|
|
||||||
function save(drafts, update_count = true) {
|
function save(drafts: Record<string, LocalStorageDraft>, update_count = true): void {
|
||||||
ls.set(KEY, drafts);
|
ls.set(KEY, drafts);
|
||||||
if (update_count) {
|
if (update_count) {
|
||||||
set_count(Object.keys(drafts).length);
|
set_count(Object.keys(drafts).length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.addDraft = function (draft, update_count = true) {
|
function addDraft(draft: LocalStorageDraft, update_count = true): string {
|
||||||
const drafts = get();
|
const drafts = get();
|
||||||
|
|
||||||
// use the base16 of the current time + a random string to reduce
|
// use the base16 of the current time + a random string to reduce
|
||||||
|
@ -146,13 +171,13 @@ export const draft_model = (function () {
|
||||||
save(drafts, update_count);
|
save(drafts, update_count);
|
||||||
|
|
||||||
return id;
|
return id;
|
||||||
};
|
}
|
||||||
|
|
||||||
exports.editDraft = function (id, draft) {
|
function editDraft(id: string, draft: LocalStorageDraft): boolean {
|
||||||
const drafts = get();
|
const drafts = get();
|
||||||
let changed = false;
|
let changed = false;
|
||||||
|
|
||||||
function check_if_equal(draft_a, draft_b) {
|
function check_if_equal(draft_a: LocalStorageDraft, draft_b: LocalStorageDraft): boolean {
|
||||||
return _.isEqual(_.omit(draft_a, ["updatedAt"]), _.omit(draft_b, ["updatedAt"]));
|
return _.isEqual(_.omit(draft_a, ["updatedAt"]), _.omit(draft_b, ["updatedAt"]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,31 +187,40 @@ export const draft_model = (function () {
|
||||||
save(drafts);
|
save(drafts);
|
||||||
}
|
}
|
||||||
return changed;
|
return changed;
|
||||||
};
|
}
|
||||||
|
|
||||||
exports.deleteDraft = function (id) {
|
function deleteDraft(id: string): void {
|
||||||
const drafts = get();
|
const drafts = get();
|
||||||
|
|
||||||
|
// TODO(typescript) rework this to store the draft data in a map.
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||||
delete drafts[id];
|
delete drafts[id];
|
||||||
save(drafts);
|
save(drafts);
|
||||||
};
|
}
|
||||||
|
|
||||||
return exports;
|
return {
|
||||||
|
get,
|
||||||
|
getDraft,
|
||||||
|
getDraftCount,
|
||||||
|
addDraft,
|
||||||
|
editDraft,
|
||||||
|
deleteDraft,
|
||||||
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
export function sync_count() {
|
export function sync_count(): void {
|
||||||
const drafts = draft_model.get();
|
const drafts = draft_model.get();
|
||||||
set_count(Object.keys(drafts).length);
|
set_count(Object.keys(drafts).length);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function delete_all_drafts() {
|
export function delete_all_drafts(): void {
|
||||||
const drafts = draft_model.get();
|
const drafts = draft_model.get();
|
||||||
for (const [id] of Object.entries(drafts)) {
|
for (const [id] of Object.entries(drafts)) {
|
||||||
draft_model.deleteDraft(id);
|
draft_model.deleteDraft(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function confirm_delete_all_drafts() {
|
export function confirm_delete_all_drafts(): void {
|
||||||
const html_body = render_confirm_delete_all_drafts();
|
const html_body = render_confirm_delete_all_drafts();
|
||||||
|
|
||||||
confirm_dialog.launch({
|
confirm_dialog.launch({
|
||||||
|
@ -196,11 +230,24 @@ export function confirm_delete_all_drafts() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function rename_stream_recipient(old_stream_id, old_topic, new_stream_id, new_topic) {
|
export function rename_stream_recipient(
|
||||||
|
old_stream_id: number,
|
||||||
|
old_topic: string,
|
||||||
|
new_stream_id: number,
|
||||||
|
new_topic: string,
|
||||||
|
): void {
|
||||||
const current_drafts = draft_model.get();
|
const current_drafts = draft_model.get();
|
||||||
for (const draft_id of Object.keys(current_drafts)) {
|
for (const draft_id of Object.keys(current_drafts)) {
|
||||||
const draft = current_drafts[draft_id];
|
const draft = current_drafts[draft_id];
|
||||||
if (util.same_stream_and_topic(draft, {stream_id: old_stream_id, topic: old_topic})) {
|
if (draft.type !== "stream" || draft.stream_id === undefined) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
util.same_stream_and_topic(
|
||||||
|
{stream_id: draft.stream_id, topic: draft.topic},
|
||||||
|
{stream_id: old_stream_id, topic: old_topic},
|
||||||
|
)
|
||||||
|
) {
|
||||||
// If new_stream_id is undefined, that means the stream wasn't updated.
|
// If new_stream_id is undefined, that means the stream wasn't updated.
|
||||||
if (new_stream_id !== undefined) {
|
if (new_stream_id !== undefined) {
|
||||||
draft.stream_id = new_stream_id;
|
draft.stream_id = new_stream_id;
|
||||||
|
@ -214,7 +261,7 @@ export function rename_stream_recipient(old_stream_id, old_topic, new_stream_id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function snapshot_message() {
|
export function snapshot_message(): LocalStorageDraft | undefined {
|
||||||
if (!compose_state.composing() || compose_state.message_content().length <= 2) {
|
if (!compose_state.composing() || compose_state.message_content().length <= 2) {
|
||||||
// If you aren't in the middle of composing the body of a
|
// If you aren't in the middle of composing the body of a
|
||||||
// message or the message is shorter than 2 characters long, don't try to snapshot.
|
// message or the message is shorter than 2 characters long, don't try to snapshot.
|
||||||
|
@ -229,44 +276,61 @@ export function snapshot_message() {
|
||||||
};
|
};
|
||||||
if (message.type === "private") {
|
if (message.type === "private") {
|
||||||
const recipient = compose_state.private_message_recipient();
|
const recipient = compose_state.private_message_recipient();
|
||||||
message.reply_to = recipient;
|
return {
|
||||||
message.private_message_recipient = recipient;
|
...message,
|
||||||
} else {
|
type: "private",
|
||||||
message.stream_id = compose_state.stream_id();
|
reply_to: recipient,
|
||||||
message.topic = compose_state.topic();
|
private_message_recipient: recipient,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
return message;
|
assert(message.type === "stream");
|
||||||
|
return {
|
||||||
|
...message,
|
||||||
|
type: "stream",
|
||||||
|
stream_id: compose_state.stream_id(),
|
||||||
|
topic: compose_state.topic(),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function restore_message(draft) {
|
type ComposeArguments =
|
||||||
|
| {
|
||||||
|
type: "stream";
|
||||||
|
stream_id: number | undefined;
|
||||||
|
topic: string;
|
||||||
|
content: string;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: "private";
|
||||||
|
private_message_recipient: string;
|
||||||
|
content: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function restore_message(draft: LocalStorageDraft): ComposeArguments {
|
||||||
// This is kinda the inverse of snapshot_message, and
|
// This is kinda the inverse of snapshot_message, and
|
||||||
// we are essentially making a deep copy of the draft,
|
// we are essentially making a deep copy of the draft,
|
||||||
// being explicit about which fields we send to the compose
|
// being explicit about which fields we send to the compose
|
||||||
// system.
|
// system.
|
||||||
let compose_args;
|
|
||||||
|
|
||||||
if (draft.type === "stream") {
|
if (draft.type === "stream") {
|
||||||
compose_args = {
|
return {
|
||||||
type: "stream",
|
type: "stream",
|
||||||
stream_id: draft.stream_id,
|
stream_id: draft.stream_id,
|
||||||
topic: draft.topic,
|
topic: draft.topic,
|
||||||
content: draft.content,
|
content: draft.content,
|
||||||
};
|
};
|
||||||
} else {
|
|
||||||
const recipient_emails = draft.private_message_recipient
|
|
||||||
.split(",")
|
|
||||||
.filter((email) => people.is_valid_email_for_compose(email));
|
|
||||||
compose_args = {
|
|
||||||
type: draft.type,
|
|
||||||
private_message_recipient: recipient_emails.join(","),
|
|
||||||
content: draft.content,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return compose_args;
|
const recipient_emails = draft.private_message_recipient
|
||||||
|
.split(",")
|
||||||
|
.filter((email) => people.is_valid_email_for_compose(email));
|
||||||
|
return {
|
||||||
|
type: "private",
|
||||||
|
private_message_recipient: recipient_emails.join(","),
|
||||||
|
content: draft.content,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function draft_notify() {
|
function draft_notify(): void {
|
||||||
// Display a tooltip to notify the user about the saved draft.
|
// Display a tooltip to notify the user about the saved draft.
|
||||||
const instance = tippy(".top_left_drafts .unread_count", {
|
const instance = tippy(".top_left_drafts .unread_count", {
|
||||||
content: $t({defaultMessage: "Saved as draft"}),
|
content: $t({defaultMessage: "Saved as draft"}),
|
||||||
|
@ -274,20 +338,25 @@ function draft_notify() {
|
||||||
placement: "right",
|
placement: "right",
|
||||||
})[0];
|
})[0];
|
||||||
instance.show();
|
instance.show();
|
||||||
function remove_instance() {
|
function remove_instance(): void {
|
||||||
instance.destroy();
|
instance.destroy();
|
||||||
}
|
}
|
||||||
setTimeout(remove_instance, 3000);
|
setTimeout(remove_instance, 3000);
|
||||||
}
|
}
|
||||||
|
|
||||||
function maybe_notify(no_notify) {
|
function maybe_notify(no_notify: boolean): void {
|
||||||
if (!no_notify) {
|
if (!no_notify) {
|
||||||
draft_notify();
|
draft_notify();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function update_draft(opts = {}) {
|
type UpdateDraftOptions = {
|
||||||
const no_notify = opts.no_notify || false;
|
no_notify?: boolean;
|
||||||
|
update_count?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function update_draft(opts: UpdateDraftOptions = {}): string | undefined {
|
||||||
|
const no_notify = opts.no_notify ?? false;
|
||||||
const draft = snapshot_message();
|
const draft = snapshot_message();
|
||||||
|
|
||||||
if (draft === undefined) {
|
if (draft === undefined) {
|
||||||
|
@ -312,7 +381,7 @@ export function update_draft(opts = {}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// We have never saved a draft for this message, so add one.
|
// We have never saved a draft for this message, so add one.
|
||||||
const update_count = opts.update_count === undefined ? true : opts.update_count;
|
const update_count = opts.update_count ?? true;
|
||||||
const new_draft_id = draft_model.addDraft(draft, update_count);
|
const new_draft_id = draft_model.addDraft(draft, update_count);
|
||||||
$("textarea#compose-textarea").data("draft-id", new_draft_id);
|
$("textarea#compose-textarea").data("draft-id", new_draft_id);
|
||||||
maybe_notify(no_notify);
|
maybe_notify(no_notify);
|
||||||
|
@ -322,7 +391,11 @@ export function update_draft(opts = {}) {
|
||||||
|
|
||||||
export const DRAFT_LIFETIME = 30;
|
export const DRAFT_LIFETIME = 30;
|
||||||
|
|
||||||
export function current_recipient_data() {
|
export function current_recipient_data(): {
|
||||||
|
stream_name: string | undefined;
|
||||||
|
topic: string | undefined;
|
||||||
|
private_recipients: string | undefined;
|
||||||
|
} {
|
||||||
// Prioritize recipients from the compose box first. If the compose
|
// Prioritize recipients from the compose box first. If the compose
|
||||||
// box isn't open, just return data from the current narrow.
|
// box isn't open, just return data from the current narrow.
|
||||||
if (!compose_state.composing()) {
|
if (!compose_state.composing()) {
|
||||||
|
@ -355,7 +428,9 @@ export function current_recipient_data() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function filter_drafts_by_compose_box_and_recipient(drafts) {
|
export function filter_drafts_by_compose_box_and_recipient(
|
||||||
|
drafts: Record<string, LocalStorageDraft>,
|
||||||
|
): Record<string, LocalStorageDraft> {
|
||||||
const {stream_name, topic, private_recipients} = current_recipient_data();
|
const {stream_name, topic, private_recipients} = current_recipient_data();
|
||||||
const stream_id = stream_name ? stream_data.get_stream_id(stream_name) : undefined;
|
const stream_id = stream_name ? stream_data.get_stream_id(stream_name) : undefined;
|
||||||
const narrow_drafts_ids = [];
|
const narrow_drafts_ids = [];
|
||||||
|
@ -364,8 +439,13 @@ export function filter_drafts_by_compose_box_and_recipient(drafts) {
|
||||||
if (
|
if (
|
||||||
stream_id &&
|
stream_id &&
|
||||||
topic &&
|
topic &&
|
||||||
|
draft.type === "stream" &&
|
||||||
draft.topic &&
|
draft.topic &&
|
||||||
util.same_recipient(draft, {type: "stream", stream_id, topic})
|
draft.stream_id !== undefined &&
|
||||||
|
util.same_recipient(
|
||||||
|
{type: "stream", stream_id: draft.stream_id, topic: draft.topic},
|
||||||
|
{type: "stream", stream_id, topic},
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
narrow_drafts_ids.push(id);
|
narrow_drafts_ids.push(id);
|
||||||
}
|
}
|
||||||
|
@ -394,17 +474,39 @@ export function filter_drafts_by_compose_box_and_recipient(drafts) {
|
||||||
return _.pick(drafts, narrow_drafts_ids);
|
return _.pick(drafts, narrow_drafts_ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function remove_old_drafts() {
|
export function remove_old_drafts(): void {
|
||||||
const old_date = subDays(new Date(), DRAFT_LIFETIME).getTime();
|
const old_date = subDays(new Date(), DRAFT_LIFETIME).getTime();
|
||||||
const drafts = draft_model.get();
|
const drafts = draft_model.get();
|
||||||
for (const [id, draft] of Object.entries(drafts)) {
|
for (const [id, draft] of Object.entries(drafts)) {
|
||||||
if (draft.updatedAt < old_date) {
|
if (draft.updatedAt !== undefined && draft.updatedAt < old_date) {
|
||||||
draft_model.deleteDraft(id);
|
draft_model.deleteDraft(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function format_draft(draft) {
|
type FormattedDraft =
|
||||||
|
| {
|
||||||
|
is_stream: true;
|
||||||
|
draft_id: string;
|
||||||
|
stream_name?: string;
|
||||||
|
recipient_bar_color: string;
|
||||||
|
stream_privacy_icon_color: string;
|
||||||
|
topic: string;
|
||||||
|
raw_content: string;
|
||||||
|
stream_id: number | undefined;
|
||||||
|
time_stamp: string;
|
||||||
|
invite_only: boolean;
|
||||||
|
is_web_public: boolean;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
is_stream: false;
|
||||||
|
draft_id: string;
|
||||||
|
recipients: string;
|
||||||
|
raw_content: string;
|
||||||
|
time_stamp: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function format_draft(draft: LocalStorageDraftWithId): FormattedDraft | undefined {
|
||||||
const id = draft.id;
|
const id = draft.id;
|
||||||
const time = new Date(draft.updatedAt);
|
const time = new Date(draft.updatedAt);
|
||||||
let invite_only = false;
|
let invite_only = false;
|
||||||
|
@ -436,9 +538,7 @@ export function format_draft(draft) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (draft.type === "stream") {
|
if (draft.type === "stream") {
|
||||||
// In case there is no stream for the draft, we need a
|
let stream_name;
|
||||||
// single space char for proper rendering of the stream label
|
|
||||||
let stream_name = new Handlebars.SafeString(" ");
|
|
||||||
let sub;
|
let sub;
|
||||||
if (draft.stream_id) {
|
if (draft.stream_id) {
|
||||||
sub = sub_store.get(draft.stream_id);
|
sub = sub_store.get(draft.stream_id);
|
||||||
|
@ -480,7 +580,7 @@ export function format_draft(draft) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function initialize() {
|
export function initialize(): void {
|
||||||
remove_old_drafts();
|
remove_old_drafts();
|
||||||
|
|
||||||
window.addEventListener("beforeunload", () => {
|
window.addEventListener("beforeunload", () => {
|
|
@ -7,7 +7,11 @@
|
||||||
<span class="stream-privacy-modified-color-{{stream_id}} stream-privacy filter-icon" style="color: {{stream_privacy_icon_color}}">
|
<span class="stream-privacy-modified-color-{{stream_id}} stream-privacy filter-icon" style="color: {{stream_privacy_icon_color}}">
|
||||||
{{> stream_privacy}}
|
{{> stream_privacy}}
|
||||||
</span>
|
</span>
|
||||||
{{stream_name}}
|
{{#if stream_name}}
|
||||||
|
{{stream_name}}
|
||||||
|
{{else}}
|
||||||
|
|
||||||
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
<span class="stream_topic_separator"><i class="zulip-icon zulip-icon-chevron-right"></i></span>
|
<span class="stream_topic_separator"><i class="zulip-icon zulip-icon-chevron-right"></i></span>
|
||||||
<span class="stream_topic">
|
<span class="stream_topic">
|
||||||
|
|
Loading…
Reference in New Issue