def _completable_key(key):
         return f'_completable_{key}'
 
-    def append_completable(self, key: str, value: str) -> None:
+    def _on_completable(self, method: str, key: str, value: str, stay_complete
+                        ) -> None:
+        completable = getattr(self, self._completable_key(key))
+        getattr(completable, method)(value, stay_complete)
+        if stay_complete:
+            self._on_update(key)
+
+    def append_completable(self, key: str, value: str, stay_complete=False
+                           ) -> None:
         'To completable list of key append value.'
-        getattr(self, self._completable_key(key)).append(value)
+        self._on_completable('append', key, value, stay_complete)
+
+    def remove_completable(self, key: str, value: str, stay_complete=False
+                           ) -> None:
+        'From completable list of key remove value.'
+        self._on_completable('remove', key, value, stay_complete)
 
     def declare_complete(self, key: str) -> None:
         'Declare completable at key complete.'
             self.set_nick(msg.params[0], confirmed=True)
         elif msg.match('NOTICE', 2) or msg.match('PRIVMSG', 2):
             scope = LogScope.CHAT if '!' in msg.source else LogScope.SERVER
-            self._log(msg.params[-1], scope=scope, channel=msg.params[0],
+            self._log(msg.params[-1], scope=scope, target=msg.params[0],
                       out=False, as_notice=msg.verb == 'NOTICE',
                       sender=msg.nick_from_source)
         elif msg.match('PART', len_is_min=True) or msg.match('JOIN'):
             log_msg = f'{msg.nick_from_source} {msg.verb.lower()}s {channel}'
             if msg.match('PART', 2, True):
                 log_msg += f': {msg.params[1]}'
-            self._log(log_msg, scope=LogScope.CHAT, channel=channel)
+            self._log(log_msg, scope=LogScope.CHAT, target=channel)
             if msg.verb == 'JOIN':
-                self._db.chan(channel).users.append(msg.nick_from_source)
+                if msg.nick_from_source != self._db.nickname:
+                    self._db.chan(channel).append_completable(
+                            'users', msg.nick_from_source, stay_complete=True)
             elif msg.nick_from_source == self._db.nickname:
                 self._db.del_chan(channel)
             else:
-                self._db.chan(channel).users.remove(msg.nick_from_source)
+                self._db.chan(channel).remove_completable(
+                        'users', msg.nick_from_source, stay_complete=True)
         elif msg.match('PING'):
             self.send(IrcMessage(verb='PONG', params=(msg.params[0],)))
         else:
 
     _tui_new_window: Callable
 
     def __post_init__(self, *_, **__) -> None:
-        self._db = _TuiClientDb()
+        self.db = _TuiClientDb()
         self.windows: list[_ClientWindow] = []
         for scope in (LogScope.SERVER, LogScope.RAW):
             self._new_win(scope)
             kwargs['win_cls'] = (_ChannelWindow if chatname[0] == '#'
                                  else _ChatWindow)
             kwargs['chatname'] = chatname
-            kwargs['get_nick_data'] = lambda: (self._db.nickname,)
+            kwargs['get_nick_data'] = lambda: (self.db.nickname,)
         win = self._tui_new_window(**kwargs)
         self.windows += [win]
         return win
         if 'out' in kwargs and scope != LogScope.SERVER:
             first_char = _LOG_PREFIX_OUT if kwargs['out'] else _LOG_PREFIX_IN
             if scope == LogScope.CHAT:
-                sender_label = ' [' + (self._db.nickname if kwargs['out']
+                sender_label = ' [' + (self.db.nickname if kwargs['out']
                                        else kwargs['sender']) + ']'
         if kwargs.get('as_notice', False):
             first_char *= 3
         self._tui_log(msg, scope=scope, prefix=prefix, **kwargs)
 
     def update_db(self, update: _Update) -> bool:
-        'Apply update to ._db, and if changing anything, log and trigger.'
+        'Apply update to .db, and if changing anything, log and trigger.'
         scope = (LogScope.CHAT if update.is_chan
                  else (LogScope.ALL if update.path == 'connection_state'
                        else LogScope.SERVER))
         verb = 'cleared' if update.value is None else 'changed to:'
         what = f'{update.path}:{update.arg}' if update.arg else update.path
-        log_kwargs = {'channel': update.path} if update.is_chan else {}
-        if not self._db.set_and_check_for_change(update):
+        log_kwargs = {'target': update.path} if update.is_chan else {}
+        if not self.db.set_and_check_for_change(update):
             return False
         announcement = f'{what} {verb}'
         if isinstance(update.value, tuple) or announcement[-1] != ':':
         self._client_mngrs: dict[str, _ClientWindowsManager] = {}
 
     def _log_target_wins(self, **kwargs) -> Sequence[Window]:
-        target = kwargs.get('scope', LogScope.SAME)
-        if target != LogScope.SAME:
+        scope = kwargs.get('scope', LogScope.SAME)
+        if scope != LogScope.SAME:
             m = self._client_mngrs[kwargs['client_id']]
-            if target == LogScope.ALL:
+            if scope == LogScope.ALL:
                 return m.windows
-            if target == LogScope.SERVER:
+            if scope == LogScope.SERVER:
                 return [m.window(LogScope.SERVER), m.window(LogScope.RAW)]
-            if target == LogScope.CHAT:
-                chatname = (kwargs['target'] if kwargs.get('out', False)
-                            else kwargs.get('sender', kwargs['channel']))
+            if scope == LogScope.CHAT:
+                chatname = (
+                        kwargs['target'] if kwargs['target'] != m.db.nickname
+                        else kwargs['sender'])
                 return [m.window(LogScope.CHAT, chatname=chatname)]
-            return [m.window(target)]
+            return [m.window(scope)]
         return super()._log_target_wins(**kwargs)
 
     def for_client_do(self, client_id: str, todo: str, **kwargs) -> None: