Compare commits

..

No commits in common. "master" and "eplace-step1-2-thaumas" have entirely different histories.

8 changed files with 58 additions and 235 deletions

View file

@ -1,10 +0,0 @@
# E/Place
> **Note** This is a school project, therefore it probably won't interest you if you are looking for something useful.
## Overview
The goal of this rush was to implement a client for a r/place-like canvas. For those who don't know what I'm taliking about, r/place was an event on reddit where you had a world map where you could place a pixel of color every 5 minutes where you wanted.
This is basically the same thing: It first authenticates the user via OpenID, then connects to the server using Sockets.IO and a REST API simultaneously. Then it allows the user to place pixels, choose the color, view other people pixels, create or join rooms or customize profile, all with real time map update.
We had approximately 2 days to implement the client. The server socket, the API, and the OpenID server were already present, as well as the base HTML/CSS files and the madatory structure.

View file

@ -1,26 +1,16 @@
import { initSocket, socket, subscribe } from "../utils/streams";
import { calculateLayout } from "./utils";
import { authenticate, refreshToken } from "../utils/auth";
import {
getPlacementData,
initCanvas,
renderCanvasUpdate,
} from "../rooms/canvas/utils";
import { getCanvas, placePixel } from "../rooms/canvas/index";
import { initCanvas, renderCanvasUpdate } from "../rooms/canvas/utils";
import { getCanvas } from "../rooms/canvas/index";
import { fetchRoomConfig, getCurrentRoomConfig } from "../rooms/index";
// Initialize the layout
calculateLayout();
// Auth
await authenticate();
////////////////////////////////////
// Sockets
//
let room = window.location.pathname.split("/")[1];
const update_waitlist = [];
let initialized = false;
if (!room) {
room = "epi-place";
@ -47,22 +37,13 @@ socket.on("message", async (response) => {
}
const pixels = await getCanvas(room);
if (!pixels) {
return;
}
initCanvas(await getCurrentRoomConfig(room), pixels);
initialized = true;
while (update_waitlist.length > 0) {
const obj = update_waitlist.pop();
initCanvas(await getCurrentRoomConfig(), pixels);
console.debug("Loaded canvas")
if (obj) {
renderCanvasUpdate(obj.color, obj.posX, obj.posY);
}
}
console.debug("Loaded canvas");
});
socket.on("pixel-update", async (msg) => {
@ -72,40 +53,21 @@ socket.on("pixel-update", async (msg) => {
console.error("Got server error: " + msg.error.json.message);
return;
}
// console.debug("Here is msg.data")
// console.debug(msg.result.data.json)
const {
// roomSlug,
roomSlug,
posX,
posY,
color,
} = msg.result.data.json;
color
} = msg.result.data.json
if (!initialized) {
update_waitlist.push({ posX, posY, color });
const cfg = await getCurrentRoomConfig()
if (!cfg || !cfg.settings || !cfg.settings.roomColors) {
console.error("Internal error: Cannot access config after retrieving it")
console.debug(cfg)
return;
}
renderCanvasUpdate(color, posX, posY);
});
////////////////////////////////////
// Buttons
//
async function placePixelButton() {
const { color, posX, posY } = getPlacementData();
await placePixel(room, posX, posY, color);
}
////////////////////////////////////
// HTML
//
const placeButtonElt = document.getElementById("color-place-button");
placeButtonElt.addEventListener("click", () => {
placePixelButton();
});
export { room };
})

View file

