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