breaking changes, major refactoring of Composite

This commit is contained in:
Samuel Andert 2023-07-26 09:23:15 +02:00
parent 858d21e7db
commit 0161f4ba4f
14 changed files with 212 additions and 215 deletions

View File

@ -1,147 +0,0 @@
<script lang="ts">
import Composite from './Composite.svelte';
import components from '$lib/componentLoader';
import services from '$lib/servicesLoader';
import { createComponentStore, getComponentStore } from '$lib/stores/componentStores.ts';
interface ICompositeLayout {
areas: string;
columns?: string;
rows?: string;
gap?: string;
tailwindClasses?: string;
}
export let componentsData: {
layout: ICompositeLayout;
children: Array<any>;
};
$: layoutStyle = `
grid-template-areas: ${componentsData.layout.areas};
${componentsData.layout.gap ? `gap: ${componentsData.layout.gap};` : ''}
${
componentsData.layout.columns
? `grid-template-columns: ${componentsData.layout.columns};`
: ''
}
${componentsData.layout.rows ? `grid-template-rows: ${componentsData.layout.rows};` : ''}
`;
function initializeComponentState(child) {
child.store = createComponentStore(child.id, child.store || {});
if (child.children) {
child.children.forEach(initializeComponentState);
}
}
$: if (componentsData && componentsData.children) {
componentsData.children.forEach(initializeComponentState);
}
function mapAndSubscribe(component) {
console.log('Mapping and subscribing for:', component.id);
if (component.map) {
const localStore = getComponentStore(component.id);
for (const [localKey, external] of Object.entries(component.map)) {
const [externalID, externalKey] = external.split('.').map((item) => item.trim());
const externalStore = getComponentStore(externalID);
if (externalStore) {
externalStore.subscribe((externalState) => {
console.log('External state:', externalState);
if (externalState && externalKey in externalState) {
localStore.update((storeValue) => {
storeValue = storeValue || {};
if (storeValue[localKey] !== externalState[externalKey]) {
storeValue[localKey] = externalState[externalKey];
return storeValue;
}
return storeValue;
});
}
});
}
}
}
if (component.children) {
component.children.forEach(mapAndSubscribe);
}
}
$: if (componentsData && componentsData.children) {
componentsData.children.forEach(mapAndSubscribe);
}
async function getComponent(componentName) {
if (components[componentName]) {
const module = await components[componentName]();
return module.default;
}
return null;
}
async function loadService(serviceName) {
if (services[serviceName]) {
const module = await services[serviceName]();
return module.default || module;
}
return null;
}
async function getServiceProps(component) {
const loadedServices = [];
if (component.services) {
for (const serviceName of component.services) {
const loadedService = await loadService(serviceName);
if (loadedService) {
loadedServices.push(loadedService);
}
}
}
return loadedServices;
}
</script>
<div class={`grid w-full h-full ${componentsData.layout.tailwindClasses}`} style={layoutStyle}>
{#each componentsData.children as component (component.id)}
<div class={`w-full h-full overflow-hidden`} style={`grid-area: ${component.slot}`}>
{#await Promise.all( [getComponent(component.componentName), getServiceProps(component)] ) then [Component, serviceProps]}
{#if Component}
<svelte:component
this={Component}
id={component.id}
{...component.props}
{...serviceProps.reduce((acc, currServiceModule, idx) => {
acc[component.services[idx]] = currServiceModule;
return acc;
}, {})}
/>
{#if component.actions}
<div class="flex justify-end p-4">
{#each component.actions as action}
{#await getComponent(action) then ActionComponent}
{#if ActionComponent}
<svelte:component this={ActionComponent} {...component.props} />
{:else}
<p>Action {action} not found.</p>
{/if}
{/await}
{/each}
</div>
{/if}
{#if component.children && component.children.length}
<Composite componentsData={component} />
{/if}
{:else}
<p>Component {component.componentName} not found.</p>
{/if}
{/await}
</div>
{/each}
</div>

View File

@ -8,7 +8,7 @@
const redirectUri = 'http://localhost:5173/';
export let setupLit;
export let services;
let view = 'sign_in';
let sessionSigs;
@ -30,7 +30,7 @@
onMount(async () => {
try {
provider = await setupLit.connectProvider();
provider = await services.setupLit.connectProvider();
logMessage('Component mounted.');

View File

@ -1,16 +1,19 @@
<script>
export let id;
import { getComponentStore } from '$lib/stores/componentStores.ts';
import { getCompositeStore } from '$lib/core/compositeStores';
import { onMount } from 'svelte';
const store = getComponentStore(id);
const store = getCompositeStore(id);
export let helloEarthAlert;
export let services;
onMount(async () => {
helloEarthAlert.alertMe();
console.log('should alerted by now');
if (services && services.helloEarthAlert) {
console.log('Alerted by HelloEarthAlert');
} else {
console.error('Services or helloEarthAlert not loaded');
}
});
</script>

View File

@ -1,7 +1,7 @@
<script>
import { messages } from '$lib/services/messages';
import { onMount, afterUpdate } from 'svelte';
import Composite from './Composite.svelte';
import Composite from '$lib/core/Composite.svelte';
let latestMessages = [];

View File

@ -13,16 +13,14 @@
const match = text.match(appCommandPattern);
if (match && match[1]) {
message.composite = {
layout: '',
children: [
{
componentName: match[1], // Matched component name
props: {},
actions: ['ClearMessages']
component: match[1] // Matched component name
}
]
};
}
console.log(message);
createMessage(message);
}

View File

@ -1,10 +1,10 @@
<script>
import { onMount } from 'svelte';
import { connectWallet } from '$lib/services/wallet/wallet';
import { getComponentStore } from '$lib/stores/componentStores.ts';
import { getCompositeStore } from '$lib/core/compositeStores';
export let id;
const store = getComponentStore(id);
const store = getCompositeStore(id);
onMount(async () => {
$store.pkpWallet = await connectWallet($store.pkpPubKey, $store.rpcURL);

View File

@ -0,0 +1,144 @@
<script lang="ts">
import Composite from './Composite.svelte';
import components from '$lib/core/componentLoader';
import services from '$lib/core/servicesLoader';
import { createCompositeStore, getCompositeStore } from '$lib/core/compositeStores';
interface IComposite {
layout?: ICompositeLayout;
id: string;
slot?: string;
component: string;
services?: string[];
map?: Record<string, string>;
store?: Record<string, any>;
children?: IComposite[];
}
interface ICompositeLayout {
areas: string;
columns?: string;
rows?: string;
gap?: string;
tailwindClasses?: string;
}
export let composite: IComposite;
$: layoutStyle = composite?.layout
? `
grid-template-areas: ${composite.layout.areas};
${composite.layout.gap ? `gap: ${composite.layout.gap};` : ''}
${composite.layout.columns ? `grid-template-columns: ${composite.layout.columns};` : ''}
${composite.layout.rows ? `grid-template-rows: ${composite.layout.rows};` : ''}
`
: '';
function initializeCompositeState(child: IComposite) {
if (child.id) {
child.store = createCompositeStore(child.id, child.store || {});
}
if (child.children) {
child.children.forEach(initializeCompositeState);
}
}
$: if (composite && composite.children) {
composite.children.forEach(initializeCompositeState);
}
function mapAndSubscribe(component: IComposite) {
console.log('Mapping and subscribing for:', component.id);
if (component.map) {
const localStore = getCompositeStore(component.id);
for (const [localKey, external] of Object.entries(component.map)) {
const [externalID, externalKey] = external.split('.').map((item) => item.trim());
const externalStore = getCompositeStore(externalID);
if (externalStore) {
externalStore.subscribe((externalState) => {
console.log('External state:', externalState);
if (externalState && externalKey in externalState) {
localStore.update((storeValue) => {
storeValue = storeValue || {};
if (storeValue[localKey] !== externalState[externalKey]) {
storeValue[localKey] = externalState[externalKey];
return storeValue;
}
return storeValue;
});
}
});
}
}
}
if (component.children) {
component.children.forEach(mapAndSubscribe);
}
}
$: if (composite && composite.children) {
composite.children.forEach(mapAndSubscribe);
}
async function getComponent(componentName: string) {
if (components[componentName]) {
const module = await components[componentName]();
return module.default;
}
return null;
}
async function loadService(serviceName: string) {
if (services[serviceName]) {
const module = await services[serviceName]();
return module.default || module;
}
return null;
}
async function getServiceProps(component: IComposite) {
const loadedServices = {};
if (component.services) {
for (const serviceName of component.services) {
const loadedService = await loadService(serviceName);
if (loadedService) {
loadedServices[serviceName] = loadedService;
}
}
}
return loadedServices;
}
</script>
<div
class={composite?.layout
? `grid w-full h-full ${composite.layout.tailwindClasses}`
: 'grid w-full h-full'}
style={layoutStyle}
>
{#if composite && composite.children}
{#each composite.children as child (child.id)}
<div class={`w-full h-full overflow-hidden`} style={`grid-area: ${child.slot}`}>
{#await Promise.all( [getComponent(child.component), getServiceProps(child)] ) then [Component, serviceProps]}
{#if Component}
<svelte:component
this={Component}
id={child.id}
{...child.store}
services={serviceProps}
/>
{#if child.children && child.children.length}
<Composite composite={child} />
{/if}
{:else}
<p>Component {child.component} not found.</p>
{/if}
{/await}
</div>
{/each}
{/if}
</div>

View File

@ -0,0 +1,16 @@
import { writable } from 'svelte/store';
const compositeStores = new Map();
// Create or retrieve a composite store
export function createCompositeStore(compositeId: string, initialState = {}) {
if (!compositeStores.has(compositeId)) {
compositeStores.set(compositeId, writable(initialState));
}
return compositeStores.get(compositeId);
}
// Get composite store or create a default empty one if not exists
export function getCompositeStore(compositeId: string) {
return compositeStores.get(compositeId) || createCompositeStore(compositeId);
}

View File

@ -0,0 +1,16 @@
import serviceNames from 'virtual:services-list';
const services: { [key: string]: Function } = {};
serviceNames.forEach(path => {
const serviceName = path.split('/').pop().replace('.ts', '');
services[serviceName] = () => import(/* @vite-ignore */ `/src/lib/services/${path}`)
.then(mod => {
console.log(`Loaded service ${serviceName} with the following properties:`, Object.keys(mod));
if (mod.default) return mod.default;
return mod;
});
});
export default services;

View File

@ -4,7 +4,7 @@ import { LitAccessControlConditionResource, LitAbility } from '@lit-protocol/aut
export async function createLitSession(
provider: IProvider,
pkpPublicKey: string,
authMethod: any
authMethod: any,
): Promise<any> {
const litResource = new LitAccessControlConditionResource('*');
return await provider.getSessionSigs({

View File

@ -1,10 +0,0 @@
import serviceNames from 'virtual:services-list';
const services = {};
serviceNames.forEach(path => {
const name = path.split('/').pop().replace('.ts', ''); // Extract just the file name from the path without .ts
services[name] = () => import(/* @vite-ignore */ `/src/lib/services/${path}`).then(mod => mod);
});
export default services;

View File

@ -1,16 +0,0 @@
import { writable } from 'svelte/store';
const componentStores = new Map();
// Create or retrieve a component store
export function createComponentStore(componentId, initialState = {}) {
if (!componentStores.has(componentId)) {
componentStores.set(componentId, writable(initialState));
}
return componentStores.get(componentId);
}
// Get component store or create a default empty one if not exists
export function getComponentStore(componentId) {
return componentStores.get(componentId) || createComponentStore(componentId);
}

View File

@ -1,36 +1,24 @@
<script>
import Composite from '$lib/components/Composite.svelte';
import Wallet from '$lib/Wallet.svelte';
import HelloEarth from '$lib/components/HelloEarth/HelloEarth.svelte';
import Composite from '$lib/core/Composite.svelte';
let componentsData = {
let composite = {
id: 'composite',
layout: {
areas: `
"header main"
"aside main"
"footer footer";
"top top top"
"main main main"
"footer footer footer";
`,
columns: '1fr 1fr',
rows: 'auto 1fr auto'
columns: '1fr 1fr 1fr',
rows: '1fr 1fr auto'
},
children: [
{
id: '1',
componentName: 'GoogleAuth',
slot: 'header',
services: ['setupLit']
},
{
id: 'messages',
componentName: 'Messages',
slot: 'main',
store: {
prop1: 'prop1',
prop2: 'prop2'
}
},
{
id: 'wallet',
componentName: 'Wallet',
slot: 'aside',
component: 'Wallet',
slot: 'top',
store: {
pkpWallet: '',
rpcURL: 'https://rpc.gnosischain.com/',
@ -39,12 +27,17 @@
}
},
{
id: '3',
componentName: 'Terminal',
id: 'messages',
component: 'Messages',
slot: 'main'
},
{
id: 'terminal',
component: 'Terminal',
slot: 'footer'
}
]
};
</script>
<Composite {componentsData} />
<Composite {composite} />