Kick off conflicting usernames + Frontend mobile fixes

* When JWT tokens are used to join the chat and the username conflicts:
  instead of renaming the new user to add a "2" it will disconnect the
  original login (sending a message that they have signed in somewhere
  else and are logged out now)
* When disconnected the text entry box will be greyed out.
* Improvements for the mobile user experience: if you're viewing the
  chat history panel and have unread messages or DMs, a number indicator
  appears on the channels button. It is grey for public channel messages
  or red if any of them are DMs
* Fix the emoji picker drop-down on the first messages of a DM thread
This commit is contained in:
Noah 2023-07-17 20:38:07 -07:00
parent 6724792ba0
commit 75c7511410
4 changed files with 60 additions and 13 deletions

View File

@ -51,7 +51,25 @@ func (s *Server) OnLogin(sub *Subscriber, msg Message) {
}
// Ensure the username is unique, or rename it.
msg.Username = s.UniqueUsername(msg.Username)
username, err := s.UniqueUsername(msg.Username)
if err != nil {
// If JWT authentication was used: disconnect the original (conflicting) username.
if claims.Subject == msg.Username {
if other, err := s.GetSubscriber(msg.Username); err == nil {
other.ChatServer("You have been signed out of chat because you logged in from another location.")
other.SendJSON(Message{
Action: ActionKick,
})
s.DeleteSubscriber(other)
}
// They will take over their original username.
username = msg.Username
}
// If JWT auth was not used: UniqueUsername already gave them a uniquely spelled name.
}
msg.Username = username
// Use their username.
sub.Username = msg.Username

View File

@ -275,8 +275,8 @@ func (s *Server) IterSubscribers(isLocked ...bool) []*Subscriber {
return result
}
// UniqueUsername ensures a username will be unique or renames it.
func (s *Server) UniqueUsername(username string) string {
// UniqueUsername ensures a username will be unique or renames it. If the name is already unique, the error result is nil.
func (s *Server) UniqueUsername(username string) (string, error) {
var (
subs = s.IterSubscribers()
usernames = map[string]interface{}{}
@ -297,7 +297,11 @@ func (s *Server) UniqueUsername(username string) string {
}
}
return username
if username != origUsername {
return username, errors.New("username was not unique and a unique name has been returned")
}
return username, nil
}
// Broadcast a message to the chat room.

View File

@ -975,6 +975,23 @@ const app = Vue.createApp({
}
return this.channels[channel].unread;
},
hasAnyUnread() {
// Returns total unread count (for mobile responsive view to show in the left drawer button)
let count = 0;
for (let channel of Object.keys(this.channels)) {
count += this.channels[channel].unread;
}
return count;
},
anyUnreadDMs() {
// Returns true if any unread messages are DM threads
for (let channel of Object.keys(this.channels)) {
if (channel.indexOf("@") === 0 && this.channels[channel].unread > 0) {
return true;
}
}
return false;
},
openDMs(user) {
let channel = "@" + user.username;
this.initHistory(channel);

View File

@ -503,7 +503,7 @@
<div class="columns is-mobile card-header-title has-text-light">
<div class="column is-narrow mobile-only">
<button type="button"
class="button is-success"
class="button is-success px-2"
@click="openChatPanel">
<i class="fa fa-arrow-left"></i>
</button>
@ -525,7 +525,7 @@
:class="{'is-active': c.ID == channel}">
[[c.Name]]
<span v-if="hasUnread(c.ID)"
class="tag is-danger">
class="tag">
[[hasUnread(c.ID)]]
</span>
</a>
@ -581,11 +581,16 @@
<div class="column is-narrow mobile-only pr-0">
<!-- Responsive mobile button to pan to Left Column -->
<button type="button"
class="button is-success"
:class="{'is-small': isDM}"
class="button is-success px-2"
@click="openChannelsPanel">
<i v-if="isDM" class="fa fa-arrow-left"></i>
<i v-else class="fa fa-message"></i>
<i v-else class="fa fa-comments"></i>
<!-- Indicator badge for unread messages -->
<span v-if="hasAnyUnread() > 0"
class="tag ml-1" :class="{'is-danger': anyUnreadDMs()}">
[[hasAnyUnread()]]
</span>
</button>
</div>
<div class="column">
@ -613,7 +618,7 @@
<div v-if="!isDM" class="column is-narrow mobile-only">
<!-- Responsive mobile button to pan to Right Column -->
<button type="button"
class="button is-success"
class="button is-success px-2"
@click="openWhoPanel">
<i class="fa fa-user-group"></i>
</button>
@ -881,7 +886,9 @@
</div>
<!-- Emoji reactions menu -->
<div v-if="msg.msgID" class="dropdown is-up is-right emoji-button" onclick="this.classList.toggle('is-active')">
<div v-if="msg.msgID" class="dropdown is-right emoji-button"
:class="{'is-up': i >= 2}"
onclick="this.classList.toggle('is-active')">
<div class="dropdown-trigger">
<button class="button is-small px-2" aria-haspopup="true" :aria-controls="`react-menu-${msg.msgID}`">
<span>
@ -956,7 +963,8 @@
<input type="text" class="input"
v-model="message"
placeholder="Message"
@keydown="sendTypingNotification()">
@keydown="sendTypingNotification()"
:disabled="!ws.connected">
</form>
</div>
<div class="column pl-1 is-narrow">
@ -979,7 +987,7 @@
<div class="column">Who Is Online</div>
<div class="column is-narrow mobile-only">
<button type="button"
class="button is-success"
class="button is-success px-2"
@click="openChatPanel">
<i class="fa fa-arrow-left"></i>
</button>