Skip to content

Commit

Permalink
fix: Refactor/custom templates (#397)
Browse files Browse the repository at this point in the history
* Fix custom template flake

* Refactor custom templates
  • Loading branch information
c12i committed Oct 31, 2024
1 parent 9d9320c commit f654d92
Show file tree
Hide file tree
Showing 103 changed files with 808 additions and 516 deletions.
2 changes: 1 addition & 1 deletion flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@

lib.wrapCustomTemplate = { system, pkgs, customTemplatePath }:
let
scaffolding = inputs.holochain.packages.${system}.hc-scaffold;
scaffolding = inputs.holonix.packages.${system}.hc-scaffold;
in
pkgs.runCommand "hc-scaffold"
{
Expand Down
14 changes: 8 additions & 6 deletions templates/custom-template/custom-template/flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,30 @@

inputs = {
holonix.url = "github:holochain/holonix?ref=main";

scaffolding.url = "github:holochain/scaffolding/holochain-weekly";
nixpkgs.follows = "holonix/nixpkgs";
flake-parts.follows = "holonix/flake-parts";
};

outputs = inputs@{ flake-parts, ... }: flake-parts.lib.mkFlake { inherit inputs; } {
systems = builtins.attrNames inputs.holonix.devShells;
perSystem = { inputs', pkgs, ... }: {
perSystem = { inputs', pkgs, system, ... }: {
formatter = pkgs.nixpkgs-fmt;

devShells.default = pkgs.mkShell {
inputsFrom = [ inputs'.holonix.devShells.default ];

packages = (with pkgs; [
nodejs_20
binaryen
]);
packages = (with pkgs; [ nodejs_20 binaryen ]);

shellHook = ''
export PS1='\[\033[1;34m\][holonix:\w]\$\[\033[0m\] '
'';
};

packages.app = inputs.scaffolding.lib.wrapCustomTemplate {
inherit pkgs system;
customTemplatePath = ./template;
};
};
};
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,2 @@
{{#if (eq collection_type.type "Global")}}
At first, the UI for this application is empty. If you want the newly scaffolded collection to be the entry point for its UI, import the element in `ui/src/holochain-app.ts`:

import './{{dna_role_name}}/{{coordinator_zome_manifest.name}}/{{kebab_case collection_name}}';

And insert it in the `<div id="content"></div>` like this:

<div id="content"><{{kebab_case collection_name}}></{{kebab_case collection_name}}></div>
{{/if}}
If you want the newly scaffolded collection's component to be the entry point for its UI, import the
generated <{{kebab_case collection_name}}></{{kebab_case collection_name}}> component.
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
import { assert, test } from "vitest";

import { runScenario, dhtSync, CallableCell } from '@holochain/tryorama';
import { NewEntryAction, ActionHash, Record, Link, AppBundleSource, fakeActionHash, fakeAgentPubKey, fakeEntryHash } from '@holochain/client';
import {
NewEntryAction,
ActionHash,
Record,
Link,
AppBundleSource,
fakeActionHash,
fakeAgentPubKey,
fakeEntryHash
} from '@holochain/client';
import { decode } from '@msgpack/msgpack';

import { create{{pascal_case referenceable.name}} } from './common.js';
Expand Down Expand Up @@ -66,4 +75,3 @@ test('create a {{pascal_case referenceable.name}} and get {{lower_case collectio
{{/if}}
});
});

Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { AppClient, AgentPubKey, Link, EntryHash, ActionHash, Record, NewEntryAc
import { consume } from '@lit-labs/context';
import { Task } from '@lit-labs/task';

import { sharedStyles } from '../../shared-styles';
import { clientContext } from '../../contexts';
import { {{pascal_case coordinator_zome_manifest.name}}Signal } from './types';

Expand All @@ -24,7 +25,7 @@ export class {{pascal_case collection_name}} extends LitElement {
@state()
signaledHashes: Array<{{referenceable.hash_type}}> = [];

_fetch{{pascal_case (plural referenceable.name)}} = new Task(this, ([{{#if (eq collection_type.type "ByAuthor")}}author{{/if}}]) => this.client.callZome({
_fetch{{pascal_case (plural referenceable.name)}} = new Task(this, ([{{#if (eq collection_type.type "ByAuthor")}}author{{/if}}]: any) => this.client.callZome({
cap_secret: null,
role_name: '{{dna_role_name}}',
zome_name: '{{coordinator_zome_manifest.name}}',
Expand All @@ -34,12 +35,12 @@ export class {{pascal_case collection_name}} extends LitElement {

firstUpdated() {
{{#if (eq collection_type.type "ByAuthor")}}
if (this.author === undefined) {
if (!this.author) {
throw new Error(`The author property is required for the {{kebab_case collection_name}} element`);
}
{{/if}}

this.client.on('signal', signal => {
this.client?.on('signal', signal => {
if (!(SignalType.App in signal)) return;
if (signal.App.zome_name !== '{{coordinator_zome_manifest.name}}') return;
const payload = signal.App.payload as {{pascal_case coordinator_zome_manifest.name}}Signal;
Expand All @@ -53,25 +54,27 @@ export class {{pascal_case collection_name}} extends LitElement {
}

renderList(hashes: Array<{{referenceable.hash_type}}>) {
if (hashes.length === 0) return html`<span>No {{lower_case (plural referenceable.name)}} found{{#if (eq collection_type.type "ByAuthor")}} for this author{{/if}}.</span>`;
if (!hashes.length) return html`<div class="alert">No {{lower_case (plural referenceable.name)}} found{{#if (eq collection_type.type "ByAuthor")}} for this author{{/if}}.</div>`;

return html`

<div style="display: flex; flex-direction: column">
${hashes.map(hash =>
html`<{{kebab_case referenceable.name}}-detail .{{camel_case referenceable.name}}Hash=${hash} style="margin-bottom: 16px;" @{{kebab_case referenceable.name}}-deleted=${() => { this._fetch{{pascal_case (plural referenceable.name)}}.run(); this.signaledHashes = []; } }></{{kebab_case referenceable.name}}-detail>`
)}
<div>
${hashes.map(hash => html`
<{{kebab_case referenceable.name}}-detail
.{{camel_case referenceable.name}}Hash=${hash}
@{{kebab_case referenceable.name}}-deleted=${() => { this._fetch{{pascal_case (plural referenceable.name)}}.run(); this.signaledHashes = []; } }
></{{kebab_case referenceable.name}}-detail>
`)}
</div>
`;
}

render() {
return this._fetch{{pascal_case (plural referenceable.name)}}.render({
pending: () => html`<div style="display: flex; flex: 1; align-items: center; justify-content: center">
<mwc-circular-progress indeterminate></mwc-circular-progress>
</div>`,
pending: () => html`<progress></progress>`,
complete: (links) => this.renderList([...this.signaledHashes, ...links.map(l => l.target)]),
error: (e: any) => html`<span>Error fetching the {{lower_case (plural referenceable.name)}}: ${e.message}.</span>`
error: (e: any) => html`<div class="alert">Error fetching the {{lower_case (plural referenceable.name)}}: ${e.message}.</div>`
});
}

static styles = sharedStyles;
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
import { CallableCell } from '@holochain/tryorama';
import { NewEntryAction, ActionHash, Record, AppBundleSource, fakeActionHash, fakeAgentPubKey, fakeEntryHash, fakeDnaHash } from '@holochain/client';

Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,3 @@ export async function create{{pascal_case entry_type.name}}(cell: CallableCell,
payload: {{camel_case entry_type.name}} || await sample{{pascal_case entry_type.name}}(cell),
});
}

Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
import { assert, test } from "vitest";

import { runScenario, dhtSync, CallableCell } from '@holochain/tryorama';
import { Link, NewEntryAction, ActionHash, Record, AppBundleSource, fakeDnaHash, fakeActionHash, fakeAgentPubKey, fakeEntryHash } from '@holochain/client';
import {
NewEntryAction,
ActionHash,
Record,
Link,
CreateLink,
DeleteLink,
SignedActionHashed,
AppBundleSource,
fakeActionHash,
fakeAgentPubKey,
fakeEntryHash
} from '@holochain/client';
import { decode } from '@msgpack/msgpack';

import { create{{pascal_case entry_type.name}}, sample{{pascal_case entry_type.name}} } from './common.js';
Expand All @@ -12,7 +24,7 @@ test('create {{pascal_case entry_type.name}}', async () => {
// This assumes app bundle created by the `hc app pack` command.
const testAppPath = process.cwd() + '/../workdir/{{app_name}}.happ';

// Set up the app to be installed
// Set up the app to be installed
const appSource = { appBundleSource: { path: testAppPath } };

// Add 2 players with the test app to the Scenario. The returned players
Expand All @@ -35,7 +47,7 @@ test('create and read {{pascal_case entry_type.name}}', async () => {
// This assumes app bundle created by the `hc app pack` command.
const testAppPath = process.cwd() + '/../workdir/{{app_name}}.happ';

// Set up the app to be installed
// Set up the app to be installed
const appSource = { appBundleSource: { path: testAppPath } };

// Add 2 players with the test app to the Scenario. The returned players
Expand Down Expand Up @@ -87,7 +99,7 @@ test('create and update {{pascal_case entry_type.name}}', async () => {
// This assumes app bundle created by the `hc app pack` command.
const testAppPath = process.cwd() + '/../workdir/{{app_name}}.happ';

// Set up the app to be installed
// Set up the app to be installed
const appSource = { appBundleSource: { path: testAppPath } };

// Add 2 players with the test app to the Scenario. The returned players
Expand All @@ -101,9 +113,9 @@ test('create and update {{pascal_case entry_type.name}}', async () => {
// Alice creates a {{pascal_case entry_type.name}}
const record: Record = await create{{pascal_case entry_type.name}}(alice.cells[0]);
assert.ok(record);

const originalActionHash = record.signed_action.hashed.hash;

// Alice updates the {{pascal_case entry_type.name}}
let contentUpdate: any = await sample{{pascal_case entry_type.name}}(alice.cells[0]);
let updateInput = {
Expand All @@ -123,7 +135,7 @@ test('create and update {{pascal_case entry_type.name}}', async () => {

// Wait for the updated entry to be propagated to the other node.
await dhtSync([alice, bob], alice.cells[0].cell_id[0]);

// Bob gets the updated {{pascal_case entry_type.name}}
const readUpdatedOutput0: Record = await bob.cells[0].callZome({
zome_name: "{{coordinator_zome_manifest.name}}",
Expand All @@ -134,7 +146,7 @@ test('create and update {{pascal_case entry_type.name}}', async () => {

// Alice updates the {{pascal_case entry_type.name}} again
contentUpdate = await sample{{pascal_case entry_type.name}}(alice.cells[0]);
updateInput = {
updateInput = {
{{#if link_from_original_to_each_update}}
original_{{snake_case entry_type.name}}_hash: originalActionHash,
{{/if}}
Expand All @@ -151,7 +163,7 @@ test('create and update {{pascal_case entry_type.name}}', async () => {

// Wait for the updated entry to be propagated to the other node.
await dhtSync([alice, bob], alice.cells[0].cell_id[0]);

// Bob gets the updated {{pascal_case entry_type.name}}
const readUpdatedOutput1: Record = await bob.cells[0].callZome({
zome_name: "{{coordinator_zome_manifest.name}}",
Expand Down Expand Up @@ -179,7 +191,7 @@ test('create and delete {{pascal_case entry_type.name}}', async () => {
// This assumes app bundle created by the `hc app pack` command.
const testAppPath = process.cwd() + '/../workdir/{{app_name}}.happ';

// Set up the app to be installed
// Set up the app to be installed
const appSource = { appBundleSource: { path: testAppPath } };

// Add 2 players with the test app to the Scenario. The returned players
Expand Down Expand Up @@ -223,17 +235,17 @@ test('create and delete {{pascal_case entry_type.name}}', async () => {

// Wait for the entry deletion to be propagated to the other node.
await dhtSync([alice, bob], alice.cells[0].cell_id[0]);

// Bob gets the oldest delete for the {{pascal_case entry_type.name}}
const oldestDeleteFor{{pascal_case entry_type.name}} = await bob.cells[0].callZome({
const oldestDeleteFor{{pascal_case entry_type.name}}: SignedActionHashed = await bob.cells[0].callZome({
zome_name: "{{coordinator_zome_manifest.name}}",
fn_name: "get_oldest_delete_for_{{snake_case entry_type.name}}",
payload: record.signed_action.hashed.hash,
});
assert.ok(oldestDeleteFor{{pascal_case entry_type.name}});

// Bob gets the deletions for {{pascal_case entry_type.name}}
const deletesFor{{pascal_case entry_type.name}} = await bob.cells[0].callZome({
// Bob gets the deletions for the {{pascal_case entry_type.name}}
const deletesFor{{pascal_case entry_type.name}}: SignedActionHashed[] = await bob.cells[0].callZome({
zome_name: "{{coordinator_zome_manifest.name}}",
fn_name: "get_all_deletes_for_{{snake_case entry_type.name}}",
payload: record.signed_action.hashed.hash,
Expand All @@ -251,8 +263,8 @@ test('create and delete {{pascal_case entry_type.name}}', async () => {
});
assert.equal(linksTo{{pascal_case (plural linked_from.name)}}.length, 0);

// Bob gets the deleted {{pascal_case (plural linked_from.name)}} for the {{pascal_case ../entry_type.name}}
const deletedLinksTo{{pascal_case (plural linked_from.name)}} = await bob.cells[0].callZome({
// Bob gets the deleted {{pascal_case (plural linked_from.name)}} for the {{pascal_case ../entry_type.name}}
const deletedLinksTo{{pascal_case (plural linked_from.name)}}: Array<[SignedActionHashed<CreateLink>, SignedActionHashed<DeleteLink>[]]> = await bob.cells[0].callZome({
zome_name: "{{../coordinator_zome_manifest.name}}",
fn_name: "get_deleted_{{snake_case (plural ../entry_type.name)}}_for_{{snake_case linked_from.name}}",
payload: {{#if (eq cardinality "vector")}}sample.{{field_name}}[0]{{else}}sample.{{field_name}}{{/if}}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { LitElement, html } from 'lit';
import { state, customElement, property } from 'lit/decorators.js';
import { InstalledCell, ActionHash, Record, AgentPubKey, EntryHash, AppClient, DnaHash } from '@holochain/client';
import { InstalledCell, ActionHash, Record, AgentPubKey, EntryHash, AppClient, DnaHash, HolochainError } from '@holochain/client';
import { consume } from '@lit-labs/context';
import '@material/mwc-button';
import '@material/mwc-snackbar';
import { Snackbar } from '@material/mwc-snackbar';
{{#uniq_lines}}
{{#each entry_type.fields}}
{{#if widget}}
Expand All @@ -14,6 +11,7 @@ import { Snackbar } from '@material/mwc-snackbar';
{{/each}}
{{/uniq_lines}}

import { sharedStyles } from '../../shared-styles';
import { clientContext } from '../../contexts';
import { {{pascal_case entry_type.name}}{{#each entry_type.fields}}{{#if (eq field_type.type "Enum")}}, {{field_type.label}}{{/if}}{{/each}} } from './types';

Expand Down Expand Up @@ -53,7 +51,7 @@ export class Create{{pascal_case entry_type.name}} extends LitElement {
{{/if}}
{{/if}}
{{/each}}

firstUpdated() {
{{#each entry_type.fields}}
{{#if (not widget) }}
Expand All @@ -71,7 +69,7 @@ export class Create{{pascal_case entry_type.name}} extends LitElement {
}

async create{{pascal_case entry_type.name}}() {
const {{camel_case entry_type.name}}: {{pascal_case entry_type.name}} = {
const {{camel_case entry_type.name}}: {{pascal_case entry_type.name}} = {
{{#each entry_type.fields}}
{{#if widget}}
{{snake_case field_name}}: this._{{camel_case field_name}},
Expand All @@ -97,41 +95,37 @@ export class Create{{pascal_case entry_type.name}} extends LitElement {
{{camel_case entry_type.name}}Hash: record.signed_action.hashed.hash
}
}));
} catch (e: any) {
const errorSnackbar = this.shadowRoot?.getElementById('create-error') as Snackbar;
errorSnackbar.labelText = `Error creating the {{lower_case entry_type.name}}: ${e.message}`;
errorSnackbar.show();
} catch (e) {
alert((e as HolochainError).message);
}
}

render() {
return html`
<mwc-snackbar id="create-error" leading>
</mwc-snackbar>

<div style="display: flex; flex-direction: column">
<span style="font-size: 18px">Create {{title_case entry_type.name}}</span>

<div>
<h3>Create {{pascal_case entry_type.name}}</h3>
{{#each entry_type.fields}}
{{#if widget}}
<div style="margin-bottom: 16px">
<div>
{{#if (not (eq cardinality "vector") )}}
{{> (concat field_type.type "/" widget "/edit/render") label=(title_case field_name) variable_to_read=(concat "this._" (camel_case field_name) ) variable_to_change=(concat "this._" (camel_case field_name) ) required=(eq cardinality "single") }}
{{> (concat field_type.type "/" widget "/edit/render") label=(title_case field_name) variable_to_read=(concat "this._" (camel_case field_name) ) variable_to_change=(concat "this._" (camel_case field_name) ) required=(eq cardinality "single") }}
{{else}}
{{> Vec/edit/render field_name=field_name field_type=field_type widget=widget }}
{{> Vec/edit/render field_name=field_name field_type=field_type widget=widget }}
{{/if}}

</div>


</div>
{{/if}}
{{/each}}

<mwc-button
raised
label="Create {{title_case entry_type.name}}"
<button
.disabled=${!this.is{{pascal_case entry_type.name}}Valid()}
@click=${() => this.create{{pascal_case entry_type.name}}()}
></mwc-button>
</div>`;
>
Create {{title_case entry_type.name}}
</button>
</div>
`;
}
}

static styles = sharedStyles;
}
Loading

0 comments on commit f654d92

Please sign in to comment.