0

Merge pull request #241 from jeyemwey/jv-119-remove-avatars

Remove Chat Avatars
Thanks for the cleanup! Looks great.
This commit is contained in:
gingervitis 2020-10-14 11:16:58 -07:00 committed by GitHub
commit 57f2e4b567
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 19 additions and 98 deletions

View File

@ -36,7 +36,6 @@ func createTable(db *sql.DB) {
"id" string NOT NULL PRIMARY KEY, "id" string NOT NULL PRIMARY KEY,
"author" TEXT, "author" TEXT,
"body" TEXT, "body" TEXT,
"image" TEXT,
"messageType" TEXT, "messageType" TEXT,
"visible" INTEGER, "visible" INTEGER,
"timestamp" DATE "timestamp" DATE
@ -51,11 +50,11 @@ func createTable(db *sql.DB) {
func addMessage(message models.ChatMessage) { func addMessage(message models.ChatMessage) {
tx, err := _db.Begin() tx, err := _db.Begin()
stmt, err := tx.Prepare("INSERT INTO messages(id, author, body, image, messageType, visible, timestamp) values(?, ?, ?, ?, ?, ?, ?)") stmt, err := tx.Prepare("INSERT INTO messages(id, author, body, messageType, visible, timestamp) values(?, ?, ?, ?, ?, ?)")
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
_, err = stmt.Exec(message.ID, message.Author, message.Body, message.Image, message.MessageType, 1, message.Timestamp) _, err = stmt.Exec(message.ID, message.Author, message.Body, message.MessageType, 1, message.Timestamp)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@ -78,12 +77,11 @@ func getChatHistory() []models.ChatMessage {
var id string var id string
var author string var author string
var body string var body string
var image string
var messageType string var messageType string
var visible int var visible int
var timestamp time.Time var timestamp time.Time
err = rows.Scan(&id, &author, &body, &image, &messageType, &visible, &timestamp) err = rows.Scan(&id, &author, &body, &messageType, &visible, &timestamp)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@ -92,7 +90,6 @@ func getChatHistory() []models.ChatMessage {
message.ID = id message.ID = id
message.Author = author message.Author = author
message.Body = body message.Body = body
message.Image = image
message.MessageType = messageType message.MessageType = messageType
message.Visible = visible == 1 message.Visible = visible == 1
message.Timestamp = timestamp message.Timestamp = timestamp

View File

@ -140,7 +140,7 @@ func (s *server) sendWelcomeMessageToClient(c *Client) {
time.Sleep(7 * time.Second) time.Sleep(7 * time.Second)
initialChatMessageText := fmt.Sprintf("Welcome to %s! %s", config.Config.InstanceDetails.Title, config.Config.InstanceDetails.Summary) initialChatMessageText := fmt.Sprintf("Welcome to %s! %s", config.Config.InstanceDetails.Title, config.Config.InstanceDetails.Summary)
initialMessage := models.ChatMessage{"owncast-server", config.Config.InstanceDetails.Name, initialChatMessageText, config.Config.InstanceDetails.Logo.Small, "initial-message-1", "CHAT", true, time.Now()} initialMessage := models.ChatMessage{"owncast-server", config.Config.InstanceDetails.Name, initialChatMessageText, "initial-message-1", "CHAT", true, time.Now()}
c.Write(initialMessage) c.Write(initialMessage)
}() }()

View File

@ -18,7 +18,6 @@ type ChatMessage struct {
Author string `json:"author"` Author string `json:"author"`
Body string `json:"body"` Body string `json:"body"`
Image string `json:"image"`
ID string `json:"id"` ID string `json:"id"`
MessageType string `json:"type"` MessageType string `json:"type"`
Visible bool `json:"visible"` Visible bool `json:"visible"`

View File

@ -311,9 +311,6 @@ paths:
body: body:
type: string type: string
description: Escaped HTML of the chat message content. description: Escaped HTML of the chat message content.
image:
type: string
description: URL of the chat user avatar.
id: id:
type: string type: string
description: Unique ID of the chat message. description: Unique ID of the chat message.

View File

@ -4,8 +4,8 @@ const html = htm.bind(h);
import Chat from './components/chat/chat.js'; import Chat from './components/chat/chat.js';
import Websocket from './utils/websocket.js'; import Websocket from './utils/websocket.js';
import { getLocalStorage, generateAvatar, generateUsername } from './utils/helpers.js'; import { getLocalStorage, generateUsername } from './utils/helpers.js';
import { KEY_USERNAME, KEY_AVATAR } from './utils/constants.js'; import { KEY_USERNAME } from './utils/constants.js';
export default class StandaloneChat extends Component { export default class StandaloneChat extends Component {
constructor(props, context) { constructor(props, context) {
@ -15,28 +15,25 @@ export default class StandaloneChat extends Component {
websocket: new Websocket(), websocket: new Websocket(),
chatEnabled: true, // always true for standalone chat chatEnabled: true, // always true for standalone chat
username: getLocalStorage(KEY_USERNAME) || generateUsername(), username: getLocalStorage(KEY_USERNAME) || generateUsername(),
userAvatarImage: getLocalStorage(KEY_AVATAR) || generateAvatar(`${this.username}${Date.now()}`),
}; };
this.websocket = null; this.websocket = null;
this.handleUsernameChange = this.handleUsernameChange.bind(this); this.handleUsernameChange = this.handleUsernameChange.bind(this);
} }
handleUsernameChange(newName, newAvatar) { handleUsernameChange(newName) {
this.setState({ this.setState({
username: newName, username: newName,
userAvatarImage: newAvatar,
}); });
} }
render(props, state) { render(props, state) {
const { username, userAvatarImage, websocket } = state; const { username, websocket } = state;
return ( return (
html` html`
<${Chat} <${Chat}
websocket=${websocket} websocket=${websocket}
username=${username} username=${username}
userAvatarImage=${userAvatarImage}
messagesOnly messagesOnly
/> />
` `

View File

@ -14,7 +14,6 @@ import {
classNames, classNames,
clearLocalStorage, clearLocalStorage,
debounce, debounce,
generateAvatar,
generateUsername, generateUsername,
getLocalStorage, getLocalStorage,
pluralize, pluralize,
@ -22,7 +21,6 @@ import {
} from './utils/helpers.js'; } from './utils/helpers.js';
import { import {
HEIGHT_SHORT_WIDE, HEIGHT_SHORT_WIDE,
KEY_AVATAR,
KEY_CHAT_DISPLAYED, KEY_CHAT_DISPLAYED,
KEY_USERNAME, KEY_USERNAME,
MESSAGE_OFFLINE, MESSAGE_OFFLINE,
@ -50,9 +48,6 @@ export default class App extends Component {
displayChat: chatStorage === null ? true : chatStorage, displayChat: chatStorage === null ? true : chatStorage,
chatInputEnabled: false, // chat input box state chatInputEnabled: false, // chat input box state
username: getLocalStorage(KEY_USERNAME) || generateUsername(), username: getLocalStorage(KEY_USERNAME) || generateUsername(),
userAvatarImage:
getLocalStorage(KEY_AVATAR) ||
generateAvatar(`${this.username}${Date.now()}`),
configData: {}, configData: {},
extraPageContent: '', extraPageContent: '',
@ -282,10 +277,9 @@ export default class App extends Component {
} }
handleUsernameChange(newName, newAvatar) { handleUsernameChange(newName) {
this.setState({ this.setState({
username: newName, username: newName,
userAvatarImage: newAvatar,
}); });
} }
@ -330,7 +324,6 @@ export default class App extends Component {
playerActive, playerActive,
streamOnline, streamOnline,
streamStatusMessage, streamStatusMessage,
userAvatarImage,
username, username,
viewerCount, viewerCount,
websocket, websocket,
@ -415,7 +408,6 @@ export default class App extends Component {
> >
<${UsernameForm} <${UsernameForm}
username=${username} username=${username}
userAvatarImage=${userAvatarImage}
handleUsernameChange=${this.handleUsernameChange} handleUsernameChange=${this.handleUsernameChange}
/> />
<button <button
@ -499,7 +491,6 @@ export default class App extends Component {
<${Chat} <${Chat}
websocket=${websocket} websocket=${websocket}
username=${username} username=${username}
userAvatarImage=${userAvatarImage}
chatInputEnabled=${chatInputEnabled} chatInputEnabled=${chatInputEnabled}
/> />
</div> </div>

View File

@ -43,14 +43,14 @@ export default class Chat extends Component {
componentDidUpdate(prevProps, prevState) { componentDidUpdate(prevProps, prevState) {
const { username: prevName } = prevProps; const { username: prevName } = prevProps;
const { username, userAvatarImage } = this.props; const { username } = this.props;
const { messages: prevMessages } = prevState; const { messages: prevMessages } = prevState;
const { messages } = this.state; const { messages } = this.state;
// if username updated, send a message // if username updated, send a message
if (prevName !== username) { if (prevName !== username) {
this.sendUsernameChange(prevName, username, userAvatarImage); this.sendUsernameChange(prevName, username);
} }
// scroll to bottom of messages list when new ones come in // scroll to bottom of messages list when new ones come in
if (messages.length > prevMessages.length) { if (messages.length > prevMessages.length) {
@ -94,12 +94,11 @@ export default class Chat extends Component {
}); });
} }
sendUsernameChange(oldName, newName, image) { sendUsernameChange(oldName, newName) {
const nameChange = { const nameChange = {
type: SOCKET_MESSAGE_TYPES.NAME_CHANGE, type: SOCKET_MESSAGE_TYPES.NAME_CHANGE,
oldName, oldName,
newName, newName,
image,
}; };
this.websocket.send(nameChange); this.websocket.send(nameChange);
} }
@ -145,11 +144,10 @@ export default class Chat extends Component {
if (!content) { if (!content) {
return; return;
} }
const { username, userAvatarImage } = this.props; const { username } = this.props;
const message = { const message = {
body: content, body: content,
author: username, author: username,
image: userAvatarImage,
type: SOCKET_MESSAGE_TYPES.CHAT, type: SOCKET_MESSAGE_TYPES.CHAT,
}; };
this.websocket.send(message); this.websocket.send(message);

View File

@ -3,7 +3,6 @@ import htm from '/js/web_modules/htm.js';
const html = htm.bind(h); const html = htm.bind(h);
import { messageBubbleColorForString } from '../../utils/user-colors.js'; import { messageBubbleColorForString } from '../../utils/user-colors.js';
import { generateAvatar } from '../../utils/helpers.js';
import { convertToText } from '../../utils/chat.js'; import { convertToText } from '../../utils/chat.js';
import { SOCKET_MESSAGE_TYPES } from '../../utils/websocket.js'; import { SOCKET_MESSAGE_TYPES } from '../../utils/websocket.js';
@ -12,23 +11,15 @@ export default class Message extends Component {
const { message, username } = props; const { message, username } = props;
const { type } = message; const { type } = message;
if (type === SOCKET_MESSAGE_TYPES.CHAT) { if (type === SOCKET_MESSAGE_TYPES.CHAT) {
const { image, author, body, timestamp } = message; const { author, body, timestamp } = message;
const formattedMessage = formatMessageText(body, username); const formattedMessage = formatMessageText(body, username);
const avatar = image || generateAvatar(author);
const formattedTimestamp = formatTimestamp(timestamp); const formattedTimestamp = formatTimestamp(timestamp);
const authorColor = messageBubbleColorForString(author); const authorColor = messageBubbleColorForString(author);
const avatarBgColor = { backgroundColor: authorColor };
const authorTextColor = { color: authorColor }; const authorTextColor = { color: authorColor };
return ( return (
html` html`
<div class="message flex flex-row items-start p-3"> <div class="message flex flex-row items-start p-3">
<div
class="message-avatar rounded-full flex items-center justify-center mr-3"
style=${avatarBgColor}
>
<img src=${avatar} class="p-1" />
</div>
<div class="message-content text-sm break-words w-full"> <div class="message-content text-sm break-words w-full">
<div class="message-author text-white font-bold" style=${authorTextColor}> <div class="message-author text-white font-bold" style=${authorTextColor}>
${author} ${author}
@ -44,17 +35,11 @@ export default class Message extends Component {
</div> </div>
`); `);
} else if (type === SOCKET_MESSAGE_TYPES.NAME_CHANGE) { } else if (type === SOCKET_MESSAGE_TYPES.NAME_CHANGE) {
const { oldName, newName, image } = message; const { oldName, newName } = message;
return ( return (
html` html`
<div class="message message-name-change flex items-center justify-start p-3"> <div class="message message-name-change flex items-center justify-start p-3">
<div class="message-content flex flex-row items-center justify-center text-sm w-full"> <div class="message-content flex flex-row items-center justify-center text-sm w-full">
<div
class="message-avatar rounded-full mr-3 bg-gray-900"
>
<img class="mr-2 p-1" src=${image} />
</div>
<div class="text-white text-center opacity-50"> <div class="text-white text-center opacity-50">
<span class="font-bold">${oldName}</span> is now known as <span class="font-bold">${newName}</span>. <span class="font-bold">${oldName}</span> is now known as <span class="font-bold">${newName}</span>.
</div> </div>

View File

@ -2,8 +2,8 @@ import { h, Component, createRef } from '/js/web_modules/preact.js';
import htm from '/js/web_modules/htm.js'; import htm from '/js/web_modules/htm.js';
const html = htm.bind(h); const html = htm.bind(h);
import { generateAvatar, setLocalStorage } from '../../utils/helpers.js'; import { setLocalStorage } from '../../utils/helpers.js';
import { KEY_USERNAME, KEY_AVATAR } from '../../utils/constants.js'; import { KEY_USERNAME } from '../../utils/constants.js';
export default class UsernameForm extends Component { export default class UsernameForm extends Component {
constructor(props, context) { constructor(props, context) {
@ -47,11 +47,9 @@ export default class UsernameForm extends Component {
let newName = this.textInput.current.value; let newName = this.textInput.current.value;
newName = newName.trim(); newName = newName.trim();
if (newName !== '' && newName !== curName) { if (newName !== '' && newName !== curName) {
const newAvatar = generateAvatar(`${newName}${Date.now()}`);
setLocalStorage(KEY_USERNAME, newName); setLocalStorage(KEY_USERNAME, newName);
setLocalStorage(KEY_AVATAR, newAvatar);
if (handleUsernameChange) { if (handleUsernameChange) {
handleUsernameChange(newName, newAvatar); handleUsernameChange(newName);
} }
this.handleHideForm(); this.handleHideForm();
} }
@ -59,7 +57,7 @@ export default class UsernameForm extends Component {
} }
render(props, state) { render(props, state) {
const { username, userAvatarImage } = props; const { username } = props;
const { displayForm } = state; const { displayForm } = state;
const narrowSpace = document.body.clientWidth < 640; const narrowSpace = document.body.clientWidth < 640;
@ -77,12 +75,6 @@ export default class UsernameForm extends Component {
html` html`
<div id="user-info"> <div id="user-info">
<div id="user-info-display" style=${styles.info} title="Click to update user name" class="flex flex-row justify-end items-center cursor-pointer py-2 px-4 overflow-hidden w-full opacity-1 transition-opacity duration-200 hover:opacity-75" onClick=${this.handleDisplayForm}> <div id="user-info-display" style=${styles.info} title="Click to update user name" class="flex flex-row justify-end items-center cursor-pointer py-2 px-4 overflow-hidden w-full opacity-1 transition-opacity duration-200 hover:opacity-75" onClick=${this.handleDisplayForm}>
<img
src=${userAvatarImage}
alt=""
id="username-avatar"
class="rounded-full bg-black bg-opacity-50 border border-solid border-gray-700 mr-2 h-8 w-8"
/>
<span id="username-display" class="text-indigo-600 text-xs font-semibold truncate overflow-hidden whitespace-no-wrap">${username}</span> <span id="username-display" class="text-indigo-600 text-xs font-semibold truncate overflow-hidden whitespace-no-wrap">${username}</span>
</div> </div>

View File

@ -22,7 +22,6 @@ export const PLAYER_VOLUME = 'owncast_volume';
export const KEY_USERNAME = 'owncast_username'; export const KEY_USERNAME = 'owncast_username';
export const KEY_AVATAR = 'owncast_avatar';
export const KEY_CHAT_DISPLAYED = 'owncast_chat'; export const KEY_CHAT_DISPLAYED = 'owncast_chat';
export const KEY_CHAT_FIRST_MESSAGE_SENT = 'owncast_first_message_sent'; export const KEY_CHAT_FIRST_MESSAGE_SENT = 'owncast_first_message_sent';
export const CHAT_INITIAL_PLACEHOLDER_TEXT = 'Type here to chat, no account necessary.'; export const CHAT_INITIAL_PLACEHOLDER_TEXT = 'Type here to chat, no account necessary.';

View File

@ -92,16 +92,6 @@ export function getOrientation(forTouch = false) {
} }
} }
// generate random avatar from https://robohash.org
export function generateAvatar(hash) {
const avatarSource = 'https://robohash.org/';
const optionSize = '?size=80x80';
const optionSet = '&set=set2';
const optionBg = ''; // or &bgset=bg1 or bg2
return avatarSource + hash + optionSize + optionSet + optionBg;
}
export function generateUsername() { export function generateUsername() {
return `User ${(Math.floor(Math.random() * 42) + 1)}`; return `User ${(Math.floor(Math.random() * 42) + 1)}`;
} }

View File

@ -115,22 +115,6 @@
scrollbar-color: var(--category-button-color) black; scrollbar-color: var(--category-button-color) black;
} }
/******************************/
.message-avatar {
height: 3.0em;
width: 3.0em;
}
.message-avatar img {
max-width: unset;
height: 3.0em;
width: 3.0em;
padding: 5px;
}
/* MESSAGE TEXT HTML */ /* MESSAGE TEXT HTML */
/* MESSAGE TEXT HTML */ /* MESSAGE TEXT HTML */
/* MESSAGE TEXT HTML */ /* MESSAGE TEXT HTML */

View File

@ -28,14 +28,6 @@ The styles in this file mostly ovveride those coming from chat.css
#messages-only .message-content { #messages-only .message-content {
text-shadow: 1px 1px 0px rgba(0,0,0,0.25); text-shadow: 1px 1px 0px rgba(0,0,0,0.25);
} }
#messages-only .message-avatar {
display: none;
box-shadow: 0px 0px 3px 0px rgba(0,0,0,0.25);
}
#messages-only .message-avatar img {
height: 1.8em;
width: 1.8em;
}
#messages-only .message { #messages-only .message {
padding: .5em; padding: .5em;
} }