├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── config.yml ├── stale.yml └── workflows │ └── publish.yml ├── .gitignore ├── .husky └── pre-commit ├── .nvmrc ├── LICENSE ├── README.md ├── apps ├── electron │ ├── .editorconfig │ ├── .erb │ │ ├── configs │ │ │ ├── .eslintrc │ │ │ ├── webpack.config.base.ts │ │ │ ├── webpack.config.eslint.ts │ │ │ ├── webpack.config.main.prod.ts │ │ │ ├── webpack.config.preload.dev.ts │ │ │ ├── webpack.config.renderer.dev.dll.ts │ │ │ ├── webpack.config.renderer.dev.ts │ │ │ ├── webpack.config.renderer.prod.ts │ │ │ └── webpack.paths.ts │ │ └── scripts │ │ │ ├── .eslintrc │ │ │ ├── check-build-exists.ts │ │ │ ├── check-native-dep.js │ │ │ ├── check-node-env.js │ │ │ ├── check-port-in-use.js │ │ │ ├── clean.js │ │ │ ├── delete-source-maps.js │ │ │ ├── electron-rebuild.js │ │ │ ├── link-modules.ts │ │ │ └── notarize.js │ ├── .eslintignore │ ├── .eslintrc.js │ ├── .gitattributes │ ├── .gitignore │ ├── assets │ │ ├── Inter.woff2 │ │ ├── assets.d.ts │ │ ├── entitlements.mac.plist │ │ ├── icon.png │ │ ├── macos-background.png │ │ └── macos-background.tiff │ ├── electron-builder.yml │ ├── package.json │ ├── release │ │ └── app │ │ │ ├── package-lock.json │ │ │ ├── package.json │ │ │ └── yarn.lock │ ├── src │ │ ├── __tests__ │ │ │ └── App.test.tsx │ │ ├── main │ │ │ ├── ipc │ │ │ │ ├── events.ts │ │ │ │ ├── handlers.ts │ │ │ │ └── invokers.ts │ │ │ ├── main.ts │ │ │ ├── menu.ts │ │ │ ├── preload.ts │ │ │ └── util.ts │ │ ├── renderer │ │ │ ├── App.scss │ │ │ ├── App.tsx │ │ │ ├── components │ │ │ │ ├── DependencyErrors │ │ │ │ │ ├── DependencyErrors.module.scss │ │ │ │ │ └── DependencyErrors.tsx │ │ │ │ └── loaders │ │ │ │ │ ├── ExitLoader.tsx │ │ │ │ │ ├── Loader.module.scss │ │ │ │ │ └── UpdateLoader.tsx │ │ │ ├── index.ejs │ │ │ ├── index.tsx │ │ │ ├── ipc-index-cache.ts │ │ │ ├── preload.d.ts │ │ │ └── router.tsx │ │ ├── services │ │ │ ├── analytics.service.ts │ │ │ ├── app-update.service.ts │ │ │ ├── blockchain-index.service.ts │ │ │ ├── dependency-manager.service.ts │ │ │ ├── file-storage.service.ts │ │ │ ├── fingerprint.service.ts │ │ │ ├── flowser-app.service.ts │ │ │ ├── sentry-main.service.ts │ │ │ ├── sentry-renderer.service.ts │ │ │ ├── types.ts │ │ │ └── workspace.service.ts │ │ └── utils.ts │ └── tsconfig.json └── web │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── next.config.js │ ├── package.json │ ├── postcss.config.js │ ├── public │ ├── Inter-Black.ttf │ ├── Inter-Regular.ttf │ ├── contractbrowser.png │ ├── flowdiver.png │ └── flowview.png │ ├── src │ ├── app │ │ ├── Inter.woff2 │ │ ├── [networkId] │ │ │ ├── [interaction] │ │ │ │ ├── opengraph-image.tsx │ │ │ │ └── page.tsx │ │ │ └── page.tsx │ │ ├── favicon.ico │ │ ├── get-address-index │ │ │ └── route.ts │ │ ├── get-address-info │ │ │ └── route.ts │ │ ├── index.scss │ │ ├── layout.tsx │ │ └── parse-interaction │ │ │ └── route.ts │ └── common │ │ ├── NetworkDropdown.tsx │ │ ├── ProfileDropdown.tsx │ │ ├── default-flow.json │ │ ├── interaction-page-params.ts │ │ ├── metadata.ts │ │ ├── root-loader.tsx │ │ ├── root.tsx │ │ └── use-interaction-page-params.ts │ ├── tailwind.config.ts │ └── tsconfig.json ├── go.mod ├── go.sum ├── internal ├── main.go └── misc │ ├── address.go │ ├── interaction.go │ └── interacton_test.go ├── package.json ├── packages ├── api │ ├── .prettierrc │ ├── package.json │ ├── src │ │ ├── capabilities.ts │ │ ├── index.ts │ │ └── resources.ts │ └── tsconfig.json ├── config │ ├── package.json │ └── tsconfig.json ├── core │ ├── .prettierrc │ ├── package.json │ ├── src │ │ ├── fcl-value.ts │ │ ├── flix-v1-utils.ts │ │ ├── flix-v1.ts │ │ ├── flix-v11-utils.ts │ │ ├── flix-v11.ts │ │ ├── flow-flix-v1.service.ts │ │ ├── flow-flix-v11.service.ts │ │ ├── flow-gateway.service.ts │ │ ├── flow-indexer.service.ts │ │ ├── flow-interactions.service.ts │ │ ├── flow-names.service.ts │ │ ├── flow-snapshots.service.ts │ │ ├── flow-storage.service.ts │ │ ├── flow-utils.ts │ │ ├── http.service.ts │ │ ├── in-memory-index.ts │ │ ├── index.ts │ │ ├── logger.ts │ │ ├── persistent-storage.ts │ │ ├── utils.spec.ts │ │ └── utils.ts │ └── tsconfig.json ├── nodejs │ ├── .gitignore │ ├── .prettierrc │ ├── build-go-bindings.sh │ ├── install-go.js │ ├── package.json │ └── src │ │ ├── async-interval-scheduler.ts │ │ ├── flow-cli.service.ts │ │ ├── flow-config.service.ts │ │ ├── flow-dev-wallet.service.ts │ │ ├── flow-emulator.service.ts │ │ ├── flow-interactions.service.ts │ │ ├── go-bindings.service.ts │ │ ├── index.ts │ │ ├── processes │ │ ├── managed-process.ts │ │ └── process-manager.service.ts │ │ └── wallet.service.ts └── ui │ ├── .prettierrc │ ├── package.json │ ├── src │ ├── accounts │ │ ├── AccountAvatar │ │ │ ├── AccountAvatar.tsx │ │ │ └── avatars │ │ │ │ ├── 1.jpg │ │ │ │ ├── 10.jpg │ │ │ │ ├── 11.jpg │ │ │ │ ├── 12.jpg │ │ │ │ ├── 13.jpg │ │ │ │ ├── 14.jpg │ │ │ │ ├── 15.jpg │ │ │ │ ├── 16.jpg │ │ │ │ ├── 2.jpg │ │ │ │ ├── 3.jpg │ │ │ │ ├── 4.jpg │ │ │ │ ├── 5.jpg │ │ │ │ ├── 6.jpg │ │ │ │ ├── 7.jpg │ │ │ │ ├── 8.jpg │ │ │ │ ├── 9.jpg │ │ │ │ ├── README.md │ │ │ │ └── service.png │ │ ├── AccountDetails │ │ │ ├── AccountDetails.module.scss │ │ │ └── AccountDetails.tsx │ │ ├── AccountKeysTable │ │ │ ├── AccountKeysTable.module.scss │ │ │ └── AccountKeysTable.tsx │ │ ├── AccountLink │ │ │ ├── AccountLink.module.scss │ │ │ └── AccountLink.tsx │ │ ├── AccountName │ │ │ └── AccountName.tsx │ │ ├── AccountStorage │ │ │ ├── AccountStorage.module.scss │ │ │ └── AccountStorage.tsx │ │ ├── AccountsTable │ │ │ ├── AccountsTable.module.scss │ │ │ └── AccountsTable.tsx │ │ ├── InternalStorageCard │ │ │ ├── InternalStorageCard.module.scss │ │ │ └── InternalStorageCard.tsx │ │ ├── PublicPrivateStorageCard │ │ │ ├── PublicPrivateStorageCard.module.scss │ │ │ ├── PublicPrivateStorageCard.tsx │ │ │ └── gradient.png │ │ ├── StorageDataTypes │ │ │ ├── StorageDataTypes.module.scss │ │ │ └── StorageDataTypes.tsx │ │ └── StorageDomainBadge │ │ │ ├── StorageDomainBadge.module.scss │ │ │ └── StorageDomainBadge.tsx │ ├── api.ts │ ├── assets.d.ts │ ├── assets │ │ ├── logo-foreground.svg │ │ └── long_logo.png │ ├── blocks │ │ ├── BlockDetails │ │ │ ├── BlockDetails.module.scss │ │ │ └── BlockDetails.tsx │ │ ├── BlockLink │ │ │ ├── BlockLink.module.scss │ │ │ └── BlockLink.tsx │ │ └── BlocksTable │ │ │ ├── BlocksTable.module.scss │ │ │ └── BlocksTable.tsx │ ├── common │ │ ├── buttons │ │ │ ├── ActionButton │ │ │ │ ├── ActionButton.module.scss │ │ │ │ └── ActionButton.tsx │ │ │ ├── Button │ │ │ │ ├── Button.module.scss │ │ │ │ └── Button.tsx │ │ │ ├── CopyButton │ │ │ │ ├── CopyButton.module.scss │ │ │ │ └── CopyButton.tsx │ │ │ ├── IconButton │ │ │ │ ├── IconButton.module.scss │ │ │ │ └── IconButton.tsx │ │ │ ├── LoaderButton │ │ │ │ └── LoaderButton.tsx │ │ │ ├── PrimaryButton │ │ │ │ ├── PrimaryButton.module.scss │ │ │ │ └── PrimaryButton.tsx │ │ │ ├── RadioButton │ │ │ │ ├── RadioButton.module.scss │ │ │ │ └── RadioButton.tsx │ │ │ ├── SimpleButton │ │ │ │ ├── SimpleButton.module.scss │ │ │ │ └── SimpleButton.tsx │ │ │ └── ToggleButton │ │ │ │ ├── ToggleButton.module.scss │ │ │ │ └── ToggleButton.tsx │ │ ├── cards │ │ │ ├── BaseCard │ │ │ │ ├── BaseCard.module.scss │ │ │ │ └── BaseCard.tsx │ │ │ └── DetailsCard │ │ │ │ ├── DetailsCard.module.scss │ │ │ │ └── DetailsCard.tsx │ │ ├── code │ │ │ ├── CadenceEditor │ │ │ │ ├── CadenceEditor.tsx │ │ │ │ └── cadence-editor.css │ │ │ └── JsonView │ │ │ │ ├── JsonView.module.scss │ │ │ │ └── JsonView.tsx │ │ ├── ellipsis │ │ │ ├── Ellipsis.tsx │ │ │ └── MiddleEllipsis.tsx │ │ ├── errors │ │ │ ├── ErrorMessage.module.scss │ │ │ ├── ErrorMessage.tsx │ │ │ └── Message.tsx │ │ ├── icons │ │ │ ├── CaretIcon │ │ │ │ ├── CaretIcon.module.scss │ │ │ │ └── CaretIcon.tsx │ │ │ ├── FlowserIcon.tsx │ │ │ ├── TokenIcon │ │ │ │ └── TokenIcon.tsx │ │ │ └── assets │ │ │ │ ├── account.svg │ │ │ │ ├── artist_palette.svg │ │ │ │ ├── back-button.svg │ │ │ │ ├── block.svg │ │ │ │ ├── blocks.svg │ │ │ │ ├── bottle.svg │ │ │ │ ├── calendar.svg │ │ │ │ ├── cancel.svg │ │ │ │ ├── caret.svg │ │ │ │ ├── circle-check.svg │ │ │ │ ├── circle-cross.svg │ │ │ │ ├── circle-question-mark.svg │ │ │ │ ├── circle_arrow_left.svg │ │ │ │ ├── clock.svg │ │ │ │ ├── close-logs.svg │ │ │ │ ├── close.svg │ │ │ │ ├── connect-circle.svg │ │ │ │ ├── contract.svg │ │ │ │ ├── contracts.svg │ │ │ │ ├── copy.svg │ │ │ │ ├── cursor_click.svg │ │ │ │ ├── document-with-arrow.svg │ │ │ │ ├── events.svg │ │ │ │ ├── executed-tx-icon.svg │ │ │ │ ├── exit.svg │ │ │ │ ├── expand.svg │ │ │ │ ├── finalised-tx-icon.svg │ │ │ │ ├── flow.svg │ │ │ │ ├── key.svg │ │ │ │ ├── link.svg │ │ │ │ ├── logo.svg │ │ │ │ ├── logo_round.svg │ │ │ │ ├── logout.svg │ │ │ │ ├── logs.svg │ │ │ │ ├── open-logs.svg │ │ │ │ ├── open.svg │ │ │ │ ├── pending-tx-icon.svg │ │ │ │ ├── play.svg │ │ │ │ ├── plus-round.svg │ │ │ │ ├── plus.svg │ │ │ │ ├── question-mark.svg │ │ │ │ ├── restart.svg │ │ │ │ ├── script.svg │ │ │ │ ├── search.svg │ │ │ │ ├── settings.svg │ │ │ │ ├── share.svg │ │ │ │ ├── shrink.svg │ │ │ │ ├── snapshot.svg │ │ │ │ ├── star-fill.svg │ │ │ │ ├── star.svg │ │ │ │ ├── transaction.svg │ │ │ │ ├── transactions.svg │ │ │ │ ├── trash.svg │ │ │ │ └── user.svg │ │ ├── inputs │ │ │ ├── Input │ │ │ │ ├── Input.module.scss │ │ │ │ └── Input.tsx │ │ │ ├── SearchInput │ │ │ │ ├── SearchInput.module.scss │ │ │ │ └── SearchInput.tsx │ │ │ ├── SelectInput │ │ │ │ ├── SelectInput.module.scss │ │ │ │ └── SelectInput.tsx │ │ │ └── index.ts │ │ ├── layouts │ │ │ ├── BasicLayout │ │ │ │ ├── BasicLayout.module.scss │ │ │ │ └── BasicLayout.tsx │ │ │ └── ProjectLayout │ │ │ │ ├── ProjectLayout.module.scss │ │ │ │ └── WorkspaceLayout.tsx │ │ ├── links │ │ │ ├── ExternalLink │ │ │ │ ├── ExternalLink.module.scss │ │ │ │ └── ExternalLink.tsx │ │ │ ├── IdeLink.tsx │ │ │ └── ProjectLink │ │ │ │ ├── ProjectLink.module.scss │ │ │ │ └── ProjectLink.tsx │ │ ├── loaders │ │ │ ├── FullScreenLoading │ │ │ │ ├── FullScreenLoading.module.scss │ │ │ │ └── FullScreenLoading.tsx │ │ │ ├── Shimmer │ │ │ │ └── Shimmer.tsx │ │ │ └── Spinner │ │ │ │ ├── Spinner.module.scss │ │ │ │ ├── Spinner.tsx │ │ │ │ └── SpinnerWithLabel.tsx │ │ ├── misc │ │ │ ├── BaseBadge │ │ │ │ ├── BaseBadge.module.scss │ │ │ │ └── BaseBadge.tsx │ │ │ ├── BaseTable │ │ │ │ ├── BaseTable.module.scss │ │ │ │ └── BaseTable.tsx │ │ │ ├── Breadcrumbs │ │ │ │ ├── Breadcrumbs.module.scss │ │ │ │ └── Breadcrumbs.tsx │ │ │ ├── Callout │ │ │ │ ├── Callout.module.scss │ │ │ │ └── Callout.tsx │ │ │ ├── Label │ │ │ │ ├── Label.module.scss │ │ │ │ └── Label.tsx │ │ │ ├── LineSeparator │ │ │ │ ├── LineSeparator.module.scss │ │ │ │ └── LineSeparator.tsx │ │ │ ├── SideNavigation │ │ │ │ ├── SideNavigation.module.scss │ │ │ │ └── SideNavigation.tsx │ │ │ ├── SizedBox │ │ │ │ └── SizedBox.tsx │ │ │ └── Value │ │ │ │ ├── Value.module.scss │ │ │ │ └── Value.tsx │ │ ├── overlays │ │ │ ├── Menu │ │ │ │ ├── Menu.scss │ │ │ │ └── Menu.tsx │ │ │ ├── Tooltip │ │ │ │ ├── Tooltip.scss │ │ │ │ └── Tooltip.tsx │ │ │ └── dialogs │ │ │ │ ├── action │ │ │ │ ├── ActionDialog.module.scss │ │ │ │ └── ActionDialog.tsx │ │ │ │ ├── base │ │ │ │ ├── BaseDialog.module.scss │ │ │ │ └── BaseDialog.tsx │ │ │ │ ├── confirmation │ │ │ │ └── ConfirmationDialog.tsx │ │ │ │ ├── consent │ │ │ │ ├── ConsentDialog.module.scss │ │ │ │ └── ConsentDialog.tsx │ │ │ │ └── snapshot │ │ │ │ ├── SnapshotDialog.module.scss │ │ │ │ └── SnapshotDialog.tsx │ │ ├── status │ │ │ ├── ErrorMessage.module.scss │ │ │ ├── ErrorMessage.tsx │ │ │ ├── ExecutionStatus.tsx │ │ │ ├── ExecutionStatusBadge.tsx │ │ │ ├── GrcpStatus.tsx │ │ │ ├── GrcpStatusBadge.tsx │ │ │ ├── Status.module.scss │ │ │ └── TransactionStatusBadge.module.scss │ │ ├── tabs │ │ │ ├── BaseTabs │ │ │ │ ├── BaseTabs.module.scss │ │ │ │ └── BaseTabs.tsx │ │ │ └── StyledTabs │ │ │ │ ├── StyledTabs.module.scss │ │ │ │ └── StyledTabs.tsx │ │ └── time │ │ │ ├── DateDisplay │ │ │ ├── DateDisplay.module.scss │ │ │ └── DateDisplay.tsx │ │ │ └── TimeAgo │ │ │ └── TimeAgo.tsx │ ├── contexts │ │ ├── confirm-dialog.context.tsx │ │ ├── file-picker.context.tsx │ │ ├── flow-network.context.tsx │ │ ├── navigation.context.tsx │ │ ├── service-registry.context.tsx │ │ ├── snapshots.context.tsx │ │ └── workspace.context.tsx │ ├── contracts │ │ ├── ContractDetails │ │ │ ├── ContractDetails.module.scss │ │ │ └── ContractDetails.tsx │ │ ├── ContractName │ │ │ ├── ContractName.module.scss │ │ │ └── ContractName.tsx │ │ └── ContractsTable │ │ │ ├── ContractsTable.module.scss │ │ │ └── ContractsTable.tsx │ ├── events │ │ ├── EventDetails │ │ │ ├── EventDetails.module.scss │ │ │ └── EventDetails.tsx │ │ ├── EventOriginLink │ │ │ └── EventOriginLink.tsx │ │ ├── EventsTable │ │ │ ├── EventsTable.module.scss │ │ │ └── EventsTable.tsx │ │ └── utils.ts │ ├── hooks │ │ ├── use-analytics.ts │ │ ├── use-current-project-id.ts │ │ ├── use-error-handler.ts │ │ ├── use-filter-data.ts │ │ ├── use-flix.ts │ │ └── use-mouse-move.ts │ ├── index.ts │ ├── interactions │ │ ├── InteractionsPage.module.scss │ │ ├── InteractionsPage.tsx │ │ ├── components │ │ │ ├── EditTemplateNameDialog │ │ │ │ └── EditTemplateNameDialog.tsx │ │ │ ├── ExecutionSettings │ │ │ │ ├── ExecutionSettings.module.scss │ │ │ │ └── ExecutionSettings.tsx │ │ │ ├── FlixInfo │ │ │ │ ├── FlixInfo.module.scss │ │ │ │ └── FlixInfo.tsx │ │ │ ├── InteractionHistory │ │ │ │ ├── InteractionHistory.module.scss │ │ │ │ └── InteractionHistory.tsx │ │ │ ├── InteractionIcon │ │ │ │ ├── InteractionIcon.module.scss │ │ │ │ └── InteractionIcon.tsx │ │ │ ├── InteractionLabel │ │ │ │ ├── InteractionLabel.module.scss │ │ │ │ └── InteractionLabel.tsx │ │ │ ├── InteractionOutcomeDisplay │ │ │ │ ├── InteractionOutcomeDisplay.module.scss │ │ │ │ └── InteractionOutcomeDisplay.tsx │ │ │ ├── InteractionTemplates │ │ │ │ ├── InteractionTemplates.module.scss │ │ │ │ └── InteractionTemplates.tsx │ │ │ ├── ParamBuilder │ │ │ │ ├── ParamBuilder.module.scss │ │ │ │ └── ParamBuilder.tsx │ │ │ ├── SaveSnippetDialog │ │ │ │ └── SaveSnippetDialog.tsx │ │ │ └── ValueBuilder │ │ │ │ ├── AddressBuilder │ │ │ │ ├── AddressBuilder.module.scss │ │ │ │ └── AddressBuilder.tsx │ │ │ │ ├── ArrayBuilder │ │ │ │ ├── ArrayBuilder.module.scss │ │ │ │ └── ArrayBuilder.tsx │ │ │ │ ├── BoolBuilder │ │ │ │ └── BoolBuilder.tsx │ │ │ │ ├── DictionaryBuilder │ │ │ │ ├── DictionaryBuilder.module.scss │ │ │ │ └── DictionaryBuilder.tsx │ │ │ │ ├── FixedPointNumberBuilder │ │ │ │ └── FixedPointNumberBuilder.tsx │ │ │ │ ├── IntegerNumberBuilder │ │ │ │ └── IntegerNumberBuilder.tsx │ │ │ │ ├── PathBuilder │ │ │ │ └── PathBuilder.tsx │ │ │ │ ├── TextualBuilder │ │ │ │ └── TextualBuilder.tsx │ │ │ │ ├── ValueBuilder.tsx │ │ │ │ └── interface.ts │ │ ├── contexts │ │ │ ├── definition.context.tsx │ │ │ ├── interaction-registry.context.tsx │ │ │ ├── outcome.context.tsx │ │ │ └── templates.context.tsx │ │ ├── core │ │ │ ├── core-types.ts │ │ │ └── core-utils.ts │ │ └── hooks │ │ │ └── use-transaction-name.ts │ ├── logs │ │ ├── Logs.module.scss │ │ └── Logs.tsx │ ├── styles │ │ ├── animations.scss │ │ ├── colors.scss │ │ ├── rules.scss │ │ ├── scrollbars.scss │ │ ├── spacings.scss │ │ └── typography.scss │ ├── transactions │ │ ├── SignaturesTable │ │ │ ├── SignaturesTable.module.scss │ │ │ └── SignaturesTable.tsx │ │ ├── TransactionDetails │ │ │ ├── TransactionDetails.module.scss │ │ │ └── TransactionDetails.tsx │ │ ├── TransactionDetailsTabs │ │ │ └── TransactionDetailsTabs.tsx │ │ ├── TransactionLink │ │ │ ├── TransactionLink.module.scss │ │ │ └── TransactionLink.tsx │ │ ├── TransactionOverview │ │ │ └── TransactionOverview.tsx │ │ ├── TransactionSource │ │ │ ├── TransactionSource.module.scss │ │ │ └── TransactionSource.tsx │ │ └── TransactionsTable │ │ │ ├── TransactionsTable.module.scss │ │ │ └── TransactionsTable.tsx │ ├── utils │ │ ├── common-utils.ts │ │ ├── flow-utils.test.ts │ │ ├── flow-utils.ts │ │ ├── multi-map.ts │ │ └── text-utils.ts │ └── workspaces │ │ ├── FormFields │ │ ├── FormFields.module.scss │ │ └── FormFields.tsx │ │ ├── SettingsSection │ │ ├── SettingsSection.module.scss │ │ └── SettingsSection.tsx │ │ ├── WorkspaceList │ │ ├── WorkspaceList.module.scss │ │ └── WorkspaceList.tsx │ │ └── WorkspaceSettings │ │ ├── WorkspaceSettings.module.scss │ │ └── WorkspaceSettings.tsx │ └── tsconfig.json ├── pkg ├── .gitignore ├── flowser │ └── app.go └── main │ └── main.go ├── turbo.json └── yarn.lock /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug, feedback 6 | assignees: bartolomej 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Additional context** 32 | Add any other context about the problem here. 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement, feedback 6 | assignees: bartolomej 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/config.yml: -------------------------------------------------------------------------------- 1 | requiredHeaders: 2 | - Prerequisites 3 | - Expected Behavior 4 | - Current Behavior 5 | - Possible Solution 6 | - Your Environment 7 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 60 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 7 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - discussion 8 | - security 9 | # Label to use when marking an issue as stale 10 | staleLabel: wontfix 11 | # Comment to post when marking an issue as stale. Set to `false` to disable 12 | markComment: > 13 | This issue has been automatically marked as stale because it has not had 14 | recent activity. It will be closed if no further activity occurs. Thank you 15 | for your contributions. 16 | # Comment to post when closing a stale issue. Set to `false` to disable 17 | closeComment: false 18 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | release: 5 | types: 6 | - created 7 | 8 | jobs: 9 | publish: 10 | runs-on: ${{ matrix.os }} 11 | 12 | strategy: 13 | matrix: 14 | os: [ macos-latest ] 15 | 16 | steps: 17 | - name: Checkout git repo 18 | uses: actions/checkout@v3 19 | 20 | - name: Install Node and NPM 21 | uses: actions/setup-node@v3 22 | with: 23 | node-version: 18 24 | cache: npm 25 | 26 | - name: Run install 27 | uses: borales/actions-yarn@v4 28 | with: 29 | cmd: install 30 | 31 | - name: Setup Go environment 32 | uses: actions/setup-go@v4.1.0 33 | 34 | - name: Build 35 | run: | 36 | yarn run build 37 | 38 | - name: Publish releases 39 | env: 40 | # These values are used for auto updates signing 41 | APPLE_ID: ${{ secrets.APPLE_ID }} 42 | APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }} 43 | CSC_LINK: ${{ secrets.CSC_LINK }} 44 | CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }} 45 | # This is used for uploading release assets to github 46 | GH_TOKEN: ${{ secrets.GH_TOKEN }} 47 | run: | 48 | npm exec electron-builder -- --publish always --win --mac --linux 49 | working-directory: ./apps/electron 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | node_modules 3 | .DS_Store 4 | .env 5 | database/db 6 | flowdb 7 | flow.json 8 | .flowser 9 | .envrc 10 | server/certbot 11 | .envrc 12 | dist 13 | .turbo 14 | *.tsbuildinfo 15 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npx lint-staged 5 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 18 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Flowser 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /apps/electron/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /apps/electron/.erb/configs/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-console": "off", 4 | "global-require": "off", 5 | "import/no-dynamic-require": "off" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /apps/electron/.erb/configs/webpack.config.eslint.ts: -------------------------------------------------------------------------------- 1 | /* eslint import/no-unresolved: off, import/no-self-import: off */ 2 | 3 | module.exports = require('./webpack.config.renderer.dev').default; 4 | -------------------------------------------------------------------------------- /apps/electron/.erb/configs/webpack.paths.ts: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const rootPath = path.join(__dirname, '../..'); 4 | 5 | const dllPath = path.join(__dirname, '../dll'); 6 | 7 | const srcPath = path.join(rootPath, 'src'); 8 | const srcMainPath = path.join(srcPath, 'main'); 9 | const srcRendererPath = path.join(srcPath, 'renderer'); 10 | 11 | const releasePath = path.join(rootPath, 'release'); 12 | const appPath = path.join(releasePath, 'app'); 13 | const appPackagePath = path.join(appPath, 'package.json'); 14 | const appNodeModulesPath = path.join(appPath, 'node_modules'); 15 | const srcNodeModulesPath = path.join(srcPath, 'node_modules'); 16 | 17 | const distPath = path.join(appPath, 'dist'); 18 | const distMainPath = path.join(distPath, 'main'); 19 | const distRendererPath = path.join(distPath, 'renderer'); 20 | 21 | const buildPath = path.join(releasePath, 'build'); 22 | 23 | export default { 24 | rootPath, 25 | dllPath, 26 | srcPath, 27 | srcMainPath, 28 | srcRendererPath, 29 | releasePath, 30 | appPath, 31 | appPackagePath, 32 | appNodeModulesPath, 33 | srcNodeModulesPath, 34 | distPath, 35 | distMainPath, 36 | distRendererPath, 37 | buildPath, 38 | }; 39 | -------------------------------------------------------------------------------- /apps/electron/.erb/scripts/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-console": "off", 4 | "global-require": "off", 5 | "import/no-dynamic-require": "off", 6 | "import/no-extraneous-dependencies": "off" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /apps/electron/.erb/scripts/check-build-exists.ts: -------------------------------------------------------------------------------- 1 | // Check if the renderer and main bundles are built 2 | import path from 'path'; 3 | import chalk from 'chalk'; 4 | import fs from 'fs'; 5 | import webpackPaths from '../configs/webpack.paths'; 6 | 7 | const mainPath = path.join(webpackPaths.distMainPath, 'main.js'); 8 | const rendererPath = path.join(webpackPaths.distRendererPath, 'renderer.js'); 9 | 10 | if (!fs.existsSync(mainPath)) { 11 | throw new Error( 12 | chalk.whiteBright.bgRed.bold( 13 | 'The main process is not built yet. Build it by running "npm run build:main"', 14 | ), 15 | ); 16 | } 17 | 18 | if (!fs.existsSync(rendererPath)) { 19 | throw new Error( 20 | chalk.whiteBright.bgRed.bold( 21 | 'The renderer process is not built yet. Build it by running "npm run build:renderer"', 22 | ), 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /apps/electron/.erb/scripts/check-node-env.js: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | 3 | export default function checkNodeEnv(expectedEnv) { 4 | if (!expectedEnv) { 5 | throw new Error('"expectedEnv" not set'); 6 | } 7 | 8 | if (process.env.NODE_ENV !== expectedEnv) { 9 | console.log( 10 | chalk.whiteBright.bgRed.bold( 11 | `"process.env.NODE_ENV" must be "${expectedEnv}" to use this webpack config`, 12 | ), 13 | ); 14 | process.exit(2); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /apps/electron/.erb/scripts/check-port-in-use.js: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | import detectPort from 'detect-port'; 3 | 4 | const port = process.env.PORT || '1212'; 5 | 6 | detectPort(port, (_err, availablePort) => { 7 | if (port !== String(availablePort)) { 8 | throw new Error( 9 | chalk.whiteBright.bgRed.bold( 10 | `Port "${port}" on "localhost" is already in use. Please use another port. ex: PORT=4343 npm start`, 11 | ), 12 | ); 13 | } else { 14 | process.exit(0); 15 | } 16 | }); 17 | -------------------------------------------------------------------------------- /apps/electron/.erb/scripts/clean.js: -------------------------------------------------------------------------------- 1 | import { rimrafSync } from 'rimraf'; 2 | import fs from 'fs'; 3 | import webpackPaths from '../configs/webpack.paths'; 4 | 5 | const foldersToRemove = [ 6 | webpackPaths.distPath, 7 | webpackPaths.buildPath, 8 | webpackPaths.dllPath, 9 | ]; 10 | 11 | foldersToRemove.forEach((folder) => { 12 | if (fs.existsSync(folder)) rimrafSync(folder); 13 | }); 14 | -------------------------------------------------------------------------------- /apps/electron/.erb/scripts/delete-source-maps.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import path from 'path'; 3 | import { rimrafSync } from 'rimraf'; 4 | import webpackPaths from '../configs/webpack.paths'; 5 | 6 | export default function deleteSourceMaps() { 7 | if (fs.existsSync(webpackPaths.distMainPath)) 8 | rimrafSync(path.join(webpackPaths.distMainPath, '*.js.map'), { 9 | glob: true, 10 | }); 11 | if (fs.existsSync(webpackPaths.distRendererPath)) 12 | rimrafSync(path.join(webpackPaths.distRendererPath, '*.js.map'), { 13 | glob: true, 14 | }); 15 | } 16 | -------------------------------------------------------------------------------- /apps/electron/.erb/scripts/electron-rebuild.js: -------------------------------------------------------------------------------- 1 | import { execSync } from 'child_process'; 2 | import fs from 'fs'; 3 | import { dependencies } from '../../release/app/package.json'; 4 | import webpackPaths from '../configs/webpack.paths'; 5 | 6 | if ( 7 | Object.keys(dependencies || {}).length > 0 && 8 | fs.existsSync(webpackPaths.appNodeModulesPath) 9 | ) { 10 | const electronRebuildCmd = 11 | '../../node_modules/.bin/electron-rebuild --force --types prod,dev,optional --module-dir .'; 12 | const cmd = 13 | process.platform === 'win32' 14 | ? electronRebuildCmd.replace(/\//g, '\\') 15 | : electronRebuildCmd; 16 | execSync(cmd, { 17 | cwd: webpackPaths.appPath, 18 | stdio: 'inherit', 19 | }); 20 | } 21 | -------------------------------------------------------------------------------- /apps/electron/.erb/scripts/link-modules.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import webpackPaths from '../configs/webpack.paths'; 3 | 4 | const { srcNodeModulesPath } = webpackPaths; 5 | const { appNodeModulesPath } = webpackPaths; 6 | 7 | if (!fs.existsSync(srcNodeModulesPath) && fs.existsSync(appNodeModulesPath)) { 8 | fs.symlinkSync(appNodeModulesPath, srcNodeModulesPath, 'junction'); 9 | } 10 | -------------------------------------------------------------------------------- /apps/electron/.erb/scripts/notarize.js: -------------------------------------------------------------------------------- 1 | const { notarize } = require('@electron/notarize'); 2 | 3 | exports.default = async function notarizeMacos(context) { 4 | const { electronPlatformName, appOutDir } = context; 5 | if (electronPlatformName !== 'darwin') { 6 | return; 7 | } 8 | 9 | if (process.env.CI !== 'true') { 10 | console.warn('Skipping notarizing step. Packaging is not running in CI'); 11 | return; 12 | } 13 | 14 | if ( 15 | !('APPLE_ID' in process.env && 'APPLE_APP_SPECIFIC_PASSWORD' in process.env) 16 | ) { 17 | console.warn( 18 | 'Skipping notarizing step. APPLE_ID and APPLE_APP_SPECIFIC_PASSWORD env variables must be set', 19 | ); 20 | return; 21 | } 22 | 23 | const appName = context.packager.appInfo.productFilename; 24 | 25 | await notarize({ 26 | // Must be kept in sync with `appId` field in `electron-builder.yml` 27 | appBundleId: 'dev.flowser.app', 28 | appPath: `${appOutDir}/${appName}.app`, 29 | teamId: 'A425JF6L5H', 30 | tool: 'notarytool', 31 | appleId: process.env.APPLE_ID, 32 | appleIdPassword: process.env.APPLE_APP_SPECIFIC_PASSWORD, 33 | }); 34 | }; 35 | -------------------------------------------------------------------------------- /apps/electron/.eslintignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Coverage directory used by tools like istanbul 11 | coverage 12 | .eslintcache 13 | 14 | # Dependency directory 15 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 16 | node_modules 17 | 18 | # OSX 19 | .DS_Store 20 | 21 | release/app/dist 22 | release/build 23 | .erb/dll 24 | 25 | .idea 26 | npm-debug.log.* 27 | *.css.d.ts 28 | *.sass.d.ts 29 | *.scss.d.ts 30 | 31 | # eslint ignores hidden directories by default: 32 | # https://github.com/eslint/eslint/issues/8429 33 | !.erb 34 | -------------------------------------------------------------------------------- /apps/electron/.gitattributes: -------------------------------------------------------------------------------- 1 | * text eol=lf 2 | *.exe binary 3 | *.png binary 4 | *.jpg binary 5 | *.jpeg binary 6 | *.ico binary 7 | *.icns binary 8 | *.eot binary 9 | *.otf binary 10 | *.ttf binary 11 | *.woff binary 12 | *.woff2 binary 13 | -------------------------------------------------------------------------------- /apps/electron/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Coverage directory used by tools like istanbul 11 | coverage 12 | .eslintcache 13 | 14 | # Dependency directory 15 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 16 | node_modules 17 | 18 | # OSX 19 | .DS_Store 20 | 21 | release/app/dist 22 | release/build 23 | .erb/dll 24 | 25 | .idea 26 | npm-debug.log.* 27 | *.css.d.ts 28 | *.sass.d.ts 29 | *.scss.d.ts 30 | 31 | generated-icons 32 | -------------------------------------------------------------------------------- /apps/electron/assets/Inter.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onflowser/flowser/1e42a42d186bb18cba216bf2e9c01275552e4d60/apps/electron/assets/Inter.woff2 -------------------------------------------------------------------------------- /apps/electron/assets/assets.d.ts: -------------------------------------------------------------------------------- 1 | type Styles = Record; 2 | 3 | declare module '*.svg' { 4 | import React = require('react'); 5 | 6 | export const ReactComponent: React.FC>; 7 | 8 | const content: string; 9 | export default content; 10 | } 11 | 12 | declare module '*.png' { 13 | const content: string; 14 | export default content; 15 | } 16 | 17 | declare module '*.jpg' { 18 | const content: string; 19 | export default content; 20 | } 21 | 22 | declare module '*.scss' { 23 | const content: Styles; 24 | export default content; 25 | } 26 | 27 | declare module '*.sass' { 28 | const content: Styles; 29 | export default content; 30 | } 31 | 32 | declare module '*.css' { 33 | const content: Styles; 34 | export default content; 35 | } 36 | -------------------------------------------------------------------------------- /apps/electron/assets/entitlements.mac.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.cs.allow-unsigned-executable-memory 6 | 7 | com.apple.security.cs.allow-jit 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /apps/electron/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onflowser/flowser/1e42a42d186bb18cba216bf2e9c01275552e4d60/apps/electron/assets/icon.png -------------------------------------------------------------------------------- /apps/electron/assets/macos-background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onflowser/flowser/1e42a42d186bb18cba216bf2e9c01275552e4d60/apps/electron/assets/macos-background.png -------------------------------------------------------------------------------- /apps/electron/assets/macos-background.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onflowser/flowser/1e42a42d186bb18cba216bf2e9c01275552e4d60/apps/electron/assets/macos-background.tiff -------------------------------------------------------------------------------- /apps/electron/release/app/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flowser", 3 | "version": "3.2.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "flowser", 9 | "version": "3.2.0", 10 | "hasInstallScript": true, 11 | "license": "MIT" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /apps/electron/release/app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flowser", 3 | "version": "3.2.0", 4 | "description": "Supercharged development on Flow blockchain ⚡", 5 | "license": "MIT", 6 | "author": { 7 | "name": "Flowser", 8 | "email": "bart@flowser.dev", 9 | "url": "https://github.com/onflowser/flowser" 10 | }, 11 | "main": "./dist/main/main.js", 12 | "scripts": { 13 | "rebuild": "node -r ts-node/register ../../.erb/scripts/electron-rebuild.js", 14 | "postinstall": "npm run rebuild && npm run link-modules", 15 | "link-modules": "node -r ts-node/register ../../.erb/scripts/link-modules.ts" 16 | }, 17 | "dependencies": {} 18 | } 19 | -------------------------------------------------------------------------------- /apps/electron/release/app/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | -------------------------------------------------------------------------------- /apps/electron/src/__tests__/App.test.tsx: -------------------------------------------------------------------------------- 1 | import '@testing-library/jest-dom'; 2 | import { render } from '@testing-library/react'; 3 | import App from '../renderer/App'; 4 | 5 | describe('App', () => { 6 | it('should render', () => { 7 | expect(render()).toBeTruthy(); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /apps/electron/src/main/preload.ts: -------------------------------------------------------------------------------- 1 | import { contextBridge } from 'electron'; 2 | import { electronInvokers } from './ipc/invokers'; 3 | 4 | contextBridge.exposeInMainWorld('electron', electronInvokers); 5 | 6 | export type ElectronInvokers = typeof electronInvokers; 7 | -------------------------------------------------------------------------------- /apps/electron/src/main/util.ts: -------------------------------------------------------------------------------- 1 | /* eslint import/prefer-default-export: off */ 2 | import { URL } from 'url'; 3 | import path from 'path'; 4 | 5 | export function resolveHtmlPath(htmlFileName: string) { 6 | if (process.env.NODE_ENV === 'development') { 7 | const port = process.env.PORT || 1212; 8 | const url = new URL(`http://localhost:${port}`); 9 | url.pathname = htmlFileName; 10 | return url.href; 11 | } 12 | return `file://${path.resolve(__dirname, '../renderer/', htmlFileName)}`; 13 | } 14 | -------------------------------------------------------------------------------- /apps/electron/src/renderer/App.scss: -------------------------------------------------------------------------------- 1 | @import '../../../../packages/ui/src/styles/colors'; 2 | @import '../../../../packages/ui/src/styles/typography'; 3 | 4 | @font-face { 5 | font-family: 'Inter'; 6 | font-style: normal; 7 | font-weight: 400; 8 | /* Path to font must start without "/" prefix, otherwise it won't work on Windows. */ 9 | src: url(../../assets/Inter.woff2) format('woff2'); 10 | unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, 11 | U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, 12 | U+2193, U+2212, U+2215, U+FEFF, U+FFFD; 13 | } 14 | 15 | * { 16 | margin: 0; 17 | padding: 0; 18 | box-sizing: content-box; 19 | outline: none; 20 | 21 | &:focus-visible { 22 | outline: 2px solid $blue; 23 | } 24 | } 25 | 26 | html, 27 | body { 28 | color: $gray-10; 29 | background: $gray-110; 30 | font-family: "Inter"; 31 | font-size: $font-size-normal; 32 | font-style: normal; 33 | font-weight: normal; 34 | letter-spacing: $character-spacing-small; 35 | line-height: $line-spacing-m; 36 | -webkit-font-smoothing: antialiased; 37 | -moz-osx-font-smoothing: grayscale; 38 | 39 | min-height: 100%; 40 | height: 100%; 41 | 42 | scroll-behavior: smooth; 43 | 44 | &::-webkit-scrollbar { 45 | display: none; 46 | } 47 | } 48 | 49 | a { 50 | text-decoration: none; 51 | color: $blue; 52 | } 53 | -------------------------------------------------------------------------------- /apps/electron/src/renderer/components/DependencyErrors/DependencyErrors.module.scss: -------------------------------------------------------------------------------- 1 | @import '../../../../../../packages/ui/src/styles/typography'; 2 | @import '../../../../../../packages/ui/src/styles/spacings'; 3 | 4 | .missingRequirementItem { 5 | .title { 6 | font-size: $font-size-large; 7 | margin-bottom: $spacing-base; 8 | display: block; 9 | } 10 | .description { 11 | margin-bottom: $spacing-s; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /apps/electron/src/renderer/components/loaders/ExitLoader.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactElement } from 'react'; 2 | import { Spinner } from '../../../../../../packages/ui/src/common/loaders/Spinner/Spinner'; 3 | import classes from './Loader.module.scss'; 4 | 5 | export function ExitLoader(): ReactElement { 6 | return ( 7 | <> 8 |
9 |
10 | 11 | Flowser is exiting 12 |
13 | 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /apps/electron/src/renderer/components/loaders/Loader.module.scss: -------------------------------------------------------------------------------- 1 | @import '../../../../../../packages/ui/src/styles/colors'; 2 | @import '../../../../../../packages/ui/src/styles/spacings'; 3 | @import '../../../../../../packages/ui/src/styles/animations'; 4 | @import '../../../../../../packages/ui/src/styles/typography'; 5 | 6 | .background { 7 | @include fadeInAnimation(0.4s); 8 | & { 9 | position: absolute; 10 | top: 0; 11 | left: 0; 12 | right: 0; 13 | bottom: 0; 14 | background: $gray-110; 15 | opacity: 0.5; 16 | } 17 | } 18 | 19 | .root { 20 | @include fadeInAnimation(0.4s); 21 | & { 22 | position: absolute; 23 | top: 0; 24 | left: 0; 25 | right: 0; 26 | bottom: 0; 27 | display: flex; 28 | flex-direction: column; 29 | justify-content: center; 30 | align-items: center; 31 | backdrop-filter: blur(2px); 32 | z-index: 100; 33 | } 34 | 35 | .loader { 36 | @include pulse(1s); 37 | } 38 | 39 | .title { 40 | color: $color-light-gray; 41 | font-size: $font-size-large; 42 | margin-top: $spacing-base; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /apps/electron/src/renderer/components/loaders/UpdateLoader.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactElement } from 'react'; 2 | import { Spinner } from '../../../../../../packages/ui/src/common/loaders/Spinner/Spinner'; 3 | import classes from './Loader.module.scss'; 4 | 5 | export type UpdateLoaderProps = { 6 | loadingPercentage: number; 7 | }; 8 | 9 | export function UpdateLoader({ 10 | loadingPercentage, 11 | }: UpdateLoaderProps): ReactElement { 12 | return ( 13 | <> 14 |
15 |
16 | 17 | 18 | Installing update ({Math.round(loadingPercentage)}%) 19 | 20 |
21 | 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /apps/electron/src/renderer/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | Flowser 10 | 11 | 12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /apps/electron/src/renderer/index.tsx: -------------------------------------------------------------------------------- 1 | import { createRoot } from 'react-dom/client'; 2 | import { App } from './App'; 3 | 4 | const container = document.getElementById('root') as HTMLElement; 5 | const root = createRoot(container); 6 | root.render(); 7 | -------------------------------------------------------------------------------- /apps/electron/src/renderer/ipc-index-cache.ts: -------------------------------------------------------------------------------- 1 | import { IndexableResource, IResourceIndexReader } from '@onflowser/api'; 2 | import { BlockchainIndexes } from '../services/blockchain-index.service'; 3 | 4 | export const indexSyncIntervalInMs = 500; 5 | 6 | export class IpcIndexCache 7 | implements IResourceIndexReader 8 | { 9 | private cachedResources: Resource[]; 10 | 11 | constructor(private readonly indexName: keyof BlockchainIndexes) { 12 | this.cachedResources = []; 13 | this.syncOnInterval(); 14 | } 15 | 16 | async findOneById(id: string): Promise { 17 | const resource = this.cachedResources.find((e) => e.id === id); 18 | 19 | // Throw if not found, 20 | // so that we can easily handle loading state (data=undefined) with SWR. 21 | if (!resource) { 22 | throw new Error('Resource not found'); 23 | } 24 | 25 | return resource; 26 | } 27 | 28 | async findAll(): Promise { 29 | return this.cachedResources.sort( 30 | (a, b) => b.createdAt.getTime() - a.createdAt.getTime(), 31 | ); 32 | } 33 | 34 | syncOnInterval() { 35 | setInterval(() => this.sync(), indexSyncIntervalInMs); 36 | } 37 | 38 | async sync() { 39 | this.cachedResources = await window.electron.indexes.getAll(this.indexName); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /apps/electron/src/renderer/preload.d.ts: -------------------------------------------------------------------------------- 1 | import { ElectronInvokers } from '../main/preload'; 2 | 3 | declare global { 4 | // eslint-disable-next-line no-unused-vars 5 | interface Window { 6 | electron: ElectronInvokers; 7 | } 8 | } 9 | 10 | export {}; 11 | -------------------------------------------------------------------------------- /apps/electron/src/services/analytics.service.ts: -------------------------------------------------------------------------------- 1 | import mixpanel, { Dict } from 'mixpanel-browser'; 2 | import { FingerprintService } from './fingerprint.service'; 3 | 4 | const enableAnalytics = process.env.NODE_ENV === 'production'; 5 | 6 | export class AnalyticsService { 7 | private fingerprintService: FingerprintService; 8 | 9 | constructor() { 10 | this.fingerprintService = new FingerprintService(); 11 | this.init(); 12 | } 13 | async init(): Promise { 14 | mixpanel.init('1b358339dc3d7476217983016b83fcab', { 15 | debug: !enableAnalytics, 16 | }); 17 | 18 | try { 19 | mixpanel.identify(await this.fingerprintService.getUserFingerprintId()); 20 | } catch (e) { 21 | console.error('Failed to identify user', e); 22 | } 23 | } 24 | 25 | disable(): void { 26 | mixpanel.opt_out_tracking(); 27 | } 28 | 29 | enable(): void { 30 | mixpanel.opt_in_tracking(); 31 | } 32 | 33 | track(event: string, properties: Dict = {}): void { 34 | if (!enableAnalytics) { 35 | console.log('Analytics disabled. Skipping event ', event); 36 | return; 37 | } 38 | 39 | try { 40 | mixpanel.track(event, properties); 41 | } catch (e) { 42 | console.error('Mixpanel error:', e); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /apps/electron/src/services/blockchain-index.service.ts: -------------------------------------------------------------------------------- 1 | import { 2 | FlowAccount, 3 | FlowAccountStorage, 4 | FlowAccountKey, 5 | FlowBlock, 6 | FlowContract, 7 | FlowEvent, 8 | FlowTransaction, 9 | IdentifiableResource, 10 | IResourceIndex, 11 | TimestampedResource, 12 | } from '@onflowser/api'; 13 | 14 | export type BlockchainIndexes = { 15 | accountStorage: IResourceIndex; 16 | contract: IResourceIndex; 17 | accountKey: IResourceIndex; 18 | block: IResourceIndex; 19 | event: IResourceIndex; 20 | transaction: IResourceIndex; 21 | account: IResourceIndex; 22 | }; 23 | 24 | export class BlockchainIndexService { 25 | constructor(public readonly indexes: BlockchainIndexes) {} 26 | 27 | clear() { 28 | this.findAll().forEach((index) => index.clear()); 29 | } 30 | 31 | private findAll(): IResourceIndex< 32 | IdentifiableResource & TimestampedResource 33 | >[] { 34 | return Object.values(this.indexes); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /apps/electron/src/services/file-storage.service.ts: -------------------------------------------------------------------------------- 1 | import { PersistentStorage } from '@onflowser/core/src/persistent-storage'; 2 | import fs from 'fs/promises'; 3 | import path from 'path'; 4 | import { app } from 'electron'; 5 | 6 | export class FileStorageService implements PersistentStorage { 7 | private fileName: string | undefined; 8 | 9 | constructor(fileName?: string) { 10 | this.fileName = fileName; 11 | } 12 | 13 | setFileName(fileName: string) { 14 | this.fileName = fileName; 15 | } 16 | 17 | async read(): Promise { 18 | try { 19 | return await fs.readFile(this.getStorageFilePath(), { 20 | encoding: 'utf-8', 21 | }); 22 | } catch (error) { 23 | // Assume file not found error 24 | return undefined; 25 | } 26 | } 27 | 28 | async write(data: string): Promise { 29 | await fs.writeFile(this.getStorageFilePath(), data); 30 | } 31 | 32 | private getStorageFilePath() { 33 | if (this.fileName === undefined) { 34 | throw new Error('File name is not set'); 35 | } 36 | return path.join(app.getPath('userData'), `${this.fileName}`); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /apps/electron/src/services/fingerprint.service.ts: -------------------------------------------------------------------------------- 1 | import FingerprintJS from '@fingerprintjs/fingerprintjs'; 2 | 3 | export class FingerprintService { 4 | // Alternative fingerprinting idea: 5 | // At the very beginning generate an uuid and store it permanently. 6 | // Every subsequent time the app is opened, reuse the persisted uuid. 7 | async getUserFingerprintId(): Promise { 8 | const fingerprint = await FingerprintJS.load(); 9 | const { visitorId, confidence } = await fingerprint.get({ debug: false }); 10 | console.log( 11 | `Identifying user ${visitorId} with confidence ${confidence.score}`, 12 | ); 13 | return visitorId; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /apps/electron/src/services/sentry-main.service.ts: -------------------------------------------------------------------------------- 1 | import * as Sentry from '@sentry/electron'; 2 | import { FingerprintService } from './fingerprint.service'; 3 | 4 | const enableAnalytics = process.env.NODE_ENV === 'production'; 5 | 6 | export class SentryMainService { 7 | private fingerprintService: FingerprintService; 8 | constructor() { 9 | this.fingerprintService = new FingerprintService(); 10 | } 11 | async init(): Promise { 12 | if (!enableAnalytics) { 13 | return; 14 | } 15 | Sentry.init({ 16 | dsn: 'https://38a653b591734e0bbe2695a321f554d9@o1430444.ingest.sentry.io/6781494', 17 | }); 18 | try { 19 | Sentry.setUser({ 20 | id: await this.fingerprintService.getUserFingerprintId(), 21 | }); 22 | } catch (e) { 23 | console.log('Failed to identify user:', e); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /apps/electron/src/services/sentry-renderer.service.ts: -------------------------------------------------------------------------------- 1 | import * as Sentry from '@sentry/electron/renderer'; 2 | import { FingerprintService } from './fingerprint.service'; 3 | 4 | const enableAnalytics = process.env.NODE_ENV === 'production'; 5 | 6 | export class SentryRendererService { 7 | private fingerprintService: FingerprintService; 8 | constructor() { 9 | this.fingerprintService = new FingerprintService(); 10 | } 11 | async init(): Promise { 12 | if (!enableAnalytics) { 13 | return; 14 | } 15 | Sentry.init({ 16 | dsn: 'https://38a653b591734e0bbe2695a321f554d9@o1430444.ingest.sentry.io/6781494', 17 | }); 18 | try { 19 | Sentry.setUser({ 20 | id: await this.fingerprintService.getUserFingerprintId(), 21 | }); 22 | } catch (e) { 23 | console.log('Failed to identify user:', e); 24 | } 25 | } 26 | 27 | captureError(error: unknown, options?: Record): void { 28 | Sentry.captureException(error, options); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /apps/electron/src/services/types.ts: -------------------------------------------------------------------------------- 1 | export enum FlowserDependencyErrorType { 2 | UNSUPPORTED_CLI_VERSION, 3 | MISSING_FLOW_CLI, 4 | } 5 | 6 | export interface FlowserDependencyError { 7 | name: string; 8 | type: FlowserDependencyErrorType; 9 | 10 | unsupportedCliVersion?: { 11 | minSupportedVersion: string; 12 | actualVersion: string; 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /apps/electron/src/utils.ts: -------------------------------------------------------------------------------- 1 | type ErrorWithMessage = { message: string }; 2 | 3 | export function isErrorWithMessage(error: unknown): error is ErrorWithMessage { 4 | return typeof error === 'object' && error !== null && 'message' in error; 5 | } 6 | -------------------------------------------------------------------------------- /apps/electron/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "incremental": true, 4 | "target": "es2022", 5 | "module": "commonjs", 6 | "lib": ["dom", "es2022"], 7 | "jsx": "react-jsx", 8 | "strict": true, 9 | "sourceMap": true, 10 | "moduleResolution": "node", 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "resolveJsonModule": true, 14 | "allowJs": true, 15 | "outDir": ".erb/dll" 16 | }, 17 | "exclude": ["test", "release/build", "release/app/dist", ".erb/dll"] 18 | } 19 | -------------------------------------------------------------------------------- /apps/web/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /apps/web/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | 38 | # Generated at build time 39 | public/bin 40 | -------------------------------------------------------------------------------- /apps/web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@onflowser/web", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build-web": "npm run setup-bin && next build", 8 | "setup-bin": "mkdir -p public/bin && cp ../../packages/nodejs/bin/flowser-internal-amd64-linux public/bin/flowser-internal-amd64-linux", 9 | "start": "next start", 10 | "lint": "next lint" 11 | }, 12 | "dependencies": { 13 | "@onflowser/nodejs": "*", 14 | "@onflowser/ui": "*", 15 | "@svgr/webpack": "^8.1.0", 16 | "@vercel/analytics": "^1.3.1", 17 | "next": "^14.0.4", 18 | "react": "^18", 19 | "react-dom": "^18", 20 | "swr": "^2.2.4" 21 | }, 22 | "devDependencies": { 23 | "@types/node": "^20", 24 | "@types/react": "^18", 25 | "@types/react-dom": "^18", 26 | "autoprefixer": "^10.0.1", 27 | "eslint": "^8", 28 | "eslint-config-next": "14.0.3", 29 | "postcss": "^8", 30 | "tailwindcss": "^3.3.0", 31 | "typescript": "^5" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /apps/web/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /apps/web/public/Inter-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onflowser/flowser/1e42a42d186bb18cba216bf2e9c01275552e4d60/apps/web/public/Inter-Black.ttf -------------------------------------------------------------------------------- /apps/web/public/Inter-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onflowser/flowser/1e42a42d186bb18cba216bf2e9c01275552e4d60/apps/web/public/Inter-Regular.ttf -------------------------------------------------------------------------------- /apps/web/public/contractbrowser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onflowser/flowser/1e42a42d186bb18cba216bf2e9c01275552e4d60/apps/web/public/contractbrowser.png -------------------------------------------------------------------------------- /apps/web/public/flowdiver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onflowser/flowser/1e42a42d186bb18cba216bf2e9c01275552e4d60/apps/web/public/flowdiver.png -------------------------------------------------------------------------------- /apps/web/public/flowview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onflowser/flowser/1e42a42d186bb18cba216bf2e9c01275552e4d60/apps/web/public/flowview.png -------------------------------------------------------------------------------- /apps/web/src/app/Inter.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onflowser/flowser/1e42a42d186bb18cba216bf2e9c01275552e4d60/apps/web/src/app/Inter.woff2 -------------------------------------------------------------------------------- /apps/web/src/app/[networkId]/[interaction]/page.tsx: -------------------------------------------------------------------------------- 1 | import { RootLoader } from "@/common/root-loader"; 2 | import { Metadata } from "next"; 3 | import { getInteractionPageMetadata } from "@/common/metadata"; 4 | import { InteractionsPageParams } from "@/common/interaction-page-params"; 5 | 6 | type Props = { 7 | params: InteractionsPageParams 8 | } 9 | 10 | export async function generateMetadata( 11 | props: Props, 12 | ): Promise { 13 | return getInteractionPageMetadata(props.params) 14 | } 15 | 16 | export default function Page() { 17 | return 18 | } 19 | -------------------------------------------------------------------------------- /apps/web/src/app/[networkId]/page.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from 'next' 2 | import { getInteractionPageMetadata } from "@/common/metadata"; 3 | import { RootLoader } from '@/common/root-loader'; 4 | import { InteractionsPageParams } from "@/common/interaction-page-params"; 5 | 6 | type Props = { 7 | params: InteractionsPageParams 8 | } 9 | 10 | export async function generateMetadata( 11 | props: Props, 12 | ): Promise { 13 | return getInteractionPageMetadata(props.params) 14 | } 15 | 16 | export default function Page({ params }: Props) { 17 | return 18 | } 19 | -------------------------------------------------------------------------------- /apps/web/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onflowser/flowser/1e42a42d186bb18cba216bf2e9c01275552e4d60/apps/web/src/app/favicon.ico -------------------------------------------------------------------------------- /apps/web/src/app/index.scss: -------------------------------------------------------------------------------- 1 | @import '../../../../packages/ui/src/styles/colors'; 2 | @import '../../../../packages/ui/src/styles/typography'; 3 | @import "tailwindcss/base"; 4 | @import "tailwindcss/components"; 5 | @import "tailwindcss/utilities"; 6 | 7 | * { 8 | margin: 0; 9 | padding: 0; 10 | box-sizing: content-box; 11 | outline: none; 12 | 13 | &:focus-visible { 14 | outline: 2px solid $blue; 15 | } 16 | } 17 | 18 | html, 19 | body { 20 | color: $gray-10; 21 | background: $gray-110; 22 | font-size: $font-size-normal; 23 | font-style: normal; 24 | font-weight: normal; 25 | letter-spacing: $character-spacing-small; 26 | line-height: $line-spacing-m; 27 | -webkit-font-smoothing: antialiased; 28 | -moz-osx-font-smoothing: grayscale; 29 | 30 | min-height: 100%; 31 | height: 100%; 32 | 33 | scroll-behavior: smooth; 34 | 35 | &::-webkit-scrollbar { 36 | display: none; 37 | } 38 | } 39 | 40 | a { 41 | text-decoration: none; 42 | color: $blue; 43 | } 44 | -------------------------------------------------------------------------------- /apps/web/src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from 'next' 2 | import localFont from "next/font/local"; 3 | import "./index.scss" 4 | 5 | const inter = localFont({ 6 | src: "./Inter.woff2" 7 | }) 8 | 9 | export const metadata: Metadata = { 10 | title: 'Flowser', 11 | description: 'Supercharged development on Flow blockchain 🏄‍♂️ ⚡', 12 | } 13 | 14 | export default function RootLayout({ 15 | children, 16 | }: { 17 | children: React.ReactNode 18 | }) { 19 | return ( 20 | 21 | {children} 22 | 23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /apps/web/src/common/NetworkDropdown.tsx: -------------------------------------------------------------------------------- 1 | import { Menu } from "@onflowser/ui/src/common/overlays/Menu/Menu"; 2 | import { MenuItem } from "@szhsin/react-menu"; 3 | import { FlowNetworkId, FlowUtils } from "@onflowser/core/src/flow-utils"; 4 | import { FlowserIcon } from "@onflowser/ui/src/common/icons/FlowserIcon"; 5 | 6 | type NetworkDropdownProps = { 7 | value: FlowNetworkId; 8 | onChange: (network: FlowNetworkId) => void; 9 | } 10 | 11 | export function NetworkDropdown(props: NetworkDropdownProps) { 12 | const { value, onChange } = props; 13 | 14 | const supportedNetworks = FlowUtils.getValidFlowNetworks(); 15 | 16 | return ( 17 | 21 | {value} 22 | 23 | 24 | } 25 | > 26 | {supportedNetworks.map(network => ( 27 | { 30 | if (network !== value) { 31 | onChange(network) 32 | } 33 | }} 34 | className="capitalize" 35 | > 36 | {network} 37 | 38 | ))} 39 | 40 | ) 41 | } 42 | -------------------------------------------------------------------------------- /apps/web/src/common/interaction-page-params.ts: -------------------------------------------------------------------------------- 1 | import { FlowNetworkId } from "@onflowser/core/src/flow-utils"; 2 | 3 | // Must be placed in separate (type-only) file, 4 | // so that opengraph-image doesn't reach code size limit. 5 | export type InteractionsPageParams = { 6 | networkId: FlowNetworkId; 7 | interaction?: string; 8 | } 9 | -------------------------------------------------------------------------------- /apps/web/src/common/root-loader.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import dynamic from "next/dynamic"; 4 | import FullScreenLoading from "@onflowser/ui/src/common/loaders/FullScreenLoading/FullScreenLoading"; 5 | 6 | export function RootLoader() { 7 | // If we load Root component directly, 8 | // it will complain that window/document is not defined. 9 | // This is a temporary workaround for now, 10 | // see if we can avoid that in the first place. 11 | // Also see: https://github.com/mac-s-g/react-json-view/issues/296 12 | const Root = dynamic(() => import("./root"), { 13 | ssr: false, 14 | loading: () => , 15 | }); 16 | 17 | return 18 | } 19 | -------------------------------------------------------------------------------- /apps/web/src/common/use-interaction-page-params.ts: -------------------------------------------------------------------------------- 1 | import { useParams, useRouter } from "next/navigation"; 2 | import { FlowNetworkId, FlowUtils } from "@onflowser/core/src/flow-utils"; 3 | import { InteractionsPageParams } from "@/common/interaction-page-params"; 4 | 5 | type UseInteractionPageParams = InteractionsPageParams & { 6 | setNetworkId: (network: FlowNetworkId) => void; 7 | } 8 | 9 | export function useInteractionsPageParams(): UseInteractionPageParams { 10 | const params = useParams(); 11 | const router = useRouter(); 12 | 13 | const interaction = params.interaction as string | undefined; 14 | const networkId = params.networkId; 15 | 16 | if (!FlowUtils.isValidFlowNetwork(networkId)) { 17 | throw new Error(`Unknown Flow network: ${networkId}`) 18 | } 19 | 20 | function setNetworkId(network: FlowNetworkId) { 21 | if (interaction) { 22 | router.replace(`/${network}/${interaction}`) 23 | } else { 24 | router.replace(`/${network}`) 25 | } 26 | } 27 | 28 | return { 29 | networkId, 30 | interaction, 31 | setNetworkId 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /apps/web/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from 'tailwindcss' 2 | 3 | const config: Config = { 4 | content: [ 5 | './src/**/*.{js,ts,jsx,tsx,mdx}', 6 | ], 7 | theme: { 8 | extend: { 9 | backgroundImage: { 10 | 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))', 11 | 'gradient-conic': 12 | 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))', 13 | }, 14 | }, 15 | }, 16 | plugins: [], 17 | } 18 | export default config 19 | -------------------------------------------------------------------------------- /apps/web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2015", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "noEmit": true, 9 | "esModuleInterop": true, 10 | "module": "esnext", 11 | "moduleResolution": "bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "jsx": "preserve", 15 | "incremental": true, 16 | "plugins": [ 17 | { 18 | "name": "next" 19 | } 20 | ], 21 | "paths": { 22 | "@/*": ["./src/*"] 23 | } 24 | }, 25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 26 | "exclude": ["node_modules"] 27 | } 28 | -------------------------------------------------------------------------------- /internal/misc/address.go: -------------------------------------------------------------------------------- 1 | package misc 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/onflow/flow-go/model/flow" 7 | ) 8 | 9 | func GetAddressIndex(targetChainID string, hexAddress string) (uint64, error) { 10 | chainID, err := getChainID(targetChainID) 11 | 12 | if err != nil { 13 | return 0, err 14 | } 15 | 16 | flowAddress := flow.HexToAddress(hexAddress) 17 | 18 | index, err := chainID.Chain().IndexFromAddress(flowAddress) 19 | 20 | if err != nil { 21 | return 0, err 22 | } 23 | 24 | return index, nil 25 | } 26 | 27 | func getChainID(targetChainID string) (flow.ChainID, error) { 28 | chainIDs := []flow.ChainID{ 29 | flow.Mainnet, 30 | flow.Testnet, 31 | flow.Emulator, 32 | flow.Sandboxnet, 33 | } 34 | 35 | for _, chainID := range chainIDs { 36 | if chainID.String() == targetChainID { 37 | return chainID, nil 38 | } 39 | } 40 | 41 | return "", errors.New("chain ID not found") 42 | } 43 | -------------------------------------------------------------------------------- /packages/api/.prettierrc: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /packages/api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@onflowser/api", 3 | "version": "1.0.0", 4 | "files": [ 5 | "src" 6 | ], 7 | "scripts": { 8 | "format": "prettier . --write" 9 | }, 10 | "main": "./src/index.ts", 11 | "types": "./src/index.ts", 12 | "keywords": [], 13 | "author": "", 14 | "license": "ISC", 15 | "dependencies": { 16 | "@onflowser/config": "*" 17 | }, 18 | "devDependencies": { 19 | "prettier": "3.0.3", 20 | "typescript": "^4.3.5" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/api/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./capabilities"; 2 | export * from "./resources"; 3 | -------------------------------------------------------------------------------- /packages/api/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@onflowser/config/tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@onflowser/config", 3 | "version": "0.0.0", 4 | "private": true, 5 | "files": [ 6 | "tsconfig.json" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /packages/config/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["ES2015"], 4 | "strict": true, 5 | "module": "commonjs", 6 | "moduleResolution": "node", 7 | "declaration": true, 8 | "removeComments": true, 9 | "emitDecoratorMetadata": true, 10 | "experimentalDecorators": true, 11 | "allowSyntheticDefaultImports": true, 12 | "target": "es2017", 13 | "types": ["node", "jest"], 14 | "downlevelIteration": true, 15 | "sourceMap": true, 16 | "incremental": true, 17 | "skipLibCheck": true, 18 | "strictNullChecks": true, 19 | "noImplicitAny": true, 20 | "strictBindCallApply": true, 21 | "forceConsistentCasingInFileNames": true, 22 | "noFallthroughCasesInSwitch": true, 23 | "esModuleInterop": true, 24 | "allowJs": true, 25 | "resolveJsonModule": true, 26 | "isolatedModules": true 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/core/.prettierrc: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /packages/core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@onflowser/core", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "./src/index.ts", 6 | "types": "./src/index.ts", 7 | "scripts": { 8 | "format": "prettier . --write" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "@onflow/fcl": "^1.6.0", 15 | "@onflowser/api": "*", 16 | "@onflowser/config": "*", 17 | "axios": "^1.6.5", 18 | "axios-retry": "^4.0.0", 19 | "node-abort-controller": "^3.0.1", 20 | "js-sha3": "^0.8.0" 21 | }, 22 | "devDependencies": { 23 | "@types/jest": "^29.5.5", 24 | "@types/node": "^16.11.6", 25 | "prettier": "3.0.3", 26 | "typescript": "^4.3.5" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/core/src/flix-v1.ts: -------------------------------------------------------------------------------- 1 | // https://github.com/onflow/flips/blob/main/application/20220503-interaction-templates.md#interaction-interfaces 2 | import { FlowNetworkId } from "./flow-utils"; 3 | 4 | export type FlixV1Template = { 5 | id: string; 6 | f_type: "InteractionTemplate"; 7 | f_version: "1.0.0"; 8 | data: { 9 | messages: FlixV1Messages; 10 | dependencies: Record; 11 | cadence: string; 12 | arguments: Record; 13 | }; 14 | }; 15 | 16 | export type FlixV1Auditor = { 17 | f_type: "FlowInteractionTemplateAuditor"; 18 | f_version: string; 19 | address: string; 20 | name: string; 21 | twitter_url: string; 22 | website_url: string; 23 | } 24 | 25 | export type FlixV1Argument = { 26 | index: number; 27 | type: string; 28 | messages: FlixV1Messages; 29 | } 30 | 31 | type FlixV1Dependency = Record< 32 | string, 33 | // Only defined for `testnet` and `mainnet` networks. 34 | Record 35 | >; 36 | 37 | type FlixV1DependencyOnNetwork = { 38 | address: string; 39 | fq_address: string; 40 | pin: string; 41 | pin_block_height: number; 42 | }; 43 | 44 | type FlixV1Messages = { 45 | title?: FlixV1I18nMessage; 46 | description?: FlixV1I18nMessage; 47 | }; 48 | 49 | type FlixV1I18nMessage = { 50 | i18n: { 51 | "en-US"?: string; 52 | }; 53 | }; 54 | -------------------------------------------------------------------------------- /packages/core/src/flix-v11.ts: -------------------------------------------------------------------------------- 1 | // https://github.com/onflow/flips/blob/main/application/20220503-interaction-templates.md#interaction-interfaces 2 | import { FlowNetworkId } from "./flow-utils"; 3 | 4 | export type FlixV11Template = { 5 | id: string; 6 | f_type: "InteractionTemplate"; 7 | f_version: "1.1.0"; 8 | data: { 9 | messages: FlixV11Message[] | null; 10 | cadence: { 11 | body: string; 12 | network_pins: FlixV11NetworkPin[]; 13 | }; 14 | parameters: FlixV11Parameter[]; 15 | dependencies: { contracts: FlixV11ContractDependency[] }[] | null; 16 | }; 17 | }; 18 | 19 | export type FlixV11ContractDependency = { 20 | // Name of the contract (e.g. "EVM") 21 | contract: string; 22 | networks: FlixV11ContractNetwork[]; 23 | } 24 | 25 | export type FlixV11ContractNetwork = { 26 | network: FlowNetworkId; 27 | address: string; 28 | } 29 | 30 | export type FlixV11NetworkPin = { 31 | network: FlowNetworkId; 32 | pin_self: string; 33 | } 34 | 35 | export type FlixV11Message = { 36 | // Standard keys: "title", "description",.. 37 | key: string; 38 | i18n: FlixV11MessageI18n[]; 39 | } 40 | 41 | export type FlixV11MessageI18n = { 42 | // Language key (e.g. "en-US") 43 | tag: string; 44 | translation: string; 45 | } 46 | 47 | export type FlixV11Parameter = { 48 | label: string; 49 | index: 0; 50 | type: string; 51 | messages: FlixV11Message[]; 52 | } 53 | -------------------------------------------------------------------------------- /packages/core/src/flow-flix-v1.service.ts: -------------------------------------------------------------------------------- 1 | import { HttpService } from "./http.service"; 2 | import { FlixV1Template } from "./flix-v1"; 3 | 4 | type FlowFlixV1ServiceConfig = { 5 | flixServerUrl: string; 6 | } 7 | 8 | export class FlowFlixV1Service { 9 | constructor( 10 | private readonly config: FlowFlixV1ServiceConfig, 11 | private readonly httpService: HttpService 12 | ) {} 13 | 14 | async getById(id: string): Promise { 15 | const response = await this.httpService.request({ 16 | url: `${this.config.flixServerUrl}/v1/templates/${id}` 17 | }); 18 | 19 | if (response.status === 204) { 20 | return undefined; 21 | } else { 22 | return response.data; 23 | } 24 | } 25 | 26 | // TODO: Add other methods from use-flix.ts 27 | } 28 | -------------------------------------------------------------------------------- /packages/core/src/flow-interactions.service.ts: -------------------------------------------------------------------------------- 1 | import { ParsedInteractionOrError } from "@onflowser/api"; 2 | 3 | export interface IFlowInteractions { 4 | parse(sourceCode: string): Promise; 5 | } 6 | -------------------------------------------------------------------------------- /packages/core/src/http.service.ts: -------------------------------------------------------------------------------- 1 | import axios, { Method } from "axios"; 2 | import axiosRetry from 'axios-retry'; 3 | import { IFlowserLogger } from "./logger"; 4 | 5 | type RequestConfig = { 6 | url: string; 7 | method?: Method; 8 | } 9 | 10 | export class HttpService { 11 | constructor(private readonly logger: IFlowserLogger) {} 12 | 13 | public async isReachable(url: string) { 14 | try { 15 | await axios.request({ 16 | method: "GET", 17 | url, 18 | // Prevent axios from throwing on certain http response codes 19 | // https://github.com/axios/axios/issues/41 20 | validateStatus: () => true, 21 | }); 22 | // Assume that if response is received, the HTTP server is online. 23 | return true; 24 | } catch (error) { 25 | return false; 26 | } 27 | } 28 | 29 | public async request(config: RequestConfig) { 30 | const client = axios.create({ 31 | // Prevent axios from throwing on certain http response codes 32 | // https://github.com/axios/axios/issues/41 33 | validateStatus: () => true, 34 | }); 35 | 36 | axiosRetry(client, { 37 | retries: 4, 38 | retryDelay: axiosRetry.exponentialDelay, 39 | onRetry: retryCount => { 40 | this.logger.debug(`Retrying ${config.url} (${retryCount})`); 41 | } 42 | }); 43 | 44 | return client.request(config); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /packages/core/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./fcl-value"; 2 | export * from "./utils"; 3 | export * from "./flix-v1-utils"; 4 | export * from "./in-memory-index"; 5 | export * from "./logger"; 6 | 7 | export * from "./flow-gateway.service"; 8 | export * from "./flow-snapshots.service"; 9 | export * from "./flow-storage.service"; 10 | export * from "./flow-indexer.service"; 11 | export * from "./flow-interactions.service"; 12 | export * from "./flow-flix-v1.service"; 13 | export * from "./flix-v1"; 14 | -------------------------------------------------------------------------------- /packages/core/src/logger.ts: -------------------------------------------------------------------------------- 1 | export interface IFlowserLogger { 2 | error(message: any, error?: unknown): void; 3 | log(message: any): void; 4 | warn(message: any): void; 5 | debug(message: any): void; 6 | verbose(message: any): void; 7 | } 8 | -------------------------------------------------------------------------------- /packages/core/src/persistent-storage.ts: -------------------------------------------------------------------------------- 1 | export interface PersistentStorage { 2 | // Returns `undefined` if storage is empty. 3 | read(): Promise; 4 | write(data: string): Promise; 5 | } 6 | -------------------------------------------------------------------------------- /packages/core/src/utils.spec.ts: -------------------------------------------------------------------------------- 1 | import { computeEntitiesDiff } from "./utils"; 2 | 3 | describe("Utils", function () { 4 | it("should compute entities diff using deep compare", function () { 5 | const oldEntities = [ 6 | { 7 | createdAt: "2023-06-20T10:02:04.989Z", 8 | updatedAt: "2023-06-20T10:02:04.989Z", 9 | pathIdentifier: "test", 10 | pathDomain: 3, 11 | data: { value: "New Test" }, 12 | accountAddress: "0xf8d6e0586b0a20c7", 13 | id: "0xf8d6e0586b0a20c7/storage/test", 14 | }, 15 | ]; 16 | const newEntities = [ 17 | { 18 | createdAt: "2023-06-20T10:02:20.304Z", 19 | updatedAt: "2023-06-20T10:02:20.304Z", 20 | pathIdentifier: "test", 21 | pathDomain: 3, 22 | data: { value: "New Test Updated" }, 23 | accountAddress: "0xf8d6e0586b0a20c7", 24 | id: "0xf8d6e0586b0a20c7/storage/test", 25 | }, 26 | ]; 27 | const { created, updated, deleted } = computeEntitiesDiff({ 28 | primaryKey: "id", 29 | oldEntities: oldEntities, 30 | newEntities: newEntities, 31 | deepCompare: true, 32 | }); 33 | 34 | expect(created).toHaveLength(0); 35 | expect(updated).toHaveLength(1); 36 | expect(deleted).toHaveLength(0); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /packages/core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@onflowser/config/tsconfig.json", 3 | "include": ["src"], 4 | "exclude": ["dist", "node_modules"], 5 | "compilerOptions": { 6 | "outDir": "./dist" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/nodejs/.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | -------------------------------------------------------------------------------- /packages/nodejs/.prettierrc: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /packages/nodejs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@onflowser/nodejs", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "./src/index.ts", 6 | "types": "./src/index.ts", 7 | "scripts": { 8 | "dev": "sh build-go-bindings.sh --darwin", 9 | "build-web": "node install-go.js && sh build-go-bindings.sh --linux", 10 | "build-electron": "npm run build-bin --linux --darwin --windows", 11 | "format": "prettier . --write" 12 | }, 13 | "keywords": [], 14 | "files": [ 15 | "bin" 16 | ], 17 | "author": "", 18 | "license": "ISC", 19 | "dependencies": { 20 | "@onflow/fcl": "^1.6.0", 21 | "@onflowser/config": "*", 22 | "elliptic": "^6.5.4" 23 | }, 24 | "devDependencies": { 25 | "@types/elliptic": "^6.4.14", 26 | "@types/node": "^16.11.6", 27 | "prettier": "3.0.3", 28 | "typescript": "^4.3.5" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/nodejs/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./async-interval-scheduler"; 2 | export * from "./go-bindings.service"; 3 | export * from "./flow-interactions.service"; 4 | export * from "./flow-emulator.service"; 5 | export * from "./flow-dev-wallet.service"; 6 | export * from "./flow-config.service"; 7 | export * from "./flow-cli.service"; 8 | export * from "./wallet.service"; 9 | export * from "./processes/process-manager.service"; 10 | export * from "./processes/managed-process"; 11 | -------------------------------------------------------------------------------- /packages/ui/.prettierrc: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /packages/ui/src/accounts/AccountAvatar/avatars/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onflowser/flowser/1e42a42d186bb18cba216bf2e9c01275552e4d60/packages/ui/src/accounts/AccountAvatar/avatars/1.jpg -------------------------------------------------------------------------------- /packages/ui/src/accounts/AccountAvatar/avatars/10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onflowser/flowser/1e42a42d186bb18cba216bf2e9c01275552e4d60/packages/ui/src/accounts/AccountAvatar/avatars/10.jpg -------------------------------------------------------------------------------- /packages/ui/src/accounts/AccountAvatar/avatars/11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onflowser/flowser/1e42a42d186bb18cba216bf2e9c01275552e4d60/packages/ui/src/accounts/AccountAvatar/avatars/11.jpg -------------------------------------------------------------------------------- /packages/ui/src/accounts/AccountAvatar/avatars/12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onflowser/flowser/1e42a42d186bb18cba216bf2e9c01275552e4d60/packages/ui/src/accounts/AccountAvatar/avatars/12.jpg -------------------------------------------------------------------------------- /packages/ui/src/accounts/AccountAvatar/avatars/13.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onflowser/flowser/1e42a42d186bb18cba216bf2e9c01275552e4d60/packages/ui/src/accounts/AccountAvatar/avatars/13.jpg -------------------------------------------------------------------------------- /packages/ui/src/accounts/AccountAvatar/avatars/14.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onflowser/flowser/1e42a42d186bb18cba216bf2e9c01275552e4d60/packages/ui/src/accounts/AccountAvatar/avatars/14.jpg -------------------------------------------------------------------------------- /packages/ui/src/accounts/AccountAvatar/avatars/15.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onflowser/flowser/1e42a42d186bb18cba216bf2e9c01275552e4d60/packages/ui/src/accounts/AccountAvatar/avatars/15.jpg -------------------------------------------------------------------------------- /packages/ui/src/accounts/AccountAvatar/avatars/16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onflowser/flowser/1e42a42d186bb18cba216bf2e9c01275552e4d60/packages/ui/src/accounts/AccountAvatar/avatars/16.jpg -------------------------------------------------------------------------------- /packages/ui/src/accounts/AccountAvatar/avatars/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onflowser/flowser/1e42a42d186bb18cba216bf2e9c01275552e4d60/packages/ui/src/accounts/AccountAvatar/avatars/2.jpg -------------------------------------------------------------------------------- /packages/ui/src/accounts/AccountAvatar/avatars/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onflowser/flowser/1e42a42d186bb18cba216bf2e9c01275552e4d60/packages/ui/src/accounts/AccountAvatar/avatars/3.jpg -------------------------------------------------------------------------------- /packages/ui/src/accounts/AccountAvatar/avatars/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onflowser/flowser/1e42a42d186bb18cba216bf2e9c01275552e4d60/packages/ui/src/accounts/AccountAvatar/avatars/4.jpg -------------------------------------------------------------------------------- /packages/ui/src/accounts/AccountAvatar/avatars/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onflowser/flowser/1e42a42d186bb18cba216bf2e9c01275552e4d60/packages/ui/src/accounts/AccountAvatar/avatars/5.jpg -------------------------------------------------------------------------------- /packages/ui/src/accounts/AccountAvatar/avatars/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onflowser/flowser/1e42a42d186bb18cba216bf2e9c01275552e4d60/packages/ui/src/accounts/AccountAvatar/avatars/6.jpg -------------------------------------------------------------------------------- /packages/ui/src/accounts/AccountAvatar/avatars/7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onflowser/flowser/1e42a42d186bb18cba216bf2e9c01275552e4d60/packages/ui/src/accounts/AccountAvatar/avatars/7.jpg -------------------------------------------------------------------------------- /packages/ui/src/accounts/AccountAvatar/avatars/8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onflowser/flowser/1e42a42d186bb18cba216bf2e9c01275552e4d60/packages/ui/src/accounts/AccountAvatar/avatars/8.jpg -------------------------------------------------------------------------------- /packages/ui/src/accounts/AccountAvatar/avatars/9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onflowser/flowser/1e42a42d186bb18cba216bf2e9c01275552e4d60/packages/ui/src/accounts/AccountAvatar/avatars/9.jpg -------------------------------------------------------------------------------- /packages/ui/src/accounts/AccountAvatar/avatars/README.md: -------------------------------------------------------------------------------- 1 | ## Toy Faces Library - 3D Avatars 2 | 3 | from amritpaldesign.com 4 | 5 | Licenses Explained: https://amritpaldesign.com/licenses 6 | 7 | ## Thanks for your support ;) 8 | 9 | Please contact me if you have any questions via amrit@amritpaldesign.com 10 | -------------------------------------------------------------------------------- /packages/ui/src/accounts/AccountAvatar/avatars/service.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onflowser/flowser/1e42a42d186bb18cba216bf2e9c01275552e4d60/packages/ui/src/accounts/AccountAvatar/avatars/service.png -------------------------------------------------------------------------------- /packages/ui/src/accounts/AccountDetails/AccountDetails.module.scss: -------------------------------------------------------------------------------- 1 | @import "../../styles/spacings"; 2 | @import "../../styles/colors"; 3 | @import "../../styles/typography"; 4 | 5 | .root { 6 | display: flex; 7 | flex-direction: column; 8 | 9 | .header { 10 | display: flex; 11 | .detailsCard { 12 | flex: 3; 13 | } 14 | } 15 | 16 | .flowCurrency { 17 | color: $strong-green; 18 | margin-left: $spacing-s; 19 | } 20 | 21 | .tabContent { 22 | overflow: unset; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/ui/src/accounts/AccountKeysTable/AccountKeysTable.module.scss: -------------------------------------------------------------------------------- 1 | @import "../../styles/spacings"; 2 | @import "../../styles/colors"; 3 | @import "../../styles/typography"; 4 | 5 | .cellRoot { 6 | display: flex; 7 | flex-direction: column; 8 | row-gap: $spacing-base; 9 | font-size: $font-size-small; 10 | 11 | .keyWrapper { 12 | display: flex; 13 | flex-direction: row; 14 | justify-content: flex-start; 15 | align-items: center; 16 | column-gap: $spacing-base; 17 | } 18 | 19 | .badges { 20 | display: flex; 21 | column-gap: $spacing-base; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/ui/src/accounts/AccountLink/AccountLink.module.scss: -------------------------------------------------------------------------------- 1 | @import "../../styles/colors"; 2 | @import "../../styles/spacings"; 3 | 4 | .root { 5 | display: flex; 6 | align-items: center; 7 | column-gap: $spacing-s; 8 | } 9 | -------------------------------------------------------------------------------- /packages/ui/src/accounts/AccountName/AccountName.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactElement } from "react"; 2 | import { TextUtils } from "../../utils/text-utils"; 3 | import { useGetAddressNameInfo } from "../../api"; 4 | 5 | type AccountNameProps = { 6 | address: string; 7 | short?: boolean; 8 | className?: string; 9 | }; 10 | 11 | export function AccountName(props: AccountNameProps): ReactElement { 12 | const { address, short, className } = props; 13 | const { data: domainProfiles } = useGetAddressNameInfo(address); 14 | 15 | if (domainProfiles && domainProfiles.length > 0) { 16 | return ( 17 |
18 | {domainProfiles[0].name} 19 |
20 | ) 21 | } 22 | 23 | return ( 24 |
25 | {short ? TextUtils.shorten(address, 6) : address} 26 |
27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /packages/ui/src/accounts/AccountStorage/AccountStorage.module.scss: -------------------------------------------------------------------------------- 1 | .grid { 2 | width: 100%; 3 | max-width: 100%; 4 | display: grid; 5 | column-gap: 15px; 6 | row-gap: 20px; 7 | grid-template-columns: repeat(3, 1fr); 8 | margin-bottom: 40px; 9 | } 10 | 11 | .gridExtendable { 12 | width: 100%; 13 | max-width: 100%; 14 | display: grid; 15 | column-gap: 15px; 16 | row-gap: 20px; 17 | grid-template-columns: repeat(4, 1fr); 18 | 19 | .gridItemExtended { 20 | grid-column: 1 / span 4; 21 | height: 600px; 22 | } 23 | } 24 | 25 | @media all and (max-width: 1350px) { 26 | .gridExtendable { 27 | grid-template-columns: repeat(3, 1fr); 28 | 29 | .gridItemExtended { 30 | grid-column: 1 / span 3; 31 | height: 600px; 32 | } 33 | } 34 | } 35 | 36 | @media all and (max-width: 900px) { 37 | .grid { 38 | grid-template-columns: repeat(2, 1fr); 39 | } 40 | 41 | .gridExtendable { 42 | grid-template-columns: repeat(2, 1fr); 43 | 44 | .gridItemExtended { 45 | grid-column: 1 / span 2; 46 | height: 600px; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /packages/ui/src/accounts/AccountsTable/AccountsTable.module.scss: -------------------------------------------------------------------------------- 1 | @import "../../styles/spacings"; 2 | @import "../../styles/colors"; 3 | 4 | .tagsColumn { 5 | display: flex; 6 | column-gap: $spacing-base; 7 | .tag { 8 | border-color: $green-10; 9 | color: $green-10; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/ui/src/accounts/PublicPrivateStorageCard/gradient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onflowser/flowser/1e42a42d186bb18cba216bf2e9c01275552e4d60/packages/ui/src/accounts/PublicPrivateStorageCard/gradient.png -------------------------------------------------------------------------------- /packages/ui/src/accounts/StorageDataTypes/StorageDataTypes.module.scss: -------------------------------------------------------------------------------- 1 | @import "../../styles/spacings"; 2 | @import "../../styles/colors"; 3 | 4 | .root { 5 | display: flex; 6 | flex-wrap: wrap; 7 | overflow: hidden; 8 | row-gap: $spacing-s; 9 | column-gap: $spacing-s; 10 | 11 | .badge { 12 | margin-bottom: 8px; 13 | width: fit-content; 14 | border: 0.5px solid $purple; 15 | color: $gray-10; 16 | border-radius: 10px; 17 | padding: 2px 10px 2px 10px; 18 | font-size: 13px; 19 | transition: 0.3s; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/ui/src/accounts/StorageDomainBadge/StorageDomainBadge.module.scss: -------------------------------------------------------------------------------- 1 | @import "../../styles/spacings"; 2 | @import "../../styles/colors"; 3 | @import "../../styles/typography"; 4 | 5 | .root { 6 | border-radius: 8px; 7 | padding-left: 10px; 8 | padding-right: 10px; 9 | font-weight: 600; 10 | background: $gradient-1; 11 | color: $violet-100; 12 | font-size: $font-size-normal; 13 | text-transform: uppercase; 14 | width: fit-content; 15 | } 16 | -------------------------------------------------------------------------------- /packages/ui/src/accounts/StorageDomainBadge/StorageDomainBadge.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactElement } from "react"; 2 | import classes from "./StorageDomainBadge.module.scss"; 3 | import { FlowUtils } from "../../utils/flow-utils"; 4 | import { FlowStorageDomain } from "@onflowser/api"; 5 | 6 | export type StorageBadgeProps = { 7 | pathDomain: FlowStorageDomain; 8 | }; 9 | 10 | export function StorageDomainBadge({ 11 | pathDomain, 12 | }: StorageBadgeProps): ReactElement { 13 | return ( 14 |
15 | {FlowUtils.getLowerCasedPathDomain(pathDomain)} 16 |
17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /packages/ui/src/assets.d.ts: -------------------------------------------------------------------------------- 1 | type Styles = Record; 2 | 3 | declare module "*.svg" { 4 | import React = require("react"); 5 | 6 | export const ReactComponent: React.FC>; 7 | 8 | const content: string; 9 | export default content; 10 | } 11 | 12 | declare module "*.png" { 13 | const content: string; 14 | export default content; 15 | } 16 | 17 | declare module "*.jpg" { 18 | const content: string; 19 | export default content; 20 | } 21 | 22 | declare module "*.scss" { 23 | const content: Styles; 24 | export default content; 25 | } 26 | 27 | declare module "*.sass" { 28 | const content: Styles; 29 | export default content; 30 | } 31 | 32 | declare module "*.css" { 33 | const content: Styles; 34 | export default content; 35 | } 36 | -------------------------------------------------------------------------------- /packages/ui/src/assets/long_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onflowser/flowser/1e42a42d186bb18cba216bf2e9c01275552e4d60/packages/ui/src/assets/long_logo.png -------------------------------------------------------------------------------- /packages/ui/src/blocks/BlockDetails/BlockDetails.module.scss: -------------------------------------------------------------------------------- 1 | @import "../../styles/spacings"; 2 | 3 | .root { 4 | display: flex; 5 | flex-direction: column; 6 | } 7 | -------------------------------------------------------------------------------- /packages/ui/src/blocks/BlockLink/BlockLink.module.scss: -------------------------------------------------------------------------------- 1 | .ellipsis { 2 | max-width: 13vw; 3 | display: inline-block; 4 | overflow: hidden; 5 | } 6 | -------------------------------------------------------------------------------- /packages/ui/src/blocks/BlocksTable/BlocksTable.module.scss: -------------------------------------------------------------------------------- 1 | @import "../../styles/spacings"; 2 | @import "../../styles/animations"; 3 | 4 | .card { 5 | display: flex; 6 | justify-content: space-between; 7 | 8 | & > div { 9 | display: flex; 10 | flex-direction: column; 11 | } 12 | 13 | .hash { 14 | max-width: 20vw; 15 | min-width: 20vw; 16 | display: inline-block; 17 | overflow: hidden; 18 | } 19 | } 20 | 21 | .hash { 22 | max-width: 21vw; 23 | min-width: 10vw; 24 | display: inline-block; 25 | } 26 | 27 | .tableRow { 28 | .blockHeight { 29 | flex: 0.8; 30 | } 31 | 32 | .blockID { 33 | flex: 1.3; 34 | } 35 | 36 | .blockSeals { 37 | flex: 0.8; 38 | } 39 | 40 | .signatures { 41 | flex: 0.8; 42 | } 43 | 44 | .snapshot { 45 | flex: 1.2; 46 | } 47 | 48 | .time { 49 | flex: 0.8; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /packages/ui/src/common/buttons/ActionButton/ActionButton.module.scss: -------------------------------------------------------------------------------- 1 | @import "../../../styles/spacings"; 2 | @import "../../../styles/colors"; 3 | @import "../../../styles/typography"; 4 | 5 | .root { 6 | display: flex; 7 | padding: $spacing-base * 0.6 0; 8 | align-items: center; 9 | 10 | &:hover { 11 | .header { 12 | color: $grey; 13 | } 14 | .icon { 15 | transform: scale(1.05); 16 | } 17 | } 18 | 19 | .mainWrapper { 20 | display: flex; 21 | flex-direction: column; 22 | margin-left: $spacing-s; 23 | align-items: flex-start; 24 | } 25 | 26 | .header { 27 | transition: 0.3s; 28 | color: $gray-10; 29 | } 30 | 31 | .footer { 32 | color: $gray-10; 33 | opacity: 0.5; 34 | font-size: $font-size-small; 35 | } 36 | 37 | .icon { 38 | height: 30px; 39 | width: 30px; 40 | transition: 0.2s ease; 41 | 42 | & > * { 43 | width: 100%; 44 | height: 100%; 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /packages/ui/src/common/buttons/ActionButton/ActionButton.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactElement } from "react"; 2 | import { SimpleButton } from "../SimpleButton/SimpleButton"; 3 | import classes from "./ActionButton.module.scss"; 4 | 5 | export type ActionButtonProps = { 6 | onClick: () => void; 7 | title: string; 8 | footer?: string; 9 | icon: ReactElement; 10 | }; 11 | 12 | export function ActionButton({ 13 | onClick, 14 | title, 15 | footer, 16 | icon, 17 | }: ActionButtonProps): ReactElement { 18 | return ( 19 | 20 |
{icon}
21 |
22 |
{title}
23 | {footer && {footer}} 24 |
25 |
26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /packages/ui/src/common/buttons/CopyButton/CopyButton.module.scss: -------------------------------------------------------------------------------- 1 | .root { 2 | cursor: pointer; 3 | display: inline-flex; 4 | } 5 | -------------------------------------------------------------------------------- /packages/ui/src/common/buttons/CopyButton/CopyButton.tsx: -------------------------------------------------------------------------------- 1 | import React, { FunctionComponent, useCallback } from "react"; 2 | import copy from "copy-to-clipboard"; 3 | import toast from "react-hot-toast"; 4 | import classes from "./CopyButton.module.scss"; 5 | import { FlowserIcon } from "../../icons/FlowserIcon"; 6 | 7 | export type CopyButtonProps = { 8 | value: string; 9 | }; 10 | 11 | export const CopyButton: FunctionComponent = ({ value }) => { 12 | const MAX_CHARS_DISPLAY = 60; 13 | 14 | const displayValue = 15 | value.length > MAX_CHARS_DISPLAY 16 | ? value.substr(0, MAX_CHARS_DISPLAY) + "..." 17 | : value; 18 | 19 | const copyToClipboard = useCallback(() => { 20 | copy(value); 21 | toast(`"${displayValue}" copied to your clipboard`); 22 | }, []); 23 | return ( 24 | 25 | 26 | 27 | ); 28 | }; 29 | -------------------------------------------------------------------------------- /packages/ui/src/common/buttons/IconButton/IconButton.module.scss: -------------------------------------------------------------------------------- 1 | @import "../../../styles/spacings"; 2 | 3 | .root { 4 | position: relative; 5 | padding: $spacing-s $spacing-base; 6 | 7 | .before, 8 | .after { 9 | display: flex; 10 | align-items: center; 11 | } 12 | 13 | .before svg { 14 | margin-right: $spacing-base; 15 | } 16 | 17 | .after svg { 18 | margin-left: $spacing-base; 19 | } 20 | 21 | .afterEnd svg { 22 | position: absolute; 23 | right: $spacing-l; 24 | top: 40%; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/ui/src/common/buttons/IconButton/IconButton.tsx: -------------------------------------------------------------------------------- 1 | import React, { FunctionComponent, ReactElement } from "react"; 2 | import Button, { ButtonProps } from "../Button/Button"; 3 | import classes from "./IconButton.module.scss"; 4 | import classNames from "classnames"; 5 | 6 | interface IconButtonProps extends ButtonProps { 7 | icon: ReactElement; 8 | iconPosition?: "before" | "after" | "after-end"; 9 | } 10 | 11 | const IconButton: FunctionComponent = ({ 12 | icon, 13 | iconPosition = "before", 14 | ...restProps 15 | }) => { 16 | let children = icon; 17 | if (restProps.children) { 18 | if (iconPosition === "before") { 19 | children = ( 20 | 21 | {icon} {restProps.children} 22 | 23 | ); 24 | } else if (iconPosition === "after-end") { 25 | children = ( 26 | 27 | {restProps.children} {icon} 28 | 29 | ); 30 | } else { 31 | children = ( 32 | 33 | {restProps.children} {icon} 34 | 35 | ); 36 | } 37 | } 38 | return ( 39 | 45 | ); 46 | }; 47 | 48 | export default IconButton; 49 | -------------------------------------------------------------------------------- /packages/ui/src/common/buttons/LoaderButton/LoaderButton.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactElement, useState } from "react"; 2 | import { SimpleButtonProps } from "../SimpleButton/SimpleButton"; 3 | import { Spinner } from "../../loaders/Spinner/Spinner"; 4 | import { PrimaryButton } from "../PrimaryButton/PrimaryButton"; 5 | 6 | type LoaderButtonProps = Omit & { 7 | onClick: () => Promise; 8 | loadingContent?: string; 9 | }; 10 | 11 | export function LoaderButton(props: LoaderButtonProps): ReactElement { 12 | const { 13 | onClick, 14 | loadingContent = "Loading", 15 | children, 16 | ...simpleButtonProps 17 | } = props; 18 | const [isLoading, setIsLoading] = useState(false); 19 | 20 | return ( 21 | { 24 | try { 25 | setIsLoading(true); 26 | await onClick(); 27 | } finally { 28 | setIsLoading(false); 29 | } 30 | }} 31 | > 32 | {isLoading ? ( 33 |
36 | 37 | {loadingContent} 38 |
39 | ) : ( 40 | children 41 | )} 42 |
43 | ); 44 | } 45 | -------------------------------------------------------------------------------- /packages/ui/src/common/buttons/PrimaryButton/PrimaryButton.module.scss: -------------------------------------------------------------------------------- 1 | @import "../../../styles/colors"; 2 | @import "../../../styles/rules"; 3 | @import "../../../styles/spacings"; 4 | @import "../../../styles/typography"; 5 | 6 | .root { 7 | flex: 1; 8 | padding: $spacing-base; 9 | background: $violet-100; 10 | color: white; 11 | font-weight: bold; 12 | font-size: $font-size-large; 13 | border-radius: $border-radius-button; 14 | &[disabled] { 15 | background: $gray-50; 16 | cursor: not-allowed; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/ui/src/common/buttons/PrimaryButton/PrimaryButton.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactElement } from "react"; 2 | import classNames from "classnames"; 3 | import { SimpleButton, SimpleButtonProps } from "../SimpleButton/SimpleButton"; 4 | import classes from "./PrimaryButton.module.scss"; 5 | 6 | type PrimaryButtonProps = SimpleButtonProps; 7 | 8 | export function PrimaryButton(props: PrimaryButtonProps): ReactElement { 9 | return ( 10 | 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /packages/ui/src/common/buttons/RadioButton/RadioButton.module.scss: -------------------------------------------------------------------------------- 1 | @import "../../../styles/colors"; 2 | 3 | .root { 4 | width: 16px; 5 | height: 16px; 6 | border: 2px solid $gray-30; 7 | border-radius: 50%; 8 | cursor: pointer; 9 | display: flex; 10 | justify-content: center; 11 | align-items: center; 12 | 13 | &.unchecked { 14 | & > span { 15 | visibility: hidden; 16 | } 17 | } 18 | 19 | &.checked { 20 | & > span { 21 | display: block; 22 | visibility: visible; 23 | background-color: $gray-10; 24 | border-radius: 50%; 25 | width: 10px; 26 | height: 10px; 27 | } 28 | } 29 | 30 | &.disabled { 31 | & > span { 32 | background-color: $color-grey; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/ui/src/common/buttons/RadioButton/RadioButton.tsx: -------------------------------------------------------------------------------- 1 | import React, { FunctionComponent, useEffect, useState } from "react"; 2 | import classes from "./RadioButton.module.scss"; 3 | import classNames from "classnames"; 4 | 5 | type RadioButtonProps = { 6 | checked: boolean; 7 | disabled?: boolean; 8 | onChange: (checked: boolean) => void; 9 | }; 10 | 11 | const RadioButton: FunctionComponent = ({ 12 | checked = false, 13 | disabled = false, 14 | onChange, 15 | }) => { 16 | const [isChecked, setIsChecked] = useState(checked); 17 | 18 | useEffect(() => { 19 | setIsChecked(checked); 20 | }, [checked]); 21 | 22 | const onClick = () => { 23 | if (!disabled) { 24 | setIsChecked(true); 25 | onChange(isChecked); 26 | } 27 | }; 28 | 29 | return ( 30 |
38 | 39 |
40 | ); 41 | }; 42 | 43 | export default RadioButton; 44 | -------------------------------------------------------------------------------- /packages/ui/src/common/buttons/SimpleButton/SimpleButton.module.scss: -------------------------------------------------------------------------------- 1 | @import "../../../styles/colors"; 2 | @import "../../../styles/typography"; 3 | 4 | .root { 5 | background: none; 6 | border: none; 7 | cursor: pointer; 8 | color: $blue; 9 | font-size: $font-size-normal; 10 | } 11 | -------------------------------------------------------------------------------- /packages/ui/src/common/buttons/SimpleButton/SimpleButton.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactElement } from "react"; 2 | import classes from "./SimpleButton.module.scss"; 3 | import classNames from "classnames"; 4 | 5 | export type SimpleButtonProps = React.ButtonHTMLAttributes; 6 | 7 | export function SimpleButton(props: SimpleButtonProps): ReactElement { 8 | return ( 9 |