Implement user profiles.
This commit is contained in:
parent
02be8a41b6
commit
5e9e839480
@ -231,9 +231,9 @@ impl App {
|
||||
)).await.ok();
|
||||
}
|
||||
|
||||
QRPacketData::RUserInfo(response) => {
|
||||
QRPacketData::RUserProfile(response) => {
|
||||
socket.send(Message::Binary(
|
||||
encode_response(CODE_USER_INFO, response.encode())
|
||||
encode_response(CODE_USER_PROFILE, response.encode())
|
||||
)).await.ok();
|
||||
}
|
||||
|
||||
|
@ -414,18 +414,6 @@ pub async fn thread_system(mut app:App, bus:Bus<protocol::QRPacket>)
|
||||
QRPacketData::QSessionList(request) => {
|
||||
app.log.log("Request: Session List");
|
||||
|
||||
println!("range {} {}", request.filter.start, request.filter.count);
|
||||
|
||||
if let Some(value) = request.filter.is_complete {
|
||||
println!("is_complete {}", value);
|
||||
}
|
||||
if let Some(value) = request.filter.is_live {
|
||||
println!("is_live {}", value);
|
||||
}
|
||||
if let Some(value) = request.filter.is_player {
|
||||
println!("is_player {}", value);
|
||||
}
|
||||
|
||||
let mut response = PacketSessionListResponse::new();
|
||||
response.records = filter_sessions(&app, user_id, request.filter);
|
||||
Some(QRPacket::new(qr.id, QRPacketData::RSessionList(response)))
|
||||
@ -923,11 +911,11 @@ pub async fn thread_system(mut app:App, bus:Bus<protocol::QRPacket>)
|
||||
Some(QRPacket::new(qr.id, QRPacketData::RUserList(response)))
|
||||
}
|
||||
|
||||
// UserInfo
|
||||
QRPacketData::QUserInfo(request) => {
|
||||
app.log.log("Request: User Info");
|
||||
// UserProfile
|
||||
QRPacketData::QUserProfile(request) => {
|
||||
app.log.log("Request: User Profile");
|
||||
|
||||
let mut response = PacketUserInfoResponse::new();
|
||||
let mut response = PacketUserProfileResponse::new();
|
||||
|
||||
if let Some(uid) = app.user_handle.get(&request.handle.to_lowercase().as_bytes()).cloned() {
|
||||
response.status = STATUS_OK;
|
||||
@ -938,14 +926,22 @@ pub async fn thread_system(mut app:App, bus:Bus<protocol::QRPacket>)
|
||||
}
|
||||
} else {
|
||||
response.status = STATUS_ERROR;
|
||||
}
|
||||
};
|
||||
|
||||
// Get user sessions
|
||||
if response.status == STATUS_OK {
|
||||
|
||||
let mut filter = SessionFilter::new();
|
||||
filter.count = 30;
|
||||
filter.player = [Some(request.handle.to_lowercase()), None];
|
||||
|
||||
response.history = filter_sessions(&app, user_id, filter);
|
||||
response.history.sort_by(|a, b| {
|
||||
a.is_complete.cmp(&b.is_complete)
|
||||
});
|
||||
}
|
||||
|
||||
Some(QRPacket::new(qr.id, QRPacketData::RUserInfo(response)))
|
||||
Some(QRPacket::new(qr.id, QRPacketData::RUserProfile(response)))
|
||||
}
|
||||
|
||||
// InviteAcquire
|
||||
|
@ -198,6 +198,16 @@ pub async fn handle_ws(ws:WebSocketStream<TokioIo<Upgraded>>, args:HttpServiceAr
|
||||
).ok();
|
||||
}
|
||||
|
||||
CODE_USER_PROFILE => match PacketUserProfile::decode(&data, &mut index) {
|
||||
Ok(packet) => {
|
||||
args.bus.send(
|
||||
bus_ds,
|
||||
QRPacket::new(conn_id, QRPacketData::QUserProfile(packet))
|
||||
).ok();
|
||||
}
|
||||
Err(_) => { println!("error: packet decode failed."); }
|
||||
}
|
||||
|
||||
CODE_INVITE_LIST => {
|
||||
args.bus.send(
|
||||
bus_ds,
|
||||
|
@ -43,7 +43,7 @@ pub const CODE_CHALLENGE_ANSWER :u16 = 0x0061;
|
||||
pub const CODE_CHALLENGE_LIST :u16 = 0x0062;
|
||||
|
||||
pub const CODE_USER_LIST :u16 = 0x0100;
|
||||
pub const CODE_USER_INFO :u16 = 0x0101;
|
||||
pub const CODE_USER_PROFILE :u16 = 0x0102;
|
||||
|
||||
pub const CODE_INVITE_LIST :u16 = 0x0180;
|
||||
pub const CODE_INVITE_ACQUIRE :u16 = 0x0181;
|
||||
|
@ -29,8 +29,8 @@ pub enum QRPacketData {
|
||||
QUserList,
|
||||
RUserList(PacketUserListResponse),
|
||||
|
||||
QUserInfo(PacketUserInfo),
|
||||
RUserInfo(PacketUserInfoResponse),
|
||||
QUserProfile(PacketUserProfile),
|
||||
RUserProfile(PacketUserProfileResponse),
|
||||
|
||||
QAccountInfo(PacketAccountInfo),
|
||||
RAccountInfo(PacketAccountInfoResponse),
|
||||
|
@ -19,7 +19,7 @@ mod challenge_answer; pub use challenge_answer::*;
|
||||
mod challenge_list; pub use challenge_list::*;
|
||||
|
||||
mod user_list; pub use user_list::*;
|
||||
mod user_info; pub use user_info::*;
|
||||
mod user_profile; pub use user_profile::*;
|
||||
|
||||
mod account_info; pub use account_info::*;
|
||||
mod account_update; pub use account_update::*;
|
||||
|
@ -6,11 +6,11 @@ use super::{
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PacketUserInfo {
|
||||
pub struct PacketUserProfile {
|
||||
pub handle:String,
|
||||
|
||||
}
|
||||
impl PacketUserInfo {
|
||||
impl PacketUserProfile {
|
||||
pub fn new() -> Self
|
||||
{
|
||||
Self {
|
||||
@ -18,7 +18,7 @@ impl PacketUserInfo {
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Packet for PacketUserInfo {
|
||||
impl Packet for PacketUserProfile {
|
||||
type Data = Self;
|
||||
|
||||
fn decode(data:&Vec<u8>, index:&mut usize) -> Result<Self::Data, ()>
|
||||
@ -35,13 +35,13 @@ impl Packet for PacketUserInfo {
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PacketUserInfoResponse {
|
||||
pub struct PacketUserProfileResponse {
|
||||
pub status:u16,
|
||||
pub handle:String,
|
||||
pub is_online:bool,
|
||||
pub history:Vec<PacketSessionListResponseRecord>,
|
||||
}
|
||||
impl PacketUserInfoResponse {
|
||||
impl PacketUserProfileResponse {
|
||||
pub fn new() -> Self
|
||||
{
|
||||
Self {
|
||||
@ -52,7 +52,7 @@ impl PacketUserInfoResponse {
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Packet for PacketUserInfoResponse {
|
||||
impl Packet for PacketUserProfileResponse {
|
||||
type Data = Self;
|
||||
|
||||
fn encode(&self) -> Vec<u8>
|
@ -17,9 +17,18 @@ main.profile>header>h1 {
|
||||
|
||||
line-height: 3rem;
|
||||
font-size: 2.5rem;
|
||||
color: #909090;
|
||||
}
|
||||
main.profile>header>h1.online {
|
||||
color: #f0f0f0;
|
||||
}
|
||||
|
||||
main.profile>section.history {
|
||||
width: 100%;
|
||||
flex-grow: 1;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
main.profile>section.history>table {
|
||||
width: 100%;
|
||||
}
|
||||
|
@ -9,3 +9,7 @@ button.warn {
|
||||
}
|
||||
|
||||
span.monospace {font-family:monospace;}
|
||||
|
||||
a {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ const OpCode = {
|
||||
ChallengeList :0x0062,
|
||||
|
||||
UserList :0x0100,
|
||||
UserInfo :0x0101,
|
||||
UserProfile :0x0102,
|
||||
|
||||
InviteList :0x0180,
|
||||
InviteAcquire :0x0181,
|
||||
|
@ -467,7 +467,7 @@ const SCENES = {
|
||||
let table = document.createElement("table");
|
||||
table.setAttribute("id", "content");
|
||||
table.setAttribute("class", "list session");
|
||||
table.appendChild(UI.session_table(this.data));
|
||||
table.appendChild(UI.session_table_resume(this.data));
|
||||
UI.maincontent(table);
|
||||
|
||||
UI.page_indicator((this.data.length > 0)? 1 : 0, this.data.length, this.data.length);
|
||||
@ -721,24 +721,32 @@ const SCENES = {
|
||||
Profile:class{
|
||||
constructor() {
|
||||
this.handle = "";
|
||||
this.is_online = false;
|
||||
this.stats = {
|
||||
games_played: 0,
|
||||
};
|
||||
this.history = [ ];
|
||||
}
|
||||
preload(data) {
|
||||
this.data.handle = data.handle;
|
||||
this.handle = data.handle;
|
||||
|
||||
MESSAGE_COMPOSE([
|
||||
PACK.u16(OpCode.UserInfo),
|
||||
PACK.string(this.data.handle, PACK.u8),
|
||||
PACK.u16(OpCode.UserProfile),
|
||||
PACK.string(this.handle, PACK.u8),
|
||||
]);
|
||||
return true;
|
||||
}
|
||||
load(msg) {
|
||||
if(msg.code != OpCode.UserInfo) {
|
||||
if(msg.code == OpCode.UserProfile) {
|
||||
if(msg.data.status != Status.Ok) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
this.handle = msg.handle;
|
||||
this.handle = msg.data.handle;
|
||||
this.is_online = msg.data.is_online;
|
||||
this.history = msg.data.history;
|
||||
|
||||
UI.mainmenu_account(this.handle, "profile");
|
||||
|
||||
@ -755,7 +763,12 @@ const SCENES = {
|
||||
|
||||
let header = document.createElement("header");
|
||||
let title = document.createElement("h1");
|
||||
title.innerText = this.handle;
|
||||
if(this.is_online) {
|
||||
title.innerText = this.handle + " ●";
|
||||
title.setAttribute("class", "online");
|
||||
} else {
|
||||
title.innerText = this.handle + " ○";
|
||||
}
|
||||
header.appendChild(title);
|
||||
MAIN.appendChild(header);
|
||||
|
||||
@ -763,7 +776,11 @@ const SCENES = {
|
||||
|
||||
let container_history = document.createElement("section");
|
||||
container_history.setAttribute("class", "history");
|
||||
|
||||
let table_history = document.createElement("table");
|
||||
table_history.setAttribute("id", "history");
|
||||
table_history.setAttribute("class", "list session");
|
||||
table_history.appendChild(UI.session_table_history(this.history));
|
||||
|
||||
container_history.appendChild(table_history);
|
||||
MAIN.appendChild(container_history);
|
||||
|
@ -491,6 +491,100 @@ function MESSAGE(event) {
|
||||
}
|
||||
} break;
|
||||
|
||||
case OpCode.UserProfile: {
|
||||
data = {
|
||||
status:0,
|
||||
handle:"",
|
||||
is_online:false,
|
||||
history:[ ],
|
||||
};
|
||||
|
||||
// Status
|
||||
result = UNPACK.u16(bytes, index);
|
||||
index = result.index;
|
||||
data.status = result.data;
|
||||
|
||||
// Flags
|
||||
result = UNPACK.u16(bytes, index);
|
||||
index = result.index;
|
||||
let flags = result.data;
|
||||
data.is_online = (flags & 1) != 0;
|
||||
|
||||
// Handle
|
||||
result = UNPACK.string(bytes, index, UNPACK.u8);
|
||||
index = result.index;
|
||||
data.handle = result.data;
|
||||
|
||||
// History
|
||||
result = UNPACK.u16(bytes, index);
|
||||
index = result.index;
|
||||
let length = result.data;
|
||||
|
||||
for(let i = 0; i < length; ++i) {
|
||||
let record = {
|
||||
token: new Uint8Array(8),
|
||||
dawn: "",
|
||||
dusk: "",
|
||||
turn: 0,
|
||||
moves: [ ],
|
||||
viewers: 0,
|
||||
player: false,
|
||||
is_turn: false,
|
||||
is_complete: 0,
|
||||
};
|
||||
|
||||
// Token
|
||||
if(index <= bytes.length + 8) {
|
||||
for(let i = 0; i < 8; ++i) {
|
||||
record.token[i] = bytes[index];
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Flags
|
||||
result = UNPACK.u32(bytes, index);
|
||||
index = result.index;
|
||||
let flags = result.data;
|
||||
|
||||
record.player = flags & 3;
|
||||
record.is_turn = ((flags >> 2) & 1) != 0;
|
||||
record.is_complete = ((flags >> 3) & 3);
|
||||
|
||||
// Dawn handle
|
||||
result = UNPACK.string(bytes, index);
|
||||
index = result.index;
|
||||
record.dawn = result.data;
|
||||
|
||||
// Dusk handle
|
||||
result = UNPACK.string(bytes, index);
|
||||
index = result.index;
|
||||
record.dusk = result.data;
|
||||
|
||||
// Turn number
|
||||
result = UNPACK.u16(bytes, index);
|
||||
index = result.index;
|
||||
record.turn = result.data;
|
||||
|
||||
// Last moves
|
||||
result = UNPACK.u32(bytes, index);
|
||||
index = result.index;
|
||||
let moves = result.data;
|
||||
for(let m = 0; m < 4; ++m) {
|
||||
if((moves & 0x80) != 0) {
|
||||
record.moves.push(moves & 0xFF);
|
||||
moves >>= 8;
|
||||
}
|
||||
}
|
||||
|
||||
// Reviewer count
|
||||
result = UNPACK.u32(bytes, index);
|
||||
index = result.index;
|
||||
record.viewers = result.data;
|
||||
|
||||
data.history.push(record);
|
||||
}
|
||||
} break;
|
||||
|
||||
case OpCode.InviteList: {
|
||||
console.log("RECV InviteList");
|
||||
|
||||
|
36
www/js/ui.js
36
www/js/ui.js
@ -10,7 +10,14 @@ const UI = {
|
||||
return document.createTextNode(value);
|
||||
},
|
||||
|
||||
button(text, callback, select=false) {
|
||||
link(text, callback=null) {
|
||||
let link = document.createElement("a");
|
||||
link.innerText = text;
|
||||
if(callback !== null) { link.addEventListener("click", callback); }
|
||||
return link;
|
||||
},
|
||||
|
||||
button(text, callback=null, select=false) {
|
||||
let button = document.createElement("button");
|
||||
button.innerText = text;
|
||||
if(select) { button.setAttribute("class", "selected"); }
|
||||
@ -246,8 +253,14 @@ const UI = {
|
||||
buttons.push(UI.button(LANG("view"), spectate_callback));
|
||||
}
|
||||
|
||||
let dawn = UI.text(record.dawn);
|
||||
let dusk = UI.text(record.dusk);
|
||||
let callback_profile_dawn = function() { SCENE.load(SCENES.Profile, { handle: this.handle }); }
|
||||
callback_profile_dawn = callback_profile_dawn.bind({ handle: record.dawn });
|
||||
|
||||
let callback_profile_dusk = function() { SCENE.load(SCENES.Profile, { handle: this.handle }); }
|
||||
callback_profile_dusk = callback_profile_dusk.bind({ handle: record.dusk });
|
||||
|
||||
let dawn = UI.link(record.dawn, callback_profile_dawn);
|
||||
let dusk = UI.link(record.dusk, callback_profile_dusk);
|
||||
|
||||
let moves = document.createElement("canvas");
|
||||
setTimeout(UI.draw_play_icons, 10, moves, record.moves);
|
||||
@ -304,8 +317,13 @@ const UI = {
|
||||
let handle = records[r].dawn;
|
||||
if(records[r].player == 1) { handle = records[r].dusk; }
|
||||
|
||||
let callback_profile = function() { SCENE.load(SCENES.Profile, { handle: this.handle }); }
|
||||
callback_profile = callback_profile.bind({ handle: handle });
|
||||
|
||||
let link_handle = UI.link(handle, callback_profile);
|
||||
|
||||
rows.push([
|
||||
UI.text(handle),
|
||||
link_handle,
|
||||
UI.text(records[r].turn),
|
||||
UI.text(records[r].viewers),
|
||||
buttons,
|
||||
@ -339,8 +357,14 @@ const UI = {
|
||||
|
||||
buttons.push(UI.button(LANG("review"), view_callback));
|
||||
|
||||
let dawn = UI.text(record.dawn);
|
||||
let dusk = UI.text(record.dusk);
|
||||
let callback_profile_dawn = function() { SCENE.load(SCENES.Profile, { handle: this.handle }); }
|
||||
callback_profile_dawn = callback_profile_dawn.bind({ handle: record.dawn });
|
||||
|
||||
let callback_profile_dusk = function() { SCENE.load(SCENES.Profile, { handle: this.handle }); }
|
||||
callback_profile_dusk = callback_profile_dusk.bind({ handle: record.dusk });
|
||||
|
||||
let dawn = UI.link(record.dawn, callback_profile_dawn);
|
||||
let dusk = UI.link(record.dusk, callback_profile_dusk);
|
||||
|
||||
switch(record.is_complete) {
|
||||
case 1: dawn = UI.span([dawn], "c_dawn bold"); break;
|
||||
|
Loading…
x
Reference in New Issue
Block a user