ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 1m30s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 2m57s
ci / ts / ts (packages + web) (push) Successful in 1m16s
ci / docker / docker (образы собираются — их сервер собирает сам при обновлении) (push) Successful in 33s
- My Wave is rendered with react-native-skia on the UI thread; physics and colour helpers moved to @pipu/core (wave-anim) and shared with web. - Full-screen player honours ui_prefs.mobile_player (cover / wave). - Theme switching (dark / light / system) via a theme context and useColors() across all screens. - Profile: theme, wave variant, player view, sound quality sheet (Wi-Fi / mobile / downloads), recent-tracks cache clearing on next cold start, and a change-password sheet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
45 lines
1.7 KiB
TypeScript
45 lines
1.7 KiB
TypeScript
import type { Track } from '@pipu/core';
|
|
import { useSyncExternalStore } from 'react';
|
|
import { useTranslation } from 'react-i18next';
|
|
import { ActivityIndicator, Pressable } from 'react-native';
|
|
|
|
import { downloads, type DownloadItem, type DownloadsState } from '../offline/downloads';
|
|
import { useColors } from '../theme';
|
|
import { Icon } from './kit';
|
|
|
|
export function useDownloads(): DownloadsState {
|
|
return useSyncExternalStore(downloads.subscribe, () => downloads.state);
|
|
}
|
|
|
|
export function useDownloadItem(trackId: string): DownloadItem | undefined {
|
|
return useSyncExternalStore(downloads.subscribe, () => downloads.state.items.get(trackId));
|
|
}
|
|
|
|
/** Кнопка «скачать / скачано» у трека: повторное нажатие удаляет загрузку. */
|
|
export function DownloadButton({ track, size = 26 }: { track: Track; size?: number }) {
|
|
const colors = useColors();
|
|
const { t } = useTranslation();
|
|
const item = useDownloadItem(track.id);
|
|
const busy = item?.status === 'queued' || item?.status === 'downloading';
|
|
const onPress = () => void (item ? downloads.remove([track.id]) : downloads.enqueue([track]));
|
|
return (
|
|
<Pressable
|
|
onPress={onPress}
|
|
hitSlop={10}
|
|
disabled={!track.available}
|
|
accessibilityRole="button"
|
|
accessibilityLabel={t(item ? 'mobile.dl_remove' : 'mobile.dl_download')}
|
|
>
|
|
{busy ? (
|
|
<ActivityIndicator size="small" color={colors.accent2} />
|
|
) : (
|
|
<Icon
|
|
name={item?.status === 'done' ? 'download-done' : item?.status === 'error' ? 'error-outline' : 'download'}
|
|
size={size}
|
|
color={item?.status === 'done' ? colors.accent2 : item?.status === 'error' ? colors.danger : colors.text2}
|
|
/>
|
|
)}
|
|
</Pressable>
|
|
);
|
|
}
|