velconnect-svelte with easy subscribing
parent
a0ce622c5d
commit
ae3b5b3bd0
|
|
@ -96,15 +96,25 @@
|
|||
<h3>Settings</h3>
|
||||
|
||||
<label>
|
||||
User Name
|
||||
Device Name
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Enter username..."
|
||||
placeholder="Enter friendly device name..."
|
||||
bind:value={$deviceFields.friendly_name}
|
||||
on:input={delayedSend}
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
Nickname
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Enter nickname..."
|
||||
bind:value={$deviceData.data.nickname}
|
||||
on:input={delayedSend}
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
Avatar URL
|
||||
<a href="https://demo.readyplayer.me" target="blank">
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ namespace VELConnect
|
|||
[CanBeNull] public string pairing_code;
|
||||
[CanBeNull] public string last_online;
|
||||
[CanBeNull] public DeviceExpand expand;
|
||||
public DataBlock deviceData => expand.data;
|
||||
public DataBlock deviceData => expand?.data;
|
||||
|
||||
public class DeviceExpand
|
||||
{
|
||||
|
|
@ -175,7 +175,7 @@ namespace VELConnect
|
|||
// allows running multiple builds on the same computer
|
||||
// return SystemInfo.deviceUniqueIdentifier + Hash128.Compute(Application.dataPath);
|
||||
sb.Append(Application.dataPath);
|
||||
sb.Append("EDITOR");
|
||||
sb.Append("EDITOR2");
|
||||
#endif
|
||||
string id = Convert.ToBase64String(md5.ComputeHash(Encoding.UTF8.GetBytes(sb.ToString())));
|
||||
deviceId = id[..15];
|
||||
|
|
@ -349,6 +349,32 @@ namespace VELConnect
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// on the initial state, also activate callbacks for null values
|
||||
if (isInitialState)
|
||||
{
|
||||
foreach ((string deviceDataKey, List<CallbackListener> callbackList) in deviceDataCallbacks)
|
||||
{
|
||||
if (!state.device.deviceData.data.ContainsKey(deviceDataKey))
|
||||
{
|
||||
// send the callbacks
|
||||
callbackList.ForEach(e =>
|
||||
{
|
||||
if (e.sendInitialState)
|
||||
{
|
||||
try
|
||||
{
|
||||
e.callback(null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError(ex);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -394,6 +420,32 @@ namespace VELConnect
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// on the initial state, also activate callbacks for null values
|
||||
if (isInitialState)
|
||||
{
|
||||
foreach ((string key, List<CallbackListener> callbackList) in roomDataCallbacks)
|
||||
{
|
||||
if (!state.room.data.ContainsKey(key))
|
||||
{
|
||||
// send the callbacks
|
||||
callbackList.ForEach(e =>
|
||||
{
|
||||
if (e.sendInitialState)
|
||||
{
|
||||
try
|
||||
{
|
||||
e.callback(null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError(ex);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lastState = state;
|
||||
|
|
@ -453,6 +505,7 @@ namespace VELConnect
|
|||
|
||||
/// <summary>
|
||||
/// Adds a change listener callback to a particular field name within the Device data JSON.
|
||||
/// If the initial state doesn't contain this key, this sends back null
|
||||
/// </summary>
|
||||
public static void AddDeviceDataListener(string key, MonoBehaviour keepAliveObject, Action<string> callback,
|
||||
bool sendInitialState = false)
|
||||
|
|
@ -469,25 +522,24 @@ namespace VELConnect
|
|||
sendInitialState = sendInitialState
|
||||
});
|
||||
|
||||
if (sendInitialState)
|
||||
// if we have already received data, and we should send right away
|
||||
if (sendInitialState && instance.state != null)
|
||||
{
|
||||
string val = GetDeviceData(key);
|
||||
if (val != null)
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
callback(val);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError(e);
|
||||
}
|
||||
callback(val);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a change listener callback to a particular field name within the Room data JSON.
|
||||
/// If the initial state doesn't contain this key, this sends back null
|
||||
/// </summary>
|
||||
public static void AddRoomDataListener(string key, MonoBehaviour keepAliveObject, Action<string> callback,
|
||||
bool sendInitialState = false)
|
||||
|
|
@ -504,31 +556,29 @@ namespace VELConnect
|
|||
sendInitialState = sendInitialState
|
||||
});
|
||||
|
||||
if (sendInitialState)
|
||||
// if we have already received data, and we should send right away
|
||||
if (sendInitialState && instance.state != null)
|
||||
{
|
||||
string val = GetRoomData(key);
|
||||
if (val != null)
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
callback(val);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError(e);
|
||||
}
|
||||
callback(val);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetDeviceData(string key)
|
||||
public static string GetDeviceData(string key, string defaultValue = null)
|
||||
{
|
||||
return instance != null ? instance.lastState?.device?.TryGetData(key) : null;
|
||||
return instance != null ? instance.lastState?.device?.TryGetData(key) : defaultValue;
|
||||
}
|
||||
|
||||
public static string GetRoomData(string key)
|
||||
public static string GetRoomData(string key, string defaultValue = null)
|
||||
{
|
||||
return instance != null ? instance.lastState?.room?.TryGetData(key) : null;
|
||||
return instance != null ? instance.lastState?.room?.TryGetData(key) : defaultValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -769,6 +819,7 @@ namespace VELConnect
|
|||
byte[] bodyRaw = Encoding.UTF8.GetBytes(postData);
|
||||
UploadHandlerRaw uploadHandler = new UploadHandlerRaw(bodyRaw);
|
||||
webRequest.uploadHandler = uploadHandler;
|
||||
webRequest.downloadHandler = new DownloadHandlerBuffer();
|
||||
webRequest.SetRequestHeader("Content-Type", "application/json");
|
||||
if (headers != null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@velaboratory/velconnect-svelte",
|
||||
"version": "1.0.1",
|
||||
"version": "1.0.2",
|
||||
"description": "Use VEL-Connect with a Svelte dashboard",
|
||||
"main": "src/index.js",
|
||||
"files": [
|
||||
|
|
|
|||
|
|
@ -3,6 +3,12 @@ import { writable } from "svelte/store";
|
|||
import type { Record } from "pocketbase";
|
||||
import { get } from "svelte/store";
|
||||
|
||||
let debugLog = false;
|
||||
|
||||
export function setDebugLog(val: boolean) {
|
||||
debugLog = val;
|
||||
}
|
||||
|
||||
export const pb = new PocketBase();
|
||||
|
||||
export const currentUser = writable(pb.authStore.model);
|
||||
|
|
@ -27,6 +33,8 @@ export interface Device extends Record {
|
|||
expand: { data?: DataBlock };
|
||||
}
|
||||
export interface DataBlock extends Record {
|
||||
block_id: string;
|
||||
owner: string;
|
||||
data: { [key: string]: string };
|
||||
}
|
||||
|
||||
|
|
@ -53,30 +61,45 @@ export async function startListening(baseUrl: string) {
|
|||
const d = (await pb.collection("Device").getOne(get(currentDeviceId), {
|
||||
expand: "data",
|
||||
})) as Device;
|
||||
deviceFields.set(d);
|
||||
deviceData.set(d.expand.data as DataBlock);
|
||||
// we don't need expand anymore, since it doesn't work in subscribe()
|
||||
d.expand = {};
|
||||
deviceFields.set(d);
|
||||
}
|
||||
|
||||
unsubscribeCurrentDevice = currentDeviceId.subscribe(async (val) => {
|
||||
console.log("current device changed");
|
||||
log("currentDeviceId subscribe change event");
|
||||
unsubscribeDeviceFields?.();
|
||||
unsubscribeDeviceData?.();
|
||||
if (val != "") {
|
||||
const d = (await pb
|
||||
.collection("Device")
|
||||
.getOne(get(currentDeviceId), { expand: "data" })) as Device;
|
||||
deviceFields.set(d);
|
||||
deviceData.set(d.expand.data as DataBlock);
|
||||
// we don't need expand anymore, since it doesn't work in subscribe()
|
||||
d.expand = {};
|
||||
deviceFields.set(d);
|
||||
|
||||
unsubscribeDeviceData = await pb
|
||||
.collection("DataBlock")
|
||||
.subscribe(d.data, async (data) => {
|
||||
log("deviceData subscribe change event");
|
||||
deviceData.set(data.record as DataBlock);
|
||||
});
|
||||
|
||||
unsubscribeDeviceFields = await pb
|
||||
.collection("Device")
|
||||
.subscribe(val, async (data) => {
|
||||
log("deviceFields subscribe change event");
|
||||
const d = data.record as Device;
|
||||
deviceFields.set(d);
|
||||
|
||||
// if the devie changes, the devicedata could change, so we need to resubscribe
|
||||
unsubscribeDeviceData?.();
|
||||
unsubscribeDeviceData = await pb
|
||||
.collection("DataBlock")
|
||||
.subscribe(d.id, async (data) => {
|
||||
.subscribe(d.data, async (data) => {
|
||||
log("deviceData subscribe change event");
|
||||
deviceData.set(data.record as DataBlock);
|
||||
});
|
||||
|
||||
|
|
@ -92,6 +115,7 @@ export async function startListening(baseUrl: string) {
|
|||
});
|
||||
|
||||
unsubscribeCurrentUser = currentUser.subscribe((user) => {
|
||||
log(`currentUser changed ${user}`);
|
||||
pairedDevices.set(user?.["devices"] ?? []);
|
||||
currentDeviceId.set(get(pairedDevices)[0] ?? "");
|
||||
});
|
||||
|
|
@ -110,16 +134,26 @@ async function getRoomData(device: Device) {
|
|||
unsubscribeRoomData?.();
|
||||
|
||||
// create or just fetch room by name
|
||||
const r = (await pb
|
||||
.collection("DataBlock")
|
||||
.getFirstListItem(
|
||||
`block_id="${device.current_app}_${device.current_room}"`
|
||||
)) as DataBlock;
|
||||
let r: DataBlock | null = null;
|
||||
try {
|
||||
r = (await pb
|
||||
.collection("DataBlock")
|
||||
.getFirstListItem(
|
||||
`block_id="${device.current_app}_${device.current_room}"`
|
||||
)) as DataBlock;
|
||||
} catch (e: any) {
|
||||
r = (await pb.collection("DataBlock").create({
|
||||
block_id: `${device.current_app}_${device.current_room}`,
|
||||
category: "room",
|
||||
data: {},
|
||||
})) as DataBlock;
|
||||
}
|
||||
roomData.set(r);
|
||||
if (r) {
|
||||
if (r != null) {
|
||||
unsubscribeRoomData = await pb
|
||||
.collection("DataBlock")
|
||||
.subscribe(r.id, (data) => {
|
||||
log("roomData subscribe change event");
|
||||
roomData.set(data.record as DataBlock);
|
||||
});
|
||||
} else {
|
||||
|
|
@ -148,9 +182,10 @@ export function send() {
|
|||
console.log("sending...");
|
||||
sending = true;
|
||||
let promises: Promise<any>[] = [];
|
||||
const room = get(roomData);
|
||||
const device = get(deviceFields);
|
||||
const data = get(deviceData);
|
||||
const room = get(roomData);
|
||||
// TODO send changes only
|
||||
if (device) {
|
||||
promises.push(pb.collection("Device").update(device.id, device));
|
||||
}
|
||||
|
|
@ -195,7 +230,9 @@ export async function pair(pairingCode: string) {
|
|||
|
||||
// add it to the local data
|
||||
currentDeviceId.set(device.id);
|
||||
pairedDevices.set([...get(pairedDevices), device.id]);
|
||||
if (!get(pairedDevices).includes(device.id)) {
|
||||
pairedDevices.set([...get(pairedDevices), device.id]);
|
||||
}
|
||||
|
||||
// add it to my account if logged in
|
||||
const u = get(currentUser);
|
||||
|
|
@ -242,26 +279,32 @@ export async function pair(pairingCode: string) {
|
|||
export async function login(username: string, password: string) {
|
||||
try {
|
||||
await pb.collection("Users").authWithPassword(username, password);
|
||||
return "";
|
||||
} catch (err) {
|
||||
return err as string;
|
||||
return {};
|
||||
} catch (err: any) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
export async function signUp(username: string, password: string) {
|
||||
try {
|
||||
const data = {
|
||||
email: username,
|
||||
username: username,
|
||||
password,
|
||||
passwordConfirm: password,
|
||||
};
|
||||
await pb.collection("Users").create(data);
|
||||
return await login(username, password);
|
||||
} catch (err) {
|
||||
return err as string;
|
||||
} catch (err: any) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
export function signOut() {
|
||||
pb.authStore.clear();
|
||||
}
|
||||
|
||||
function log(msg: string) {
|
||||
if (debugLog) {
|
||||
console.log(msg);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -147,10 +147,12 @@ func main() {
|
|||
|
||||
// apply to the db
|
||||
if err := dao.SaveRecord(deviceRecord); err != nil {
|
||||
return err
|
||||
log.Fatalln(err)
|
||||
return c.String(500, err.Error())
|
||||
}
|
||||
if err := dao.SaveRecord(deviceDataRecord); err != nil {
|
||||
return err
|
||||
log.Fatalln(err)
|
||||
return c.String(500, err.Error())
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, deviceRecord)
|
||||
|
|
@ -180,11 +182,11 @@ func main() {
|
|||
}
|
||||
|
||||
apis.EnrichRecord(c, app.Dao(), deviceRecord, "data")
|
||||
room, roomErr := app.Dao().FindFirstRecordByData("DataBlock", "block_id", deviceRecord.GetString("current_app")+"_"+deviceRecord.GetString("current_room"))
|
||||
room, _ := app.Dao().FindFirstRecordByData("DataBlock", "block_id", deviceRecord.GetString("current_app")+"_"+deviceRecord.GetString("current_room"))
|
||||
user, _ := app.Dao().FindRecordById("Users", deviceRecord.GetString("owner"))
|
||||
|
||||
log.Println(deviceRecord.GetString("current_room"))
|
||||
log.Println(roomErr)
|
||||
// log.Println(deviceRecord.GetString("current_room"))
|
||||
// log.Println(roomErr)
|
||||
|
||||
output := map[string]interface{}{
|
||||
"device": deviceRecord,
|
||||
|
|
|
|||
Loading…
Reference in New Issue