From 15ab722a8e63aa8bfe3db3394aa81233e2b1c1c3 Mon Sep 17 00:00:00 2001 From: Vu Nguyen Date: Thu, 30 Nov 2023 02:22:50 +0700 Subject: [PATCH] feat(updater): add markdown display (#30) --- package.json | 3 +- pnpm-lock.yaml | 56 ++++++++++ src/ui/core/button.tsx | 1 + src/ui/updater.tsx | 236 +++++++++++++++++++++++++++++++++++++---- 4 files changed, 276 insertions(+), 20 deletions(-) diff --git a/package.json b/package.json index 9a81f9a..a4824cc 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "@bacons/link-assets": "^1.1.0", "@gorhom/bottom-sheet": "^4.4.5", "@graphql-typed-document-node/core": "^3.2.0", + "@jonasmerlin/react-native-markdown-display": "github:jonasmerlin/react-native-markdown-display", "@plussub/srt-vtt-parser": "^1.1.0", "@react-native-community/viewpager": "^5.0.11", "@react-native-picker/picker": "2.4.10", @@ -144,4 +145,4 @@ "osMetadata": { "initVersion": "4.2.2" } -} \ No newline at end of file +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 694de81..8d9c720 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,6 +14,9 @@ dependencies: '@graphql-typed-document-node/core': specifier: ^3.2.0 version: 3.2.0(graphql@16.8.1) + '@jonasmerlin/react-native-markdown-display': + specifier: github:jonasmerlin/react-native-markdown-display + version: github.com/jonasmerlin/react-native-markdown-display/d4bf241ae67a33885ac69c58466c4a0e8ab79d49(react-native@0.72.5)(react@18.2.0) '@plussub/srt-vtt-parser': specifier: ^1.1.0 version: 1.1.0 @@ -8227,6 +8230,11 @@ packages: dependencies: once: 1.4.0 + /entities@3.0.1: + resolution: {integrity: sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==} + engines: {node: '>=0.12'} + dev: false + /entities@4.4.0: resolution: {integrity: sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==} engines: {node: '>=0.12'} @@ -11500,6 +11508,12 @@ packages: /lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + /linkify-it@4.0.1: + resolution: {integrity: sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==} + dependencies: + uc.micro: 1.0.6 + dev: false + /lint-staged@13.1.2: resolution: {integrity: sha512-K9b4FPbWkpnupvK3WXZLbgu9pchUJ6N7TtVZjbaPsoizkqFUDkUReUL25xdrCljJs7uLUF3tZ7nVPeo/6lp+6w==} engines: {node: ^14.13.1 || >=16.0.0} @@ -11872,6 +11886,17 @@ packages: engines: {node: '>=8'} dev: true + /markdown-it@13.0.2: + resolution: {integrity: sha512-FtwnEuuK+2yVU7goGn/MJ0WBZMM9ZPgU9spqlFs7/A/pDIUNSOQZhUgOqYCficIuR2QaFnrt8LHqBWsbTAoI5w==} + hasBin: true + dependencies: + argparse: 2.0.1 + entities: 3.0.1 + linkify-it: 4.0.1 + mdurl: 1.0.1 + uc.micro: 1.0.6 + dev: false + /md5-file@3.2.3: resolution: {integrity: sha512-3Tkp1piAHaworfcCgH0jKbTvj1jWWFgbvh2cXaNCgHwyTCBxxvD1Y04rmfpvdPm1P4oXMOpm6+2H7sr7v9v8Fw==} engines: {node: '>=0.10'} @@ -11904,6 +11929,10 @@ packages: resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==} dev: false + /mdurl@1.0.1: + resolution: {integrity: sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==} + dev: false + /media-typer@0.3.0: resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} engines: {node: '>= 0.6'} @@ -13752,6 +13781,12 @@ packages: react-native: 0.72.5(@babel/core@7.21.0)(@babel/preset-env@7.22.10)(react@18.2.0) 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.12.0(react-native@0.72.5)(react@18.2.0): resolution: {integrity: sha512-rr+XwVzXAVpY8co25ukvyI38fKCxTQjz7WajeZktl8qUPdh1twnSExgpT47DqDi4n+m+OiJPAnHfZOkqqAQMOg==} peerDependencies: @@ -15702,6 +15737,10 @@ packages: resolution: {integrity: sha512-xV8kqRKM+jhMvcHWUKthV9fNebIzrNy//2O9ZwWcfiBFR5f25XVZPLlEajk/sf3Ra15V92isyQqnIEXRDaZWEA==} dev: false + /uc.micro@1.0.6: + resolution: {integrity: sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==} + dev: false + /uglify-es@3.3.9: resolution: {integrity: sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==} engines: {node: '>=0.8.0'} @@ -16361,3 +16400,20 @@ packages: /zod@3.21.4: resolution: {integrity: sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==} dev: false + + github.com/jonasmerlin/react-native-markdown-display/d4bf241ae67a33885ac69c58466c4a0e8ab79d49(react-native@0.72.5)(react@18.2.0): + resolution: {tarball: https://codeload.github.com/jonasmerlin/react-native-markdown-display/tar.gz/d4bf241ae67a33885ac69c58466c4a0e8ab79d49} + id: github.com/jonasmerlin/react-native-markdown-display/d4bf241ae67a33885ac69c58466c4a0e8ab79d49 + name: '@jonasmerlin/react-native-markdown-display' + version: 1.0.2 + peerDependencies: + react: ^16.2.0 || ^18.0.0 + react-native: '>=0.50.4' + dependencies: + css-to-react-native: 3.0.0 + markdown-it: 13.0.2 + prop-types: 15.8.1 + react: 18.2.0 + react-native: 0.72.5(@babel/core@7.21.0)(@babel/preset-env@7.22.10)(react@18.2.0) + react-native-fit-image: 1.5.5 + dev: false diff --git a/src/ui/core/button.tsx b/src/ui/core/button.tsx index e6e1c79..639c326 100644 --- a/src/ui/core/button.tsx +++ b/src/ui/core/button.tsx @@ -81,6 +81,7 @@ export const Button = ({ buttonVariants[variant].indicator, indicatorClassName )} + color="white" {...loadingProps} /> ) : ( diff --git a/src/ui/updater.tsx b/src/ui/updater.tsx index 049a54a..668bacd 100644 --- a/src/ui/updater.tsx +++ b/src/ui/updater.tsx @@ -2,12 +2,13 @@ import type { BottomSheetModal } from '@gorhom/bottom-sheet'; import axios from 'axios'; import * as FileSystem from 'expo-file-system'; import * as IntentLauncher from 'expo-intent-launcher'; -import { useCallback, useEffect, useRef, useState } from 'react'; -import React from 'react'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; +import { Platform } from 'react-native'; +import Markdown from 'react-native-markdown-display'; import { Env } from '@/core/env'; -import { Button, Text } from './core'; +import { Button, Text, View } from './core'; import BottomSheet from './core/bottom-sheet'; export interface Release { @@ -170,28 +171,225 @@ const Updater = () => { return ( - - New update available:{' '} - - {release.name} + + + New update available:{' '} + + {release.name} + - - Changelog: + {/* @ts-expect-error */} + {release.body} - {release.body} + - - - {errorMessage ? ( - - Error: {errorMessage} - - ) : null} + {errorMessage ? ( + + Error: {errorMessage} + + ) : null} + ); }; +const markdownStyles = { + // The main container + body: {}, + + // Headings + heading1: { + flexDirection: 'row', + fontSize: 32, + fontFamily: 'Outfit-Bold', + }, + heading2: { + flexDirection: 'row', + fontSize: 24, + }, + heading3: { + flexDirection: 'row', + fontSize: 18, + }, + heading4: { + flexDirection: 'row', + fontSize: 16, + }, + heading5: { + flexDirection: 'row', + fontSize: 13, + }, + heading6: { + flexDirection: 'row', + fontSize: 11, + }, + + // Horizontal Rule + hr: { + backgroundColor: '#000000', + height: 1, + }, + + // Emphasis + strong: { + fontWeight: 'bold', + }, + em: { + fontStyle: 'italic', + }, + s: { + textDecorationLine: 'line-through', + }, + + // Blockquotes + blockquote: { + backgroundColor: '#F5F5F5', + borderColor: '#CCC', + borderLeftWidth: 4, + marginLeft: 5, + paddingHorizontal: 5, + }, + + // Lists + bullet_list: {}, + ordered_list: {}, + list_item: { + marginTop: 8, + flexDirection: 'row', + justifyContent: 'flex-start', + }, + // @pseudo class, does not have a unique render rule + bullet_list_icon: { + marginLeft: 10, + marginRight: 10, + color: 'white', + }, + // @pseudo class, does not have a unique render rule + bullet_list_content: { + flex: 1, + }, + // @pseudo class, does not have a unique render rule + ordered_list_icon: { + marginLeft: 10, + marginRight: 10, + }, + // @pseudo class, does not have a unique render rule + ordered_list_content: { + flex: 1, + }, + + // Code + code_inline: { + borderWidth: 1, + borderColor: '#CCCCCC', + backgroundColor: '#f5f5f5', + padding: 10, + borderRadius: 4, + ...Platform.select({ + ['ios']: { + fontFamily: 'Courier', + }, + ['android']: { + fontFamily: 'monospace', + }, + }), + }, + code_block: { + borderWidth: 1, + borderColor: '#CCCCCC', + backgroundColor: '#f5f5f5', + padding: 10, + borderRadius: 4, + ...Platform.select({ + ['ios']: { + fontFamily: 'Courier', + }, + ['android']: { + fontFamily: 'monospace', + }, + }), + }, + fence: { + borderWidth: 1, + borderColor: '#CCCCCC', + backgroundColor: '#f5f5f5', + padding: 10, + borderRadius: 4, + ...Platform.select({ + ['ios']: { + fontFamily: 'Courier', + }, + ['android']: { + fontFamily: 'monospace', + }, + }), + }, + + // Tables + table: { + borderWidth: 1, + borderColor: '#000000', + borderRadius: 3, + }, + thead: {}, + tbody: {}, + th: { + flex: 1, + padding: 5, + }, + tr: { + borderBottomWidth: 1, + borderColor: '#000000', + flexDirection: 'row', + }, + td: { + flex: 1, + padding: 5, + }, + + // Links + link: { + textDecorationLine: 'underline', + }, + blocklink: { + flex: 1, + borderColor: '#000000', + borderBottomWidth: 1, + }, + + // Images + image: { + flex: 1, + }, + + // Text Output + text: { + color: 'white', + fontFamily: 'Outfit-Medium', + }, + textgroup: {}, + paragraph: { + marginTop: 24, + marginBottom: 24, + flexWrap: 'wrap', + flexDirection: 'row', + alignItems: 'flex-start', + justifyContent: 'flex-start', + width: '100%', + }, + hardbreak: { + width: '100%', + height: 1, + }, + softbreak: {}, + + // Believe these are never used but retained for completeness + pre: {}, + inline: {}, + span: {}, +}; + export default Updater;