implementing focus module

This commit is contained in:
Roland Osborne 2024-12-03 17:14:25 -08:00
parent 5ae031f268
commit 2ddf3e0d94
7 changed files with 5340 additions and 7864 deletions

View File

@ -1,7 +1,7 @@
import { useState, useContext, useEffect, useRef } from 'react'
import { AppContext } from '../context/AppContext'
import { DisplayContext } from '../context/DisplayContext'
import { Focus, Topic, AssetSource, HostingMode, TransformType } from 'databag-client-sdk'
import { Focus, FocusDetail, Topic, AssetSource, HostingMode, TransformType } from 'databag-client-sdk'
import { ContextType } from '../context/ContextType'
const img =
@ -31,14 +31,14 @@ export function useConversation() {
const setTopics = (topics: Topic[]) => {
console.log(topics);
}
const setStatus = (status: string) => {
console.log(status);
const setDetail = (focused: { cardId: string | null, channelId: string, detail: FocusDetail | null }) => {
console.log(focused);
}
focus.addTopicListener(setTopics);
focus.addStatusListener(setStatus);
focus.addDetailListener(setDetail);
return () => {
focus.removeTopicListener(setTopics);
focus.removeStatusListener(setStatus);
focus.removeDetailListener(setDetail);
}
}
}, [app.state.focus]);

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
import type { Channel, Topic, AssetSource, Asset, Tag, Article, Group, Card, Profile, Call, Config, NodeConfig, NodeAccount, Participant } from './types';
import type { Channel, Topic, AssetSource, Asset, Tag, Article, Group, Card, Profile, Call, FocusDetail, Config, NodeConfig, NodeAccount, Participant } from './types';
export interface Session {
getSettings(): Settings;
@ -138,8 +138,8 @@ export interface Focus {
addTopicListener(ev: (topics: Topic[]) => void): void;
removeTopicListener(ev: (topics: Topic[]) => void): void;
addStatusListener(ev: (status: string) => void): void;
removeStatusListener(ev: (status: string) => void): void;
addDetailListener(ev: (focused: { cardId: string | null, channelId: string, detail: FocusDetail | null }) => void): void;
removeDetailListener(ev: (focused: { cardId: string | null, channelId: string, detail: FocusDetail | null }) => void): void;
}
export interface Node {

View File

@ -2,7 +2,7 @@ import { EventEmitter } from 'eventemitter3';
import type { Contact, Focus } from './api';
import { Logging } from './logging';
import { FocusModule } from './focus';
import type { Card, Channel, Article, Topic, Asset, Tag, Profile, Participant } from './types';
import type { Card, Channel, Article, Topic, Asset, Tag, FocusDetail, Profile, Participant } from './types';
import { type CardEntity, avatar } from './entities';
import type { ArticleDetail, ChannelSummary, ChannelDetail, CardProfile, CardDetail, CardItem, ChannelItem, ArticleItem } from './items';
import { defaultCardItem, defaultChannelItem } from './items';
@ -504,6 +504,20 @@ export class ContactModule implements Contact {
};
entry.item.unsealedDetail = null;
await this.unsealChannelDetail(cardId, id, entry.item);
if (this.focus) {
const { dataType, data, enableImage, enableAudio, enableVideo, enableBinary } = detail;
const sealed = dataType === 'sealed';
const focusDetail = {
sealed,
dataType,
data: sealed ? entry.item.unsealedDetail : data,
enableImage,
enableAudio,
enableVideo,
enableBinary,
}
this.focus.setDetail(cardId, id, focusDetail);
}
entry.channel = this.setChannel(cardId, id, entry.item);
await this.store.setContactCardChannelDetail(guid, cardId, id, entry.item.detail, entry.item.unsealedDetail);
}
@ -526,9 +540,9 @@ export class ContactModule implements Contact {
this.setChannelUnread(cardId, id);
entry.channel = this.setChannel(cardId, id, entry.item);
await this.store.setContactCardChannelSummary(guid, cardId, id, entry.item.summary, entry.item.unsealedSummary);
}
if (this.focus) {
await this.focus.setRevision(cardId, id, topicRevision);
if (this.focus) {
await this.focus.setRevision(cardId, id, topicRevision);
}
}
} else {
const channels = this.getChannelEntries(cardId);
@ -632,6 +646,7 @@ export class ContactModule implements Contact {
const channelsEntry = this.channelEntries.get(cardId);
const channelEntry = channelsEntry?.get(channelId);
if (cardEntry && channelEntry) {
// allocate focus
const node = cardEntry.item.profile.node;
const guid = cardEntry.item.profile.guid;
const token = cardEntry.item.detail.token;
@ -640,6 +655,20 @@ export class ContactModule implements Contact {
const sealEnabled = Boolean(this.seal);
const insecure = /^(?!0)(?!.*\.$)((1?\d?\d|25[0-5]|2[0-4]\d)(\.|:\d+$|$)){4}$/.test(node);
this.focus = new FocusModule(this.log, this.store, this.crypto, this.media, cardId, channelId, this.guid, { node, secure: !insecure, token: `${guid}.${token}` }, channelKey, sealEnabled, revision, markRead, flagTopic);
// set current detail
const { dataType, data, enableImage, enableAudio, enableVideo, enableBinary } = channelEntry.item.detail;
const sealed = dataType === 'sealed';
const focusDetail = {
sealed,
dataType,
data: sealed ? channelEntry.item.unsealedDetail : data,
enableImage,
enableAudio,
enableVideo,
enableBinary,
}
this.focus.setDetail(cardId, channelId, focusDetail);
} else {
this.focus = new FocusModule(this.log, this.store, this.crypto, this.media, cardId, channelId, this.guid, null, null, false, 0, markRead, flagTopic);
}
@ -1022,6 +1051,19 @@ export class ContactModule implements Contact {
if (item.channelKey) {
const { data } = await this.crypto.aesDecrypt(subjectEncrypted, subjectIv, item.channelKey);
item.unsealedDetail = data;
if (this.focus) {
const { dataType, enableImage, enableAudio, enableVideo, enableBinary } = item.detail;
const focusDetail = {
sealed: true,
dataType,
data,
enableImage,
enableAudio,
enableVideo,
enableBinary,
}
this.focus.setDetail(cardId, channelId, focusDetail);
}
return true;
}
} catch (err) {

View File

@ -2,7 +2,7 @@ import { EventEmitter } from 'eventemitter3';
import type { Focus } from './api';
import type { TopicItem, AssetItem, TopicDetail } from './items';
import type { Topic, Asset, AssetSource, Participant } from './types';
import { TransformType, HostingMode, AssetType } from './types';
import { TransformType, HostingMode, AssetType, FocusDetail } from './types';
import type { Logging } from './logging';
import { Store } from './store';
import { Crypto } from './crypto';
@ -50,6 +50,7 @@ export class FocusModule implements Focus {
private unsealAll: boolean;
private markRead: ()=>Promise<void>;
private flagChannelTopic: (id: string)=>Promise<void>;
private focusDetail: FocusDetail | null;
private markers: Set<string>;
@ -79,6 +80,7 @@ export class FocusModule implements Focus {
this.closing = false;
this.closeMedia = [];
this.nextRevision = null;
this.focusDetail = null;
this.loadMore = false;
this.unsealAll = false;
this.localComplete = false;
@ -829,20 +831,36 @@ export class FocusModule implements Focus {
this.emitter.emit('topic', topics);
}
public addStatusListener(ev: (status: string) => void) {
this.emitter.on('status', ev);
const status = this.connection ? 'connected' : 'disconnected'
ev(status);
public addDetailListener(ev: (focused: { cardId: string | null, channelId: string, detail: FocusDetail | null }) => void) {
const { cardId, channelId } = this;
const access = Boolean(this.connection && (!this.focusDetail?.sealed || (this.sealEnabled && this.channelKey)))
const detail = access ? this.focusDetail : null;
this.emitter.on('detail', ev);
ev({ cardId, channelId, detail });
}
public removeStatusListener(ev: (status: string) => void) {
this.emitter.off('status', ev);
public removeDetailListener(ev: (focused: { cardId: string | null, channelId: string, detail: FocusDetail | null }) => void) {
this.emitter.off('detail', ev);
}
private emitDetail() {
const { cardId, channelId } = this;
const access = Boolean(this.connection && (!this.focusDetail?.sealed || (this.sealEnabled && this.channelKey)))
const detail = access ? this.focusDetail : null;
this.emitter.emit('detail', { cardId, channelId, detail });
}
public disconnect(cardId: string | null, channelId: string) {
if (cardId === this.cardId && channelId === this.channelId) {
this.connection = null;
this.emitStatus();
this.emitDetail();
}
}
public setDetail(cardId: string | null, channelId: string, detail: FocusDetail) {
if (cardId === this.cardId && channelId === this.channelId) {
this.focusDetail = detail;
this.emitDetail();
}
}
@ -856,6 +874,7 @@ export class FocusModule implements Focus {
public async setSealEnabled(enable: boolean) {
this.sealEnabled = enable;
this.unsealAll = true;
this.emitDetail();
await this.sync();
}
@ -863,6 +882,7 @@ export class FocusModule implements Focus {
if (cardId === this.cardId && channelId === this.channelId) {
this.channelKey = channelKey;
this.unsealAll = true;
this.emitDetail();
await this.sync();
}
}
@ -877,11 +897,6 @@ export class FocusModule implements Focus {
});
}
private emitStatus() {
const status = this.connection ? 'connected' : 'disconnected'
this.emitter.emit('status', status);
}
private getTopicData(item: TopicItem): { data: any, assets: AssetItem[] } {
const topicDetail = item.detail.sealed ? item.unsealedDetail : item.detail.data;
const { revision } = item.detail;

View File

@ -141,6 +141,20 @@ export class StreamModule {
entry.item.unsealedDetail = null;
await this.unsealChannelDetail(id, entry.item);
entry.channel = this.setChannel(id, entry.item);
if (this.focus) {
const { dataType, data, enableImage, enableAudio, enableVideo, enableBinary } = detail;
const sealed = dataType === 'sealed';
const focusDetail = {
sealed,
dataType,
data: sealed ? entry.item.unsealedDetail : data,
enableImage,
enableAudio,
enableVideo,
enableBinary,
}
this.focus.setDetail(null, id, focusDetail);
}
await this.store.setContentChannelDetail(guid, id, entry.item.detail, entry.item.unsealedDetail);
}
@ -375,8 +389,6 @@ export class StreamModule {
focus.close();
}
const entry = this.channelEntries.get(channelId);
const markRead = async () => {
try {
await this.setUnreadChannel(channelId, false);
@ -390,15 +402,26 @@ export class StreamModule {
await addFlag(node, secure, guid, { channelId, topicId });
}
const entry = this.channelEntries.get(channelId);
const channelKey = entry ? entry.item.channelKey : null;
const revision = entry ? entry.item.summary.revision : 0;
const sealEnabled = Boolean(this.seal);
this.focus = new FocusModule(this.log, this.store, this.crypto, this.media, null, channelId, this.guid, { node, secure, token }, channelKey, sealEnabled, revision, markRead, flagTopic);
if (entry) {
const channelKey = entry.item.channelKey;
const sealEnabled = Boolean(this.seal);
const revision = entry.item.summary.revision;
this.focus = new FocusModule(this.log, this.store, this.crypto, this.media, null, channelId, this.guid, { node, secure, token }, channelKey, sealEnabled, revision, markRead, flagTopic);
const { dataType, data, enableImage, enableAudio, enableVideo, enableBinary } = entry.item.detail;
const sealed = dataType === 'sealed';
const focusDetail = {
sealed,
dataType,
data: sealed ? entry.item.unsealedDetail : data,
enableImage,
enableAudio,
enableVideo,
enableBinary,
}
this.focus.setDetail(null, channelId, focusDetail);
}
else {
this.focus = new FocusModule(this.log, this.store, this.crypto, this.media, null, channelId, this.guid, null, null, false, 0, markRead, flagTopic);
}
return this.focus;
}
@ -542,6 +565,19 @@ export class StreamModule {
if (item.channelKey) {
const { data } = await this.crypto.aesDecrypt(subjectEncrypted, subjectIv, item.channelKey);
item.unsealedDetail = data;
if (this.focus) {
const { dataType, enableImage, enableAudio, enableVideo, enableBinary } = item.detail;
const focusDetail = {
sealed: true,
dataType,
data,
enableImage,
enableAudio,
enableVideo,
enableBinary,
}
this.focus.setDetail(null, channelId, focusDetail);
}
return true;
}
} catch (err) {

View File

@ -65,6 +65,16 @@ export type Channel = {
members: Member[];
};
export type FocusDetail = {
sealed: boolean;
dataType: string;
data: any;
enableImage: boolean;
enableAudio: boolean;
enableVideo: boolean;
enableBinary: boolean;
}
export type Member = {
guid: string;
//pushEnabled: boolean;