Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FLIX (v1.0) integration #207

Merged
merged 23 commits into from
Dec 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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