react-together
Version:
A library to seamlessly add real-time multi-user interaction to your React app!
355 lines (354 loc) • 9.83 kB
JavaScript
import { useRef as E, useCallback as U, useState as M, useEffect as S, useMemo as z } from "react";
import _ from "./hooks/useMyId.js";
import B from "./hooks/useReactTogetherContext.js";
import { useMultisynqContext as I, useJoinedViews as D, useModelRoot as W, useView as x, useSessionParams as y, useIsJoined as q } from "@multisynq/react";
import G from "object-hash";
import H from "./hooks/getNewValue.js";
import "./hooks/useChat.js";
import "./hooks/useCreateRandomSession.js";
import "./models/ReactTogetherModel.js";
import "./context/ReactTogetherContext.js";
import "unique-names-generator";
import "./hooks/useFunctionTogether.js";
import { getJoinUrl as $ } from "./utils/urls.js";
import "./hooks/useLeaveSession.js";
function A(e, t) {
const s = E(0), r = E(null), a = R();
return U(
(...n) => {
if (!a) {
t(...n);
return;
}
const o = Date.now();
if (o - s.current >= e)
s.current = o, t(...n);
else {
const l = e - (o - s.current);
r.current && clearTimeout(r.current), r.current = setTimeout(() => {
t(...n), s.current = Date.now();
}, l);
}
},
[t, e, a]
);
}
const T = Object.freeze({});
function F(e) {
return Object.fromEntries(e.entries());
}
function Y(e, t) {
const s = F(e), r = G(s);
return t && delete s[t], {
allValues: s,
allValuesHash: r
};
}
function Q({
prevLocalValue: e,
initialValue: t,
sessionValue: s,
resetOnConnect: r,
overwriteSessionValue: a
}) {
return r ? t : e === void 0 ? s ?? t : s === void 0 ? e ?? t : a ? e : s;
}
function N(e, t, s = {}) {
const {
resetOnDisconnect: r = !1,
resetOnConnect: a = !1,
keepValues: n = !1,
overwriteSessionValue: o = !1,
omitMyValue: l = !1,
throttleDelay: f = 100
} = s, [c] = M(t), { session: h, view: u, model: i } = I(), d = _(), [m, V] = M(() => {
if (!u || !i || d === null)
return {
localValue: c,
allValues: T,
allValuesHash: null
};
const g = i.statePerUser.get(e) ?? /* @__PURE__ */ new Map([[d, c]]), { allValues: p, allValuesHash: v } = Y(
g,
l ? d : void 0
);
return {
localValue: g.get(d) ?? c,
allValues: p,
allValuesHash: v
};
});
S(() => {
if (u && i && d) {
const g = i.statePerUserConfig.get(e);
(!g || g.keepValues !== n) && u.publish(i.id, "configureStatePerUser", {
rtKey: e,
options: {
// intentionally not passing other options to save bandwidth.
// These values do not need to be synchronized
// across users, and are only used locally to determine the behavior of the setter
keepValues: n
}
});
}
}, [u, i, e, r, a, n, d]), S(() => {
if (!h || !u || !i || d === null) {
V((v) => ({
localValue: r ? c : v.localValue,
allValues: T,
allValuesHash: null
}));
return;
}
(() => {
V((v) => {
const w = new Map(i.statePerUser.get(e)), L = w.get(d), b = Q({
prevLocalValue: v.localValue,
initialValue: c,
sessionValue: L,
resetOnConnect: a,
overwriteSessionValue: o
});
b !== L && (u.publish(i.id, "setStatePerUser", {
rtKey: e,
userId: d,
value: b
}), w.set(d, b));
const { allValues: j, allValuesHash: J } = Y(
w,
l ? d : void 0
);
return { localValue: b, allValues: j, allValuesHash: J };
});
})();
const p = () => {
V((v) => {
const w = i.statePerUser.get(e) ?? /* @__PURE__ */ new Map(), { allValues: L, allValuesHash: b } = Y(w, l ? d : void 0);
return v.allValuesHash === b ? v : {
localValue: w.get(d) ?? c,
allValues: L,
allValuesHash: b
};
});
};
return u.subscribe(
e,
{ event: "updated", handling: "oncePerFrame" },
p
), () => u.unsubscribe(e, "updated", p);
}, [
e,
h,
u,
i,
c,
d,
r,
a,
n,
o,
l
]);
const C = A(
f,
U(
(g) => {
if (!u || !i || d === null)
V((p) => {
const v = H(p.localValue, g);
return p.localValue === v ? p : {
localValue: v,
allValues: T,
allValuesHash: null
};
});
else {
const p = i.statePerUser.get(e);
let v = p == null ? void 0 : p.get(d);
v === void 0 && (console.warn(
`[useStateTogetherWithPerUserValues:setter] prevLocalValue is undefined.Using initialValue: ${c}`
), v = c);
const w = H(v, g);
u.publish(i.id, "setStatePerUser", {
rtKey: e,
userId: d,
value: w
});
}
},
[e, u, i, c, d]
)
), { localValue: k, allValues: P } = m;
return [k, C, P];
}
const X = "__rt-nickname";
function O() {
const { deriveNickname: e, rememberUsers: t } = B(), s = _() ?? Math.random().toString(36).substring(2, 15), r = localStorage.getItem(X), a = t && r !== null ? r : e(s), [n, o, l] = N("__nicknames", a, {
// Storing all nicknames in the session so that
// they are available even after users leave
keepValues: !0,
overwriteSessionValue: !0
}), f = U(
(c) => {
t && localStorage.setItem(X, c), o(c);
},
[o, t]
);
return [n, f, l];
}
function Z() {
const [, , e] = O();
return e;
}
const K = [];
function he() {
D();
const e = Z(), t = W(), s = _();
return t ? Array.from(t.userIdCount.keys()).map((r) => {
const a = e[r];
return {
userId: r,
isYou: r === s,
nickname: a,
get name() {
return console.warn(
"useConnectedUsers: name is deprecated. Use nickname instead."
), a;
}
};
}) : K;
}
function ve(e = {}) {
const {
throttleDelay: t = 50,
deleteOnLeave: s = !1,
omitMyValue: r = !0
} = e, [a, n, o] = N("__cursors", null, {
omitMyValue: r,
throttleDelay: t
}), l = E(null);
return S(() => {
const f = (m) => {
l.current = m, n(m);
}, c = (m) => {
f({
pageX: m.pageX,
pageY: m.pageY,
clientX: m.clientX,
clientY: m.clientY,
percentX: m.pageX / document.body.scrollWidth,
percentY: m.pageY / document.body.scrollHeight
});
}, h = () => {
s && n(null);
}, u = (m) => {
const V = m.touches[0];
V && c(V);
}, i = (m) => {
const V = m.touches[0];
V && c(V);
}, d = () => {
if (l.current) {
const m = l.current.clientX + window.scrollX, V = l.current.clientY + window.scrollY, C = l.current.clientX, k = l.current.clientY, P = m / document.body.scrollWidth, g = V / document.body.scrollHeight;
f({
pageX: m,
pageY: V,
clientX: C,
clientY: k,
percentX: P,
percentY: g
});
}
};
return document.addEventListener("mousemove", c), document.addEventListener("mouseleave", h), document.addEventListener("touchstart", u), document.addEventListener("touchmove", i), document.addEventListener("scroll", d), () => {
document.removeEventListener("mousemove", c), document.removeEventListener("mouseleave", h), document.removeEventListener("touchstart", u), document.removeEventListener("touchmove", i), document.removeEventListener("scroll", d);
};
}, [n, s, t]), { myCursor: a, allCursors: o };
}
function Ve(e, t = {}) {
const s = E(null), [r, a, n] = N(e, !1);
S(() => {
const l = s.current;
if (!l)
return;
const f = (h) => {
const u = h.rtProcessedBy;
u === void 0 ? (a(!0), h.rtProcessedBy = e) : u !== e && a(!1);
}, c = () => a(!1);
return l.addEventListener("mouseover", f), l.addEventListener("mouseleave", c), () => {
l.removeEventListener("mouseover", f), l.removeEventListener("mouseleave", c);
};
}, [a, e]);
const o = Object.entries(n).filter(([, l]) => l).map(([l]) => l);
return [s, o, r];
}
function pe() {
const e = x() !== null, { view: t, model: s } = I(), [r, a] = M(!1), [, , n] = O(), o = Object.keys(n).length > 0;
return S(() => {
if (!t || !s) {
a(!1);
return;
}
return t.subscribe(t.viewId, "synced", a), () => t.unsubscribe(t.viewId, "synced", a);
}, [t, s]), o && e && r;
}
function ge() {
const { name: e, password: t } = y(), s = R();
return z(() => !s || !e || !t ? null : $(new URL(window.location.href), e, t).toString(), [e, t, s]);
}
function we(e, t, {
resetOnDisconnect: s = !1,
throttleDelay: r = 100
} = {}) {
const { session: a, view: n, model: o } = I(), [l, f] = M(() => !n || !o ? t : o.state.has(e) ? o.state.get(e) : (n.publish(o.id, "setState", { rtKey: e, value: t }), t));
S(() => {
if (!a || !n || !o) {
s && f(t);
return;
}
const h = () => {
f((u) => {
if (!o.state.has(e))
return n.publish(o.id, "setState", { rtKey: e, value: u }), u;
const i = o.state.get(e);
return u === i ? u : i;
});
};
return n.subscribe(
e,
{ event: "updated", handling: "oncePerFrame" },
h
), h(), () => n.unsubscribe(e, "updated", h);
}, [a, n, o, e, f, t, s]);
const c = A(
r,
U(
(h) => {
if (o && n) {
const u = o.state.get(e);
n.publish(o.id, "setState", {
rtKey: e,
value: H(u, h)
});
} else
f(h);
},
[f, o, n, e]
)
);
return [l, c];
}
const R = q;
export {
he as a,
ve as b,
Ve as c,
pe as d,
R as e,
ge as f,
O as g,
we as h,
N as i,
A as j,
Z as u
};