breaking changes, major refactoring of Composite
This commit is contained in:
parent
858d21e7db
commit
0161f4ba4f
@ -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>
|
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
const redirectUri = 'http://localhost:5173/';
|
const redirectUri = 'http://localhost:5173/';
|
||||||
|
|
||||||
export let setupLit;
|
export let services;
|
||||||
|
|
||||||
let view = 'sign_in';
|
let view = 'sign_in';
|
||||||
let sessionSigs;
|
let sessionSigs;
|
||||||
@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
try {
|
try {
|
||||||
provider = await setupLit.connectProvider();
|
provider = await services.setupLit.connectProvider();
|
||||||
|
|
||||||
logMessage('Component mounted.');
|
logMessage('Component mounted.');
|
||||||
|
|
||||||
|
@ -1,16 +1,19 @@
|
|||||||
<script>
|
<script>
|
||||||
export let id;
|
export let id;
|
||||||
|
|
||||||
import { getComponentStore } from '$lib/stores/componentStores.ts';
|
import { getCompositeStore } from '$lib/core/compositeStores';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
|
|
||||||
const store = getComponentStore(id);
|
const store = getCompositeStore(id);
|
||||||
|
|
||||||
export let helloEarthAlert;
|
export let services;
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
helloEarthAlert.alertMe();
|
if (services && services.helloEarthAlert) {
|
||||||
console.log('should alerted by now');
|
console.log('Alerted by HelloEarthAlert');
|
||||||
|
} else {
|
||||||
|
console.error('Services or helloEarthAlert not loaded');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import { messages } from '$lib/services/messages';
|
import { messages } from '$lib/services/messages';
|
||||||
import { onMount, afterUpdate } from 'svelte';
|
import { onMount, afterUpdate } from 'svelte';
|
||||||
import Composite from './Composite.svelte';
|
import Composite from '$lib/core/Composite.svelte';
|
||||||
|
|
||||||
let latestMessages = [];
|
let latestMessages = [];
|
||||||
|
|
||||||
|
@ -13,16 +13,14 @@
|
|||||||
const match = text.match(appCommandPattern);
|
const match = text.match(appCommandPattern);
|
||||||
if (match && match[1]) {
|
if (match && match[1]) {
|
||||||
message.composite = {
|
message.composite = {
|
||||||
layout: '',
|
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
componentName: match[1], // Matched component name
|
component: match[1] // Matched component name
|
||||||
props: {},
|
|
||||||
actions: ['ClearMessages']
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
console.log(message);
|
||||||
|
|
||||||
createMessage(message);
|
createMessage(message);
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
<script>
|
<script>
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { connectWallet } from '$lib/services/wallet/wallet';
|
import { connectWallet } from '$lib/services/wallet/wallet';
|
||||||
import { getComponentStore } from '$lib/stores/componentStores.ts';
|
import { getCompositeStore } from '$lib/core/compositeStores';
|
||||||
|
|
||||||
export let id;
|
export let id;
|
||||||
const store = getComponentStore(id);
|
const store = getCompositeStore(id);
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
$store.pkpWallet = await connectWallet($store.pkpPubKey, $store.rpcURL);
|
$store.pkpWallet = await connectWallet($store.pkpPubKey, $store.rpcURL);
|
||||||
|
144
src/lib/core/Composite.svelte
Normal file
144
src/lib/core/Composite.svelte
Normal 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>
|
16
src/lib/core/compositeStores.ts
Normal file
16
src/lib/core/compositeStores.ts
Normal 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);
|
||||||
|
}
|
16
src/lib/core/servicesLoader.ts
Normal file
16
src/lib/core/servicesLoader.ts
Normal 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;
|
||||||
|
|
@ -4,7 +4,7 @@ import { LitAccessControlConditionResource, LitAbility } from '@lit-protocol/aut
|
|||||||
export async function createLitSession(
|
export async function createLitSession(
|
||||||
provider: IProvider,
|
provider: IProvider,
|
||||||
pkpPublicKey: string,
|
pkpPublicKey: string,
|
||||||
authMethod: any
|
authMethod: any,
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
const litResource = new LitAccessControlConditionResource('*');
|
const litResource = new LitAccessControlConditionResource('*');
|
||||||
return await provider.getSessionSigs({
|
return await provider.getSessionSigs({
|
||||||
|
@ -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;
|
|
@ -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);
|
|
||||||
}
|
|
@ -1,36 +1,24 @@
|
|||||||
<script>
|
<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: {
|
layout: {
|
||||||
areas: `
|
areas: `
|
||||||
"header main"
|
"top top top"
|
||||||
"aside main"
|
"main main main"
|
||||||
"footer footer";
|
"footer footer footer";
|
||||||
`,
|
`,
|
||||||
columns: '1fr 1fr',
|
columns: '1fr 1fr 1fr',
|
||||||
rows: 'auto 1fr auto'
|
rows: '1fr 1fr auto'
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
|
||||||
id: '1',
|
|
||||||
componentName: 'GoogleAuth',
|
|
||||||
slot: 'header',
|
|
||||||
services: ['setupLit']
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'messages',
|
|
||||||
componentName: 'Messages',
|
|
||||||
slot: 'main',
|
|
||||||
store: {
|
|
||||||
prop1: 'prop1',
|
|
||||||
prop2: 'prop2'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: 'wallet',
|
id: 'wallet',
|
||||||
componentName: 'Wallet',
|
component: 'Wallet',
|
||||||
slot: 'aside',
|
slot: 'top',
|
||||||
store: {
|
store: {
|
||||||
pkpWallet: '',
|
pkpWallet: '',
|
||||||
rpcURL: 'https://rpc.gnosischain.com/',
|
rpcURL: 'https://rpc.gnosischain.com/',
|
||||||
@ -39,12 +27,17 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '3',
|
id: 'messages',
|
||||||
componentName: 'Terminal',
|
component: 'Messages',
|
||||||
|
slot: 'main'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'terminal',
|
||||||
|
component: 'Terminal',
|
||||||
slot: 'footer'
|
slot: 'footer'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Composite {componentsData} />
|
<Composite {composite} />
|
||||||
|
Loading…
x
Reference in New Issue
Block a user