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'}