diff --git a/apps/expo/package.json b/apps/expo/package.json index 290d396..8d90c01 100644 --- a/apps/expo/package.json +++ b/apps/expo/package.json @@ -61,6 +61,7 @@ "react-native-context-menu-view": "^1.14.1", "react-native-gesture-handler": "~2.14.1", "react-native-ios-modal": "^0.1.8", + "react-native-markdown-display": "^7.0.2", "react-native-mmkv": "^2.12.2", "react-native-modal": "^13.0.1", "react-native-quick-base64": "^2.0.8", diff --git a/apps/expo/src/app/(tabs)/settings.tsx b/apps/expo/src/app/(tabs)/settings.tsx index c169b26..83318e4 100644 --- a/apps/expo/src/app/(tabs)/settings.tsx +++ b/apps/expo/src/app/(tabs)/settings.tsx @@ -1,6 +1,7 @@ import type { SelectProps } from "tamagui"; -import React from "react"; +import React, { useState } from "react"; import { Platform } from "react-native"; +import Markdown from "react-native-markdown-display"; import * as Application from "expo-application"; import * as Brightness from "expo-brightness"; import * as WebBrowser from "expo-web-browser"; @@ -13,6 +14,7 @@ import { useToastController } from "@tamagui/toast"; import { Adapt, Label, + ScrollView, Select, Separator, Sheet, @@ -44,6 +46,9 @@ export default function SettingsScreen() { const { gestureControls, setGestureControls, autoPlay, setAutoPlay } = usePlayerSettingsStore(); const toastController = useToastController(); + const [showUpdateSheet, setShowUpdateSheet] = useState(false); + const [updateMarkdownContent, setUpdateMarkdownContent] = useState(""); + const [downloadUrl, setDownloadUrl] = useState(""); const handleGestureControlsToggle = async (isEnabled: boolean) => { if (isEnabled) { @@ -57,14 +62,17 @@ export default function SettingsScreen() { }; const handleVersionPress = async () => { - const url = await checkForUpdate(); - if (url) { - toastController.show("Update available", { - burntOptions: { preset: "none" }, - native: true, - duration: 500, - }); - await WebBrowser.openBrowserAsync(url); + const res = await checkForUpdate(); + if (res) { + setUpdateMarkdownContent(res.data.body!); + setDownloadUrl( + res.data.assets.find( + (asset) => + asset.name === + `movie-web.${Platform.select({ ios: "ipa", android: "apk" })}`, + )?.browser_download_url ?? "", + ); + setShowUpdateSheet(true); } else { toastController.show("No updates available", { burntOptions: { preset: "none" }, @@ -122,10 +130,87 @@ export default function SettingsScreen() { + ); } +export function UpdateSheet({ + markdownContent, + open, + setShowUpdateSheet, + downloadUrl, +}: { + markdownContent: string; + open: boolean; + setShowUpdateSheet: (value: boolean) => void; + downloadUrl: string; +}) { + const theme = useTheme(); + + return ( + + + + + + {markdownContent} + + + + } + onPress={() => WebBrowser.openBrowserAsync(downloadUrl)} + > + Download + + + + + ); +} + export function ThemeSelector(props: SelectProps) { const theme = useTheme(); const themeStore = useThemeStore((s) => s.theme); diff --git a/apps/expo/src/lib/update.ts b/apps/expo/src/lib/update.ts index e4a8f38..3a6539d 100644 --- a/apps/expo/src/lib/update.ts +++ b/apps/expo/src/lib/update.ts @@ -24,7 +24,7 @@ function isVersionHigher(newVersion: string, currentVersion: string): boolean { return false; } -export async function checkForUpdate(): Promise { +export async function checkForUpdate() { const octokit = new Octokit(); const res = await octokit.repos @@ -40,6 +40,6 @@ export async function checkForUpdate(): Promise { const currentVersion = Application.nativeApplicationVersion ?? "0.0.0"; if (isVersionHigher(latestVersion, currentVersion)) { - return res.data.html_url; + return res; } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e98f426..cb588f2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -155,6 +155,9 @@ importers: react-native-ios-modal: specifier: ^0.1.8 version: 0.1.8(react-native@0.73.6)(react@18.2.0) + react-native-markdown-display: + specifier: ^7.0.2 + version: 7.0.2(react-native@0.73.6)(react@18.2.0) react-native-mmkv: specifier: ^2.12.2 version: 2.12.2(react-native@0.73.6)(react@18.2.0) @@ -6262,6 +6265,10 @@ packages: engines: {node: '>=10'} dev: false + /camelize@1.0.1: + resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==} + dev: false + /caniuse-lite@1.0.30001583: resolution: {integrity: sha512-acWTYaha8xfhA/Du/z4sNZjHUWjkiuoAi2LM+T/aL+kemKQgPT1xBb/YKjlQ0Qo8gvbHsGNplrEJ+9G3gL7i4Q==} @@ -6786,6 +6793,11 @@ packages: engines: {node: '>=8'} dev: false + /css-color-keywords@1.0.0: + resolution: {integrity: sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==} + engines: {node: '>=4'} + dev: false + /css-in-js-utils@3.1.0: resolution: {integrity: sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A==} dependencies: @@ -6802,6 +6814,14 @@ packages: nth-check: 2.1.1 dev: false + /css-to-react-native@3.2.0: + resolution: {integrity: sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==} + dependencies: + camelize: 1.0.1 + css-color-keywords: 1.0.0 + postcss-value-parser: 4.2.0 + dev: false + /css-tree@1.1.3: resolution: {integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==} engines: {node: '>=8.0.0'} @@ -7162,6 +7182,10 @@ packages: once: 1.4.0 dev: false + /entities@2.0.3: + resolution: {integrity: sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==} + dev: false + /entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} @@ -9809,6 +9833,12 @@ packages: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} dev: false + /linkify-it@2.2.0: + resolution: {integrity: sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==} + dependencies: + uc.micro: 1.0.6 + dev: false + /locate-path@3.0.0: resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==} engines: {node: '>=6'} @@ -9947,6 +9977,17 @@ packages: tmpl: 1.0.5 dev: false + /markdown-it@10.0.0: + resolution: {integrity: sha512-YWOP1j7UbDNz+TumYP1kpwnP0aEa711cJjrAQrzd0UXlbJfc5aAq0F/PZHjiioqDC1NKgvIMX+o+9Bk7yuM2dg==} + hasBin: true + dependencies: + argparse: 1.0.10 + entities: 2.0.3 + linkify-it: 2.2.0 + mdurl: 1.0.1 + uc.micro: 1.0.6 + dev: false + /marky@1.2.5: resolution: {integrity: sha512-q9JtQJKjpsVxCRVgQ+WapguSbKC3SQ5HEzFGPAJMStgh3QjCawp00UKv3MTTAArTmGmmPUvllHZoNbZ3gs0I+Q==} dev: false @@ -9991,6 +10032,10 @@ packages: resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==} dev: false + /mdurl@1.0.1: + resolution: {integrity: sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==} + dev: false + /memoize-one@5.2.1: resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==} dev: false @@ -11497,6 +11542,12 @@ packages: - supports-color dev: false + /react-native-fit-image@1.5.5: + resolution: {integrity: sha512-Wl3Vq2DQzxgsWKuW4USfck9zS7YzhvLNPpkwUUCF90bL32e1a0zOVQ3WsJILJOwzmPdHfzZmWasiiAUNBkhNkg==} + dependencies: + prop-types: 15.8.1 + dev: false + /react-native-gesture-handler@2.14.1(react-native@0.73.6)(react@18.2.0): resolution: {integrity: sha512-YiM1BApV4aKeuwsM6O4C2ufwewYEKk6VMXOt0YqEZFMwABBFWhXLySFZYjBSNRU2USGppJbfHP1q1DfFQpKhdA==} peerDependencies: @@ -11522,6 +11573,20 @@ packages: react-native: 0.73.6(@babel/core@7.23.9)(@babel/preset-env@7.23.9)(react@18.2.0) dev: false + /react-native-markdown-display@7.0.2(react-native@0.73.6)(react@18.2.0): + resolution: {integrity: sha512-Mn4wotMvMfLAwbX/huMLt202W5DsdpMO/kblk+6eUs55S57VVNni1gzZCh5qpznYLjIQELNh50VIozEfY6fvaQ==} + peerDependencies: + react: '>=16.2.0' + react-native: '>=0.50.4' + dependencies: + css-to-react-native: 3.2.0 + markdown-it: 10.0.0 + prop-types: 15.8.1 + react: 18.2.0 + react-native: 0.73.6(@babel/core@7.23.9)(@babel/preset-env@7.23.9)(react@18.2.0) + react-native-fit-image: 1.5.5 + dev: false + /react-native-mmkv@2.12.2(react-native@0.73.6)(react@18.2.0): resolution: {integrity: sha512-6058Aq0p57chPrUutLGe9fYoiDVDNMU2PKV+lLFUJ3GhoHvUrLdsS1PDSCLr00yqzL4WJQ7TTzH+V8cpyrNcfg==} peerDependencies: @@ -13243,6 +13308,10 @@ packages: resolution: {integrity: sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ==} dev: false + /uc.micro@1.0.6: + resolution: {integrity: sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==} + dev: false + /uglify-js@3.17.4: resolution: {integrity: sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==} engines: {node: '>=0.8.0'}