The Android app updates itself from our Gitea releases; feed cards fade in instead of bouncing
- API: GET /app/android/latest reads the latest Gitea release (ANDROID_RELEASES_URL, optional token for a private repo) with the APK's SHA-256 from SHA256SUMS; GET /app/android/apk/:name streams that APK. - App (modules/updater): downloads in the background, checks the SHA-256, and installs through PackageInstaller: quietly once when leaving the app (Android 12+), otherwise from the feed banner or Settings > App version. F-Droid builds leave it out and drop the install permissions. - CI: the release notes come from the fastlane changelog; the app shows the part above ---. - Settings: Create-account and Link Spotify buttons for Last.fm and ListenBrainz. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -5,7 +5,6 @@ import { useEffect, useRef, useState } from 'react';
|
||||
import { Linking, Pressable, StyleSheet, Text, View } from 'react-native';
|
||||
import Animated, {
|
||||
FadeIn,
|
||||
FadeInDown,
|
||||
FadeOut,
|
||||
useAnimatedStyle,
|
||||
useSharedValue,
|
||||
@@ -98,7 +97,7 @@ export function NowPlayingCard({
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<Animated.View key={np.track.id} entering={FadeInDown.duration(380).springify()} exiting={FadeOut.duration(200)}>
|
||||
<Animated.View key={np.track.id} entering={FadeIn.duration(250)} exiting={FadeOut.duration(200)}>
|
||||
<TrackRow track={np.track} preferred={preferred} expanded={expanded} onToggle={toggle} />
|
||||
</Animated.View>
|
||||
<ProgressBar np={np} compact={!expanded} />
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import { StyleSheet } from 'react-native';
|
||||
import { installUpdate, updaterAvailable, useUpdateState } from '@/lib/updates';
|
||||
import { Body, Button, Card, Heading } from './ui';
|
||||
|
||||
/** "Update ready" on the feed, once a newer version has downloaded (lib/updates.ts). */
|
||||
export function UpdateBanner() {
|
||||
const state = useUpdateState();
|
||||
if (!updaterAvailable || (state.status !== 'ready' && state.status !== 'installing')) return null;
|
||||
return (
|
||||
<Card highlighted>
|
||||
<Heading>musicshare {state.update.version} is ready</Heading>
|
||||
{state.update.notes ? (
|
||||
<Body muted style={styles.notes} numberOfLines={4}>
|
||||
{state.update.notes}
|
||||
</Body>
|
||||
) : null}
|
||||
<Button
|
||||
small
|
||||
variant="primary"
|
||||
title={state.status === 'installing' ? 'Installing…' : 'Install update'}
|
||||
disabled={state.status === 'installing'}
|
||||
onPress={() => installUpdate(true)}
|
||||
style={styles.button}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
notes: { fontSize: 13 },
|
||||
button: { alignSelf: 'flex-start' },
|
||||
});
|
||||
Reference in New Issue
Block a user