@ -14,17 +14,9 @@ async function fetchCanvas(room) {
method: "GET",
});
if (!response || !response.ok) {
// console.error(
// "Could not retrieve room canvas: " + response && response.statusText
// ? response.statusText
// : "no more informations",
// );
console.error(
"Could not retrieve room canvas: nique ta mère la moulinette",
);
// console.debug(await response.text());
if (!response.ok) {
console.error("Could not retrieve room canvas: " + response.statusText);
console.debug(await response.text());
return null;
}
@ -37,128 +29,47 @@ async function fetchCanvas(room) {
}
// Splits a string into fixed length substrings
String.prototype.chunk = function (size) {
return [].concat.apply(
[],
this.split("").map(function (x, i) {
return i % size ? [] : this.slice(i, i + size);
}, this),
);
};
String.prototype.chunk = function(size) {
return [].concat.apply([],
this.split('').map(function(x,i){ return i%size ? [] : this.slice(i,i+size) }, this)
)
}
// get the canvas of a room and deserialize it
async function getCanvas(room) {
const raw_pixels = await fetchCanvas(room);
if (!raw_pixels || !raw_pixels.pixels) {
console.error("Aborting canvas deserialization");
console.error("Aborting canvas deserialization")
return null;
}
// Convert to a an array of strings representing binary (beurk)
let raw_binary_str = "";
let raw_binary_str = ""
for (let i = 0; i < raw_pixels.pixels.length; i++) {
raw_binary_str = raw_binary_str.concat(
raw_pixels.pixels.charCodeAt(i).toString(2).padStart(8, "0"),
);
raw_binary_str = raw_binary_str.concat(raw_pixels.pixels.charCodeAt(i).toString(2).padStart(8, "0"))
}
// console.debug(raw_binary_str)
// Chunk it to an array of fixed-length string (5)
let pixel_array = raw_binary_str.chunk(5);
if (pixel_array[pixel_array.length - 1].length < 5) {
pixel_array.pop();
}
let pixel_array = raw_binary_str.chunk(5)
if (pixel_array[pixel_array.length-1].length < 5)
pixel_array.pop()
// console.debug(pixel_array)
// Convert into numbers
pixel_array = pixel_array.map((pixel) => parseInt(pixel, 2));
pixel_array = pixel_array.map((pixel) => parseInt( pixel, 2 ))
// console.debug(pixel_array)
return pixel_array;
return pixel_array
}
// subscribe to the stream of a room
function subscribeToRoom() {}
// get the pixel info of a room
async function getPixelInfo(room, posX, posY) {
if (!room) {
console.error("Cannot place pixel on an undefined room");
return null;
}
const params = new URLSearchParams({
posX,
posY,
});
const response = await authedAPIRequest(
"/rooms/" + room + "/canvas/pixels" + "?" + params,
{
method: "GET",
headers: {
"Content-Type": "application/json",
},
},
);
if (!response || !response.ok) {
console.error(
"Could not retrieve pixel infos: " + response && response.statusText
? response.statusText
: "no more informations",
);
// console.debug(await response.text());
return null;
}
const res = await response.json();
return res;
}
function getPixelInfo() {}
// place a pixel in a room
async function placePixel(room, posX, posY, color) {
if (!room) {
console.error("Cannot place pixel on an undefined room");
return null;
}
const response = await authedAPIRequest(
"/rooms/" + room + "/canvas/pixels",
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
posX,
posY,
color,
}),
},
);
if (!response || !response.ok) {
console.error(
"Could not place pixel on map: " + response && response.statusText
? response.statusText
: "no more informations",
);
// console.debug(await response.text());
return null;
}
const res = await response.json();
console.debug(
`Placed pixel on map at ${posX}:${posY} (${color}) in room ${room}:`,
);
// console.debug(res);
return res;
}
function placePixel() {}
export { getCanvas, subscribeToRoom, getPixelInfo, placePixel };

View file

