diff --git a/.github/workflows/build-ios-app.yml b/.github/workflows/build-ios-app.yml deleted file mode 100644 index 80414a10..00000000 --- a/.github/workflows/build-ios-app.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: build-ios-app -on: - pull_request: - push: - branches: - - 'main' -jobs: - build: - runs-on: macos-latest - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v4 - with: - node-version: 20 - - run: npm run init - - run: fastlane match - - run: fastlane beta - env: - MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} - working-directory: ./ios \ No newline at end of file diff --git a/.github/workflows/build-ios.yml b/.github/workflows/build-ios.yml new file mode 100644 index 00000000..594b2305 --- /dev/null +++ b/.github/workflows/build-ios.yml @@ -0,0 +1,28 @@ +name: build-ios +on: + push: + branches-ignore: + - "main" +jobs: + build-ios: + runs-on: macos-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: Echo package.json version to Github ENV + run: echo VERISON_NUMBER=$(node -p -e "require('./package.json').version") >> $GITHUB_ENV + + - run: npm run init + + - run: fastlane build + working-directory: ./ios + env: + # FASTLANE_PASSWORD: ${{ secrets.FASTLANE_PASSWORD }} + APPSTORE_CONNECT_API_KEY_JSON: ${{ secrets.APPSTORE_CONNECT_API_KEY_JSON }} + FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD: ${{ secrets.FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD }} + MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} + MATCH_REPO_PAT: "anultravioletaurora:${{ secrets.SIGNING_REPO_PAT }}" \ No newline at end of file diff --git a/.github/workflows/publish-ios-beta.yml b/.github/workflows/publish-ios-beta.yml new file mode 100644 index 00000000..3575c4e3 --- /dev/null +++ b/.github/workflows/publish-ios-beta.yml @@ -0,0 +1,40 @@ +name: publish-ios-beta +on: + pull_request: + push: + branches: + - 'main' +jobs: + publish-ios-beta: + runs-on: macos-latest + steps: + - uses: actions/checkout@v4 + with: + token: ${{ secrets.SIGNING_REPO_PAT }} + + - uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: Echo package.json version to Github ENV + run: echo VERISON_NUMBER=$(node -p -e "require('./package.json').version") >> $GITHUB_ENV + + - run: npm run init + + - name: Output App Store Connect API Key JSON to Fastlane + run: echo -e '${{ secrets.APPSTORE_CONNECT_API_KEY_JSON }}' > appstore_connect_api_key.json + working-directory: ./ios/fastlane + + - run: fastlane beta + working-directory: ./ios + env: + APPSTORE_CONNECT_API_KEY_JSON: ${{ secrets.APPSTORE_CONNECT_API_KEY_JSON }} + FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD: ${{ secrets.FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD }} + MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} + MATCH_REPO_PAT: "anultravioletaurora:${{ secrets.SIGNING_REPO_PAT }}" + + # Commit Fastlane Xcode build number increment + - uses: stefanzweifel/git-auto-commit-action@v5 + with: + commit_message: "[skip actions]" + file_pattern: "ios/Jellify.xcodeproj/project.pbxproj" \ No newline at end of file diff --git a/App.tsx b/App.tsx index fe4dd2d7..00e76884 100644 --- a/App.tsx +++ b/App.tsx @@ -8,6 +8,7 @@ import { useColorScheme } from 'react-native'; import jellifyConfig from './tamagui.config'; import { clientPersister } from './constants/storage'; import { queryClient } from './constants/query-client'; +import { GestureHandlerRootView } from 'react-native-gesture-handler'; export default function App(): React.JSX.Element { @@ -19,11 +20,13 @@ export default function App(): React.JSX.Element { persistOptions={{ persister: clientPersister }}> - - - - - + + + + + + + ); } \ No newline at end of file diff --git a/api/mutations/functions/playlists.ts b/api/mutations/functions/playlists.ts index af6159a1..24ac1270 100644 --- a/api/mutations/functions/playlists.ts +++ b/api/mutations/functions/playlists.ts @@ -1,4 +1,4 @@ -import Client from "@/api/client"; +import Client from "../../../api/client"; import { getPlaylistsApi } from "@jellyfin/sdk/lib/utils/api"; diff --git a/api/queries/functions/downloads.ts b/api/queries/functions/downloads.ts index 8239b225..d063c0c9 100644 --- a/api/queries/functions/downloads.ts +++ b/api/queries/functions/downloads.ts @@ -1,11 +1,26 @@ +import { Dirs, FileSystem } from "react-native-file-access"; import Client from "../../../api/client"; import { getLibraryApi } from "@jellyfin/sdk/lib/utils/api"; export async function downloadTrack(itemId: string) : Promise { + + // Make sure downloads folder exists, create if it doesn't + if (!(await FileSystem.exists(`${Dirs.DocumentDir}/downloads`))) + await FileSystem.mkdir(`${Dirs.DocumentDir}/downloads`) + getLibraryApi(Client.api!) .getDownload({ itemId }, { 'responseType': 'blob' }) + .then(async (response) => { + if (response.status < 300) { + await FileSystem.writeFile(getTrackFilePath(itemId), response.data) + } + }) +} + +function getTrackFilePath(itemId: string) { + return `${Dirs.DocumentDir}/downloads/${itemId}` } \ No newline at end of file diff --git a/components/Global/components/item-card.tsx b/components/Global/components/item-card.tsx index e2758796..8f81d915 100644 --- a/components/Global/components/item-card.tsx +++ b/components/Global/components/item-card.tsx @@ -18,7 +18,7 @@ export function ItemCard(props: CardProps) { const dimensions = props.width && typeof(props.width) === "number" ? { width: props.width, height: props.width } : { width: 150, height: 150 }; - const logoDimensions = props.width && typeof(props.width) === "number" ? { width: props.width / 2, height: props.width / 3 }: { width: 100, height: 30 }; + const logoDimensions = props.width && typeof(props.width) === "number" ? { width: props.width / 2, height: props.width / 6 }: { width: 100, height: 30 }; return ( (false); - const [progressState, setProgressState] = useState(progress!.position); + const [progressState, setProgressState] = useState(progress?.position ?? 0); const { width } = useSafeAreaFrame(); @@ -45,7 +45,7 @@ export default function PlayerScreen({ navigation }: { navigation: NativeStackNa useEffect(() => { if (!seeking) - setProgressState(Math.round(progress!.position)) + setProgressState(Math.round(progress?.position ?? 0)) }, [ progress ]); @@ -147,7 +147,7 @@ export default function PlayerScreen({ navigation }: { navigation: NativeStackNa {/* playback progress goes here */} - {progress!.duration} + {progress?.duration ?? 0} diff --git a/components/Player/screens/queue.tsx b/components/Player/screens/queue.tsx index b41e81a4..9db58b97 100644 --- a/components/Player/screens/queue.tsx +++ b/components/Player/screens/queue.tsx @@ -30,6 +30,7 @@ export default function Queue({ navigation }: { navigation: NativeStackNavigatio ( diff --git a/components/Playlist/component.tsx b/components/Playlist/component.tsx index e5260ef4..403f8fb2 100644 --- a/components/Playlist/component.tsx +++ b/components/Playlist/component.tsx @@ -1,13 +1,16 @@ import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models"; import { NativeStackNavigationProp } from "@react-navigation/native-stack"; import { StackParamList } from "../types"; -import { XStack, YStack } from "tamagui"; +import { getTokens, XStack, YStack } from "tamagui"; import { useItemTracks } from "../../api/queries/tracks"; import { RunTimeTicks } from "../Global/helpers/time-codes"; import { H4, H5, Text } from "../Global/helpers/text"; import Track from "../Global/components/track"; -import { FlatList } from "react-native"; import BlurhashedImage from "../Global/components/blurhashed-image"; +import DraggableFlatList from "react-native-draggable-flatlist"; +import { reorderPlaylist } from "../../api/mutations/functions/playlists"; +import { useState } from "react"; +import Icon from "../Global/helpers/icon"; interface PlaylistProps { playlist: BaseItemDto; @@ -19,13 +22,33 @@ export default function Playlist({ navigation }: PlaylistProps): React.JSX.Element { - const { data: tracks, isLoading } = useItemTracks(playlist.Id!); + const [editing, setEditing] = useState(false); + const { data: tracks, isLoading, refetch } = useItemTracks(playlist.Id!); + + navigation.setOptions({ + headerRight: () => { + return ( + setEditing(!editing)} + /> + ) + } + }) return ( - ( + data={tracks ?? []} + dragHitSlop={{ left: -50 }} // https://github.com/computerjazz/react-native-draggable-flatlist/issues/336 + keyExtractor={({ Id }, index) => { + return `${index}-${Id}` + }} + ListHeaderComponent={( )} numColumns={1} - renderItem={({ item: track, index }) => { + onDragEnd={({ data, from, to }) => { + reorderPlaylist(playlist.Id!, data[to].Id!, to) + refetch(); + }} + renderItem={({ item: track, getIndex, drag }) => { + + const index = getIndex(); return ( ) }} - ListFooterComponent={() => ( + ListFooterComponent={( + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Jellify.xcodeproj/xcshareddata/xcschemes/Jellify.xcscheme b/ios/Jellify.xcodeproj/xcshareddata/xcschemes/Jellify.xcscheme index b2bfedf5..26eed832 100644 --- a/ios/Jellify.xcodeproj/xcshareddata/xcschemes/Jellify.xcscheme +++ b/ios/Jellify.xcodeproj/xcshareddata/xcschemes/Jellify.xcscheme @@ -41,7 +41,7 @@ CFBundleSignature ???? CFBundleVersion - 3 + 7 ITSAppUsesNonExemptEncryption LSRequiresIPhoneOS diff --git a/ios/JellifyTests/Info.plist b/ios/JellifyTests/Info.plist index ba7f37c6..5566d295 100644 --- a/ios/JellifyTests/Info.plist +++ b/ios/JellifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 3 + 7 diff --git a/ios/fastlane/Fastfile b/ios/fastlane/Fastfile index 6aee14a5..f8bd1adf 100644 --- a/ios/fastlane/Fastfile +++ b/ios/fastlane/Fastfile @@ -17,9 +17,43 @@ default_platform(:ios) platform :ios do desc "Push a new beta build to TestFlight" + + lane :build do + setup_ci + match( + type: "appstore", + app_identifier: "com.cosmonautical.jellify", + readonly: true + ) + build_app( + scheme: "Jellify", + workspace: "Jellify.xcworkspace" + ) + end + lane :beta do - increment_build_number(xcodeproj: "Jellify.xcodeproj") - build_app(workspace: "Jellify.xcworkspace", scheme: "Jellify") - upload_to_testflight + setup_ci + match( + type: "appstore", + app_identifier: "com.cosmonautical.jellify", + readonly: true + ) + + increment_version_number( + version_number: ENV['VERISON_NUMBER'], + xcodeproj: "Jellify.xcodeproj" + ) + + increment_build_number( + xcodeproj: "Jellify.xcodeproj" + ) + build_app( + scheme: "Jellify - Release", + workspace: "Jellify.xcworkspace", + ) + # http://docs.fastlane.tools/actions/upload_to_testflight/#upload_to_testflight + upload_to_testflight( + api_key_path: "fastlane/appstore_connect_api_key.json" + ) end end diff --git a/ios/fastlane/Matchfile b/ios/fastlane/Matchfile index e852d8ed..6bd20271 100644 --- a/ios/fastlane/Matchfile +++ b/ios/fastlane/Matchfile @@ -1,4 +1,4 @@ -git_url("git@github.com:anultravioletaurora/jellify-signing.git") +git_url("https://github.com/anultravioletaurora/jellify-signing.git") storage_mode("git") @@ -11,3 +11,5 @@ username("violet@cosmonautical.cloud") # Your Apple Developer Portal username # Remove the # in the beginning of the line to enable the other options # The docs are available on https://docs.fastlane.tools/actions/match + +git_basic_authorization(Base64.strict_encode64(ENV["MATCH_REPO_PAT"])) \ No newline at end of file diff --git a/package.json b/package.json index 0e52226c..50d236fc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "jellify", - "version": "0.1.5", + "version": "0.1.7", "private": true, "scripts": { "init": "npm i && npm run pod:install",