feat: Add presence heartbeat for Matrix online status

- matrix-gateway: POST /internal/matrix/presence/online endpoint
- usePresenceHeartbeat hook with activity tracking
- Auto away after 5 min inactivity
- Offline on page close/visibility change
- Integrated in MatrixChatRoom component
This commit is contained in:
Apple
2025-11-27 00:19:40 -08:00
parent 5bed515852
commit 3de3c8cb36
6371 changed files with 1317450 additions and 932 deletions

View File

@@ -0,0 +1,30 @@
/**
* Pure domain logic for DAO operations
* No I/O, no side effects
*/
import type { DaoRecord, DaoLevel, FederationMode } from './types';
/**
* Check if DAO can become a SuperDAO
*/
export function canBecomeSuperDao(dao: DaoRecord, childCount: number): boolean {
return childCount >= 1 && dao.federationMode === 'none';
}
/**
* Check if DAO can join a federation
*/
export function canJoinFederation(dao: DaoRecord, targetLevel: DaoLevel): boolean {
// A3/A4 can join, exceptions for A2 handled by PDP
return (dao.level === 'A3' || dao.level === 'A4') && dao.federationMode === 'none';
}
/**
* Check if DAO can leave federation
*/
export function canLeaveFederation(dao: DaoRecord): boolean {
return dao.federationMode === 'member' && dao.parentDaoId !== null;
}

View File

@@ -0,0 +1,37 @@
/**
* Domain types for DAO entities
* Based on: microdao-architecture.md, superdao-federation.md
*/
export type DaoLevel = 'A1' | 'A2' | 'A3' | 'A4';
export type DaoType = 'platform' | 'public' | 'private';
export type FederationMode = 'none' | 'member' | 'superdao';
export interface DaoRecord {
daoId: string;
name: string;
description?: string;
level: DaoLevel;
type: DaoType;
parentDaoId?: string | null;
federationMode: FederationMode;
createdAt: string;
updatedAt?: string;
}
export interface CreateDaoInput {
name: string;
description?: string;
type: 'public' | 'private';
level: 'A3' | 'A4';
settings?: Record<string, unknown>;
}
export interface CreatePlatformInput {
name: string;
slug: string;
description?: string;
domain?: string; // 'energy' | 'food' | 'water' | ...
}

View File

@@ -0,0 +1,38 @@
/**
* PDP Policy Model
* Based on: pdp_access.md, core-services-mvp.md
*/
export type Decision = 'allow' | 'deny' | 'require-elevation';
export type PolicyId =
| 'policy.dao.create'
| 'policy.vendor.register'
| 'policy.platform.create'
| 'policy.federation.join'
| 'policy.federation.leave'
| 'policy.federation.create-superdao'
| 'policy.federation.dissolve'
| 'policy.agent.run';
export interface PdpContext {
userId?: string;
daoId?: string;
daoLevel?: 'A1' | 'A2' | 'A3' | 'A4';
// Additional context: roles, balances, staking, etc.
[key: string]: unknown;
}
export interface PdpRequest {
policyId: PolicyId;
resource: Record<string, unknown>;
context: PdpContext;
}
export interface PdpResponse {
decision: Decision;
reason?: string;
details?: Record<string, unknown>;
}

View File

@@ -0,0 +1,14 @@
/**
* Domain types for User
*/
export type UserRole = 'owner' | 'admin' | 'member' | 'guest' | 'agent';
export interface User {
userId: string;
email?: string;
name?: string;
roles?: UserRole[];
}

View File

@@ -0,0 +1,27 @@
/**
* Domain types for Wallet
* Based on: core-services-mvp.md, tokenomics/city-tokenomics.md
*/
export type TokenSymbol = 'DAAR' | 'DAARION';
export interface Balance {
symbol: TokenSymbol;
amount: string; // Decimal as string to avoid precision issues
}
export interface WalletBalances {
userId: string;
balances: Balance[];
}
export interface AccessCheck {
check: 'dao.create' | 'vendor.register' | 'platform.create';
}
export interface AccessCheckResult {
allowed: boolean;
reason?: string;
}