Merge branch 'dev'
CI/CD / deploy (push) Successful in 2m55s
Details
CI/CD / deploy (push) Successful in 2m55s
Details
This commit is contained in:
commit
a89a6fbebb
|
|
@ -13,6 +13,7 @@ import type { CrowdSurfDoc, CrowdSurfPrompt, Contribution } from '../schemas';
|
||||||
import { getDecayProgress, getTimeRemaining, getRightSwipeCount, isReadyToTrigger, getUrgency, parseContributions, crowdsurfSchema, crowdsurfDocId } from '../schemas';
|
import { getDecayProgress, getTimeRemaining, getRightSwipeCount, isReadyToTrigger, getUrgency, parseContributions, crowdsurfSchema, crowdsurfDocId } from '../schemas';
|
||||||
import { getModuleApiBase } from "../../../shared/url-helpers";
|
import { getModuleApiBase } from "../../../shared/url-helpers";
|
||||||
import type { DocumentId } from "../../../shared/local-first/document";
|
import type { DocumentId } from "../../../shared/local-first/document";
|
||||||
|
import { ViewHistory } from "../../../shared/view-history.js";
|
||||||
|
|
||||||
// ── Auth helpers ──
|
// ── Auth helpers ──
|
||||||
function getSession(): { accessToken: string; claims: { sub: string; did?: string; username?: string } } | null {
|
function getSession(): { accessToken: string; claims: { sub: string; did?: string; username?: string } } | null {
|
||||||
|
|
@ -45,6 +46,7 @@ class FolkCrowdSurfDashboard extends HTMLElement {
|
||||||
|
|
||||||
// State
|
// State
|
||||||
private activeTab: ViewTab = 'discover';
|
private activeTab: ViewTab = 'discover';
|
||||||
|
private _history = new ViewHistory<ViewTab>('discover', 'crowdsurf');
|
||||||
private loading = true;
|
private loading = true;
|
||||||
private prompts: CrowdSurfPrompt[] = [];
|
private prompts: CrowdSurfPrompt[] = [];
|
||||||
private currentPromptIndex = 0;
|
private currentPromptIndex = 0;
|
||||||
|
|
@ -95,9 +97,12 @@ class FolkCrowdSurfDashboard extends HTMLElement {
|
||||||
this.initMultiplayer();
|
this.initMultiplayer();
|
||||||
}
|
}
|
||||||
this._stopPresence = startPresenceHeartbeat(() => ({ module: 'crowdsurf', context: this.prompts[this.currentPromptIndex]?.text || 'CrowdSurf' }));
|
this._stopPresence = startPresenceHeartbeat(() => ({ module: 'crowdsurf', context: this.prompts[this.currentPromptIndex]?.text || 'CrowdSurf' }));
|
||||||
|
window.addEventListener('rspace-view-restored', this._onViewRestored as EventListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnectedCallback() {
|
disconnectedCallback() {
|
||||||
|
this._history.destroy();
|
||||||
|
window.removeEventListener('rspace-view-restored', this._onViewRestored as EventListener);
|
||||||
this._stopPresence?.();
|
this._stopPresence?.();
|
||||||
this._lfcUnsub?.();
|
this._lfcUnsub?.();
|
||||||
this._lfcUnsub = null;
|
this._lfcUnsub = null;
|
||||||
|
|
@ -105,6 +110,13 @@ class FolkCrowdSurfDashboard extends HTMLElement {
|
||||||
if (this._expiryTimer !== null) clearInterval(this._expiryTimer);
|
if (this._expiryTimer !== null) clearInterval(this._expiryTimer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _onViewRestored = (e: CustomEvent) => {
|
||||||
|
if (e.detail?.moduleId !== 'crowdsurf') return;
|
||||||
|
this.activeTab = e.detail.view;
|
||||||
|
this.render();
|
||||||
|
this.bindEvents();
|
||||||
|
};
|
||||||
|
|
||||||
// ── Multiplayer init ──
|
// ── Multiplayer init ──
|
||||||
|
|
||||||
private async initMultiplayer() {
|
private async initMultiplayer() {
|
||||||
|
|
@ -337,6 +349,7 @@ class FolkCrowdSurfDashboard extends HTMLElement {
|
||||||
this.lfClient.createPrompt(prompt);
|
this.lfClient.createPrompt(prompt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._history.push('discover');
|
||||||
this.activeTab = 'discover';
|
this.activeTab = 'discover';
|
||||||
this.render();
|
this.render();
|
||||||
this.bindEvents();
|
this.bindEvents();
|
||||||
|
|
@ -738,6 +751,7 @@ class FolkCrowdSurfDashboard extends HTMLElement {
|
||||||
btn.addEventListener('click', () => {
|
btn.addEventListener('click', () => {
|
||||||
const tab = btn.dataset.tab as ViewTab;
|
const tab = btn.dataset.tab as ViewTab;
|
||||||
if (tab && tab !== this.activeTab) {
|
if (tab && tab !== this.activeTab) {
|
||||||
|
this._history.push(tab);
|
||||||
this.activeTab = tab;
|
this.activeTab = tab;
|
||||||
this.render();
|
this.render();
|
||||||
this.bindEvents();
|
this.bindEvents();
|
||||||
|
|
@ -761,6 +775,7 @@ class FolkCrowdSurfDashboard extends HTMLElement {
|
||||||
|
|
||||||
// Go to create tab
|
// Go to create tab
|
||||||
this.shadow.querySelector('[data-action="go-create"]')?.addEventListener('click', () => {
|
this.shadow.querySelector('[data-action="go-create"]')?.addEventListener('click', () => {
|
||||||
|
this._history.push('create');
|
||||||
this.activeTab = 'create';
|
this.activeTab = 'create';
|
||||||
this.render();
|
this.render();
|
||||||
this.bindEvents();
|
this.bindEvents();
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import { startPresenceHeartbeat } from '../../../shared/collab-presence';
|
||||||
import { getModuleApiBase } from "../../../shared/url-helpers";
|
import { getModuleApiBase } from "../../../shared/url-helpers";
|
||||||
import type { DocumentId } from "../../../shared/local-first/document";
|
import type { DocumentId } from "../../../shared/local-first/document";
|
||||||
import { bnbSchema, bnbDocId } from "../schemas";
|
import { bnbSchema, bnbDocId } from "../schemas";
|
||||||
|
import { ViewHistory } from "../../../shared/view-history.js";
|
||||||
|
|
||||||
const BNB_TOUR_STEPS: TourStep[] = [
|
const BNB_TOUR_STEPS: TourStep[] = [
|
||||||
{ target: '.bnb-search', title: 'Search', message: 'Filter listings by location, type, or economy model.' },
|
{ target: '.bnb-search', title: 'Search', message: 'Filter listings by location, type, or economy model.' },
|
||||||
|
|
@ -63,6 +64,7 @@ class FolkBnbView extends HTMLElement {
|
||||||
#stats: any = null;
|
#stats: any = null;
|
||||||
#selectedStay: any = null;
|
#selectedStay: any = null;
|
||||||
#view: 'grid' | 'map' = 'grid';
|
#view: 'grid' | 'map' = 'grid';
|
||||||
|
private _history = new ViewHistory<'grid' | 'map'>('grid', 'rbnb');
|
||||||
#search = '';
|
#search = '';
|
||||||
#typeFilter = '';
|
#typeFilter = '';
|
||||||
#economyFilter = '';
|
#economyFilter = '';
|
||||||
|
|
@ -78,13 +80,23 @@ class FolkBnbView extends HTMLElement {
|
||||||
this.#loadData();
|
this.#loadData();
|
||||||
this._stopPresence = startPresenceHeartbeat(() => ({ module: 'rbnb', context: 'Listings' }));
|
this._stopPresence = startPresenceHeartbeat(() => ({ module: 'rbnb', context: 'Listings' }));
|
||||||
if (this.#space !== 'demo') this.subscribeOffline();
|
if (this.#space !== 'demo') this.subscribeOffline();
|
||||||
|
window.addEventListener('rspace-view-restored', this._onViewRestored as EventListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnectedCallback() {
|
disconnectedCallback() {
|
||||||
|
this._history.destroy();
|
||||||
|
window.removeEventListener('rspace-view-restored', this._onViewRestored as EventListener);
|
||||||
this._stopPresence?.();
|
this._stopPresence?.();
|
||||||
this._offlineUnsub?.(); this._offlineUnsub = null;
|
this._offlineUnsub?.(); this._offlineUnsub = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _onViewRestored = (e: CustomEvent) => {
|
||||||
|
if (e.detail?.moduleId !== 'rbnb') return;
|
||||||
|
this.#view = e.detail.view;
|
||||||
|
this.#render();
|
||||||
|
this.#renderContent();
|
||||||
|
};
|
||||||
|
|
||||||
private async subscribeOffline() {
|
private async subscribeOffline() {
|
||||||
const runtime = (window as any).__rspaceOfflineRuntime;
|
const runtime = (window as any).__rspaceOfflineRuntime;
|
||||||
if (!runtime?.isInitialized) return;
|
if (!runtime?.isInitialized) return;
|
||||||
|
|
@ -230,7 +242,9 @@ class FolkBnbView extends HTMLElement {
|
||||||
// View toggles
|
// View toggles
|
||||||
for (const btn of this.querySelectorAll('.bnb-view__toggle')) {
|
for (const btn of this.querySelectorAll('.bnb-view__toggle')) {
|
||||||
btn.addEventListener('click', () => {
|
btn.addEventListener('click', () => {
|
||||||
this.#view = (btn as HTMLElement).dataset.view as 'grid' | 'map';
|
const v = (btn as HTMLElement).dataset.view as 'grid' | 'map';
|
||||||
|
this._history.push(v);
|
||||||
|
this.#view = v;
|
||||||
this.#render();
|
this.#render();
|
||||||
this.#renderContent();
|
this.#renderContent();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import {
|
||||||
calculateAffordability,
|
calculateAffordability,
|
||||||
} from '../lib/mortgage-engine';
|
} from '../lib/mortgage-engine';
|
||||||
import type { MortgageSummary } from '../lib/mortgage-engine';
|
import type { MortgageSummary } from '../lib/mortgage-engine';
|
||||||
|
import { ViewHistory } from "../../../shared/view-history.js";
|
||||||
|
|
||||||
type ViewMode = 'mycelial' | 'flow' | 'grid' | 'lender' | 'borrower';
|
type ViewMode = 'mycelial' | 'flow' | 'grid' | 'lender' | 'borrower';
|
||||||
|
|
||||||
|
|
@ -90,6 +91,7 @@ class FolkMortgageSimulator extends HTMLElement {
|
||||||
private summary!: MortgageSummary;
|
private summary!: MortgageSummary;
|
||||||
private currentMonth = DEFAULT_CONFIG.startMonth;
|
private currentMonth = DEFAULT_CONFIG.startMonth;
|
||||||
private viewMode: ViewMode = 'mycelial';
|
private viewMode: ViewMode = 'mycelial';
|
||||||
|
private _history = new ViewHistory<ViewMode>('mycelial', 'rflows');
|
||||||
private selectedTrancheId: string | null = null;
|
private selectedTrancheId: string | null = null;
|
||||||
private controlsOpen = true;
|
private controlsOpen = true;
|
||||||
private playing = false;
|
private playing = false;
|
||||||
|
|
@ -121,12 +123,26 @@ class FolkMortgageSimulator extends HTMLElement {
|
||||||
this._recompute();
|
this._recompute();
|
||||||
this._attachListeners();
|
this._attachListeners();
|
||||||
this.render();
|
this.render();
|
||||||
|
window.addEventListener('rspace-view-restored', this._onViewRestored as EventListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnectedCallback() {
|
disconnectedCallback() {
|
||||||
|
this._history.destroy();
|
||||||
|
window.removeEventListener('rspace-view-restored', this._onViewRestored as EventListener);
|
||||||
this._stopPlayback();
|
this._stopPlayback();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _onViewRestored = (e: CustomEvent) => {
|
||||||
|
if (e.detail?.moduleId !== 'rflows') return;
|
||||||
|
this.viewMode = e.detail.view;
|
||||||
|
const tabs = this.shadow.querySelectorAll('.view-tab');
|
||||||
|
tabs.forEach((tab: Element) => {
|
||||||
|
const t = tab as HTMLElement;
|
||||||
|
t.className = `view-tab ${t.dataset.view === this.viewMode ? 'active' : ''}`;
|
||||||
|
});
|
||||||
|
this._updateView();
|
||||||
|
};
|
||||||
|
|
||||||
// ─── Core simulation ──────────────────────────────────
|
// ─── Core simulation ──────────────────────────────────
|
||||||
|
|
||||||
private _recompute() {
|
private _recompute() {
|
||||||
|
|
@ -1548,6 +1564,7 @@ class FolkMortgageSimulator extends HTMLElement {
|
||||||
|
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case 'view': {
|
case 'view': {
|
||||||
|
this._history.push(target.dataset.view as ViewMode);
|
||||||
this.viewMode = target.dataset.view as ViewMode;
|
this.viewMode = target.dataset.view as ViewMode;
|
||||||
// Update tab highlights in-place
|
// Update tab highlights in-place
|
||||||
const tabs = this.shadow.querySelectorAll('.view-tab');
|
const tabs = this.shadow.querySelectorAll('.view-tab');
|
||||||
|
|
|
||||||
|
|
@ -70,11 +70,13 @@ import { TourEngine } from "../../../shared/tour-engine";
|
||||||
import { startPresenceHeartbeat } from '../../../shared/collab-presence';
|
import { startPresenceHeartbeat } from '../../../shared/collab-presence';
|
||||||
import type { DocumentId } from "../../../shared/local-first/document";
|
import type { DocumentId } from "../../../shared/local-first/document";
|
||||||
import { networkSchema, networkDocId } from "../schemas";
|
import { networkSchema, networkDocId } from "../schemas";
|
||||||
|
import { ViewHistory } from "../../../shared/view-history.js";
|
||||||
|
|
||||||
class FolkCrmView extends HTMLElement {
|
class FolkCrmView extends HTMLElement {
|
||||||
private shadow: ShadowRoot;
|
private shadow: ShadowRoot;
|
||||||
private space = "";
|
private space = "";
|
||||||
private activeTab: Tab = "pipeline";
|
private activeTab: Tab = "pipeline";
|
||||||
|
private _history = new ViewHistory<Tab>("pipeline", "rnetwork");
|
||||||
private searchQuery = "";
|
private searchQuery = "";
|
||||||
private sortColumn = "";
|
private sortColumn = "";
|
||||||
private sortAsc = true;
|
private sortAsc = true;
|
||||||
|
|
@ -129,6 +131,7 @@ class FolkCrmView extends HTMLElement {
|
||||||
private _onTabChange = (e: Event) => {
|
private _onTabChange = (e: Event) => {
|
||||||
const tab = (e as CustomEvent).detail?.tab;
|
const tab = (e as CustomEvent).detail?.tab;
|
||||||
if (tab && tab !== this.activeTab) {
|
if (tab && tab !== this.activeTab) {
|
||||||
|
this._history.push(tab as Tab);
|
||||||
this.activeTab = tab as Tab;
|
this.activeTab = tab as Tab;
|
||||||
this.searchQuery = "";
|
this.searchQuery = "";
|
||||||
this.sortColumn = "";
|
this.sortColumn = "";
|
||||||
|
|
@ -164,13 +167,23 @@ class FolkCrmView extends HTMLElement {
|
||||||
setTimeout(() => this._tour.start(), 1200);
|
setTimeout(() => this._tour.start(), 1200);
|
||||||
}
|
}
|
||||||
this._stopPresence = startPresenceHeartbeat(() => ({ module: 'rnetwork', context: this.graphSelectedId ? 'CRM' : `CRM - ${this.activeTab}` }));
|
this._stopPresence = startPresenceHeartbeat(() => ({ module: 'rnetwork', context: this.graphSelectedId ? 'CRM' : `CRM - ${this.activeTab}` }));
|
||||||
|
window.addEventListener('rspace-view-restored', this._onViewRestored as EventListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnectedCallback() {
|
disconnectedCallback() {
|
||||||
|
this._history.destroy();
|
||||||
|
window.removeEventListener('rspace-view-restored', this._onViewRestored as EventListener);
|
||||||
document.removeEventListener("rapp-tab-change", this._onTabChange);
|
document.removeEventListener("rapp-tab-change", this._onTabChange);
|
||||||
this._stopPresence?.();
|
this._stopPresence?.();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _onViewRestored = (e: CustomEvent) => {
|
||||||
|
if (e.detail?.moduleId !== 'rnetwork') return;
|
||||||
|
this.activeTab = e.detail.view;
|
||||||
|
this.searchQuery = "";
|
||||||
|
this.render();
|
||||||
|
};
|
||||||
|
|
||||||
private async subscribeCollabOverlay() {
|
private async subscribeCollabOverlay() {
|
||||||
const runtime = (window as any).__rspaceOfflineRuntime;
|
const runtime = (window as any).__rspaceOfflineRuntime;
|
||||||
if (!runtime?.isInitialized) return;
|
if (!runtime?.isInitialized) return;
|
||||||
|
|
|
||||||
|
|
@ -156,6 +156,7 @@ import type { SwagDoc, SwagDesign } from "../schemas";
|
||||||
import { swagSchema, swagDocId } from "../schemas";
|
import { swagSchema, swagDocId } from "../schemas";
|
||||||
import { startPresenceHeartbeat } from '../../../shared/collab-presence';
|
import { startPresenceHeartbeat } from '../../../shared/collab-presence';
|
||||||
import type { DocumentId } from "../../../shared/local-first/document";
|
import type { DocumentId } from "../../../shared/local-first/document";
|
||||||
|
import { ViewHistory } from "../../../shared/view-history.js";
|
||||||
|
|
||||||
// Auth helpers
|
// Auth helpers
|
||||||
function getSession(): { accessToken: string; claims: { sub: string; did?: string; username?: string } } | null {
|
function getSession(): { accessToken: string; claims: { sub: string; did?: string; username?: string } } | null {
|
||||||
|
|
@ -195,6 +196,7 @@ class FolkSwagDesigner extends HTMLElement {
|
||||||
|
|
||||||
// Tab state
|
// Tab state
|
||||||
private activeTab: TabId = "browse";
|
private activeTab: TabId = "browse";
|
||||||
|
private _history = new ViewHistory<TabId>("browse", "rswag");
|
||||||
|
|
||||||
// Demo state
|
// Demo state
|
||||||
private selectedProduct = "tee";
|
private selectedProduct = "tee";
|
||||||
|
|
@ -284,15 +286,24 @@ class FolkSwagDesigner extends HTMLElement {
|
||||||
setTimeout(() => this._tour.start(), 1200);
|
setTimeout(() => this._tour.start(), 1200);
|
||||||
}
|
}
|
||||||
this._stopPresence = startPresenceHeartbeat(() => ({ module: 'rswag', context: 'Swag Designer' }));
|
this._stopPresence = startPresenceHeartbeat(() => ({ module: 'rswag', context: 'Swag Designer' }));
|
||||||
|
window.addEventListener('rspace-view-restored', this._onViewRestored as EventListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnectedCallback() {
|
disconnectedCallback() {
|
||||||
|
this._history.destroy();
|
||||||
|
window.removeEventListener('rspace-view-restored', this._onViewRestored as EventListener);
|
||||||
this._stopPresence?.();
|
this._stopPresence?.();
|
||||||
this._lfcUnsub?.();
|
this._lfcUnsub?.();
|
||||||
this._lfcUnsub = null;
|
this._lfcUnsub = null;
|
||||||
this.lfClient?.disconnect();
|
this.lfClient?.disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _onViewRestored = (e: CustomEvent) => {
|
||||||
|
if (e.detail?.moduleId !== 'rswag') return;
|
||||||
|
this.activeTab = e.detail.view;
|
||||||
|
this.render();
|
||||||
|
};
|
||||||
|
|
||||||
private async initMultiplayer() {
|
private async initMultiplayer() {
|
||||||
try {
|
try {
|
||||||
this.lfClient = new SwagLocalFirstClient(this.space);
|
this.lfClient = new SwagLocalFirstClient(this.space);
|
||||||
|
|
@ -929,7 +940,9 @@ class FolkSwagDesigner extends HTMLElement {
|
||||||
// Tab switching
|
// Tab switching
|
||||||
this.shadow.querySelectorAll<HTMLElement>(".tab-btn").forEach(btn => {
|
this.shadow.querySelectorAll<HTMLElement>(".tab-btn").forEach(btn => {
|
||||||
btn.addEventListener("click", () => {
|
btn.addEventListener("click", () => {
|
||||||
this.activeTab = btn.dataset.tab as TabId;
|
const tab = btn.dataset.tab as TabId;
|
||||||
|
this._history.push(tab);
|
||||||
|
this.activeTab = tab;
|
||||||
this.render();
|
this.render();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -938,7 +951,9 @@ class FolkSwagDesigner extends HTMLElement {
|
||||||
this.shadow.querySelectorAll<HTMLElement>("[data-tab]").forEach(btn => {
|
this.shadow.querySelectorAll<HTMLElement>("[data-tab]").forEach(btn => {
|
||||||
if (!btn.classList.contains("tab-btn")) {
|
if (!btn.classList.contains("tab-btn")) {
|
||||||
btn.addEventListener("click", () => {
|
btn.addEventListener("click", () => {
|
||||||
this.activeTab = btn.dataset.tab as TabId;
|
const tab = btn.dataset.tab as TabId;
|
||||||
|
this._history.push(tab);
|
||||||
|
this.activeTab = tab;
|
||||||
this.render();
|
this.render();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -1014,6 +1029,7 @@ class FolkSwagDesigner extends HTMLElement {
|
||||||
this.shadow.querySelectorAll<HTMLElement>(".dither-btn").forEach(btn => {
|
this.shadow.querySelectorAll<HTMLElement>(".dither-btn").forEach(btn => {
|
||||||
btn.addEventListener("click", () => {
|
btn.addEventListener("click", () => {
|
||||||
this.ditherDesignSlug = btn.dataset.slug || "";
|
this.ditherDesignSlug = btn.dataset.slug || "";
|
||||||
|
this._history.push("dither");
|
||||||
this.activeTab = "dither";
|
this.activeTab = "dither";
|
||||||
this.render();
|
this.render();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
import type { DocumentId } from "../../../shared/local-first/document";
|
import type { DocumentId } from "../../../shared/local-first/document";
|
||||||
import { startPresenceHeartbeat } from '../../../shared/collab-presence';
|
import { startPresenceHeartbeat } from '../../../shared/collab-presence';
|
||||||
import { commitmentsSchema, commitmentsDocId } from "../schemas";
|
import { commitmentsSchema, commitmentsDocId } from "../schemas";
|
||||||
|
import { ViewHistory } from "../../../shared/view-history.js";
|
||||||
|
|
||||||
// ── Constants ──
|
// ── Constants ──
|
||||||
|
|
||||||
|
|
@ -254,6 +255,7 @@ class FolkTimebankApp extends HTMLElement {
|
||||||
private shadow: ShadowRoot;
|
private shadow: ShadowRoot;
|
||||||
private space = 'demo';
|
private space = 'demo';
|
||||||
private currentView: 'canvas' | 'collaborate' | 'dashboard' = 'canvas';
|
private currentView: 'canvas' | 'collaborate' | 'dashboard' = 'canvas';
|
||||||
|
private _history = new ViewHistory<'canvas' | 'collaborate' | 'dashboard'>('canvas', 'rtime');
|
||||||
|
|
||||||
// Pool panel state
|
// Pool panel state
|
||||||
private canvas!: HTMLCanvasElement;
|
private canvas!: HTMLCanvasElement;
|
||||||
|
|
@ -357,6 +359,7 @@ class FolkTimebankApp extends HTMLElement {
|
||||||
this.fetchData();
|
this.fetchData();
|
||||||
this._stopPresence = startPresenceHeartbeat(() => ({ module: 'rtime', context: 'Timebank' }));
|
this._stopPresence = startPresenceHeartbeat(() => ({ module: 'rtime', context: 'Timebank' }));
|
||||||
if (this.space !== 'demo') this.subscribeOffline();
|
if (this.space !== 'demo') this.subscribeOffline();
|
||||||
|
window.addEventListener('rspace-view-restored', this._onViewRestored as EventListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async subscribeOffline() {
|
private async subscribeOffline() {
|
||||||
|
|
@ -392,10 +395,28 @@ class FolkTimebankApp extends HTMLElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnectedCallback() {
|
disconnectedCallback() {
|
||||||
|
this._history.destroy();
|
||||||
|
window.removeEventListener('rspace-view-restored', this._onViewRestored as EventListener);
|
||||||
if (this.animFrame) cancelAnimationFrame(this.animFrame);
|
if (this.animFrame) cancelAnimationFrame(this.animFrame);
|
||||||
this._stopPresence?.(); this._offlineUnsub?.(); this._offlineUnsub = null;
|
this._stopPresence?.(); this._offlineUnsub?.(); this._offlineUnsub = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _onViewRestored = (e: CustomEvent) => {
|
||||||
|
if (e.detail?.moduleId !== 'rtime') return;
|
||||||
|
this.currentView = e.detail.view;
|
||||||
|
// Toggle visibility of view panels
|
||||||
|
const canvasView = this.shadow.getElementById('canvas-view');
|
||||||
|
const collabView = this.shadow.getElementById('collaborate-view');
|
||||||
|
const dashView = this.shadow.getElementById('dashboard-view');
|
||||||
|
if (canvasView) canvasView.style.display = this.currentView === 'canvas' ? 'flex' : 'none';
|
||||||
|
if (collabView) collabView.style.display = this.currentView === 'collaborate' ? 'flex' : 'none';
|
||||||
|
if (dashView) dashView.style.display = this.currentView === 'dashboard' ? 'flex' : 'none';
|
||||||
|
this.shadow.querySelectorAll('.tab').forEach(t => t.classList.toggle('active', (t as HTMLElement).dataset.view === this.currentView));
|
||||||
|
if (this.currentView === 'canvas') { this.resizePoolCanvas(); this.rebuildSidebar(); }
|
||||||
|
if (this.currentView === 'collaborate') this.refreshCollaborate();
|
||||||
|
if (this.currentView === 'dashboard') this.refreshDashboard();
|
||||||
|
};
|
||||||
|
|
||||||
private async fetchData() {
|
private async fetchData() {
|
||||||
const base = this.getApiBase();
|
const base = this.getApiBase();
|
||||||
try {
|
try {
|
||||||
|
|
@ -707,6 +728,7 @@ class FolkTimebankApp extends HTMLElement {
|
||||||
tab.addEventListener('click', () => {
|
tab.addEventListener('click', () => {
|
||||||
const view = (tab as HTMLElement).dataset.view as 'canvas' | 'collaborate' | 'dashboard';
|
const view = (tab as HTMLElement).dataset.view as 'canvas' | 'collaborate' | 'dashboard';
|
||||||
if (view === this.currentView) return;
|
if (view === this.currentView) return;
|
||||||
|
this._history.push(view);
|
||||||
this.currentView = view;
|
this.currentView = view;
|
||||||
this.shadow.querySelectorAll('.tab').forEach(t => t.classList.toggle('active', (t as HTMLElement).dataset.view === view));
|
this.shadow.querySelectorAll('.tab').forEach(t => t.classList.toggle('active', (t as HTMLElement).dataset.view === view));
|
||||||
const canvasView = this.shadow.getElementById('canvas-view')!;
|
const canvasView = this.shadow.getElementById('canvas-view')!;
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import { authFetch, requireAuth } from "../../../shared/auth-fetch";
|
||||||
import { startPresenceHeartbeat } from '../../../shared/collab-presence';
|
import { startPresenceHeartbeat } from '../../../shared/collab-presence';
|
||||||
import type { DocumentId } from "../../../shared/local-first/document";
|
import type { DocumentId } from "../../../shared/local-first/document";
|
||||||
import { tubeSchema, tubeDocId } from "../schemas";
|
import { tubeSchema, tubeDocId } from "../schemas";
|
||||||
|
import { ViewHistory } from "../../../shared/view-history.js";
|
||||||
|
|
||||||
class FolkVideoPlayer extends HTMLElement {
|
class FolkVideoPlayer extends HTMLElement {
|
||||||
private shadow: ShadowRoot;
|
private shadow: ShadowRoot;
|
||||||
|
|
@ -17,6 +18,7 @@ class FolkVideoPlayer extends HTMLElement {
|
||||||
private videos: Array<{ name: string; size: number; duration?: string; date?: string }> = [];
|
private videos: Array<{ name: string; size: number; duration?: string; date?: string }> = [];
|
||||||
private currentVideo: string | null = null;
|
private currentVideo: string | null = null;
|
||||||
private mode: "library" | "live" | "360live" = "library";
|
private mode: "library" | "live" | "360live" = "library";
|
||||||
|
private _history = new ViewHistory<"library" | "live" | "360live">("library", "rtube");
|
||||||
private streamKey = "";
|
private streamKey = "";
|
||||||
private searchTerm = "";
|
private searchTerm = "";
|
||||||
private isDemo = false;
|
private isDemo = false;
|
||||||
|
|
@ -63,13 +65,25 @@ class FolkVideoPlayer extends HTMLElement {
|
||||||
setTimeout(() => this._tour.start(), 1200);
|
setTimeout(() => this._tour.start(), 1200);
|
||||||
}
|
}
|
||||||
this._stopPresence = startPresenceHeartbeat(() => ({ module: 'rtube', context: this.currentVideo || 'Video' }));
|
this._stopPresence = startPresenceHeartbeat(() => ({ module: 'rtube', context: this.currentVideo || 'Video' }));
|
||||||
|
window.addEventListener('rspace-view-restored', this._onViewRestored as EventListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnectedCallback() {
|
disconnectedCallback() {
|
||||||
|
this._history.destroy();
|
||||||
|
window.removeEventListener('rspace-view-restored', this._onViewRestored as EventListener);
|
||||||
this._stopPresence?.();
|
this._stopPresence?.();
|
||||||
this._offlineUnsub?.(); this._offlineUnsub = null;
|
this._offlineUnsub?.(); this._offlineUnsub = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _onViewRestored = (e: CustomEvent) => {
|
||||||
|
if (e.detail?.moduleId !== 'rtube') return;
|
||||||
|
if (this.mode === "360live" && e.detail.view !== "360live") {
|
||||||
|
this.destroyHlsPlayers();
|
||||||
|
}
|
||||||
|
this.mode = e.detail.view;
|
||||||
|
this.render();
|
||||||
|
};
|
||||||
|
|
||||||
private async subscribeOffline() {
|
private async subscribeOffline() {
|
||||||
const runtime = (window as any).__rspaceOfflineRuntime;
|
const runtime = (window as any).__rspaceOfflineRuntime;
|
||||||
if (!runtime?.isInitialized) return;
|
if (!runtime?.isInitialized) return;
|
||||||
|
|
@ -587,6 +601,7 @@ class FolkVideoPlayer extends HTMLElement {
|
||||||
if (this.mode === "360live" && newMode !== "360live") {
|
if (this.mode === "360live" && newMode !== "360live") {
|
||||||
this.destroyHlsPlayers();
|
this.destroyHlsPlayers();
|
||||||
}
|
}
|
||||||
|
this._history.push(newMode);
|
||||||
this.mode = newMode;
|
this.mode = newMode;
|
||||||
this.render();
|
this.render();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import { startPresenceHeartbeat } from '../../../shared/collab-presence';
|
||||||
import { getModuleApiBase } from "../../../shared/url-helpers";
|
import { getModuleApiBase } from "../../../shared/url-helpers";
|
||||||
import type { DocumentId } from "../../../shared/local-first/document";
|
import type { DocumentId } from "../../../shared/local-first/document";
|
||||||
import { vnbSchema, vnbDocId } from "../schemas";
|
import { vnbSchema, vnbDocId } from "../schemas";
|
||||||
|
import { ViewHistory } from "../../../shared/view-history.js";
|
||||||
|
|
||||||
const VNB_TOUR_STEPS: TourStep[] = [
|
const VNB_TOUR_STEPS: TourStep[] = [
|
||||||
{ target: '.vnb-search', title: 'Search', message: 'Filter by vehicle type, dates, or economy model.' },
|
{ target: '.vnb-search', title: 'Search', message: 'Filter by vehicle type, dates, or economy model.' },
|
||||||
|
|
@ -68,6 +69,7 @@ class FolkVnbView extends HTMLElement {
|
||||||
#stats: any = null;
|
#stats: any = null;
|
||||||
#selectedRental: any = null;
|
#selectedRental: any = null;
|
||||||
#view: 'grid' | 'map' = 'grid';
|
#view: 'grid' | 'map' = 'grid';
|
||||||
|
private _history = new ViewHistory<'grid' | 'map'>('grid', 'rvnb');
|
||||||
#search = '';
|
#search = '';
|
||||||
#typeFilter = '';
|
#typeFilter = '';
|
||||||
#economyFilter = '';
|
#economyFilter = '';
|
||||||
|
|
@ -83,13 +85,23 @@ class FolkVnbView extends HTMLElement {
|
||||||
this.#loadData();
|
this.#loadData();
|
||||||
this._stopPresence = startPresenceHeartbeat(() => ({ module: 'rvnb', context: 'Venues' }));
|
this._stopPresence = startPresenceHeartbeat(() => ({ module: 'rvnb', context: 'Venues' }));
|
||||||
if (this.#space !== 'demo') this.subscribeOffline();
|
if (this.#space !== 'demo') this.subscribeOffline();
|
||||||
|
window.addEventListener('rspace-view-restored', this._onViewRestored as EventListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnectedCallback() {
|
disconnectedCallback() {
|
||||||
|
this._history.destroy();
|
||||||
|
window.removeEventListener('rspace-view-restored', this._onViewRestored as EventListener);
|
||||||
this._stopPresence?.();
|
this._stopPresence?.();
|
||||||
this._offlineUnsub?.(); this._offlineUnsub = null;
|
this._offlineUnsub?.(); this._offlineUnsub = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _onViewRestored = (e: CustomEvent) => {
|
||||||
|
if (e.detail?.moduleId !== 'rvnb') return;
|
||||||
|
this.#view = e.detail.view;
|
||||||
|
this.#render();
|
||||||
|
this.#renderContent();
|
||||||
|
};
|
||||||
|
|
||||||
private async subscribeOffline() {
|
private async subscribeOffline() {
|
||||||
const runtime = (window as any).__rspaceOfflineRuntime;
|
const runtime = (window as any).__rspaceOfflineRuntime;
|
||||||
if (!runtime?.isInitialized) return;
|
if (!runtime?.isInitialized) return;
|
||||||
|
|
@ -234,7 +246,9 @@ class FolkVnbView extends HTMLElement {
|
||||||
// View toggles
|
// View toggles
|
||||||
for (const btn of this.querySelectorAll('.vnb-view__toggle')) {
|
for (const btn of this.querySelectorAll('.vnb-view__toggle')) {
|
||||||
btn.addEventListener('click', () => {
|
btn.addEventListener('click', () => {
|
||||||
this.#view = (btn as HTMLElement).dataset.view as 'grid' | 'map';
|
const v = (btn as HTMLElement).dataset.view as 'grid' | 'map';
|
||||||
|
this._history.push(v);
|
||||||
|
this.#view = v;
|
||||||
this.#render();
|
this.#render();
|
||||||
this.#renderContent();
|
this.#renderContent();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ import type { WalletDoc, WatchedAddress } from "../schemas";
|
||||||
import { walletSchema, walletDocId } from "../schemas";
|
import { walletSchema, walletDocId } from "../schemas";
|
||||||
import { startPresenceHeartbeat } from '../../../shared/collab-presence';
|
import { startPresenceHeartbeat } from '../../../shared/collab-presence';
|
||||||
import type { DocumentId } from "../../../shared/local-first/document";
|
import type { DocumentId } from "../../../shared/local-first/document";
|
||||||
|
import { ViewHistory } from "../../../shared/view-history.js";
|
||||||
|
|
||||||
interface ChainInfo {
|
interface ChainInfo {
|
||||||
chainId: string;
|
chainId: string;
|
||||||
|
|
@ -197,6 +198,7 @@ class FolkWalletViewer extends HTMLElement {
|
||||||
|
|
||||||
// Visualization state
|
// Visualization state
|
||||||
private activeView: ViewTab = "budget";
|
private activeView: ViewTab = "budget";
|
||||||
|
private _history = new ViewHistory<ViewTab>("budget", "rwallet");
|
||||||
private transfers: Map<string, any> | null = null;
|
private transfers: Map<string, any> | null = null;
|
||||||
private transfersLoading = false;
|
private transfersLoading = false;
|
||||||
private d3Ready = false;
|
private d3Ready = false;
|
||||||
|
|
@ -265,9 +267,12 @@ class FolkWalletViewer extends HTMLElement {
|
||||||
setTimeout(() => this._tour.start(), 1200);
|
setTimeout(() => this._tour.start(), 1200);
|
||||||
}
|
}
|
||||||
this._stopPresence = startPresenceHeartbeat(() => ({ module: 'rwallet', context: 'Wallet' }));
|
this._stopPresence = startPresenceHeartbeat(() => ({ module: 'rwallet', context: 'Wallet' }));
|
||||||
|
window.addEventListener('rspace-view-restored', this._onViewRestored as EventListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnectedCallback() {
|
disconnectedCallback() {
|
||||||
|
this._history.destroy();
|
||||||
|
window.removeEventListener('rspace-view-restored', this._onViewRestored as EventListener);
|
||||||
this._stopPresence?.();
|
this._stopPresence?.();
|
||||||
if (this.flowsPlayInterval) {
|
if (this.flowsPlayInterval) {
|
||||||
clearInterval(this.flowsPlayInterval);
|
clearInterval(this.flowsPlayInterval);
|
||||||
|
|
@ -275,6 +280,15 @@ class FolkWalletViewer extends HTMLElement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _onViewRestored = (e: CustomEvent) => {
|
||||||
|
if (e.detail?.moduleId !== 'rwallet') return;
|
||||||
|
this.activeView = e.detail.view;
|
||||||
|
this.render();
|
||||||
|
if (this.activeView !== "balances") {
|
||||||
|
requestAnimationFrame(() => this.drawActiveVisualization());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private checkAuthState() {
|
private checkAuthState() {
|
||||||
try {
|
try {
|
||||||
const session = localStorage.getItem("encryptid_session");
|
const session = localStorage.getItem("encryptid_session");
|
||||||
|
|
@ -1265,6 +1279,7 @@ class FolkWalletViewer extends HTMLElement {
|
||||||
|
|
||||||
private handleViewTabClick(view: ViewTab) {
|
private handleViewTabClick(view: ViewTab) {
|
||||||
if (this.activeView === view) return;
|
if (this.activeView === view) return;
|
||||||
|
this._history.push(view);
|
||||||
this.activeView = view;
|
this.activeView = view;
|
||||||
|
|
||||||
if (view !== "balances" && !this.transfers && !this.isDemo) {
|
if (view !== "balances" && !this.transfers && !this.isDemo) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue