Skip to content

Instantly share code, notes, and snippets.

@Elyx0

Elyx0/chat.js Secret

Created September 18, 2021 21:32
Chat.js EthGlobal March
function renderChat() {
manageNotLoggedInState();
window.badWordFilter = new Filter();
window.template = '<div class="message" style="padding-top:5px; word-break:break-word"><span class="c-msg-id" data-msg-id="" style="display:none"></span><img class="c-avatar" src="" width="18" height="18"><b class="c-name"></b><span class="c-message"></span><span class="c-time" style="display:none"></span><span class="c-type" data-chat-type="" style="display:none"><span class="c-user-id" data-user-id="" style="display:none"></span></div>';
window.messages = new List('chatbox', {
valueNames: [
'c-name',
'c-message',
'c-time',
{name: "c-avatar", attr: "src"},
{name: "c-type", attr: "data-chat-type"},
{name: "c-user-id", attr: "data-user-id"},
{name: "c-msg-id", attr: "data-msg-id"},
],
item: window.template,
indexAsync: true,
}, []);
window.messages.sort("c-time", {order: "asc"});
setInterval(function() {
hideJoinAvatars();
}, 100)
$('.auth-visible').hide();
$("#openProfile").click(function() {
openApp("user");
})
$("#profile-avatar, #profile-switch").click(function() {
window.USER.gender = ["male", "female"].filter((g) => g != window.USER.gender)[0];
updateProfileImage();
})
$("#profile-generate").click(function() {
updateProfileImage(true);
})
$("#sendHeart").click(function() {
window.chat.sendHearts();
})
$("#profile-save").click(function() {
window.USER.name = $("#profile-name").val();
window.USER.country = $("#profile-country").val();
$(".flag").text(countryToEmoji(window.USER.country||"US"))
window.chat.updatePresence(true);
updateProfileInfo();
});
$("#profile-verify").click(function() {
let fname = ($("#profile-fname").val() || "").trim();
let lname = ($("#profile-lname").val() || "").trim();
let email = ($("#profile-email").val() || "").trim();
if (fname.length > 1 && lname.length > 1 && email.length > 2 && email.indexOf("@") !== -1) {
$.ajax({
url: "https://hack.ethglobal.co/ethernet",
type: "POST",
dataType: "json",
data: {first_name: fname, last_name: lname, email: email, summit: window.EG_EVENT.authSummit || "ETHGlobalTV"},
success: function(data){
if (data.success === true) {
$("#auth-msg").html("<b>Please check your email (and spam folder) for verification link!</b>").css("color", "red");
}
},
error: function(data) {
alert("backend error, please retry in a couple of mins or email hello@ethglobal.co")
}
});
} else {
alert("invalid name or email")
}
});
db.collection(`chats/${window.ROOM}/banlist`).onSnapshot(function(querySnapshot) {
querySnapshot.docChanges().forEach(function(change) {
let data = change.doc.data();
if (change.type == "added" || change.type == "modified") {
window.state.banlist[data.id] = data;
} else if (change.type == "removed") {
delete window.state.banlist[data.id];
}
});
})
db.collection(`chats/${window.ROOM}/messages`).orderBy("time", "desc").limit(300).onSnapshot(function(querySnapshot) {
querySnapshot.docChanges().forEach(function(change) {
let data = change.doc.data();
if (change.type == "added") {
try {
if ((window.state.banlist[data.user_id]||{}).banned !== true) {
renderMessage(data, change.doc.id);
}
} catch(err) {
console.log("message renderError", err, data);
}
} else if (change.type == "removed") {
window.messages.remove("c-msg-id", change.doc.id);
} else if (change.type == "modified") {
}
});
$('#messages').scrollTop($('#messages')[0].scrollHeight);
})
db.collection("users").orderBy("last_active", "desc").onSnapshot(function(querySnapshot) {
querySnapshot.docChanges().forEach(function(change) {
let data = change.doc.data();
let prev = {...window.activeUsers[data["id"]]};
if (!data.id) {return}
window.activeUsers[data["id"]] = data;
updateMessages(data["id"]);
if (data.hearts > prev.hearts) {
queueHearts()
}
});
renderPresence();
})
db.collection(`chats/${window.ROOM}/sessions`).orderBy("last_active", "desc").limit(250).onSnapshot(function(querySnapshot) {
querySnapshot.docChanges().forEach(function(change) {
let data = change.doc.data();
if (!data.session) {return}
window.anonymousActiveUsers[data["session"]] = data;
});
})
db.collection(`chats`).doc(window.ROOM).onSnapshot(function(doc) {
let item = doc.data();
syncStage(item);
});
db.collection(`chats`).doc(window.ROOM).get().then(function(doc) {
let item = doc.data();
setBackground((Object.keys(window.themes).includes(item.theme))? item.theme: "defi");
if (item.action == "updateCaption") {
let isValid = Object.keys(window.state.agenda).includes(item.talkId)
if (isValid) {
updateCaption(item.talkId);
} else {
$("#subtitles").html("");
}
}
if (item.countdownTs > (new Date()).getTime()) {
clearInterval(window.state.countdownId);
window.state.countdownId = null;
window.state.countdownTs = item.countdownTs;
if (window.state.countdownTs > (new Date()).getTime()) {
window.state.countdownId = setInterval(displayCountdown, 1000);
$("#subtitles").slideDown("slow");
} else {
$("#subtitles").html("");
}
}
});
}
firebase.initializeApp({
apiKey: "AIzaSyDgX5-mlLFv50I8TZ4uKkd1WMjk7ajnlHs",
authDomain: "webchat-288900.firebaseapp.com",
databaseURL: "https://webchat-288900.firebaseio.com",
projectId: "webchat-288900",
storageBucket: "webchat-288900.appspot.com",
messagingSenderId: "604854920267",
appId: "1:604854920267:web:72213ed0501c8d842899ce",
measurementId: "G-QEQJD16M6B"
});
window.USER = {};
window.ROOM = window.EG_EVENT.room || "ethglobal";
window.activeUsers = {};
window.anonymousActiveUsers = {};
window.params = new URLSearchParams(window.location.search);
window.db = firebase.firestore();
function queueHearts() {
let rate = 3;
let period = 2;
let current = (new Date()).getTime()/1000;
let timeDelta = current - window.state.lastCheck;
window.state.lastCheck = current;
window.state.heartAllowance += timeDelta * (rate/period);
if (window.state.heartAllowance > rate) {
window.state.heartAllowance = rate;
}
if (window.state.heartAllowance < 1.0){
// console.log("discard")
} else {
let animate = function(t) {
return setTimeout(function() {
$("#hearts").hide();
window.state.heartActiveId = null;
}, t)
}
$("#hearts").show();
if (window.state.heartActiveId == null) {
window.state.heartActiveId = animate(1500);
}
window.state.heartAllowance -= 1;
}
}
function enableModeration() {
$(".message > .c-avatar").off("click")
$(".message > .c-avatar").on("click", function() {
let parent = $(this).parent();
let docId = $(parent).find('.c-msg-id').attr('data-msg-id');
let userId = $(parent).find('.c-user-id').attr('data-user-id');
let message = $(parent).find('.c-message').text();
let username = $(parent).find('.c-name').text();
openApp("mod")
$('#mod-usr').text(username);
$('#mod-msg').text(message);
$('#mod-block').attr('data-doc', userId);
$('#mod-del').attr('data-doc', docId);
});
$('#mod-del').off("click");
$('#mod-del').on("click", function() {
let msgId = $(this).attr("data-doc");
window.chat.deleteMessage(msgId);
});
$('#mod-block').off("click");
$('#mod-block').on("click", function() {
let userId = $(this).attr("data-doc");
window.chat.banUser(userId, true);
});
}
function renderPresence() {
let html = "";
let activeCount = 0;
let anonActiveCount = 0;
let sortedUsers = Object.values(window.activeUsers);
sortedUsers.sort(function(a, b) {
let aName = (a.name||"").toLowerCase().trim();
let bName = (b.name||"").toLowerCase().trim();
return (aName > bName) ? 1 : -1
})
let anonSortedUsers = Object.values(window.anonymousActiveUsers);
for(let u in anonSortedUsers) {
let user = anonSortedUsers[u];
let activeWindow = (new Date()).getTime()/1000 - 180;
if (user.last_active !== null && (user.last_active.seconds < activeWindow ) ) {
continue;
}
anonActiveCount += 1;
}
for(let u in sortedUsers) {
let user = sortedUsers[u];
let activeWindow = (new Date()).getTime()/1000 - 180;
if (user.last_active !== null && (user.last_active.seconds < activeWindow ) ) {
continue;
}
activeCount += 1;
html += `
<div class="message" style="padding-top:5px; display:flex;">
<div class="presence-name">
<img src="${generateAvatar(user)}" width="18" height="18">
<b>${DOMPurify.sanitize(user.name)}</b><br>
</div>
<div class="presence-country">
${countryToEmoji(user.country)}
</div>
</div>
`
}
$('.online-count').text(activeCount)
$('.total-count').text(activeCount+anonActiveCount);
$("#viewers-list").html(html);
hideJoinAvatars();
}
function hideJoinAvatars() {
// $('*[data-chat-Type="join"]').parent().find('.c-avatar').hide();
}
window.renderMessage = function(data, docId) {
let addObj = {
"c-time": (data.time) ? data.time.seconds + data.time.nanoseconds/10**9 : (new Date()).getTime()/1000,
"c-type": data.type,
"c-user-id": data.user_id,
"c-msg-id": docId,
"c-name": DOMPurify.sanitize(data.name.replace(/<[^>]*>/g, '')),
"c-avatar": generateAvatar(data),
"c-message": "<br>" + linkifyHtml(DOMPurify.sanitize(window.badWordFilter.clean(data.message)).replace(/(<([^>]+)>)/ig, ""), {target: "_blank"}),
};
if (data.type == "join") {
addObj["c-name"] = "";
addObj["c-message"] = DOMPurify.sanitize(data.name).replace(/(<([^>]+)>)/ig, "") + " joined the chat";
}
addObj["c-message"] = addObj["c-message"].slice(0, 500);
addObj["c-name"] = addObj["c-name"].slice(0, 50);
window.messages.add(addObj);
window.messages.sort("c-time", {order: "asc"});
}
function manageNotLoggedInState() {
let storage = window.localStorage;
let sessionId = storage.getItem("EGTVSessionID");
if (sessionId !== null) {
if (window.LOGGED_IN !== true) {
updateAnonPresence(storage.getItem("EGTVSessionNew") != "yes");
setInterval(updateAnonPresence, 30*1000);
}
} else {
storage.setItem("EGTVSessionID", guid());
storage.setItem("EGTVSessionNew", "yes");
setTimeout(function() {updateAnonPresence(true);}, 500);
setInterval(updateAnonPresence, 30*1000);
}
}
function updateAnonPresence(firstRun) {
let payload = {};
let sessionId = window.localStorage.getItem("EGTVSessionID");
if (sessionId == null || window.LOGGED_IN == true) {return;}
payload.session = sessionId;
payload.pulse = firebase.firestore.FieldValue.increment(1);
payload.last_active = firebase.firestore.FieldValue.serverTimestamp();
if (firstRun) {
payload.first_active = firebase.firestore.FieldValue.serverTimestamp();
}
window.db.doc(`chats/${window.ROOM}/sessions/${sessionId}`).set(payload, { merge: true });
}
window.chat = {
sendMessage: function(message, type) {
if (window.LOGGED_IN !== true) {return;}
if (message.length > 500) {alert("message too long");}
window.db.collection(`chats/${window.ROOM}/messages`).add({
name: (window.USER.name||"").slice(0, 50),
message: (message||"").slice(0, 500),
type: type||"message",
user_id: window.USER.id,
guid: window.USER.guid,
gender: window.USER.gender,
time: firebase.firestore.FieldValue.serverTimestamp(),
})
},
updatePresence: function(full) {
if (window.LOGGED_IN !== true) {return;}
let payload = (full === true) ? {...window.USER} : {};
payload.name = window.USER.name;
payload.last_active = firebase.firestore.FieldValue.serverTimestamp();
window.db.doc(`users/${window.USER.id}`).set(payload, { merge: true });
},
sendHearts: function() {
if (window.LOGGED_IN !== true) {return;}
let payload = {};
payload.hearts = firebase.firestore.FieldValue.increment(1);
window.db.doc(`users/${window.USER.id}`).set(payload, { merge: true });
},
broadcast: function(body) {
if (window.LOGGED_IN !== true) {return;}
window.db.doc(`chats/${window.ROOM}`).set(body, { merge: true });
},
deleteMessage: function(docId) {
if (window.LOGGED_IN !== true) {return;}
window.db.doc(`chats/${window.ROOM}/messages/${docId}`).delete();
},
banUser: function(userId, banned) {
if (window.LOGGED_IN !== true) {return;}
window.db.doc(`chats/${window.ROOM}/banlist/${userId}`).set({id: userId, banned: banned}, { merge: true });
},
}
function getUserMeta() {
$.get("http://ip-api.com/json/?fields=status,countryCode,country,timezone,city", function(resp) {
if (resp.status == "success") {
if (!window.USER.country) {
window.USER.country = resp.countryCode;
// updateProfileInfo();
}
}
})
}
function guid() {
return 'xxxxxxxxxxzxxxyxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
function countryToEmoji(cc) {
if (!cc) {return "";}
let chars = [...(cc.toUpperCase()||"").slice(0,2)].map(c => c.charCodeAt() + 127397);
return String.fromCodePoint(...chars);
}
function updateProfileImage(generate) {
if (generate) {
window.USER.guid = guid();
}
$('#profile-avatar').attr('src', generateAvatar(window.USER));
updateProfileInfo();
}
function generateAvatar(user) {
return `https://avatars.dicebear.com/api/${user.gender}/${user.guid}.svg?mood[]=happy`;
}
function updateMessages(userId) {
let items = window.messages.get('c-user-id', userId);
for (let i=0; i<items.length; i++) {
let user = window.activeUsers[userId];
let existing = items[i].values();
let payload = {"c-avatar": generateAvatar(user)};
if (existing["c-type"] !== "join") {
payload["c-name"] = user.name;
}
items[i].values(payload);
}
}
function updateProfileInfo() {
if (window.USER.name != null) {
$("#profile-name").val(window.USER.name);
}
if (window.USER.email != null) {
$("#profile-email").val(window.USER.email);
}
if (window.USER.country != null) {
$("#profile-country").val(window.USER.country||"US");
$(".flag").text(countryToEmoji(window.USER.country||"US"))
}
if (window.USER.name != null) {
let info = `<div style="border-bottom:1px solid #ccc; overflow: hidden; white-space: nowrap; cursor:pointer">
<img src="${generateAvatar(window.USER)}" width="32" height="32">
<b>${window.USER.name}</b>
</div>`
$("#profile").html(info)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment