Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Beta braavos mobile #151

Merged
merged 6 commits into from
Nov 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
branches:
- develop
- main
- beta-braavos-mobile
- hotfix\/v[0-9]+.[0-9]+.[0-9]+

jobs:
Expand Down
4 changes: 4 additions & 0 deletions .releaserc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
{
"name": "beta",
"prerelease": true
},
{
"name": "beta-braavos-mobile",
"prerelease": true
}
],
"plugins": [
Expand Down
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "starknetkit",
"version": "2.3.4",
"version": "2.4.0-beta-braavos-mobile.1",
"repository": "github:argentlabs/starknetkit",
"private": false,
"browser": {
Expand Down Expand Up @@ -37,6 +37,11 @@
"import": "./dist/argentMobile.js",
"require": "./dist/argentMobile.cjs"
},
"./braavosMobile": {
"types": "./dist/braavosMobile.d.ts",
"import": "./dist/braavosMobile.js",
"require": "./dist/braavosMobile.cjs"
},
"./injected": {
"types": "./dist/injectedConnector.d.ts",
"import": "./dist/injectedConnector.js",
Expand Down
39 changes: 39 additions & 0 deletions src/connectors/braavosMobile/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
export const BRAAVOS_MOBILE_APP_ICON = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="32px" height="32px" viewBox="0 0 32 32" version="1.1">
<defs>
<clipPath id="clip1">
<path d="M 5.332031 0 L 26.667969 0 C 29.613281 0 32 2.386719 32 5.332031 L 32 26.667969 C 32 29.613281 29.613281 32 26.667969 32 L 5.332031 32 C 2.386719 32 0 29.613281 0 26.667969 L 0 5.332031 C 0 2.386719 2.386719 0 5.332031 0 Z M 5.332031 0 "/>
</clipPath>
<linearGradient id="linear0" gradientUnits="userSpaceOnUse" x1="-9.552" y1="-6.444" x2="50.240002" y2="41.303001" gradientTransform="matrix(0.666667,0,0,0.666667,0,0)">
<stop offset="0" style="stop-color:rgb(3.529412%,45.09804%,75.294119%);stop-opacity:1;"/>
<stop offset="1" style="stop-color:rgb(10.980392%,28.627452%,87.058824%);stop-opacity:1;"/>
</linearGradient>
<clipPath id="clip2">
<path d="M 7 4 L 25 4 L 25 15 L 7 15 Z M 7 4 "/>
</clipPath>
<clipPath id="clip3">
<path d="M 5.332031 0 L 26.667969 0 C 29.613281 0 32 2.386719 32 5.332031 L 32 26.667969 C 32 29.613281 29.613281 32 26.667969 32 L 5.332031 32 C 2.386719 32 0 29.613281 0 26.667969 L 0 5.332031 C 0 2.386719 2.386719 0 5.332031 0 Z M 5.332031 0 "/>
</clipPath>
<clipPath id="clip4">
<path d="M 6 13 L 26 13 L 26 28 L 6 28 Z M 6 13 "/>
</clipPath>
<clipPath id="clip5">
<path d="M 5.332031 0 L 26.667969 0 C 29.613281 0 32 2.386719 32 5.332031 L 32 26.667969 C 32 29.613281 29.613281 32 26.667969 32 L 5.332031 32 C 2.386719 32 0 29.613281 0 26.667969 L 0 5.332031 C 0 2.386719 2.386719 0 5.332031 0 Z M 5.332031 0 "/>
</clipPath>
</defs>
<g id="surface1">
<g clip-path="url(#clip1)" clip-rule="nonzero">
<rect x="0" y="0" width="32" height="32" style="fill:url(#linear0);stroke:none;"/>
</g>
<g clip-path="url(#clip2)" clip-rule="nonzero">
<g clip-path="url(#clip3)" clip-rule="nonzero">
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;" d="M 19.507812 6.320312 C 19.542969 6.378906 19.496094 6.453125 19.425781 6.453125 C 18.027344 6.453125 16.886719 7.5625 16.859375 8.941406 C 16.375 8.851562 15.878906 8.847656 15.390625 8.929688 C 15.359375 7.554688 14.222656 6.453125 12.824219 6.453125 C 12.757812 6.453125 12.710938 6.378906 12.746094 6.320312 C 13.453125 5.128906 14.738281 4.398438 16.125 4.398438 C 17.515625 4.394531 18.800781 5.125 19.507812 6.320312 M 23.65625 14.847656 C 24.132812 14.992188 24.585938 14.574219 24.402344 14.109375 C 23.066406 10.65625 19.109375 9.242188 16.105469 9.242188 C 13.097656 9.242188 9.054688 10.703125 7.804688 14.128906 C 7.632812 14.589844 8.082031 14.996094 8.550781 14.851562 L 15.75 12.648438 C 15.957031 12.582031 16.179688 12.582031 16.386719 12.644531 Z M 23.65625 14.847656 "/>
</g>
</g>
<g clip-path="url(#clip4)" clip-rule="nonzero">
<g clip-path="url(#clip5)" clip-rule="nonzero">
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;" d="M 7.734375 15.511719 L 15.757812 13.078125 C 15.964844 13.015625 16.183594 13.015625 16.390625 13.078125 L 24.464844 15.511719 C 25.136719 15.714844 25.597656 16.335938 25.597656 17.042969 L 25.597656 24.378906 C 25.566406 26.167969 23.960938 27.605469 22.167969 27.605469 L 19.195312 27.605469 C 19.054688 27.605469 18.917969 27.550781 18.820312 27.449219 C 18.71875 27.347656 18.664062 27.214844 18.664062 27.074219 L 18.664062 24.5 C 18.664062 23.488281 19.261719 22.570312 20.191406 22.164062 C 21.433594 21.621094 22.90625 20.886719 23.183594 19.410156 C 23.269531 18.933594 22.960938 18.472656 22.484375 18.378906 C 21.285156 18.140625 19.953125 18.230469 18.824219 18.734375 C 17.546875 19.308594 17.210938 20.257812 17.085938 21.539062 L 16.9375 22.914062 C 16.890625 23.335938 16.496094 23.65625 16.074219 23.65625 C 15.636719 23.65625 15.308594 23.324219 15.261719 22.886719 L 15.113281 21.539062 C 15.007812 20.441406 14.8125 19.378906 13.683594 18.875 C 12.390625 18.296875 11.09375 18.105469 9.714844 18.378906 C 9.238281 18.472656 8.929688 18.933594 9.015625 19.410156 C 9.296875 20.898438 10.757812 21.617188 12.007812 22.164062 C 12.9375 22.570312 13.535156 23.488281 13.535156 24.5 L 13.535156 27.074219 C 13.535156 27.367188 13.296875 27.605469 13.003906 27.605469 L 10.03125 27.605469 C 8.238281 27.605469 6.628906 26.167969 6.597656 24.378906 L 6.597656 17.039062 C 6.597656 16.335938 7.058594 15.714844 7.734375 15.511719 "/>
</g>
</g>
</g>
</svg>
`
14 changes: 14 additions & 0 deletions src/connectors/braavosMobile/helpers/inAppBrowser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export const isInBraavosMobileAppBrowser = (): boolean => {
if (typeof window === "undefined") {
return false
}

const userAgent = navigator.userAgent.toLowerCase()
const isBraavosMobileApp = userAgent.includes("braavos")

if (!isBraavosMobileApp) {
return false
}

return isBraavosMobileApp
}
1 change: 1 addition & 0 deletions src/connectors/braavosMobile/helpers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./inAppBrowser"
117 changes: 117 additions & 0 deletions src/connectors/braavosMobile/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import { type AccountChangeEventHandler } from "@starknet-io/get-starknet-core"
import {
RequestFnCall,
RpcMessage,
RpcTypeToMessageMap,
type StarknetWindowObject,
} from "@starknet-io/types-js"
import { AccountInterface, ProviderInterface, ProviderOptions } from "starknet"
import {
Connector,
type ConnectorData,
type ConnectorIcons,
} from "../connector"
import { InjectedConnector, InjectedConnectorOptions } from "../injected"
import { isInBraavosMobileAppBrowser } from "./helpers/inAppBrowser"
import { BRAAVOS_MOBILE_APP_ICON } from "./constants"

export class BraavosMobileBaseConnector extends Connector {
private _wallet: StarknetWindowObject | null = null

constructor() {
super()
}

available(): boolean {
return true
}

async ready(): Promise<boolean> {
// return true to be compatible with starknet-react
// will need to be implemented
return true
}

get id(): string {
return "braavosMobile"
}

get name(): string {
return "Braavos (mobile)"
}

get icon(): ConnectorIcons {
return {
dark: BRAAVOS_MOBILE_APP_ICON,
light: BRAAVOS_MOBILE_APP_ICON,
}
}

get wallet(): StarknetWindowObject {
throw new Error("not implemented")
}

async connect(): Promise<ConnectorData> {
await this.ensureWallet()

// will return empty data, connect will only open braavos mobile app
// will require to implement the wallet connection
return {
account: "",
chainId: BigInt(0),
}
}

async disconnect(): Promise<void> {
throw new Error("not implemented")
}

async account(
_: ProviderOptions | ProviderInterface,
): Promise<AccountInterface> {
throw new Error("not implemented")
}

async chainId(): Promise<bigint> {
throw new Error("not implemented")
}

async request<T extends RpcMessage["type"]>(
call: RequestFnCall<T>,
): Promise<RpcTypeToMessageMap[T]["result"]> {
throw new Error("not implemented")
}

// needed, methods required by starknet-react. Otherwise an exception is throwd
async initEventListener(_: AccountChangeEventHandler) {
throw new Error("not implemented")
}

// needed, methods required by starknet-react. Otherwise an exception is throwd
async removeEventListener(_: AccountChangeEventHandler) {
throw new Error("not implemented")
}

private async ensureWallet(): Promise<void> {
window.open(`https://link.braavos.app/dapp/${window.origin}`, "_blank")
}
}

