Skip to content

Commit

Permalink
add we attachments
Browse files Browse the repository at this point in the history
  • Loading branch information
zippy committed Dec 22, 2023
1 parent 99bcbac commit ce74e8e
Show file tree
Hide file tree
Showing 10 changed files with 246 additions and 14 deletions.
6 changes: 4 additions & 2 deletions ui/src/App.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import "@holochain-open-dev/profiles/dist/elements/profile-prompt.js";
import "@holochain-open-dev/profiles/dist/elements/create-profile.js";
import KDLogoIcon from "./icons/KDLogoIcon.svelte";
import { appletServices } from './we';
const appId = import.meta.env.VITE_APP_ID ? import.meta.env.VITE_APP_ID : 'kando'
const roleName = 'kando'
Expand All @@ -16,6 +17,7 @@
const url = `ws://localhost:${appPort}`;
let client: AppAgentWebsocket
let weClient: WeClient
let profilesStore : ProfilesStore|undefined = undefined
let connected = false
Expand Down Expand Up @@ -45,7 +47,7 @@
profilesClient = new ProfilesClient(client, appId);
}
else {
const weClient = await WeClient.connect();
weClient = await WeClient.connect(appletServices);
if (
!(weClient.renderInfo.type === "applet-view")
Expand Down Expand Up @@ -81,7 +83,7 @@
{:else if $prof.status=="error"}
Error when loading profile: {$prof.error}
{:else}
<Controller client={client} profilesStore={profilesStore} roleName={roleName}></Controller>
<Controller client={client} weClient={weClient} profilesStore={profilesStore} roleName={roleName}></Controller>
{/if}

</profiles-context>
Expand Down
53 changes: 52 additions & 1 deletion ui/src/CardDetails.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import { Marked, Renderer } from "@ts-stack/markdown";
import SvgIcon from "./SvgIcon.svelte";
import ClickEdit from './ClickEdit.svelte';
import { hrlB64WithContextToRaw, hrlWithContextToB64 } from './util';
Marked.setOptions
({
Expand Down Expand Up @@ -45,7 +46,7 @@
$: selectedAvatarsForSelect = selectedAvatars.join(" ")
$: allProfiles = store.profilesStore.allProfiles
const DEFAULT_PROPS = {title:"", description:"", category: "", agents:[], labels:[]}
const DEFAULT_PROPS = {title:"", description:"", category: "", agents:[], labels:[], attachments:[]}
//let props:CardProps = DEFAULT_PROPS
let cardId:uuidv1 = ""
Expand Down Expand Up @@ -235,6 +236,21 @@
let editDescriptionElement
const addAttachment = async () => {
const hrl = await store.weClient.userSelectHrl()
if (hrl) {
if (props.attachments === undefined) {
props.attachments = []
}
props.attachments.push(hrlWithContextToB64(hrl))
handleSave(props)
}
}
const removeAttachment = (idx: number) => {
props.attachments.splice(idx,1)
handleSave(props)
}
</script>
<sl-drawer class="edit-card" bind:this={dialog}
style="--size:500px"
Expand Down Expand Up @@ -482,6 +498,41 @@

</div>
{/if}
{#if store.weClient}
<div style="margin-left:10px; margin-bottom:5px;">
<button class="control" on:click={()=>addAttachment()} >
<SvgIcon icon="faPaperclip" size="12px"/> Add Attachment
</button>
</div>
{#if props.attachments}
<div style="display:flex;flex-direction:row;flex-wrap:wrap">
{#each props.attachments as attachment, i}
{#await store.weClient.entryInfo(hrlB64WithContextToRaw(attachment).hrl)}
<sl-button size="small" loading></sl-button>
{:then { entryInfo }}
<sl-button size="small"
on:click={()=>{
const hrl = hrlB64WithContextToRaw(attachment)
store.weClient.openHrl(hrl.hrl, hrl.context)
}}
style="display:flex;flex-direction:row;margin-right:5px;margin-left:10px"><sl-icon src={entryInfo.icon_src} slot="prefix"></sl-icon>
{entryInfo.name}
</sl-button>
<sl-button circle size="small"
on:click={()=>{
removeAttachment(i)
}}
>
<SvgIcon icon="faTrash" size="12px"></SvgIcon>
</sl-button>
{:catch error}
Oops. something's wrong.
{/await}
{/each}
</div>
{/if}
{/if}

<sl-dialog bind:this={commentDialog}>
<sl-textarea bind:this={commentTextElem}></sl-textarea>
<div style="display:flex;justify-content:flex-end;margin-top:5px;">
Expand Down
2 changes: 1 addition & 1 deletion ui/src/CardEditor.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
export let categories: Array<CategoryDef>
export let title
const DEFAULT_PROPS = {title:"", description:"", category: "", agents:[], labels:[]}
const DEFAULT_PROPS = {title:"", description:"", category: "", agents:[], labels:[], attachments: []}
let props:CardProps = DEFAULT_PROPS
let cardId:uuidv1 = ""
Expand Down
11 changes: 7 additions & 4 deletions ui/src/Controller.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,19 @@
import type { SynStore } from '@holochain-syn/store';
import type { ProfilesStore } from "@holochain-open-dev/profiles";
import BoardMenu from "./BoardMenu.svelte";
import type { WeClient } from '@lightningrodlabs/we-applet';
export let roleName = ""
export let client : AppAgentClient
export let weClient : WeClient
export let profilesStore : ProfilesStore
let store: KanDoStore = new KanDoStore (
profilesStore,
client,
roleName,
);
weClient,
profilesStore,
client,
roleName,
);
let synStore: SynStore = store.synStore
$: activeBoardHash = store.boardList.activeBoardHash
Expand Down
2 changes: 2 additions & 0 deletions ui/src/board.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { get, type Readable } from "svelte/store";
import { v1 as uuidv1 } from "uuid";
import { type AgentPubKey, type EntryHash, type EntryHashB64, encodeHashToBase64, type AgentPubKeyB64, type Timestamp } from "@holochain/client";
import { BoardType } from "./boardList";
import type { HrlB64WithContext } from "@lightningrodlabs/we-applet";

export class LabelDef {
type: uuidv1
Expand All @@ -24,6 +25,7 @@ export type CardProps = {
category: uuidv1,
agents: Array<EntryHashB64>,
labels: Array<uuidv1>,
attachments: Array<HrlB64WithContext>
}

export type Comment = {
Expand Down
6 changes: 1 addition & 5 deletions ui/src/boardList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,7 @@ export class BoardList {
}

async setActiveCard(cardId: string | undefined) {
this.activeCard.update((n) => {
console.log("old card", n)
console.log("active card", cardId)

return cardId} )
this.activeCard.update((n) => {return cardId} )
}

async setActiveBoard(hash: EntryHash | undefined) : Promise<Board | undefined> {
Expand Down
2 changes: 2 additions & 0 deletions ui/src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import type { v1 as uuidv1 } from "uuid";
import { get, writable, type Writable } from "svelte/store";
import type { ProfilesStore } from '@holochain-open-dev/profiles';
import type { BoardState } from './board';
import type { WeClient } from '@lightningrodlabs/we-applet';


TimeAgo.addDefaultLocale(en)
Expand Down Expand Up @@ -93,6 +94,7 @@ export class KanDoStore {
}

constructor(
public weClient : WeClient,
public profilesStore: ProfilesStore,
protected clientIn: AppAgentClient,
protected roleName: RoleName,
Expand Down
3 changes: 2 additions & 1 deletion ui/src/svgIcons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,6 @@ export const svgIcons = {
faClose: `<svg xmlns="http://www.w3.org/2000/svg" height="16" width="12" viewBox="0 0 384 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path d="M342.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L192 210.7 86.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L146.7 256 41.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L192 301.3 297.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L237.3 256 342.6 150.6z"/></svg>`,
faBug: `<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path d="M256 0c53 0 96 43 96 96v3.6c0 15.7-12.7 28.4-28.4 28.4H188.4c-15.7 0-28.4-12.7-28.4-28.4V96c0-53 43-96 96-96zM41.4 105.4c12.5-12.5 32.8-12.5 45.3 0l64 64c.7 .7 1.3 1.4 1.9 2.1c14.2-7.3 30.4-11.4 47.5-11.4H312c17.1 0 33.2 4.1 47.5 11.4c.6-.7 1.2-1.4 1.9-2.1l64-64c12.5-12.5 32.8-12.5 45.3 0s12.5 32.8 0 45.3l-64 64c-.7 .7-1.4 1.3-2.1 1.9c6.2 12 10.1 25.3 11.1 39.5H480c17.7 0 32 14.3 32 32s-14.3 32-32 32H416c0 24.6-5.5 47.8-15.4 68.6c2.2 1.3 4.2 2.9 6 4.8l64 64c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0l-63.1-63.1c-24.5 21.8-55.8 36.2-90.3 39.6V240c0-8.8-7.2-16-16-16s-16 7.2-16 16V479.2c-34.5-3.4-65.8-17.8-90.3-39.6L86.6 502.6c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3l64-64c1.9-1.9 3.9-3.4 6-4.8C101.5 367.8 96 344.6 96 320H32c-17.7 0-32-14.3-32-32s14.3-32 32-32H96.3c1.1-14.1 5-27.5 11.1-39.5c-.7-.6-1.4-1.2-2.1-1.9l-64-64c-12.5-12.5-12.5-32.8 0-45.3z"/></svg>`,
faBars: `<svg xmlns="http://www.w3.org/2000/svg" height="16" width="14" viewBox="0 0 448 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path d="M0 96C0 78.3 14.3 64 32 64H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32C14.3 128 0 113.7 0 96zM0 256c0-17.7 14.3-32 32-32H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32c-17.7 0-32-14.3-32-32zM448 416c0 17.7-14.3 32-32 32H32c-17.7 0-32-14.3-32-32s14.3-32 32-32H416c17.7 0 32 14.3 32 32z"/></svg>`,
faClone: `<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path d="M64 464H288c8.8 0 16-7.2 16-16V384h48v64c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V224c0-35.3 28.7-64 64-64h64v48H64c-8.8 0-16 7.2-16 16V448c0 8.8 7.2 16 16 16zM224 304H448c8.8 0 16-7.2 16-16V64c0-8.8-7.2-16-16-16H224c-8.8 0-16 7.2-16 16V288c0 8.8 7.2 16 16 16zm-64-16V64c0-35.3 28.7-64 64-64H448c35.3 0 64 28.7 64 64V288c0 35.3-28.7 64-64 64H224c-35.3 0-64-28.7-64-64z"/></svg>`
faClone: `<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path d="M64 464H288c8.8 0 16-7.2 16-16V384h48v64c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V224c0-35.3 28.7-64 64-64h64v48H64c-8.8 0-16 7.2-16 16V448c0 8.8 7.2 16 16 16zM224 304H448c8.8 0 16-7.2 16-16V64c0-8.8-7.2-16-16-16H224c-8.8 0-16 7.2-16 16V288c0 8.8 7.2 16 16 16zm-64-16V64c0-35.3 28.7-64 64-64H448c35.3 0 64 28.7 64 64V288c0 35.3-28.7 64-64 64H224c-35.3 0-64-28.7-64-64z"/></svg>`,
faPaperclip: `<svg xmlns="http://www.w3.org/2000/svg" height="16" width="14" viewBox="0 0 448 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path d="M364.2 83.8c-24.4-24.4-64-24.4-88.4 0l-184 184c-42.1 42.1-42.1 110.3 0 152.4s110.3 42.1 152.4 0l152-152c10.9-10.9 28.7-10.9 39.6 0s10.9 28.7 0 39.6l-152 152c-64 64-167.6 64-231.6 0s-64-167.6 0-231.6l184-184c46.3-46.3 121.3-46.3 167.6 0s46.3 121.3 0 167.6l-176 176c-28.6 28.6-75 28.6-103.6 0s-28.6-75 0-103.6l144-144c10.9-10.9 28.7-10.9 39.6 0s10.9 28.7 0 39.6l-144 144c-6.7 6.7-6.7 17.7 0 24.4s17.7 6.7 24.4 0l176-176c24.4-24.4 24.4-64 0-88.4z"/></svg>`
}
17 changes: 17 additions & 0 deletions ui/src/util.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { decodeHashFromBase64, encodeHashToBase64 } from "@holochain/client";
import type { HrlB64WithContext, HrlWithContext } from "@lightningrodlabs/we-applet";

export function onVisible(element, callback) {
new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
Expand All @@ -6,4 +9,18 @@ export function onVisible(element, callback) {
}
});
}).observe(element);
}

export function hrlWithContextToB64(hrl: HrlWithContext): HrlB64WithContext {
return {
hrl: [encodeHashToBase64(hrl.hrl[0]), encodeHashToBase64(hrl.hrl[1])],
context: hrl.context,
};
}

export function hrlB64WithContextToRaw(hrlB64: HrlB64WithContext): HrlWithContext {
return {
hrl: [decodeHashFromBase64(hrlB64.hrl[0]), decodeHashFromBase64(hrlB64.hrl[1])],
context: hrlB64.context,
};
}
158 changes: 158 additions & 0 deletions ui/src/we.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import { DocumentStore, SynClient, SynStore, WorkspaceStore } from '@holochain-syn/core';
import { CellType, type AppAgentClient, type RoleName, type ZomeName, type DnaHash } from '@holochain/client';
import { Board, type BoardEphemeralState, type BoardState } from './board';
import { asyncDerived, pipe, sliceAndJoin, toPromise } from '@holochain-open-dev/stores';
import { BoardType } from './boardList';
import { LazyHoloHashMap } from '@holochain-open-dev/utils';
import type { AppletHash, AppletServices, EntryInfo, Hrl, HrlWithContext, WeServices } from '@lightningrodlabs/we-applet';

const ROLE_NAME = "talking-stickies"
const ZOME_NAME = "syn"

const getMyDna = async (client: AppAgentClient) : Promise<DnaHash> => {
const appInfo = await client.appInfo();
const dnaHash = (appInfo.cell_info[ROLE_NAME][0] as any)[
CellType.Provisioned
].cell_id[0];
return dnaHash
}

export const appletServices: AppletServices = {
// Types of attachment that this Applet offers for other Applets to attach
attachmentTypes: async (
appletClient: AppAgentClient,
appletHash: AppletHash,
weServices: WeServices
) => ({
board: {
label: "Board",
icon_src: "https://static-00.iconduck.com/assets.00/kanban-icon-480x512-y56i8vrh.png",
async create(attachToHrl: Hrl) {
const synStore = new SynStore(new SynClient(appletClient, ROLE_NAME));
const board = await Board.Create(synStore)
const dnaHash = await getMyDna(appletClient)
return {
hrl: [dnaHash, board.hash],
context: {},
};
},
},
}),
// Types of UI widgets/blocks that this Applet supports
blockTypes: {},
getEntryInfo: async (
appletClient: AppAgentClient,
roleName: RoleName,
integrityZomeName: ZomeName,
entryType: string,
hrl: Hrl
): Promise<EntryInfo | undefined> => {

const synClient = new SynClient(appletClient, roleName, ZOME_NAME);
const synStore = new SynStore(synClient);
const documentHash = hrl[1]
const docStore = new DocumentStore<BoardState, BoardEphemeralState> (synStore, documentHash)
const workspaces = await toPromise(docStore.allWorkspaces)
const workspace = new WorkspaceStore(docStore, Array.from(workspaces.keys())[0])
const latestSnapshot = await toPromise(workspace.latestSnapshot)

return {
icon_src: "https://static-00.iconduck.com/assets.00/kanban-icon-480x512-y56i8vrh.png",
name: latestSnapshot.name,
};
},
search: async (
appletClient: AppAgentClient,
appletHash: AppletHash,
weServices: WeServices,
searchFilter: string
): Promise<Array<HrlWithContext>> => {
const synClient = new SynClient(appletClient, ROLE_NAME, ZOME_NAME);
const synStore = new SynStore(synClient);
const boardHashes = asyncDerived(synStore.documentsByTag.get(BoardType.active),x=>Array.from(x.keys()))

const boardData = new LazyHoloHashMap( documentHash => {
const docStore = synStore.documents.get(documentHash)

const workspace = pipe(docStore.allWorkspaces,
workspaces => {
return new WorkspaceStore(docStore, Array.from(workspaces.keys())[0])
}
)
const latestState = pipe(workspace,
w => w.latestSnapshot
)
return latestState
})

const allBoardsAsync = pipe(boardHashes,
docHashes => sliceAndJoin(boardData, docHashes)
)

const allBoards = Array.from((await toPromise(allBoardsAsync)).entries())
const dnaHash = await getMyDna(appletClient)

return allBoards
.filter((r) => !!r)
.filter((r) => {
const state = r[1]
return state.name.toLowerCase().includes(searchFilter.toLowerCase())
})
.map((r) => ({ hrl: [dnaHash, r![0]], context: {} }));
},
};


// // Then handle all the different types of views that you offer
// switch (weClient.renderInfo.type) {
// case "applet-view":
// switch (weClient.renderInfo.view.type) {
// case "main":
// // here comes your rendering logic for the main view
// case "block":
// switch(weClient.renderInfo.view.block) {
// case "most_recent_posts":
// // your rendering logic to display this block type
// case "bookmarked_posts":
// // Your rendering logic to display this block type
// default:
// throw new Error("Unknown applet-view block type");
// }
// case "entry":
// switch (weClient.renderInfo.view.roleName) {
// case "forum":
// switch (weClient.renderInfo.view.integrityZomeName) {
// case "posts_integrity":
// switch (weClient.renderInfo.view.entryType) {
// case "post":
// // here comes your rendering logic for that specific entry type
// default:
// throw new Error("Unknown entry type");
// }
// default:
// throw new Error("Unknown integrity zome");
// }
// default:
// throw new Error("Unknown role name");
// }

// default:
// throw new Error("Unknown applet-view type");
// }

// case "cross-applet-view":
// switch (this.weClient.renderInfo.view.type) {
// case "main":
// // here comes your rendering logic for the cross-applet main view
// case "block":
// //
// default:
// throw new Error("Unknown cross-applet-view render type.")

// `;
// }

// default:
// throw new Error("Unknown render view type");

// }

0 comments on commit ce74e8e

Please sign in to comment.