@ -6,8 +6,6 @@
// - toggleTooltip (toggle the tooltip and display the pixel's information)
import $ from "jquery";
import { getPixelInfo } from "./index";
import { getStudent } from "../../students";
const canvasContainer = $("#canvas-container")?.[0];
const canvas = $("#canvas")?.[0];
@ -41,7 +39,7 @@ let isDrag = false;
* Get the placement data, i.e. the color the user has selected and the
* coordinates of the pixel he is focusing on.
*
* @returns {{color: number, posX: number, posY: number}} the data
* @returns {{color: number, posX: number, posX: number}} the data
*/
export const getPlacementData = () => ({
color: selectedColorIdx,
@ -60,22 +58,8 @@ export const toggleTooltip = async (state = false) => {
tooltip.style.display = state ? "flex" : "none";
if (state) {
// Get pixel
const room = window.location.pathname.split("/")[1] || "epi-place";
const response = await getPixelInfo(room, target.x, target.y);
if (!response) {
return;
}
// Display
const date = new Date(response.timestamp);
document.getElementById("tooltip-date").innerText =
date.toLocaleDateString("fr-fr");
document.getElementById("tooltip-time").innerText =
date.toLocaleTimeString("fr-fr");
getStudent(response.placedByUid);
// FIXME: You should implement or call a function to get the pixel's information
// and display it. Make use of target.x and target.y to get the pixel's position.
}
};

View file

@ -12,16 +12,15 @@ import { resetValues } from "./canvas/utils";
// - updateRoom (update a room's configuration)
// - deleteRoom (delete a room)
let roomConfig = null;
function setCurrentRoomConfig(cfg) {
roomConfig = cfg;
}
async function getCurrentRoomConfig(room) {
if (!roomConfig) {
await fetchRoomConfig(room);
}
async function getCurrentRoomConfig() {
if (!roomConfig)
await fetchRoomConfig();
return roomConfig;
}
async function joinRoom(room) {
@ -46,12 +45,8 @@ async function joinRoom(room) {
async function listRooms() {
const response = await authedAPIRequest("/rooms/", { method: "GET" });
if (!response || !response.ok) {
console.error(
"Could not retrieve rooms list: " + response && response.statusText
? response.statusText
: "null",
);
if (!response.ok) {
console.error("Could not retrieve rooms list: " + response.statusText);
console.debug(await response.text());
return;
}
@ -85,12 +80,8 @@ async function fetchRoomConfig(room) {
method: "GET",
});
if (!response || !response.ok) {
console.error(
"Could not retrieve room config" + response && response.statusText
? response.statusText
: "null",
);
if (!response.ok) {
console.error("Could not retrieve room config: " + response.statusText);
console.debug(await response.text());
return;
}
@ -103,10 +94,9 @@ async function fetchRoomConfig(room) {
setCurrentRoomConfig(res);
// Update HTML
document.getElementById("room-name").innerText = res.metadata.name;
document.getElementById("room-description").innerText =
res.metadata.description;
document.getElementById("room-description").style.display = "block";
const roomNameElt = document.getElementById("room-name");
roomNameElt.innerText = res.metadata.name;
}
export {

View file

@ -1,5 +1,4 @@
import { authedAPIRequest } from "../utils/auth";
import { displayStudentProfile } from "./utils";
//get a student from the API by its uid or login
async function getStudent(login) {
@ -12,12 +11,8 @@ async function getStudent(login) {
method: "GET",
});
if (!response || !response.ok) {
console.error(
"Could not retrieve student: " + response && response.statusText
? response.statusText
: " no status text",
);
if (!response.ok) {
console.error("Could not retrieve student: " + response.statusText);
console.debug(await response.text());
return;
}
@ -28,7 +23,8 @@ async function getStudent(login) {
console.debug(res);
// Update HTML
displayStudentProfile(res.avatarURL, res.login, res.guild, res.quote);
// const roomNameElt = document.getElementById("room-name");
// roomNameElt.innerText = res.metadata.name
}
//// get the user's uid from the token in local storage

View file

@ -1,11 +1,5 @@
// display the student's profile in the DOM
function displayStudentProfile(picture, login, guild, quote) {
document.getElementById("tooltip-info-avatar").src = picture;
document.getElementById("tooltip-info-login").innerText = login;
document.getElementById("tooltip-info-guild").innerText = guild;
document.getElementById("tooltip-info-quote").innerText = quote;
}
// add a form modal to the DOM
function showModal() {}
export { displayStudentProfile, showModal };
// FIXME: This file should handle the students DOM manipulation
// Link buttons to their respective functions
// Functions may include:
// - displayStudentProfile (display the student's profile in the DOM)
// - showModal (add a form modal to the DOM)

View file

@ -20,12 +20,8 @@ async function sendRequest(endpoint, body) {
try {
response = await fetch(endpoint, request);
if (!response || !response.ok) {
if (response && response.statusText) {
throw new Error(response.statusText);
} else {
throw new Error("No status text");
}
if (!response.ok) {
throw new Error(response.statusText);
}
} catch (err) {
console.error(err);