export interface BraavosMobileConnectorInitParams {
inAppBrowserOptions?: Omit<InjectedConnectorOptions, "id">
}

export class BraavosMobileConnector {
static init(params?: BraavosMobileConnectorInitParams): Connector {
const { inAppBrowserOptions } = params || {}
if (isInBraavosMobileAppBrowser()) {
return new InjectedConnector({
options: { id: "braavos", ...inAppBrowserOptions },
})
} else {
return new BraavosMobileBaseConnector()
}
}
}

export { isInBraavosMobileAppBrowser }
19 changes: 19 additions & 0 deletions src/helpers/defaultConnectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,25 @@ import {
ArgentMobileBaseConnector,
type ArgentMobileConnectorOptions,
} from "../connectors/argentMobile"
import { BraavosMobileBaseConnector } from "../connectors/braavosMobile"
import { InjectedConnector } from "../connectors/injected"
import { WebWalletConnector } from "../connectors/webwallet"

const isMobileDevice = () => {
// Primary method: User Agent + Touch support check
const userAgent = navigator.userAgent.toLowerCase()
const isMobileUA =
/android|webos|iphone|ipad|ipod|blackberry|windows phone/.test(userAgent)
const hasTouchSupport =
"ontouchstart" in window || navigator.maxTouchPoints > 0

// Backup method: Screen size
const isSmallScreen = window.innerWidth <= 768

// Combine checks: Must match user agent AND (touch support OR small screen)
return isMobileUA && (hasTouchSupport || isSmallScreen)
}

