Add user-generated invitation codes.
This commit is contained in:
parent
ddff618361
commit
b98c5055c5
7
server/src/app/invitation.rs
Normal file
7
server/src/app/invitation.rs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
pub type InviteToken = [u8; 12];
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct Invitation {
|
||||||
|
pub token:InviteToken,
|
||||||
|
pub parent:Option<u32>,
|
||||||
|
}
|
@ -16,6 +16,7 @@ pub mod connection; use connection::Connection;
|
|||||||
pub mod user; use user::User;
|
pub mod user; use user::User;
|
||||||
pub mod authentication; use authentication::Authentication;
|
pub mod authentication; use authentication::Authentication;
|
||||||
pub mod session; use session::{Session, SessionToken};
|
pub mod session; use session::{Session, SessionToken};
|
||||||
|
pub mod invitation; pub use invitation::{Invitation, InviteToken};
|
||||||
pub mod context;
|
pub mod context;
|
||||||
|
|
||||||
pub struct App {
|
pub struct App {
|
||||||
@ -32,6 +33,9 @@ pub struct App {
|
|||||||
pub auths:Trie<Authentication>,
|
pub auths:Trie<Authentication>,
|
||||||
pub sessions:Trie<Session>,
|
pub sessions:Trie<Session>,
|
||||||
|
|
||||||
|
pub invite_tokens:Trie<u32>,
|
||||||
|
pub invites:Pool<Invitation>,
|
||||||
|
|
||||||
pub contests:Vec<u32>,
|
pub contests:Vec<u32>,
|
||||||
|
|
||||||
pub session_time:Chain<SessionToken>,
|
pub session_time:Chain<SessionToken>,
|
||||||
@ -124,6 +128,9 @@ impl App {
|
|||||||
auths:Trie::new(),
|
auths:Trie::new(),
|
||||||
sessions,
|
sessions,
|
||||||
|
|
||||||
|
invite_tokens:Trie::new(),
|
||||||
|
invites:Pool::new(),
|
||||||
|
|
||||||
contests,
|
contests,
|
||||||
|
|
||||||
session_time,
|
session_time,
|
||||||
@ -230,6 +237,18 @@ impl App {
|
|||||||
)).await.ok();
|
)).await.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QRPacketData::RInviteList(response) => {
|
||||||
|
socket.send(Message::Binary(
|
||||||
|
encode_response(CODE_INVITE_LIST, response.encode())
|
||||||
|
)).await.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
QRPacketData::RInviteAcquire(response) => {
|
||||||
|
socket.send(Message::Binary(
|
||||||
|
encode_response(CODE_INVITE_ACQUIRE, response.encode())
|
||||||
|
)).await.ok();
|
||||||
|
}
|
||||||
|
|
||||||
QRPacketData::TestResult(response) => {
|
QRPacketData::TestResult(response) => {
|
||||||
socket.send(Message::Binary(
|
socket.send(Message::Binary(
|
||||||
encode_response(CODE_TEST_RESULT, response.encode())
|
encode_response(CODE_TEST_RESULT, response.encode())
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
pub const VERSION :&str = env!("GIT_HASH");
|
pub const VERSION :&str = env!("GIT_HASH");
|
||||||
pub const REGISTER_CODE :&str = "mountain";
|
//pub const REGISTER_CODE :&str = "mountain";
|
||||||
|
@ -142,7 +142,8 @@ pub async fn thread_system(mut app:App, bus:Bus<protocol::QRPacket>)
|
|||||||
app.log.log("Request: Register");
|
app.log.log("Request: Register");
|
||||||
|
|
||||||
let mut is_valid = true;
|
let mut is_valid = true;
|
||||||
if request.code != crate::config::REGISTER_CODE.as_bytes() { response.status = STATUS_BAD_CODE; is_valid = false; }
|
let invite = app.invite_tokens.get(&request.code).cloned();
|
||||||
|
if is_valid && invite.is_none() { response.status = STATUS_BAD_CODE; is_valid = false; }
|
||||||
if is_valid && request.handle.len() == 0 && request.handle.chars().count() <= 24 { response.status = STATUS_BAD_HANDLE; is_valid = false; }
|
if is_valid && request.handle.len() == 0 && request.handle.chars().count() <= 24 { response.status = STATUS_BAD_HANDLE; is_valid = false; }
|
||||||
if is_valid && request.secret.len() == 0 { response.status = STATUS_BAD_SECRET; is_valid = false; }
|
if is_valid && request.secret.len() == 0 { response.status = STATUS_BAD_SECRET; is_valid = false; }
|
||||||
|
|
||||||
@ -207,6 +208,14 @@ pub async fn thread_system(mut app:App, bus:Bus<protocol::QRPacket>)
|
|||||||
if let Some(conn) = app.connections.get_mut(qr.id as usize) {
|
if let Some(conn) = app.connections.get_mut(qr.id as usize) {
|
||||||
conn.auth = Some(response.token);
|
conn.auth = Some(response.token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove invitation from store.
|
||||||
|
if let Some(invite) = invite {
|
||||||
|
if let Some(invite) = app.invites.get(invite as usize).cloned() {
|
||||||
|
app.invite_tokens.unset(&invite.token);
|
||||||
|
}
|
||||||
|
app.invites.remove(invite as usize).ok();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(_) => { app.log.log("error: failed to generate salt.") }
|
Err(_) => { app.log.log("error: failed to generate salt.") }
|
||||||
@ -1000,6 +1009,95 @@ 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)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InviteAcquire
|
||||||
|
QRPacketData::QInviteAcquire => {
|
||||||
|
use crate::app::{Invitation, InviteToken};
|
||||||
|
app.log.log("Request: Invite Acquire");
|
||||||
|
|
||||||
|
let mut response = PacketInviteAcquireResponse::new();
|
||||||
|
|
||||||
|
if let Some(user_id) = user_id {
|
||||||
|
|
||||||
|
// Count number of invites acquired by user.
|
||||||
|
let mut count_invites = 0;
|
||||||
|
let invites = app.invites.list();
|
||||||
|
for id in invites {
|
||||||
|
if let Some(invite) = app.invites.get(id) {
|
||||||
|
if let Some(parent) = invite.parent {
|
||||||
|
if parent == user_id {
|
||||||
|
count_invites += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new invite if below threshold.
|
||||||
|
const MAX_INVITES :usize = 10;
|
||||||
|
if count_invites < MAX_INVITES {
|
||||||
|
let mut token = InviteToken::default();
|
||||||
|
loop {
|
||||||
|
rng.fill(&mut token).ok();
|
||||||
|
for byte in &mut token {
|
||||||
|
*byte = *byte % 62;
|
||||||
|
*byte += if *byte < 10 {
|
||||||
|
'0' as u8
|
||||||
|
} else if *byte < 36 {
|
||||||
|
'a' as u8 - 10
|
||||||
|
} else {
|
||||||
|
'A' as u8 - 36
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if app.invite_tokens.get(&token).is_none() { break }
|
||||||
|
}
|
||||||
|
|
||||||
|
let invite = Invitation {
|
||||||
|
token,
|
||||||
|
parent:Some(user_id),
|
||||||
|
};
|
||||||
|
|
||||||
|
let id = app.invites.add(invite);
|
||||||
|
app.invite_tokens.set(&token, id as u32);
|
||||||
|
|
||||||
|
response.status = STATUS_OK;
|
||||||
|
} else {
|
||||||
|
response.status = STATUS_ERROR;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
response.status = STATUS_NOAUTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(QRPacket::new(qr.id, QRPacketData::RInviteAcquire(response)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// InviteList
|
||||||
|
QRPacketData::QInviteList => {
|
||||||
|
app.log.log("Request: Invite List");
|
||||||
|
|
||||||
|
let mut response = PacketInviteListResponse::new();
|
||||||
|
|
||||||
|
if let Some(user_id) = user_id {
|
||||||
|
|
||||||
|
// Get user's invitations.
|
||||||
|
let invites = app.invites.list();
|
||||||
|
for id in invites {
|
||||||
|
if let Some(invite) = app.invites.get(id) {
|
||||||
|
if let Some(parent) = invite.parent {
|
||||||
|
if parent == user_id {
|
||||||
|
response.records.push(invite.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
response.status = STATUS_OK;
|
||||||
|
} else {
|
||||||
|
response.status = STATUS_NOAUTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(QRPacket::new(qr.id, QRPacketData::RInviteList(response)))
|
||||||
|
}
|
||||||
|
|
||||||
_ => { Some(QRPacket::new(0, QRPacketData::None)) }
|
_ => { Some(QRPacket::new(0, QRPacketData::None)) }
|
||||||
} {
|
} {
|
||||||
Some(response) => {
|
Some(response) => {
|
||||||
|
@ -198,6 +198,20 @@ pub async fn handle_ws(ws:WebSocketStream<TokioIo<Upgraded>>, args:HttpServiceAr
|
|||||||
).ok();
|
).ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CODE_INVITE_LIST => {
|
||||||
|
args.bus.send(
|
||||||
|
bus_ds,
|
||||||
|
QRPacket::new(conn_id, QRPacketData::QInviteList)
|
||||||
|
).ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
CODE_INVITE_ACQUIRE => {
|
||||||
|
args.bus.send(
|
||||||
|
bus_ds,
|
||||||
|
QRPacket::new(conn_id, QRPacketData::QInviteAcquire)
|
||||||
|
).ok();
|
||||||
|
}
|
||||||
|
|
||||||
_ => { }
|
_ => { }
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
** Status Codes
|
** Status Codes
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pub const STATUS_OK :u16 = 0x0000;
|
pub const STATUS_OK :u16 = 0x0000;
|
||||||
|
|
||||||
pub const STATUS_ERROR :u16 = 0x0001;
|
pub const STATUS_ERROR :u16 = 0x0001;
|
||||||
@ -21,7 +20,6 @@ pub const STATUS_NOT_IMPL :u16 = 0x00FF;
|
|||||||
/*
|
/*
|
||||||
** Operation Codes
|
** Operation Codes
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pub const CODE_HELLO :u16 = 0x0001;
|
pub const CODE_HELLO :u16 = 0x0001;
|
||||||
|
|
||||||
pub const CODE_REGISTER :u16 = 0x0010;
|
pub const CODE_REGISTER :u16 = 0x0010;
|
||||||
@ -46,15 +44,16 @@ 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_INFO :u16 = 0x0101;
|
||||||
//pub const CODE_USER_AWAIT_GET :u16 = 0x0110;
|
|
||||||
//pub const CODE_USER_AWAIT_SET :u16 = 0x0111;
|
pub const CODE_INVITE_LIST :u16 = 0x0180;
|
||||||
|
pub const CODE_INVITE_ACQUIRE :u16 = 0x0181;
|
||||||
|
|
||||||
pub const CODE_TEST_RESULT :u16 = 0xFFFF;
|
pub const CODE_TEST_RESULT :u16 = 0xFFFF;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Game Messages
|
** Game Messages
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pub const GMSG_ERROR :u8 = 0x00;
|
pub const GMSG_ERROR :u8 = 0x00;
|
||||||
pub const GMSG_PLAY_MOVE :u8 = 0x01;
|
pub const GMSG_PLAY_MOVE :u8 = 0x01;
|
||||||
pub const GMSG_PLAY_DROP :u8 = 0x02;
|
pub const GMSG_PLAY_DROP :u8 = 0x02;
|
||||||
|
@ -55,6 +55,12 @@ pub enum QRPacketData {
|
|||||||
QChallengeList,
|
QChallengeList,
|
||||||
RChallengeList(PacketChallengeListResponse),
|
RChallengeList(PacketChallengeListResponse),
|
||||||
|
|
||||||
|
QInviteAcquire,
|
||||||
|
RInviteAcquire(PacketInviteAcquireResponse),
|
||||||
|
|
||||||
|
QInviteList,
|
||||||
|
RInviteList(PacketInviteListResponse),
|
||||||
|
|
||||||
TestResult(PacketTestResult),
|
TestResult(PacketTestResult),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
32
server/src/protocol/packet/invite_acquire.rs
Normal file
32
server/src/protocol/packet/invite_acquire.rs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
use crate::{
|
||||||
|
util::pack::pack_u16,
|
||||||
|
app::InviteToken,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::Packet;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct PacketInviteAcquireResponse {
|
||||||
|
pub status:u16,
|
||||||
|
pub token:InviteToken,
|
||||||
|
}
|
||||||
|
impl PacketInviteAcquireResponse {
|
||||||
|
pub fn new() -> Self
|
||||||
|
{
|
||||||
|
Self {
|
||||||
|
status:0,
|
||||||
|
token:InviteToken::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Packet for PacketInviteAcquireResponse {
|
||||||
|
type Data = Self;
|
||||||
|
|
||||||
|
fn encode(&self) -> Vec<u8>
|
||||||
|
{
|
||||||
|
[
|
||||||
|
pack_u16(self.status),
|
||||||
|
self.token.to_vec(),
|
||||||
|
].concat()
|
||||||
|
}
|
||||||
|
}
|
40
server/src/protocol/packet/invite_list.rs
Normal file
40
server/src/protocol/packet/invite_list.rs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
use crate::{
|
||||||
|
util::pack::pack_u16,
|
||||||
|
app::Invitation,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::Packet;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct PacketInviteListResponse {
|
||||||
|
pub status:u16,
|
||||||
|
pub records:Vec<Invitation>,
|
||||||
|
}
|
||||||
|
impl PacketInviteListResponse {
|
||||||
|
pub fn new() -> Self
|
||||||
|
{
|
||||||
|
Self {
|
||||||
|
status:0,
|
||||||
|
records:Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Packet for PacketInviteListResponse {
|
||||||
|
type Data = Self;
|
||||||
|
|
||||||
|
fn encode(&self) -> Vec<u8>
|
||||||
|
{
|
||||||
|
let mut output = [
|
||||||
|
pack_u16(self.status),
|
||||||
|
pack_u16(self.records.len() as u16),
|
||||||
|
].concat();
|
||||||
|
|
||||||
|
for record in &self.records {
|
||||||
|
output.append(&mut [
|
||||||
|
record.token.to_vec(),
|
||||||
|
].concat());
|
||||||
|
}
|
||||||
|
|
||||||
|
output
|
||||||
|
}
|
||||||
|
}
|
@ -8,13 +8,11 @@ mod resume; pub use resume::*;
|
|||||||
mod summary; pub use summary::*;
|
mod summary; pub use summary::*;
|
||||||
|
|
||||||
mod session_list; pub use session_list::*;
|
mod session_list; pub use session_list::*;
|
||||||
//mod session_create; pub use session_create::*;
|
|
||||||
mod session_view; pub use session_view::*;
|
mod session_view; pub use session_view::*;
|
||||||
mod session_retire; pub use session_retire::*;
|
mod session_retire; pub use session_retire::*;
|
||||||
|
|
||||||
mod game_state; pub use game_state::*;
|
mod game_state; pub use game_state::*;
|
||||||
mod game_message; pub use game_message::*;
|
mod game_message; pub use game_message::*;
|
||||||
//mod game_history; pub use game_history::*;
|
|
||||||
|
|
||||||
mod challenge; pub use challenge::*;
|
mod challenge; pub use challenge::*;
|
||||||
mod challenge_answer; pub use challenge_answer::*;
|
mod challenge_answer; pub use challenge_answer::*;
|
||||||
@ -23,6 +21,9 @@ 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_info; pub use user_info::*;
|
||||||
|
|
||||||
|
mod invite_acquire; pub use invite_acquire::*;
|
||||||
|
mod invite_list; pub use invite_list::*;
|
||||||
|
|
||||||
mod test_result; pub use test_result::*;
|
mod test_result; pub use test_result::*;
|
||||||
|
|
||||||
mod prelude {
|
mod prelude {
|
||||||
|
@ -1,52 +0,0 @@
|
|||||||
use crate::{
|
|
||||||
app::session::SessionToken,
|
|
||||||
util::pack::pack_u16,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::Packet;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct PacketSessionCreate {
|
|
||||||
|
|
||||||
}
|
|
||||||
impl PacketSessionCreate {
|
|
||||||
pub fn new() -> Self
|
|
||||||
{
|
|
||||||
Self { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Packet for PacketSessionCreate {
|
|
||||||
type Data = Self;
|
|
||||||
|
|
||||||
fn decode(_data:&Vec<u8>, _index:&mut usize) -> Result<Self::Data, ()>
|
|
||||||
{
|
|
||||||
Ok(Self::new())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct PacketSessionCreateResponse {
|
|
||||||
pub status:u16,
|
|
||||||
pub token:SessionToken,
|
|
||||||
}
|
|
||||||
impl PacketSessionCreateResponse {
|
|
||||||
pub fn new() -> Self
|
|
||||||
{
|
|
||||||
Self {
|
|
||||||
status:0,
|
|
||||||
token:SessionToken::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Packet for PacketSessionCreateResponse {
|
|
||||||
type Data = Self;
|
|
||||||
|
|
||||||
fn encode(&self) -> Vec<u8>
|
|
||||||
{
|
|
||||||
[
|
|
||||||
pack_u16(self.status),
|
|
||||||
self.token.to_vec(),
|
|
||||||
].concat()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
const DEBUG_PRINT :bool = false;
|
const DEBUG_PRINT :bool = true;
|
||||||
|
|
||||||
pub struct Log {
|
pub struct Log {
|
||||||
|
|
||||||
|
@ -7,3 +7,5 @@ button.warn {
|
|||||||
background-color:#471414;
|
background-color:#471414;
|
||||||
color:#e0e0e0;
|
color:#e0e0e0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
span.monospace {font-family:monospace;}
|
||||||
|
@ -56,6 +56,9 @@ const OpCode = {
|
|||||||
UserList :0x0100,
|
UserList :0x0100,
|
||||||
UserInfo :0x0101,
|
UserInfo :0x0101,
|
||||||
|
|
||||||
|
InviteList :0x0180,
|
||||||
|
InviteAcquire :0x0181,
|
||||||
|
|
||||||
AccountManage :0x1000,
|
AccountManage :0x1000,
|
||||||
AccountCommit :0x1001,
|
AccountCommit :0x1001,
|
||||||
|
|
||||||
|
@ -43,13 +43,13 @@ const SCENES = {
|
|||||||
let container = document.createElement("section");
|
let container = document.createElement("section");
|
||||||
let form = document.createElement("form");
|
let form = document.createElement("form");
|
||||||
|
|
||||||
let tb_handle = UI.textbox("handle", "");
|
let tb_handle = UI.textbox("handle", "user");
|
||||||
tb_handle.setAttribute("maxlength", 24);
|
tb_handle.setAttribute("maxlength", 24);
|
||||||
|
|
||||||
form.appendChild(UI.table(null, [
|
form.appendChild(UI.table(null, [
|
||||||
[ UI.label(LANG("handle"), "handle"), tb_handle ],
|
[ UI.label(LANG("handle"), "handle"), tb_handle ],
|
||||||
[ UI.label(LANG("secret"), "secret"), UI.password("secret") ],
|
[ UI.label(LANG("secret"), "secret"), UI.password("secret") ],
|
||||||
[ UI.label(LANG("invitation"), "code"), UI.password("code") ],
|
[ UI.label(LANG("invitation"), "code"), UI.textbox("code", "####-####-####") ],
|
||||||
]));
|
]));
|
||||||
|
|
||||||
let button = UI.submit(LANG("register"));
|
let button = UI.submit(LANG("register"));
|
||||||
@ -68,13 +68,16 @@ const SCENES = {
|
|||||||
code.removeAttribute("class");
|
code.removeAttribute("class");
|
||||||
event.target.removeAttribute("class");
|
event.target.removeAttribute("class");
|
||||||
|
|
||||||
if(handle.value.length > 0 && handle.value.length < 24 && secret.value.length > 0 && code.value.length > 0) {
|
let invitation = code.value;
|
||||||
|
invitation = invitation.replace(/\-/g, '');
|
||||||
|
|
||||||
|
if(handle.value.length > 0 && handle.value.length < 24 && secret.value.length > 0 && invitation.length > 0) {
|
||||||
event.target.setAttribute("disabled", "");
|
event.target.setAttribute("disabled", "");
|
||||||
|
|
||||||
let enc = new TextEncoder();
|
let enc = new TextEncoder();
|
||||||
let enc_handle = enc.encode(handle.value);
|
let enc_handle = enc.encode(handle.value);
|
||||||
let enc_secret = enc.encode(secret.value);
|
let enc_secret = enc.encode(secret.value);
|
||||||
let enc_code = enc.encode(code.value);
|
let enc_code = enc.encode(invitation);
|
||||||
|
|
||||||
MESSAGE_COMPOSE([
|
MESSAGE_COMPOSE([
|
||||||
PACK.u16(OpCode.Register),
|
PACK.u16(OpCode.Register),
|
||||||
@ -147,7 +150,7 @@ const SCENES = {
|
|||||||
let container = document.createElement("section");
|
let container = document.createElement("section");
|
||||||
let form = document.createElement("form");
|
let form = document.createElement("form");
|
||||||
|
|
||||||
let tb_handle = UI.textbox("handle", "");
|
let tb_handle = UI.textbox("handle", "user");
|
||||||
tb_handle.setAttribute("maxlength", 24);
|
tb_handle.setAttribute("maxlength", 24);
|
||||||
|
|
||||||
form.appendChild(UI.table(null, [
|
form.appendChild(UI.table(null, [
|
||||||
@ -677,7 +680,13 @@ const SCENES = {
|
|||||||
UI.mainmenu_account(null, "invitations");
|
UI.mainmenu_account(null, "invitations");
|
||||||
|
|
||||||
// Left Buttons
|
// Left Buttons
|
||||||
let buttons_left = [ ];
|
let buttons_left = [
|
||||||
|
UI.button("Acquire", () => {
|
||||||
|
MESSAGE_COMPOSE([
|
||||||
|
PACK.u16(OpCode.InviteAcquire),
|
||||||
|
])
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
// Right Buttons
|
// Right Buttons
|
||||||
let buttons_right = [ ];
|
let buttons_right = [ ];
|
||||||
@ -685,10 +694,36 @@ const SCENES = {
|
|||||||
UI.mainnav(buttons_left, buttons_right);
|
UI.mainnav(buttons_left, buttons_right);
|
||||||
|
|
||||||
// Main Content
|
// Main Content
|
||||||
|
let table = document.createElement("table");
|
||||||
|
table.setAttribute("id", "content");
|
||||||
|
table.setAttribute("class", "list session");
|
||||||
|
UI.maincontent(table);
|
||||||
|
|
||||||
|
MESSAGE_COMPOSE([
|
||||||
|
PACK.u16(OpCode.InviteList),
|
||||||
|
]);
|
||||||
|
|
||||||
history.pushState(null, "Dzura - About", "/u/" + CONTEXT.Auth.handle);
|
history.pushState(null, "Dzura - About", "/u/" + CONTEXT.Auth.handle);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
message(code, data) {
|
||||||
|
switch(code) {
|
||||||
|
case OpCode.InviteAcquire: {
|
||||||
|
MESSAGE_COMPOSE([
|
||||||
|
PACK.u16(OpCode.InviteList),
|
||||||
|
]);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case OpCode.InviteList: {
|
||||||
|
let table = document.getElementById("content");
|
||||||
|
UI.clear(table);
|
||||||
|
|
||||||
|
if(data !== null) {
|
||||||
|
table.appendChild(UI.invite_table(data.records));
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
GameLoad:class{
|
GameLoad:class{
|
||||||
|
@ -490,6 +490,62 @@ function MESSAGE(event) {
|
|||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case OpCode.InviteList: {
|
||||||
|
console.log("RECV InviteList");
|
||||||
|
|
||||||
|
data = {
|
||||||
|
status:0,
|
||||||
|
records:[ ],
|
||||||
|
};
|
||||||
|
|
||||||
|
// Status
|
||||||
|
result = UNPACK.u16(bytes, index);
|
||||||
|
index = result.index;
|
||||||
|
data.status = result.data;
|
||||||
|
|
||||||
|
// Records
|
||||||
|
result = UNPACK.u16(bytes, index);
|
||||||
|
index = result.index;
|
||||||
|
let length = result.data;
|
||||||
|
|
||||||
|
for(let i = 0; i < length; ++i) {
|
||||||
|
let token = "";
|
||||||
|
|
||||||
|
for(let c = 0; c < 4; ++c) {
|
||||||
|
token += String.fromCharCode(bytes[index++]);
|
||||||
|
} token += "-";
|
||||||
|
|
||||||
|
for(let c = 0; c < 4; ++c) {
|
||||||
|
token += String.fromCharCode(bytes[index++]);
|
||||||
|
} token += "-";
|
||||||
|
|
||||||
|
for(let c = 0; c < 4; ++c) {
|
||||||
|
token += String.fromCharCode(bytes[index++]);
|
||||||
|
}
|
||||||
|
|
||||||
|
data.records.push(token);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case OpCode.InviteAcquire: {
|
||||||
|
console.log("RECV InviteAcquire");
|
||||||
|
|
||||||
|
data = {
|
||||||
|
status:0,
|
||||||
|
token:"",
|
||||||
|
};
|
||||||
|
|
||||||
|
// Status
|
||||||
|
result = UNPACK.u16(bytes, index);
|
||||||
|
index = result.index;
|
||||||
|
data.status = result.data;
|
||||||
|
|
||||||
|
// Token
|
||||||
|
for(let c = 0; c < 12; ++c) {
|
||||||
|
data.token += String.fromCharCode(bytes[index++]);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
case OpCode.TestResult: {
|
case OpCode.TestResult: {
|
||||||
result = UNPACK.u8(bytes, index);
|
result = UNPACK.u8(bytes, index);
|
||||||
index = result.index;
|
index = result.index;
|
||||||
|
21
www/js/ui.js
21
www/js/ui.js
@ -55,6 +55,7 @@ const UI = {
|
|||||||
password(id) {
|
password(id) {
|
||||||
let input = document.createElement("input");
|
let input = document.createElement("input");
|
||||||
input.setAttribute("type", "password");
|
input.setAttribute("type", "password");
|
||||||
|
input.setAttribute("placeholder", "••••••••••••");
|
||||||
if(id !== null) { input.setAttribute("id", id); }
|
if(id !== null) { input.setAttribute("id", id); }
|
||||||
return input;
|
return input;
|
||||||
},
|
},
|
||||||
@ -411,6 +412,26 @@ const UI = {
|
|||||||
return tbody;
|
return tbody;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
invite_table(records) {
|
||||||
|
let rows = [ ];
|
||||||
|
|
||||||
|
for(let r = 0; r < records.length; ++r) {
|
||||||
|
let record = records[r];
|
||||||
|
|
||||||
|
rows.push([
|
||||||
|
UI.span([UI.text(record)], "monospace"),
|
||||||
|
UI.text(""),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
let tbody = UI.table_content(
|
||||||
|
[ LANG("invitation"), "" ],
|
||||||
|
rows,
|
||||||
|
);
|
||||||
|
|
||||||
|
return tbody;
|
||||||
|
},
|
||||||
|
|
||||||
page_indicator(first, last, total) {
|
page_indicator(first, last, total) {
|
||||||
let ind = document.getElementById("indicator-page");
|
let ind = document.getElementById("indicator-page");
|
||||||
UI.clear(ind);
|
UI.clear(ind);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user