diff --git a/.github/workflows/pull_requests.yml b/.github/workflows/pull_requests.yml new file mode 100644 index 0000000..0e36e96 --- /dev/null +++ b/.github/workflows/pull_requests.yml @@ -0,0 +1,39 @@ +name: PR build +on: [pull_request] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: 🏗 Setup repo + uses: actions/checkout@v3 + + - name: Set up Java + uses: actions/setup-java@v2 + with: + java-version: 17 + distribution: "temurin" + + - name: 🏗 Setup Node + uses: actions/setup-node@v3 + with: + node-version: 18.x + cache: yarn + + - name: 🏗 Setup EAS + uses: expo/expo-github-action@v8 + with: + eas-version: latest + token: ${{ secrets.EXPO_TOKEN }} + + - name: 📦 Install dependencies + run: yarn install + + - name: 🚀 Build app + run: eas build --non-interactive --platform android --local --profile production_apk --output=./app-release.apk + + - name: Upload Artifact + uses: actions/upload-artifact@v2 + with: + name: alby-go-android.apk + path: ./app-release.apk diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..9cb1f76 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,85 @@ +name: Release tag + +on: + push: + tags: + - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 + +jobs: + draft_release: + runs-on: ubuntu-latest + outputs: + upload_url: ${{ steps.create_release.outputs.upload_url }} + id: ${{ steps.create_release.outputs.id }} + steps: + # Create Release + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: Release ${{ github.ref }} + draft: true +# prerelease: true + + build_android: + needs: [draft_release] + runs-on: ubuntu-latest + steps: + - name: 🏗 Setup repo + uses: actions/checkout@v3 + + - name: Set up Java + uses: actions/setup-java@v2 + with: + java-version: 17 + distribution: "temurin" + + - name: 🏗 Setup Node + uses: actions/setup-node@v3 + with: + node-version: 18.x + cache: yarn + + - name: 🏗 Setup EAS + uses: expo/expo-github-action@v8 + with: + eas-version: latest + token: ${{ secrets.EXPO_TOKEN }} + + - name: 📦 Install dependencies + run: yarn install + + - name: 🚀 Build app + run: eas build --non-interactive --platform android --local --profile production_apk --output=./app-release.apk + + - name: Upload Artifact + uses: actions/upload-artifact@v2 + with: + name: alby-go-android.apk + path: ./app-release.apk + + # APK + - name: Upload APK to release + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ needs.draft_release.outputs.upload_url }} + asset_path: ./app-release.apk + asset_name: alby-go-${{ github.ref_name }}-android.apk + asset_content_type: application/vnd.android.package-archive + +# publish-release: +# needs: [release, build_android] +# runs-on: ubuntu-latest +# +# steps: +# - uses: eregon/publish-release@v1 +# env: +# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} +# with: +# release_id: ${{ needs.release.outputs.id }} +# diff --git a/README.md b/README.md index 538a72d..5e4e635 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Alby Mobile +# Alby Go A simple lightning mobile wallet interface that works great with [Alby Hub](https://albyhub.com) diff --git a/app.json b/app.json index a0a78ea..478c9ce 100644 --- a/app.json +++ b/app.json @@ -1,6 +1,6 @@ { "expo": { - "name": "Alby Mobile", + "name": "Alby Go", "slug": "alby-mobile", "version": "1.3.0", "scheme": [ @@ -23,7 +23,7 @@ [ "expo-camera", { - "cameraPermission": "Allow Alby Mobile to use the camera to scan wallet connection and payment QR codes", + "cameraPermission": "Allow Alby Go to use the camera to scan wallet connection and payment QR codes", "recordAudioAndroid": false } ], diff --git a/components/Screen.tsx b/components/Screen.tsx new file mode 100644 index 0000000..c82b444 --- /dev/null +++ b/components/Screen.tsx @@ -0,0 +1,21 @@ +import { Stack } from "expo-router"; +import { StackAnimationTypes } from "react-native-screens"; +import { HeaderButtonProps } from "@react-navigation/native-stack/src/types"; + +type ScreenProps = { + title: string; + right?: (props: HeaderButtonProps) => React.ReactNode; + animation?: StackAnimationTypes; +}; + +function Screen({ title, animation, right }: ScreenProps) { + return ; +} + +export default Screen; \ No newline at end of file diff --git a/components/icons/AppIcon.tsx b/components/icons/AppIcon.tsx deleted file mode 100644 index 3559dbb..0000000 --- a/components/icons/AppIcon.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import * as React from "react"; -import Svg, { SvgProps, Path } from "react-native-svg"; -import { useColorScheme } from "~/lib/useColorScheme"; - -const AppIcon = (props: SvgProps) => { - const { isDarkColorScheme } = useColorScheme(); - const fill = isDarkColorScheme ? "white" : "black"; - - return ( - - - - - ); -}; -export default AppIcon; diff --git a/components/ui/button.tsx b/components/ui/button.tsx index e64192d..027216e 100644 --- a/components/ui/button.tsx +++ b/components/ui/button.tsx @@ -6,7 +6,7 @@ import { TextClassContext } from "~/components/ui/text"; import { cn } from "~/lib/utils"; const buttonVariants = cva( - "group flex items-center justify-center rounded-md web:ring-offset-background web:transition-colors web:focus-visible:outline-none web:focus-visible:ring-2 web:focus-visible:ring-ring web:focus-visible:ring-offset-2", + "group flex items-center justify-center rounded-lg web:ring-offset-background web:transition-colors web:focus-visible:outline-none web:focus-visible:ring-2 web:focus-visible:ring-ring web:focus-visible:ring-offset-2", { variants: { variant: { @@ -22,7 +22,7 @@ const buttonVariants = cva( size: { default: "min-h-10 px-4 py-2 native:min-h-12 native:px-5 native:py-3", sm: "min-h-9 rounded-md px-3", - lg: "min-h-11 rounded-md px-8 native:min-h-16", + lg: "min-h-11 rounded-lg px-8 native:min-h-16", icon: "min-h-10 min-w-10", }, }, diff --git a/eas.json b/eas.json index 05ee6fd..47e976b 100644 --- a/eas.json +++ b/eas.json @@ -10,7 +10,12 @@ "preview": { "distribution": "internal" }, - "production": {} + "production": {}, + "production_apk": { + "android": { + "buildType": "apk" + } + } }, "submit": { "production": {} diff --git a/hooks/useGetFiatAmount.ts b/hooks/useGetFiatAmount.ts index 2ed68ed..b93bcd2 100644 --- a/hooks/useGetFiatAmount.ts +++ b/hooks/useGetFiatAmount.ts @@ -38,8 +38,10 @@ export function useGetFiatAmount() { return `${new Intl.NumberFormat(undefined, { style: "currency", currency: fiatCurrency, + currencyDisplay: "narrowSymbol", }).format(rate * amount)}`; } + const amountWithCurrencyCode = new Intl.NumberFormat("en-US", { style: "currency", currency: fiatCurrency, @@ -47,13 +49,13 @@ export function useGetFiatAmount() { }).format(rate * amount); return amountWithCurrencyCode.substring( - amountWithCurrencyCode.search(/\s/) + 1 + amountWithCurrencyCode.search(/\s/) + 1, ); } return undefined; }, - [rate, fiatCurrency] + [rate, fiatCurrency], ); return rate ? getFiatAmount : undefined; @@ -65,7 +67,7 @@ export function useGetSatsAmount() { const getSatsAmount = React.useCallback( (fiatAmount: number) => (rate ? Math.round(fiatAmount / rate) : undefined), - [rate, fiatCurrency] + [rate, fiatCurrency], ); return rate ? getSatsAmount : undefined; diff --git a/lib/constants.ts b/lib/constants.ts index 075ecab..46c2eae 100644 --- a/lib/constants.ts +++ b/lib/constants.ts @@ -1,8 +1,8 @@ export const NAV_THEME = { light: { - background: "hsl(0 0% 100%)", // background + background: "hsl(210 20% 98%)", // background border: "hsl(240 5.9% 90%)", // border - card: "hsl(0 0% 100%)", // card + card: "hsl(210 20% 98%)", // card notification: "hsl(0 84.2% 60.2%)", // destructive primary: "hsl(240 5.9% 10%)", // primary text: "hsl(240 10% 3.9%)", // foreground diff --git a/package.json b/package.json index da219a0..2968037 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "alby-mobile", + "name": "alby-go", "version": "1.3.0", "main": "expo-router/entry", "scripts": { @@ -17,7 +17,7 @@ }, "dependencies": { "@getalby/lightning-tools": "^5.0.3", - "@getalby/sdk": "^3.6.1", + "@getalby/sdk": "^3.7.0", "@react-native-async-storage/async-storage": "1.23.1", "bech32": "^2.0.0", "buffer": "^6.0.3", diff --git a/pages/Home.tsx b/pages/Home.tsx index 56e09a1..cbf6cf1 100644 --- a/pages/Home.tsx +++ b/pages/Home.tsx @@ -1,14 +1,12 @@ -import { View, Image, Pressable, StyleSheet } from "react-native"; +import { View, Pressable, StyleSheet } from "react-native"; import React, { useState } from "react"; import { useBalance } from "hooks/useBalance"; import { useAppStore } from "lib/state/appStore"; import { WalletConnection } from "~/pages/settings/wallets/WalletConnection"; import { Link, - router, - Stack, - useFocusEffect, - useRootNavigationState, + router, useFocusEffect, + useRootNavigationState } from "expo-router"; import dayjs from "dayjs"; import relativeTime from "dayjs/plugin/relativeTime"; @@ -22,6 +20,7 @@ import LargeArrowUp from "~/components/icons/LargeArrowUp"; import LargeArrowDown from "~/components/icons/LargeArrowDown"; import { SvgProps } from "react-native-svg"; import { Button } from "~/components/ui/button"; +import Screen from "~/components/Screen"; dayjs.extend(relativeTime); @@ -69,23 +68,15 @@ export function Home() { return ( <> - ( - - ), - headerRight: () => ( - - - - ), - }} + + + + + } /> @@ -95,8 +86,8 @@ export function Home() { style={{ ...(pressed ? { - transform: "scale(0.98)", - } + transform: "scale(0.98)", + } : []), }} onPress={switchBalanceState} @@ -115,7 +106,7 @@ export function Home() { getFiatAmount(Math.floor(balance.balance / 1000))} {balanceState == BalanceState.HIDDEN && "****"} - + {balanceState == BalanceState.SATS && "sats"} @@ -140,10 +131,10 @@ export function Home() { - + @@ -185,14 +176,14 @@ function MainButton({ style={{ flex: 1, padding: 6, - borderRadius: 15, + borderRadius: 24, elevation: 2, justifyContent: "center", alignItems: "center", ...(pressed ? { - transform: "scale(0.98)", - } + transform: "scale(0.98)", + } : {}), }} > diff --git a/pages/Transaction.tsx b/pages/Transaction.tsx index f92a063..bfb309f 100644 --- a/pages/Transaction.tsx +++ b/pages/Transaction.tsx @@ -1,8 +1,9 @@ import { Nip47Transaction } from "@getalby/sdk/dist/NWCClient"; import dayjs from "dayjs"; -import { Stack, useLocalSearchParams } from "expo-router"; +import { useLocalSearchParams } from "expo-router"; import React from "react"; import { View } from "react-native"; +import Screen from "~/components/Screen"; import { MoveDownLeft, MoveUpRight } from "~/components/Icons"; import { Text } from "~/components/ui/text"; import { useGetFiatAmount } from "~/hooks/useGetFiatAmount"; @@ -17,10 +18,8 @@ export function Transaction() { return ( - {transaction.type === "incoming" ? "+" : "-"} {Math.floor(transaction.amount / 1000)} diff --git a/pages/Transactions.tsx b/pages/Transactions.tsx index 04278e9..b079577 100644 --- a/pages/Transactions.tsx +++ b/pages/Transactions.tsx @@ -1,6 +1,6 @@ import { Nip47Transaction } from "@getalby/sdk/dist/NWCClient"; import dayjs from "dayjs"; -import { Link, router, Stack } from "expo-router"; +import { Link, router } from "expo-router"; import React from "react"; import { FlatList, @@ -9,6 +9,7 @@ import { ScrollView, View, } from "react-native"; +import Screen from "~/components/Screen"; import { MoveDownLeft, MoveUpRight, X } from "~/components/Icons"; import { Button } from "~/components/ui/button"; import { Skeleton } from "~/components/ui/skeleton"; @@ -65,21 +66,16 @@ export function Transactions() { return ( - , - headerRight: () => ( - { - router.back(); - }} - > - - - ), - }} + ( { + router.back(); + }} + > + + )} /> {allTransactions && allTransactions.length ? ( - + {transaction.type === "incoming" && ( - + {transaction.description ? transaction.description : transaction.type === "incoming" @@ -145,16 +141,18 @@ export function Transactions() { + {transaction.type === "incoming" ? "+" : "-"} + {" "} {Math.floor(transaction.amount / 1000)} sats - + {getFiatAmount && getFiatAmount(Math.floor(transaction.amount / 1000))} diff --git a/pages/receive/Receive.tsx b/pages/receive/Receive.tsx index c503703..9396fa7 100644 --- a/pages/receive/Receive.tsx +++ b/pages/receive/Receive.tsx @@ -1,4 +1,4 @@ -import { Link, Stack, router } from "expo-router"; +import { Link, router } from "expo-router"; import { Keyboard, Share, TouchableWithoutFeedback, View } from "react-native"; import { Button } from "~/components/ui/button"; import * as Clipboard from "expo-clipboard"; @@ -14,6 +14,7 @@ import Loading from "~/components/Loading"; import { DualCurrencyInput } from "~/components/DualCurrencyInput"; import { useGetFiatAmount } from "~/hooks/useGetFiatAmount"; import QRCode from "~/components/QRCode"; +import Screen from "~/components/Screen"; export function Receive() { const getFiatAmount = useGetFiatAmount(); @@ -174,10 +175,8 @@ export function Receive() { return ( <> - {!enterCustomAmount && !invoice && !lightningAddress && ( <> @@ -243,7 +242,7 @@ export function Receive() { - @@ -311,3 +310,5 @@ export function Receive() { ); } + + diff --git a/pages/receive/ReceiveSuccess.tsx b/pages/receive/ReceiveSuccess.tsx index fb1ea08..311a955 100644 --- a/pages/receive/ReceiveSuccess.tsx +++ b/pages/receive/ReceiveSuccess.tsx @@ -1,7 +1,8 @@ import { Invoice } from "@getalby/lightning-tools"; -import { Stack, router, useLocalSearchParams } from "expo-router"; +import { router, useLocalSearchParams } from "expo-router"; import { View } from "react-native"; import { Paid } from "~/animations/Paid"; +import Screen from "~/components/Screen"; import { Button } from "~/components/ui/button"; import { Text } from "~/components/ui/text"; import { useGetFiatAmount } from "~/hooks/useGetFiatAmount"; @@ -15,10 +16,8 @@ export function ReceiveSuccess() { }); return ( - diff --git a/pages/send/AddressBook.tsx b/pages/send/AddressBook.tsx index a78b7df..e8bbf33 100644 --- a/pages/send/AddressBook.tsx +++ b/pages/send/AddressBook.tsx @@ -1,8 +1,7 @@ -import { Link, Stack, router } from "expo-router"; +import { Link, router } from "expo-router"; import { Pressable, View } from "react-native"; -import { PlusCircle } from "~/components/Icons"; +import Screen from "~/components/Screen"; import { Button } from "~/components/ui/button"; - import { Card, CardDescription, @@ -16,10 +15,8 @@ export function AddressBook() { const addressBookEntries = useAppStore((store) => store.addressBookEntries); return ( - {addressBookEntries.length > 0 ? addressBookEntries.map((addressBookEntry, index) => ( diff --git a/pages/send/ConfirmPayment.tsx b/pages/send/ConfirmPayment.tsx index 0e58d9b..80d5cec 100644 --- a/pages/send/ConfirmPayment.tsx +++ b/pages/send/ConfirmPayment.tsx @@ -1,9 +1,11 @@ import { Invoice } from "@getalby/lightning-tools"; -import { Stack, router, useLocalSearchParams } from "expo-router"; + +import { router, useLocalSearchParams } from "expo-router"; import React from "react"; import { View } from "react-native"; import { ZapIcon } from "~/components/Icons"; import Loading from "~/components/Loading"; +import Screen from "~/components/Screen"; import { Button } from "~/components/ui/button"; import { Text } from "~/components/ui/text"; import { useGetFiatAmount } from "~/hooks/useGetFiatAmount"; @@ -54,11 +56,10 @@ export function ConfirmPayment() { }); return ( <> - + diff --git a/pages/send/LNURLPay.tsx b/pages/send/LNURLPay.tsx index 47a778a..d75e138 100644 --- a/pages/send/LNURLPay.tsx +++ b/pages/send/LNURLPay.tsx @@ -1,4 +1,6 @@ -import { Stack, router, useLocalSearchParams } from "expo-router"; + +import Screen from "~/components/Screen"; +import { router, useLocalSearchParams } from "expo-router"; import React from "react"; import { Keyboard, TouchableWithoutFeedback, View } from "react-native"; import { Button } from "~/components/ui/button"; @@ -44,10 +46,8 @@ export function LNURLPay() { return ( <> - { diff --git a/pages/send/PaymentSuccess.tsx b/pages/send/PaymentSuccess.tsx index ab480c4..4a9a8f8 100644 --- a/pages/send/PaymentSuccess.tsx +++ b/pages/send/PaymentSuccess.tsx @@ -1,11 +1,9 @@ -import { Stack, router, useLocalSearchParams } from "expo-router"; -import { Pressable, View } from "react-native"; +import { router, useLocalSearchParams } from "expo-router"; +import { View } from "react-native"; import { Paid } from "~/animations/Paid"; import { Button } from "~/components/ui/button"; import { Text } from "~/components/ui/text"; -import * as Clipboard from "expo-clipboard"; -import Toast from "react-native-toast-message"; -import { Copy } from "~/components/Icons"; +import Screen from "~/components/Screen"; import { useGetFiatAmount } from "~/hooks/useGetFiatAmount"; export function PaymentSuccess() { @@ -18,10 +16,8 @@ export function PaymentSuccess() { }; return ( - diff --git a/pages/send/Send.tsx b/pages/send/Send.tsx index 57b8da7..6518a0f 100644 --- a/pages/send/Send.tsx +++ b/pages/send/Send.tsx @@ -1,3 +1,5 @@ + +import Screen from "~/components/Screen"; import React, { useEffect } from "react"; import { Keyboard, TouchableWithoutFeedback, View } from "react-native"; import * as Clipboard from "expo-clipboard"; @@ -8,7 +10,7 @@ import { ClipboardPaste, Keyboard as KeyboardIcon, } from "~/components/Icons"; -import { Stack, router, useLocalSearchParams } from "expo-router"; +import { router, useLocalSearchParams } from "expo-router"; import { Text } from "~/components/ui/text"; import { Input } from "~/components/ui/input"; import { errorToast } from "~/lib/errorToast"; @@ -108,10 +110,8 @@ export function Send() { return ( <> - {isLoading && ( @@ -166,13 +166,13 @@ export function Send() { LNURL.