mirror of
https://github.com/RetroShare/RSNewWebUI.git
synced 2026-09-14 02:55:47 +05:00
463 lines
14 KiB
JavaScript
463 lines
14 KiB
JavaScript
let m = require('mithril');
|
|
let rs = require('rswebui');
|
|
let people_util = require('people/people_util');
|
|
|
|
// **************** utility functions ********************
|
|
|
|
function loadLobbyDetails(id, apply) {
|
|
rs.rsJsonApiRequest('/rsMsgs/getChatLobbyInfo', {
|
|
id,
|
|
},
|
|
detail => {
|
|
if (detail.retval) {
|
|
apply(detail.info);
|
|
}
|
|
},
|
|
true, {},
|
|
undefined,
|
|
// Custom serializer NOTE:
|
|
// Since id represents 64-bit int(see deserializer note below)
|
|
// Instead of using JSON.stringify, this function directly
|
|
// creates a json string manually.
|
|
() => '{"id":' + id + '}')
|
|
}
|
|
|
|
function sortLobbies(lobbies){
|
|
if (lobbies !== undefined){
|
|
let list= [...lobbies];
|
|
list.sort((a,b) => a.lobby_name.localeCompare(b.lobby_name));
|
|
return list;
|
|
}
|
|
// return lobbies; // fallback on reload page in browser, keep undefiend
|
|
}
|
|
|
|
// ***************************** models ***********************************
|
|
|
|
let ChatRoomsModel = {
|
|
allRooms: [],
|
|
knownSubscrIds:[], // to exclude subscribed from public rooms (subscribedRooms filled to late)
|
|
subscribedRooms: {},
|
|
loadPublicRooms() {
|
|
// TODO: this doesn't preserve id of rooms,
|
|
// use regex on response to extract ids.
|
|
rs.rsJsonApiRequest('/rsMsgs/getListOfNearbyChatLobbies', {},
|
|
data => ChatRoomsModel.allRooms = sortLobbies(data.public_lobbies),
|
|
);
|
|
},
|
|
loadSubscribedRooms(after = null) {
|
|
// ChatRoomsModel.subscribedRooms = {};
|
|
rs.rsJsonApiRequest('/rsMsgs/getChatLobbyList', {},
|
|
// 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`.
|
|
// So we use the string-value 'xstr64' instead
|
|
data => {
|
|
let ids = data.cl_list.map(lid => lid.xstr64);
|
|
ChatRoomsModel.knownSubscrIds = ids;
|
|
let rooms = {};
|
|
ids.map(id => loadLobbyDetails(id, info => {
|
|
rooms[id]= info;
|
|
if (Object.keys(rooms).length===ids.length) {
|
|
// apply rooms to subscribedRooms only after reading all room-details, so sorting all or nothing
|
|
ChatRoomsModel.subscribedRooms = rooms;
|
|
}
|
|
}));
|
|
if (after != null) {
|
|
after()
|
|
}
|
|
},
|
|
)
|
|
},
|
|
subscribed(info) {
|
|
return this.knownSubscrIds.includes(info.lobby_id.xstr64);
|
|
},
|
|
};
|
|
|
|
const ChatLobbyModel = {
|
|
currentLobby: {
|
|
lobby_name: '...',
|
|
},
|
|
lobby_user: '...',
|
|
isSubscribed: false,
|
|
messages: [],
|
|
users: [],
|
|
setupAction: (lobby_id, nick) => {},
|
|
setIdentity(lobby_id, nick) {
|
|
rs.rsJsonApiRequest(
|
|
'/rsMsgs/setIdentityForChatLobby',
|
|
{},
|
|
() => m.route.set('/chat/:lobby_id',{lobby_id: lobby_id}),
|
|
true,
|
|
{},
|
|
JSON.parse,
|
|
()=> '{"lobby_id":' + lobby_id + ',"nick":"' + nick + '"}'
|
|
);
|
|
},
|
|
enterPublicLobby(lobby_id, nick) {
|
|
console.info('joinVisibleChatLobby', nick, '@', lobby_id)
|
|
rs.rsJsonApiRequest(
|
|
'/rsMsgs/joinVisibleChatLobby',
|
|
{},
|
|
() => {
|
|
loadLobbyDetails(lobby_id, info => {
|
|
ChatRoomsModel.subscribedRooms[lobby_id]= info;
|
|
ChatRoomsModel.loadSubscribedRooms(() => {
|
|
m.route.set('/chat/:lobby', { lobby: info.lobby_id.xstr64 });
|
|
})
|
|
});
|
|
},
|
|
true,
|
|
{},
|
|
JSON.parse,
|
|
()=> '{"lobby_id":' + lobby_id + ',"own_id":"' + nick + '"}'
|
|
);
|
|
},
|
|
unsubscribeChatLobby(lobby_id, follow) {
|
|
console.info('unsubscribe lobby', lobby_id)
|
|
rs.rsJsonApiRequest(
|
|
'/rsMsgs/unsubscribeChatLobby',
|
|
{},
|
|
() => ChatRoomsModel.loadSubscribedRooms(follow),
|
|
true,
|
|
{},
|
|
JSON.parse,
|
|
() => '{"lobby_id":' + lobby_id + '}'
|
|
)
|
|
},
|
|
chatId(action) {
|
|
return {type:3,lobby_id:{xstr64:m.route.param('lobby')}};
|
|
},
|
|
loadLobby (currentlobbyid) {
|
|
loadLobbyDetails(currentlobbyid, detail => {
|
|
this.setupAction= this.setIdentity;
|
|
this.currentLobby = detail;
|
|
this.isSubscribed = true;
|
|
this.lobby_user = rs.userList.username(detail.gxs_id) || '???';
|
|
let lobbyid = currentlobbyid;
|
|
// apply existing messages to current lobby view
|
|
rs.events[15].chatMessages(this.chatId(),rs.events[15], l => (this.messages = l.map(msg=> m(Message, msg))));
|
|
// register for chatEvents for future messages
|
|
rs.events[15].notify = chatMessage => {
|
|
if (chatMessage.chat_id.type===3 && chatMessage.chat_id.lobby_id.xstr64 === lobbyid) {
|
|
this.messages.push(m(Message,chatMessage));
|
|
m.redraw();
|
|
}
|
|
}
|
|
// lookup for chat-user names (only snapshot, we don't get notified about changes of participants)
|
|
var names = detail.gxs_ids.reduce((a,u) => a.concat(rs.userList.username(u.key)), []);
|
|
names.sort((a,b) => a.localeCompare(b));
|
|
this.users = [];
|
|
names.forEach(name => this.users = this.users.concat([m('.user',name)]));
|
|
return this.users;
|
|
});
|
|
},
|
|
loadPublicLobby (currentlobbyid) {
|
|
console.info('loadPublicLobby ChatRoomsModel:',ChatRoomsModel);
|
|
this.setupAction= this.enterPublicLobby;
|
|
this.isSubscribed = false;
|
|
ChatRoomsModel.allRooms.forEach(it => {
|
|
if (it.lobby_id.xstr64 === currentlobbyid) {
|
|
this.currentLobby = it;
|
|
this.lobby_user = '???';
|
|
this.lobbyid = currentlobbyid;
|
|
}
|
|
})
|
|
this.users = []
|
|
},
|
|
sendMessage(msg, onsuccess) {
|
|
rs.rsJsonApiRequest('/rsmsgs/sendChat', {},
|
|
() => {
|
|
// adding own message to log
|
|
rs.events[15].handler({
|
|
mChatMessage:{
|
|
chat_id:this.chatId(),
|
|
msg:msg,
|
|
sendTime:new Date().getTime()/1000,
|
|
lobby_peer_gxs_id:this.currentLobby.gxs_id,
|
|
}
|
|
},rs.events[15]);
|
|
onsuccess();
|
|
},
|
|
true, {}, undefined,
|
|
() => '{"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
|
|
};
|
|
},
|
|
setupEvent(info) {
|
|
return () => {
|
|
m.route.set('/chat/:lobby/setup', { lobby: info.lobby_id.xstr64 });
|
|
ChatLobbyModel.loadPublicLobby(info.lobby_id.xstr64); // update
|
|
};
|
|
}
|
|
}
|
|
|
|
// ************************* views ****************************
|
|
|
|
/**
|
|
* Message displays a single Chat-Message<br>
|
|
* currently removes formatting and in consequence inline links
|
|
* msg: Message to Display
|
|
*/
|
|
const Message = () => {
|
|
let msg = null; // message to display
|
|
let text = ''; // extracted text to display
|
|
let datetime = ''; // date time to display
|
|
let username = ''; // username to display (later may be linked)
|
|
return {
|
|
oninit: vnode => {
|
|
console.info('chat Message',vnode);
|
|
msg = vnode.attrs;
|
|
datetime = new Date(msg.sendTime * 1000).toLocaleTimeString();
|
|
username = rs.userList.username(msg.lobby_peer_gxs_id);
|
|
text = msg.msg.replaceAll('<br/>','\n').replace(new RegExp('<style[^<]*</style>|<[^>]*>','gm'),'');
|
|
console.info('chat Text', text);
|
|
},
|
|
view: () => m('.message', m('span.datetime', datetime), m('span.username', username), m('span.messagetext', text))
|
|
};
|
|
}
|
|
|
|
|
|
const Lobby = () => {
|
|
let info = {};
|
|
let tagname = '';
|
|
let onclick = e => {};
|
|
let lobbytagname = '';
|
|
return {
|
|
oninit: (v) => {
|
|
info = v.attrs.info;
|
|
tagname = v.attrs.tagname;
|
|
onclick = v.attrs.onclick || (e => {});
|
|
lobbytagname= v.attrs.lobbytagname || 'mainname';
|
|
},
|
|
view: v => {
|
|
return m(ChatLobbyModel.selected(info, '.selected-lobby', tagname), {
|
|
key: info.lobby_id.xstr64,
|
|
onclick: onclick,
|
|
}, [
|
|
m('h5', {class: lobbytagname},info.lobby_name === '' ? '<unnamed>' : info.lobby_name),
|
|
m('.topic', info.lobby_topic),
|
|
]);
|
|
},
|
|
};
|
|
};
|
|
|
|
const LobbyList = {
|
|
view(vnode) {
|
|
let tagname = vnode.attrs.tagname
|
|
let lobbytagname = vnode.attrs.lobbytagname
|
|
let onclick = vnode.attrs.onclick||(() => null)
|
|
return [
|
|
m('hr'),
|
|
vnode.attrs.rooms.map(info => m(Lobby, {
|
|
info,
|
|
tagname,
|
|
lobbytagname,
|
|
onclick: onclick(info),
|
|
}))
|
|
];
|
|
},
|
|
}
|
|
|
|
const SubscribedLeftLobbies = {
|
|
view() {
|
|
return [
|
|
m('h5.lefttitle', 'subscribed:'),
|
|
m(LobbyList, {
|
|
rooms: sortLobbies(Object.values(ChatRoomsModel.subscribedRooms)),
|
|
tagname: '.leftlobby.subscribed',
|
|
lobbytagname:'leftname',
|
|
onclick: ChatLobbyModel.switchToEvent,
|
|
})
|
|
]
|
|
}
|
|
};
|
|
|
|
const SubscribedLobbies = {
|
|
view() {
|
|
return m('.widget', [
|
|
m('h3', 'Subscribed chat rooms'),
|
|
m(LobbyList, {
|
|
rooms: sortLobbies(Object.values(ChatRoomsModel.subscribedRooms)),
|
|
tagname:'.lobby.subscribed',
|
|
onclick: ChatLobbyModel.switchToEvent,
|
|
}),
|
|
])
|
|
}
|
|
}
|
|
|
|
const PublicLeftLobbies = {
|
|
view() {
|
|
return [
|
|
m('h5.lefttitle', 'public:'),
|
|
m(LobbyList, {
|
|
rooms: Object.values(ChatRoomsModel.allRooms).filter(info => !ChatRoomsModel.subscribed(info)),
|
|
tagname: '.leftlobby.public',
|
|
lobbytagname: 'leftname',
|
|
onclick: ChatLobbyModel.setupEvent,
|
|
}),
|
|
]
|
|
}
|
|
}
|
|
|
|
const PublicLobbies = () => {
|
|
return m('.widget', [
|
|
m('h3', 'Public chat rooms'),
|
|
m(LobbyList, {
|
|
rooms: ChatRoomsModel.allRooms.filter(info => !ChatRoomsModel.subscribed(info)),
|
|
tagname: '.lobby.public',
|
|
onclick: ChatLobbyModel.setupEvent,
|
|
}),
|
|
])
|
|
};
|
|
|
|
const LobbyName = () => {
|
|
return m('h3.lobbyName',
|
|
ChatLobbyModel.isSubscribed ? [
|
|
m('span.chatusername',ChatLobbyModel.lobby_user),
|
|
m('span.chatatchar','@'),
|
|
] : [],
|
|
m('span.chatlobbyname',ChatLobbyModel.currentLobby.lobby_name),
|
|
m.route.param('subaction') !== 'setup' ? [
|
|
m('i.fas.fa-cog.setupicon', {
|
|
title: 'configure lobby',
|
|
onclick:() => m.route.set('/chat/:lobby/:subaction', {
|
|
lobby: m.route.param('lobby'),
|
|
subaction: 'setup'
|
|
}, { replace: true })
|
|
})
|
|
] : [],
|
|
ChatLobbyModel.isSubscribed ? [
|
|
m('i.fas.fa-sign-out-alt.leaveicon', {
|
|
title: 'leaving lobby',
|
|
onclick:() => ChatLobbyModel.unsubscribeChatLobby(m.route.param('lobby'), () => {
|
|
m.route.set('/chat', null, { replace: true });
|
|
})
|
|
})
|
|
] : []
|
|
);
|
|
};
|
|
|
|
// ***************************** Page Layouts ******************************
|
|
|
|
const Layout = () => {
|
|
return {
|
|
view: () => m('.tab-page', [
|
|
m(SubscribedLobbies),
|
|
PublicLobbies(),
|
|
])
|
|
}
|
|
};
|
|
|
|
const LayoutSingle = () => {
|
|
return {
|
|
oninit: () => ChatLobbyModel.loadLobby(m.route.param('lobby')),
|
|
view: vnode => m('.tab-page', [
|
|
LobbyName(),
|
|
m('.lobbies',
|
|
m(SubscribedLeftLobbies),
|
|
m(PublicLeftLobbies),
|
|
),
|
|
m('.messages', ChatLobbyModel.messages),
|
|
m('.rightbar', ChatLobbyModel.users),
|
|
m('.chatMessage', {}, m("textarea.chatMsg", {
|
|
placeholder: 'enter new message and press return to send',
|
|
onkeydown: e => {
|
|
if (e.code==='Enter') {
|
|
var msg = e.target.value;
|
|
e.target.value = ' sending ... ';
|
|
ChatLobbyModel.sendMessage(msg, () => e.target.value='');
|
|
return false;
|
|
}
|
|
}
|
|
})
|
|
),
|
|
]),
|
|
};
|
|
};
|
|
|
|
const LayoutSetup = () => {
|
|
let ownIds = [];
|
|
return {
|
|
oninit: () => people_util.ownIds(data => ownIds = data),
|
|
view: vnode => m('.tab-page', [
|
|
LobbyName(),
|
|
m('.lobbies',
|
|
m(SubscribedLeftLobbies),
|
|
m(PublicLeftLobbies),
|
|
),
|
|
m('.setup', [
|
|
m('h5.selectidentity','Select identity to use'),
|
|
ownIds.map(nick => m(
|
|
'.identity' + (ChatLobbyModel.currentLobby.gxs_id === nick ? '.selectedidentity' : ''),
|
|
{
|
|
onclick: () => ChatLobbyModel.setupAction(m.route.param('lobby'), nick)
|
|
},
|
|
rs.userList.username(nick),
|
|
)),
|
|
]),
|
|
]),
|
|
};
|
|
};
|
|
|
|
/*
|
|
/rsMsgs/initiateDistantChatConnexion
|
|
* @param[in] to_pid RsGxsId to start the connection
|
|
* @param[in] from_pid owned RsGxsId who start the connection
|
|
* @param[out] pid distant chat id
|
|
* @param[out] error_code if the connection can't be stablished
|
|
* @param[in] notify notify remote that the connection is stablished
|
|
*/
|
|
const LayoutCreateDistant = () => {
|
|
let ownIds = [];
|
|
return {
|
|
oninit: () => people_util.ownIds(data => ownIds = data),
|
|
view: vnode => m('.tab-page', [
|
|
m('.createDistantChat', [
|
|
'choose identitiy to chat with ',
|
|
rs.userList.username(m.route.param('lobby')),
|
|
ownIds.map(id => m('.identity', {
|
|
onclick: () => rs.rsJsonApiRequest(
|
|
'/rsMsgs/initiateDistantChatConnexion',{
|
|
to_pid:m.route.param('lobby'),
|
|
from_pid:id,
|
|
notify: true
|
|
}, result => {
|
|
console.info('initiateDistantChatConnexion', result);
|
|
m.route.set('/chat/:lobbyid',{lobbyid:result.pid});
|
|
}
|
|
)
|
|
}, rs.userList.username(id))),
|
|
]),
|
|
]),
|
|
};
|
|
};
|
|
|
|
module.exports = {
|
|
oninit: () => {
|
|
ChatRoomsModel.loadSubscribedRooms();
|
|
ChatRoomsModel.loadPublicRooms();
|
|
},
|
|
view: (vnode) => {
|
|
if (m.route.param('lobby') === undefined) {
|
|
return m(Layout);
|
|
} else if (m.route.param('subaction') === 'setup') {
|
|
return m(LayoutSetup);
|
|
} else if (m.route.param('subaction') === 'createdistantchat') {
|
|
return m(LayoutCreateDistant);
|
|
} else {
|
|
return m(LayoutSingle);
|
|
}
|
|
}
|
|
};
|