chat: switch chat in 3 column style

This commit is contained in:
Zener 2020-12-31 10:48:16 +01:00
parent 8d5b775028
commit 64fcb0c530
4 changed files with 187 additions and 61 deletions

View File

@ -60,6 +60,9 @@ Usage
-----
Go to `RsNewWebUI/webui/` and open the `index.html` file in your browser.
### References:
* Mithril: https://mithril.js.org/hyperscript.html
Contributing
------------
### Bug Reports & Feature requests

View File

@ -2,18 +2,61 @@
margin: 10px;
border: 1px solid #aaa;
border-radius: 20px;
}.lobby h5 {
}
.lobby .mainname {
margin: 20px;
font-weight: 100;
font-size: 1.2em;
}.lobby > p {
font-size: 0.95em;
color: #666;
margin-left: 25px;
}
.lobby.subscribed:hover {
.topic {
color: #666;
}
.lobby > .topic {
font-size: 0.95em;
margin-left: 25px;
margin-bottom: 5px;
}
.lefttitle {
margin-top: 15px;
margin-bottom: 0;
font-weight: 100;
font-size: 1.2em;
}
.leftname {
margin-top: 5px;
margin-bottom: 5px;
padding:5px;
font-weight: 100;
font-size: 1em;
}
.leftlobby > .topic {
font-size: 0.75em;
margin-left: 15px;
margin-bottom: 5px;
}
.subscribed {
cursor: pointer;
}
.leftlobby {
border: 1px solid #aaa;
border-radius: 10px;
margin-top: 5px;
background-color: white;
}
.leftlobby.selected-lobby {
color: white;
background-color: #3ba4d7;
}
.rightbar {
position: absolute;
width: 185px;
@ -32,25 +75,20 @@
padding: 15px;
}
.chatMessage {
.lobbies {
position: absolute;
background-color: white;
height: 85px;
width: 185px;
left:15px ;
bottom: 15px;
right: 215px;
left: 15px;
}
textarea.chatMsg {
height: 100%;
width: 100%;
top: 130px;
overflow:auto;
}
.messages {
position: absolute;
background-color: white;
top: 130px;
left: 15px;
left: 215px;
right: 215px;
bottom: 115px;
overflow: auto;
@ -67,4 +105,18 @@ textarea.chatMsg {
.username {
color: darkgreen;
font-weight: bolder;
}
.chatMessage {
position: absolute;
background-color: white;
height: 85px;
bottom: 15px;
right: 215px;
left: 215px;
}
textarea.chatMsg {
height: 100%;
width: 100%;
}

View File

@ -21,6 +21,7 @@ function loadLobbyDetails(id, apply) {
let ChatRoomsModel = {
allRooms: [],
knownSubscrIds:[], // to exclude subscribed from public rooms (subscribedRooms filled to late)
subscribedRooms: {},
loadPublicRooms() {
// TODO: this doesn't preserve id of rooms,
@ -32,23 +33,28 @@ let ChatRoomsModel = {
);
},
loadSubscribedRooms() {
ChatRoomsModel.subscribedRooms = {};
// ChatRoomsModel.subscribedRooms = {};
rs.rsJsonApiRequest('/rsMsgs/getChatLobbyList', {},
data => data.map(id => loadLobbyDetails(id, info => ChatRoomsModel.subscribedRooms[id] = info)),
data => {
ChatRoomsModel.knownSubscrIds = data;
let rooms = {};
data.map(id => loadLobbyDetails(id, info => rooms[id] = info));
ChatRoomsModel.subscribedRooms = rooms;
},
true, {},
// Custom deserializer NOTE:
// JS uses double precision numbers of 64 bit. It is equivalent
// to 53 bits of precision. All large precision ints will
// get truncated to an approximation.
// This API uses Cpp-style 64 bits for `id`.
// Instead of parsing using JSON.parse, this funciton manually
// Instead of parsing using JSON.parse, this function manually
// extracts all numbers and stores them as strings
// Note the g flag. The match will return an array of strings
(response) => response.match(/\d+/g),
)
},
subscribed(info) {
return this.subscribedRooms.hasOwnProperty(info.lobby_id.xstr64);
return this.knownSubscrIds.includes(info.lobby_id.xstr64);
},
};
@ -69,10 +75,10 @@ const ChatLobbyModel = {
chatId(action) {
return {type:3,lobby_id:{xstr64:m.route.param('lobby')}};
},
loadLobby () {
loadLobbyDetails(m.route.param('lobby'), detail => {
loadLobby (currentlobbyid) {
loadLobbyDetails(currentlobbyid, detail => {
this.currentLobby = detail;
let lobbyid = m.route.param('lobby');
let lobbyid = currentlobbyid;
// apply existing messages to current lobby view
rs.events[15].chatMessages(this.chatId(),rs.events[15], l => (this.messages = l.map(printMessage)));
// register for chatEvents for future messages
@ -108,37 +114,50 @@ const ChatLobbyModel = {
() => '{"id":{"type": 3,"lobby_id":' + m.route.param('lobby') + '}, "msg":' + JSON.stringify(msg) + '}'
);
},
selected(info, selName, defaultName) {
let currid = (ChatLobbyModel.currentLobby.lobby_id || {xstr64: m.route.param('lobby')}).xstr64
return ((info.lobby_id.xstr64 === currid) ? selName : '') + defaultName;
},
switchToEvent(info) {
return () => {
ChatLobbyModel.currentLobby = info;
m.route.set('/chat/:lobby', { lobby: info.lobby_id.xstr64 });
ChatLobbyModel.loadLobby(info.lobby_id.xstr64); // update
};
}
}
const Lobby = () => {
let info = {};
let tagname = '';
let onclick = e => {};
let lobbytagname = '';
return {
oninit: (v) => info = v.attrs.info,
view: (v) => m( '.lobby.' + (ChatRoomsModel.subscribed(v.attrs.info) ? 'subscribed':'public'), {
key: v.attrs.info.lobby_id.xstr64,
onclick: e => {
if (ChatRoomsModel.subscribed(v.attrs.info)) {
m.route.set('/chat/:lobby', {
lobby: v.attrs.info.lobby_id.xstr64
});
}
},
oninit: (v) => {
info = v.attrs.info;
tagname = v.attrs.tagname;
onclick = v.attrs.onclick || (e => {});
lobbytagname= v.attrs.lobbytagname || 'h5.mainname';
},
view: v => {
return m(ChatLobbyModel.selected(info, '.selected-lobby', tagname), {
key: info.lobby_id.xstr64,
onclick: onclick,
}, [
m('h5', info.lobby_name === '' ? '<unnamed>' : info.lobby_name),
m('p', info.lobby_topic),
]),
m(lobbytagname,info.lobby_name === '' ? '<unnamed>' : info.lobby_name),
m('.topic', info.lobby_topic),
]);
},
};
};
const SubscribedLobbies = () => {
lobbies = [];
return {
oninit: (v) => ChatRoomsModel.loadSubscribedRooms(),
view: () => m('.widget', [
m('h3', 'Subscribed chat rooms'),
m('hr'),
Object.values(ChatRoomsModel.subscribedRooms).map(info => m(Lobby, {
info
info, tagname:'.lobby.subscribed', onclick: e => m.route.set('/chat/:lobby', { lobby: info.lobby_id.xstr64 })
})),
]),
};
@ -146,12 +165,11 @@ const SubscribedLobbies = () => {
const PublicLobbies = () => {
return {
oninit: () => ChatRoomsModel.loadPublicRooms(),
view: () => m('.widget', [
m('h3', 'Public chat rooms'),
m('hr'),
ChatRoomsModel.allRooms.filter(info => !ChatRoomsModel.subscribed(info)).map(info => m(Lobby, {
info,
info, tagname:'.lobby.public',
})),
])
};
@ -159,6 +177,10 @@ const PublicLobbies = () => {
const Layout = () => {
return {
oninit: () => {
ChatRoomsModel.loadSubscribedRooms();
ChatRoomsModel.loadPublicRooms();
},
view: vnode => m('.tab-page', [
m(SubscribedLobbies),
m(PublicLobbies),
@ -166,11 +188,51 @@ const Layout = () => {
};
};
const LobbyList = () => {
let rooms= [];
let tagname= '';
let lobbytagname= '';
let onclick = (info => (() => {}));
return {
oninit: vnode => {
rooms = vnode.attrs.rooms;
tagname = vnode.attrs.tagname;
lobbytagname= vnode.attrs.lobbytagname;
onclick=vnode.attrs.onclick||(() => (() => {}));
},
view: vnode => rooms.map(info => m(Lobby, {
info,
tagname: tagname,
lobbytagname,
onclick: onclick(info),
})),
};
}
const LayoutSingle = () => {
return {
oninit: ChatLobbyModel.loadLobby(),
oninit: () => {
ChatRoomsModel.loadSubscribedRooms();
ChatRoomsModel.loadPublicRooms();
ChatLobbyModel.loadLobby(m.route.param('lobby'));
},
view: vnode => m('.tab-page', [
m('h3.lobbyName', ChatLobbyModel.currentLobby.lobby_name),
m('.lobbies',
m('h5.lefttitle', 'subscribed:'),
m('hr'),
m(LobbyList, {
rooms: Object.values(ChatRoomsModel.subscribedRooms),
tagname: '.leftlobby.subscribed',
lobbytagname:'h5.leftname',
onclick: ChatLobbyModel.switchToEvent,
}),
m('h5.lefttitle', 'public:'),
m('hr'),
Object.values(ChatRoomsModel.allRooms).filter(info => !ChatRoomsModel.subscribed(info)).map(info => m(Lobby, {
info, tagname: '.leftlobby.public', lobbytagname:'h5.leftname',
})),
),
m('.messages', ChatLobbyModel.messages),
m('.rightbar', ChatLobbyModel.users),
m('.chatMessage', {}, m("textarea.chatMsg", {

View File

@ -100,30 +100,40 @@ function computeIfMissing(map, key, missing = () => ({})) {
function deeperIfExist(map, key, action) {
if (map.hasOwnProperty(key)) {
action(map[key]);
return true;
} else {
return false;
}
}
const eventQueue = {
events: {
15: { // Chat-Messages
types: {
3: chat_id => chat_id.lobby_id.xstr64, // lobby_id
},
messages: {},
chatMessages: (chat_id, owner, action) => deeperIfExist(
owner.types,
chat_id.type,
keyfn => action(computeIfMissing(computeIfMissing(owner.messages,chat_id.type), keyfn(chat_id),()=>[]))
),
handler: (event,owner) => owner.chatMessages(event.mChatMessage.chat_id, owner, r => {
console.info(['adding chat' ,r , event.mChatMessage])
r.push(event.mChatMessage);
owner.notify(event.mChatMessage);
}),
notify: () => {},
types: {
3: chat_id => chat_id.lobby_id.xstr64, // lobby_id
},
messages: {},
chatMessages: (chat_id, owner, action) => deeperIfExist(
owner.types,
chat_id.type,
keyfn => action(computeIfMissing(computeIfMissing(owner.messages,chat_id.type), keyfn(chat_id),()=>[]))
),
handler: (event,owner) => owner.chatMessages(event.mChatMessage.chat_id, owner, r => {
console.info(['adding chat' ,r , event.mChatMessage]);
r.push(event.mChatMessage);
owner.notify(event.mChatMessage);
}),
notify: () => {},
},
8: { // Circles (ignore in the meantime)
handler: (event,owner) => {},
}
},
handler: event => {
if (!deeperIfExist(eventQueue.events, event.mType, owner => owner.handler(event, owner))) {
console.info(event);
};
},
handler: event => deeperIfExist(eventQueue.events, event.mType, owner => owner.handler(event, owner)),
}
const userList = {
@ -189,11 +199,10 @@ function startEventQueue(info, loginHeader = {}, displayAuthError = () => {}, di
}
} else if(data.hasOwnProperty('event')) {
data.event.queueSize = currIndex;
console.info(data.event);
eventQueue.handler(data.event);
}
}
if (currIndex > 1e6) { // max 1 MB eventQueue
if (currIndex > 1e5) { // max 100 kB eventQueue
startEventQueue('restart queue');
xhr.abort();
}