Skip to content

Commit

Permalink
FLIX (v1.0) integration (#207)
Browse files Browse the repository at this point in the history
* display all flix templates

* improve flix name formatting

* format template source code

* improve template filtering

* run format

* fix source detection

* small refactor

* show verified by flix ui, move flix hooks to hooks/flix

* search interaction names in lowercase

* wrap "open in ide" section in gray card

* first pass at non-verified state

* fix hidden trash icons due to long labels

* fix emulator flix template retrieval

onflowser/flow-interaction-template-service#4

* update comment

* disable polling of flix templates

* update to "any" network name

* stop retrying on error

* show argument descriptions

* use interaction definition instead of template for searching flix

* fix transformation to new import syntax

* use flowser flix api url

* update comment

* consolidate verified/unverified layout
  • Loading branch information
bartolomej authored Dec 16, 2023
1 parent dca4f84 commit af41c5d
Show file tree
Hide file tree
Showing 16 changed files with 412 additions and 87 deletions.
6 changes: 2 additions & 4 deletions packages/api/src/resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -328,13 +328,11 @@ export interface FlowEmulatorConfig {
snapshot: boolean;
}

export interface InteractionTemplate extends TimestampedResource {
export interface WorkspaceTemplate extends TimestampedResource {
id: string;
name: string;
code: string;
source: {
filePath?: string;
};
filePath: string;
}

export interface FlowCliInfo {
Expand Down
27 changes: 6 additions & 21 deletions packages/nodejs/src/flow-interactions.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,9 @@ import * as fs from "fs/promises";
import * as path from "path";
import { GoBindingsService } from "./go-bindings.service";
import { isDefined } from "@onflowser/core";
import { InteractionKind, ParsedInteractionOrError } from "@onflowser/api";
import { InteractionKind, ParsedInteractionOrError, WorkspaceTemplate } from "@onflowser/api";
import { IFlowInteractions } from "@onflowser/core";

export interface InteractionTemplate {
id: string;
name: string;
code: string;
source: InteractionTemplate_Source | undefined;
createdDate: string;
updatedDate: string;
}

interface InteractionTemplate_Source {
filePath: string;
}

type GetInteractionTemplatesOptions = {
workspacePath: string;
};
Expand All @@ -31,7 +18,7 @@ export class FlowInteractionsService implements IFlowInteractions {

public async getTemplates(
options: GetInteractionTemplatesOptions,
): Promise<InteractionTemplate[]> {
): Promise<WorkspaceTemplate[]> {
const potentialCadenceFilePaths = await this.findAllCadenceFiles(
options.workspacePath,
);
Expand All @@ -45,7 +32,7 @@ export class FlowInteractionsService implements IFlowInteractions {

private async buildMaybeTemplate(
filePath: string,
): Promise<InteractionTemplate | undefined> {
): Promise<WorkspaceTemplate | undefined> {
const [fileContent, fileStats] = await Promise.all([
fs.readFile(filePath),
fs.stat(filePath),
Expand All @@ -67,11 +54,9 @@ export class FlowInteractionsService implements IFlowInteractions {
id: filePath,
name: path.basename(filePath),
code,
updatedDate: fileStats.mtime.toISOString(),
createdDate: fileStats.ctime.toISOString(),
source: {
filePath,
},
updatedAt: fileStats.mtime,
createdAt: fileStats.ctime,
filePath
};
} else {
return undefined;
Expand Down
8 changes: 4 additions & 4 deletions packages/ui/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
FlowserWorkspace,
FlowStateSnapshot,
FlowTransaction,
InteractionTemplate,
WorkspaceTemplate,
ManagedProcessOutput,
ParsedInteractionOrError,
ManagedKeyPair,
Expand Down Expand Up @@ -249,12 +249,12 @@ export function useGetParsedInteraction(
return state;
}

export function useGetInteractionTemplates(): SWRResponse<
InteractionTemplate[]
export function useGetWorkspaceInteractionTemplates(): SWRResponse<
WorkspaceTemplate[]
> {
const { interactionsService } = useServiceRegistry();

return useSWR(`interaction-templates`, () =>
return useSWR(`workspace-interaction-templates`, () =>
interactionsService.getTemplates(),
);
}
Expand Down
2 changes: 2 additions & 0 deletions packages/ui/src/common/icons/FlowserIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import Shrink from "./assets/shrink.svg";
import Logs from "./assets/logs.svg";
import { TbBrandVscode } from "react-icons/tb";
import { SiWebstorm, SiIntellijidea } from "react-icons/si";
import { RiVerifiedBadgeFill } from "react-icons/ri";

export const FlowserIcon = {
Logs: Logs,
Expand Down Expand Up @@ -69,4 +70,5 @@ export const FlowserIcon = {
WebStorm: SiWebstorm,
VsCode: TbBrandVscode,
IntellijIdea: SiIntellijidea,
VerifiedCheck: RiVerifiedBadgeFill
};
10 changes: 8 additions & 2 deletions packages/ui/src/common/links/ExternalLink/ExternalLink.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,29 @@
import React, { ReactNode, ReactElement, CSSProperties } from "react";
import { FlowserIcon } from "../../icons/FlowserIcon";
import classes from "./ExternalLink.module.scss";
import classNames from "classnames";

export type ExternalLinkProps = {
children?: ReactNode;
href: string;
inline?: boolean;
style?: CSSProperties;
className?: string;
};

export function ExternalLink({
href,
children,
inline,
style,
className
}: ExternalLinkProps): ReactElement {
return (
<a
target="_blank"
rel="noreferrer"
href={href}
className={classes.root}
className={classNames(classes.root, className)}
style={{ display: "flex", ...style }}
>
{!inline && <FlowserIcon.Link className={classes.icon} />}
Expand All @@ -30,5 +33,8 @@ export function ExternalLink({
}

function prettifyUrl(url: string) {
return url.replace(/https?:\/\//, "").replace(/www\./, "").replace(/\/$/, "")
return url
.replace(/https?:\/\//, "")
.replace(/www\./, "")
.replace(/\/$/, "");
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
.horizontal {
width: 100%;
height: 1px;
margin: $spacing-l 0;
margin: $spacing-base 0;
}

.vertical {
height: 100%;
width: 1px;
margin: 0 $spacing-l;
margin: 0 $spacing-base;
}
4 changes: 2 additions & 2 deletions packages/ui/src/contexts/service-registry.context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
FlowserWorkspace,
FlowStateSnapshot,
FlowTransaction,
InteractionTemplate,
WorkspaceTemplate,
IResourceIndexReader,
ManagedProcessOutput,
ParsedInteractionOrError,
Expand All @@ -38,7 +38,7 @@ export interface IWorkspaceService {

export interface IInteractionService {
parse(sourceCode: string): Promise<ParsedInteractionOrError>;
getTemplates(): Promise<InteractionTemplate[]>;
getTemplates(): Promise<WorkspaceTemplate[]>;
}

export type SendTransactionRequest = {
Expand Down
79 changes: 79 additions & 0 deletions packages/ui/src/hooks/flix.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import useSWR, { SWRResponse } from "swr";

// https://github.com/onflow/flips/blob/main/application/20220503-interaction-templates.md#interaction-interfaces
export type FlixTemplate = {
id: string;
f_type: "InteractionTemplate";
f_version: string;
data: {
messages: FlixMessages;
dependencies: Record<string, FlixDependency>;
cadence: string;
arguments: Record<string, FlixArgument>;
};
};

export type FlixArgument = {
index: number;
type: string;
messages: FlixMessages;
}

type FlixDependency = Record<
string,
{
mainnet: FlixDependencyOnNetwork;
testnet: FlixDependencyOnNetwork;
}
>;

type FlixDependencyOnNetwork = {
address: string;
fq_address: string;
pin: string;
pin_block_height: number;
};

type FlixMessages = {
title?: FlixI18nMessage;
description?: FlixI18nMessage;
};

type FlixI18nMessage = {
i18n: {
"en-US"?: string;
};
};

export const FLOW_FLIX_URL = "https://flix.flow.com";
export const FLOWSER_FLIX_URL = "https://flowser-flix-368a32c94da2.herokuapp.com"

export function useListFlixTemplates(): SWRResponse<FlixTemplate[]> {
return useSWR(`flix/templates`, () =>
fetch(`${FLOWSER_FLIX_URL}/v1/templates`).then((res) => res.json()),
);
}

export function useFlixSearch(options: {
sourceCode: string;
// Supports "any" network as of:
// https://github.com/onflowser/flow-interaction-template-service/pull/4
network: "any" | "testnet" | "mainnet";
}): SWRResponse<FlixTemplate | undefined> {
return useSWR(`flix/templates/${options.sourceCode}`, () =>
fetch(`${FLOWSER_FLIX_URL}/v1/templates/search`, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
cadence_base64: btoa(options.sourceCode),
network: options.network
})
}).then((res) => res.json()),
{
refreshInterval: 0,
shouldRetryOnError: false
}
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ function ExecuteButton() {
}

function TopContent() {
const { setFclValue, fclValuesByIdentifier, definition, parsedInteraction } =
const { flixTemplate, setFclValue, fclValuesByIdentifier, definition, parsedInteraction } =
useInteractionDefinitionManager();

if (definition.code === "") {
Expand All @@ -70,6 +70,7 @@ function TopContent() {
parameters={parsedInteraction?.parameters ?? []}
setFclValue={setFclValue}
fclValuesByIdentifier={fclValuesByIdentifier}
flixTemplate={flixTemplate}
/>
<SizedBox height={30} />
{parsedInteraction?.kind === InteractionKind.INTERACTION_TRANSACTION && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
justify-content: center;
}
.label {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
@import "../../../styles/typography";
@import "../../../styles/animations";
@import "../../../styles/scrollbars";
@import "../../../styles/rules";

.root {
height: 100%;
Expand All @@ -11,7 +12,7 @@
justify-content: space-between;
position: relative;

.header {
& > .header {
background: $gray-100;
position: sticky;
top: 0;
Expand All @@ -29,13 +30,15 @@
.item {
cursor: pointer;
display: flex;
align-items: center;
justify-content: space-between;
font-size: $font-size-normal;
color: $gray-20;
@include whitenOnHover();

.trash {
@include scaleOnHover();
min-width: 20px;
* {
fill: $gray-50;
}
Expand All @@ -59,10 +62,51 @@
padding-top: $spacing-base;
background: $gray-100;
bottom: 0;
}

.workspaceInfo {
border-radius: $border-radius-input;
background: $gray-80;
padding: $spacing-base;
display: flex;
column-gap: $spacing-base;

.actionButtons {
display: flex;
column-gap: $spacing-base;
}
}
}

.flixInfo {
border-radius: $border-radius-input;
background: $gray-80;
padding: $spacing-base;
.header {
font-weight: bold;
display: flex;
align-items: center;
column-gap: 3px;
.title {
color: $strong-green;
display: inline-flex !important;
}
@mixin icon {
height: 15px;
width: 15px;
}
.verifiedIcon {
color: $strong-green;
@include icon;
}
.unverifiedIcon {
color: $color-red;
@include icon;
}
}
.body {
display: flex;
flex-direction: column;
row-gap: $spacing-base;
}
}
Loading

0 comments on commit af41c5d

Please sign in to comment.