export const defaultConnectors = ({
argentMobileOptions,
webWalletUrl,
Expand All @@ -30,6 +46,9 @@ export const defaultConnectors = ({
}

defaultConnectors.push(new ArgentMobileBaseConnector(argentMobileOptions))
if (isMobileDevice()) {
defaultConnectors.push(new BraavosMobileBaseConnector())
}
defaultConnectors.push(new WebWalletConnector({ url: webWalletUrl }))

return defaultConnectors
Expand Down
10 changes: 10 additions & 0 deletions src/modal/Modal.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
}
let isInAppBrowser = starknetMobile?.isInAppBrowser

const userAgent = navigator.userAgent.toLowerCase()
const isBraavosMobileApp = userAgent.includes("braavos")

const setLoadingItem = (item: string | false) => {
loadingItem = item
}
Expand Down Expand Up @@ -53,6 +56,13 @@
return
}

if (isBraavosMobileApp && window?.starknet_braavos) {
try {
callback(new InjectedConnector({ options: { id: "braavos" } }))
} catch {}
return
}

if (modalWallets.length === 1) {
try {
const [wallet] = modalWallets
Expand Down
4 changes: 4 additions & 0 deletions vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ export default defineConfig({
__dirname,
"src/connectors/argentMobile/index.ts",
),
braavosMobile: resolve(
__dirname,
"src/connectors/braavosMobile/index.ts",
),
injectedConnector: resolve(
__dirname,
"src/connectors/injected/index.ts",
Expand Down