Beispiel #1
0
    def draw(self, current: int, chats: List[Dict[str, Any]]) -> None:
        self.win.erase()
        line = curses.ACS_VLINE  # type: ignore
        self.win.vline(0, self.w - 1, line, self.h)
        for i, chat in enumerate(chats):
            is_selected = i == current
            unread_count = chat["unread_count"]
            if chat["is_marked_as_unread"]:
                unread_count = "unread"

            date = get_date(chat)
            title = chat["title"]
            is_pinned = chat["is_pinned"]
            last_msg = get_last_msg(chat)
            offset = 0
            for attr, elem in zip(
                self._chat_attributes(is_selected), [f"{date} ", title]
            ):
                self.win.addstr(
                    i,
                    offset,
                    truncate_to_len(elem, max(0, self.w - offset - 1)),
                    attr,
                )
                offset += len(elem) + sum(
                    map(len, emoji_pattern.findall(elem))
                )

            last_msg = " " + last_msg.replace("\n", " ")
            last_msg = truncate_to_len(last_msg, max(0, self.w - offset))
            if last_msg.strip():
                self.win.addstr(
                    i, offset, last_msg, self._msg_color(is_selected)
                )

            if flags := self._get_flags(unread_count, is_pinned, chat):
                flags_len = len(flags) + sum(
                    map(len, emoji_pattern.findall(flags))
                )
                self.win.addstr(
                    i,
                    self.w - flags_len - 1,
                    flags,
                    self._unread_color(is_selected),
                )
Beispiel #2
0
    def draw(
        self, current: int, chats: List[Dict[str, Any]], title: str = "Chats"
    ) -> None:
        self.win.erase()
        line = curses.ACS_VLINE  # type: ignore
        width = self.w - 1
        self.win.vline(0, width, line, self.h)
        self.win.addstr(
            0, 0, title.center(width)[:width], get_color(cyan, -1) | bold
        )

        for i, chat in enumerate(chats, 1):
            is_selected = i == current + 1
            date = get_date(chat)
            title = chat["title"]
            offset = 0

            last_msg_sender, last_msg = self._get_last_msg_data(chat)
            sender_label = f" {last_msg_sender}" if last_msg_sender else ""

            for attr, elem in zip(
                self._chat_attributes(is_selected, title, last_msg_sender),
                [f"{date} ", title, sender_label, f" {last_msg}"],
            ):
                if not elem:
                    continue
                item = truncate_to_len(elem, max(0, width - offset))
                if len(item) > 1:
                    self.win.addstr(i, offset, item, attr)
                    offset += len(elem) + sum(
                        map(len, emoji_pattern.findall(elem))
                    )

            if flags := self._get_flags(chat):
                flags_len = len(flags) + sum(
                    map(len, emoji_pattern.findall(flags))
                )
                self.win.addstr(
                    i,
                    max(0, width - flags_len),
                    flags[-width:],
                    self._unread_color(is_selected),
                )
Beispiel #3
0
    def _collect_msgs_to_draw(
        self,
        current_msg_idx: int,
        msgs: List[Tuple[int, Dict[str, Any]]],
        min_msg_padding: int,
    ) -> List[Tuple[Tuple[str, ...], bool, int]]:
        """
        Tries to collect list of messages that will satisfy `min_msg_padding`
        theshold. Long messages could prevent other messages from displaying on
        the screen. In order to prevent scenario when *selected* message moved
        out from the visible area of the screen by some long messages, this
        function will remove message one by one from the start until selected
        message could be visible on the screen.
        """
        selected_item_idx: Optional[int] = None
        collected_items: List[Tuple[Tuple[str, ...], bool, int]] = []
        for ignore_before in range(len(msgs)):
            if selected_item_idx is not None:
                break
            collected_items = []
            line_num = self.h
            for msg_idx, msg_item in msgs[ignore_before:]:
                is_selected_msg = current_msg_idx == msg_idx
                msg_proxy = MsgProxy(msg_item)
                dt = msg_proxy.date.strftime("%H:%M:%S")
                user_id_item = msg_proxy.sender_id

                user_id = self._get_user_by_id(user_id_item)
                flags = self._get_flags(msg_proxy)
                if user_id and flags:
                    # if not channel add space between name and flags
                    flags = " " + flags
                label_elements = f" {dt} ", user_id, flags
                label_len = sum(len(e) for e in label_elements)

                msg = self._format_msg(
                    msg_proxy, user_id_item, width_limit=self.w - label_len - 1
                )
                elements = *label_elements, f" {msg}"
                needed_lines = 0
                for i, msg_line in enumerate(msg.split("\n")):
                    # count wide character utf-8 symbols that take > 1 bytes to
                    # print it causes invalid offset
                    emojies_count = sum(
                        map(len, emoji_pattern.findall(msg_line))
                    )
                    line_len = len(msg_line) + emojies_count
                    # first line cotains msg lable, e.g user name, date
                    if i == 0:
                        line_len += label_len

                    needed_lines += (line_len // self.w) + 1

                line_num -= needed_lines
                if line_num < 0:
                    tail_lines = needed_lines + line_num - 1
                    # try preview long message that did fit in the screen
                    if tail_lines > 0 and not is_selected_msg:
                        limit = self.w * tail_lines
                        tail_chatacters = len(msg) - limit - 3
                        elements = (
                            "",
                            "",
                            "",
                            f" ...{msg[tail_chatacters:]}",
                        )
                        collected_items.append((elements, is_selected_msg, 0))
                    break
                collected_items.append((elements, is_selected_msg, line_num))
                if is_selected_msg:
                    selected_item_idx = len(collected_items) - 1
            if (
                # ignore first and last msg
                selected_item_idx not in (0, len(msgs) - 1, None)
                and selected_item_idx is not None
                and len(collected_items) - 1 - selected_item_idx
                < min_msg_padding
            ):
                selected_item_idx = None

        return collected_items