settings revamp

This commit is contained in:
Boof2015
2026-07-06 21:03:58 -04:00
parent efd2ebc91a
commit 31f81282e0
10 changed files with 1147 additions and 673 deletions
+10
View File
@@ -0,0 +1,10 @@
import { AppearanceSettingsPanel } from '@/components/settings/SettingsPanels';
import { SettingsSectionScreen } from '@/components/settings/SettingsSectionScaffold';
export default function AppearanceSettingsScreen() {
return (
<SettingsSectionScreen title="Appearance">
<AppearanceSettingsPanel />
</SettingsSectionScreen>
);
}
+10
View File
@@ -0,0 +1,10 @@
import { AudioSettingsPanel } from '@/components/settings/SettingsPanels';
import { SettingsSectionScreen } from '@/components/settings/SettingsSectionScaffold';
export default function AudioSettingsScreen() {
return (
<SettingsSectionScreen title="Audio">
<AudioSettingsPanel />
</SettingsSectionScreen>
);
}
+62
View File
@@ -0,0 +1,62 @@
import { useEffect } from 'react';
import { InteractionManager } from 'react-native';
import { useRouter } from 'expo-router';
import {
SettingsNavRow,
SettingsSectionLabel,
SettingsSectionScreen,
} from '@/components/settings/SettingsSectionScaffold';
import { formatRelativeTime } from '@/lib/format';
import { useColors } from '@/theme/themed';
import { useDesktopRemoteStore } from '@/stores/desktopRemoteStore';
import { useDesktopSyncStore } from '@/stores/desktopSyncStore';
export default function ExperimentalSettingsScreen() {
const colors = useColors();
const router = useRouter();
const desktopRemoteConnection = useDesktopRemoteStore((s) => s.connection);
const desktopRemoteState = useDesktopRemoteStore((s) => s.connectionState);
const initDesktopRemote = useDesktopRemoteStore((s) => s.init);
const desktopSyncStatus = useDesktopSyncStore((s) => s.status);
const desktopLastSyncAt = useDesktopSyncStore((s) => s.lastSyncAt);
const desktopSyncConflictCount = useDesktopSyncStore((s) => s.conflicts.length);
useEffect(() => {
const task = InteractionManager.runAfterInteractions(() => {
void initDesktopRemote();
});
return () => task.cancel();
}, [initDesktopRemote]);
const desktopRemoteSubtitle = desktopRemoteConnection
? `${desktopRemoteConnection.desktopName ?? 'Astra Desktop'}: ${desktopRemoteState === 'connected' ? 'connected' : desktopRemoteState}`
: 'Pair with Astra Desktop to control playback from this phone.';
const desktopSyncSubtitle = !desktopRemoteConnection
? 'Sync favorites and playlists with Astra Desktop.'
: desktopSyncConflictCount > 0
? `${desktopSyncConflictCount} conflict${desktopSyncConflictCount === 1 ? '' : 's'} to resolve.`
: desktopSyncStatus === 'syncing'
? 'Syncing.'
: desktopLastSyncAt !== null
? `Synced ${formatRelativeTime(desktopLastSyncAt)}.`
: `${desktopRemoteConnection.desktopName ?? 'Astra Desktop'}: not synced yet.`;
return (
<SettingsSectionScreen title="Experimental">
<SettingsSectionLabel>DESKTOP</SettingsSectionLabel>
<SettingsNavRow
icon="phone-portrait-outline"
title="Desktop Remote"
subtitle={desktopRemoteSubtitle}
onPress={() => router.push('/desktop-remote' as never)}
/>
<SettingsNavRow
icon="sync-outline"
title="Desktop Sync"
subtitle={desktopSyncSubtitle}
subtitleColor={desktopSyncConflictCount > 0 ? colors.warning : undefined}
onPress={() => router.push('/desktop-sync' as never)}
/>
</SettingsSectionScreen>
);
}
+112
View File
@@ -0,0 +1,112 @@
import {
Alert,
Linking,
View,
} from 'react-native';
import Constants from 'expo-constants';
import {
SettingsCard,
SettingsNavRow,
SettingsSectionLabel,
SettingsSectionScreen,
} from '@/components/settings/SettingsSectionScaffold';
import { Text } from '@/components/Text';
import { spacing } from '@/theme';
import { createThemedStyles, useColors } from '@/theme/themed';
const ASTRA_REPOSITORY_URL = 'https://github.com/Boof2015/astra-mobile';
const ASTRA_DISCORD_URL = 'https://discord.gg/hsKK8Kr9Nj';
const ASTRA_SUPPORT_URL = 'https://ko-fi.com/boof2015';
const ASTRA_LICENSE_URL = 'https://github.com/Boof2015/astra-mobile/blob/main/LICENSE';
const GPL_V3_URL = 'https://www.gnu.org/licenses/gpl-3.0.html';
async function openExternalLink(url: string, label: string) {
try {
await Linking.openURL(url);
} catch {
Alert.alert('Unable to open link', `Astra could not open ${label}.`);
}
}
export default function InfoSettingsScreen() {
const styles = useStyles();
const colors = useColors();
const appVersion = Constants.expoConfig?.version;
const appVersionLabel = appVersion ? `v${appVersion}` : 'Unavailable';
return (
<SettingsSectionScreen title="Info">
<SettingsSectionLabel>APP</SettingsSectionLabel>
<SettingsCard>
<View style={styles.infoRow}>
<Text variant="caption" color={colors.textSecondary}>
App Version
</Text>
<Text variant="body">{appVersionLabel}</Text>
</View>
</SettingsCard>
<SettingsSectionLabel spaced>ATTRIBUTION</SettingsSectionLabel>
<SettingsCard>
<Text variant="body" style={styles.paragraph}>
Astra is created and maintained by Boof2015.
</Text>
<Text variant="mono" color={colors.textSecondary}>
contact@novaml.ai
</Text>
</SettingsCard>
<SettingsNavRow
icon="logo-github"
title="GitHub Repository"
subtitle="Boof2015/astra-mobile"
rightIcon="open-outline"
onPress={() => void openExternalLink(ASTRA_REPOSITORY_URL, 'GitHub Repository')}
/>
<SettingsNavRow
icon="logo-discord"
title="Discord"
subtitle="discord.gg/hsKK8Kr9Nj"
rightIcon="open-outline"
onPress={() => void openExternalLink(ASTRA_DISCORD_URL, 'Discord')}
/>
<SettingsNavRow
icon="heart-outline"
title="Ko-fi"
subtitle="ko-fi.com/boof2015"
rightIcon="open-outline"
onPress={() => void openExternalLink(ASTRA_SUPPORT_URL, 'Ko-fi')}
/>
<SettingsSectionLabel spaced>LICENSE</SettingsSectionLabel>
<SettingsCard>
<Text variant="body" style={styles.paragraph}>
Astra is distributed under GPL-3.0-only.
</Text>
</SettingsCard>
<SettingsNavRow
icon="document-text-outline"
title="View LICENSE"
subtitle="Repository license file"
rightIcon="open-outline"
onPress={() => void openExternalLink(ASTRA_LICENSE_URL, 'the Astra license')}
/>
<SettingsNavRow
icon="reader-outline"
title="GPL v3 Text"
subtitle="gnu.org/licenses/gpl-3.0.html"
rightIcon="open-outline"
onPress={() => void openExternalLink(GPL_V3_URL, 'GPL v3 Text')}
/>
</SettingsSectionScreen>
);
}
const useStyles = createThemedStyles(() => ({
infoRow: {
gap: spacing.xs,
},
paragraph: {
lineHeight: 21,
marginBottom: spacing.sm,
},
}));
+10
View File
@@ -0,0 +1,10 @@
import { LibrarySettingsPanel } from '@/components/settings/SettingsPanels';
import { SettingsSectionScreen } from '@/components/settings/SettingsSectionScaffold';
export default function LibrarySettingsScreen() {
return (
<SettingsSectionScreen title="Library">
<LibrarySettingsPanel />
</SettingsSectionScreen>
);
}
+39
View File
@@ -0,0 +1,39 @@
import { useRouter } from 'expo-router';
import { lastFmScrobbleSubtitle } from '@/components/settings/SettingsPanels';
import {
SettingsNavRow,
SettingsSectionLabel,
SettingsSectionScreen,
} from '@/components/settings/SettingsSectionScaffold';
import { useLastFmSettingsStore } from '@/stores/lastFmSettingsStore';
import { useRemoteSourcesStore } from '@/stores/remoteSourcesStore';
export default function ServicesSettingsScreen() {
const router = useRouter();
const remoteSources = useRemoteSourcesStore((s) => s.sources);
const lastFmStatus = useLastFmSettingsStore((s) => s.status);
return (
<SettingsSectionScreen title="Services">
<SettingsSectionLabel>REMOTE SOURCES</SettingsSectionLabel>
<SettingsNavRow
icon="server-outline"
title="Subsonic / Jellyfin servers"
subtitle={
remoteSources.length === 0
? 'Stream and browse your self-hosted library.'
: `${remoteSources.length} server${remoteSources.length === 1 ? '' : 's'} connected.`
}
onPress={() => router.push('/sources')}
/>
<SettingsSectionLabel spaced>SCROBBLING</SettingsSectionLabel>
<SettingsNavRow
icon="radio-outline"
title="Last.fm & scrobbling"
subtitle={lastFmScrobbleSubtitle(lastFmStatus)}
onPress={() => router.push('/lastfm')}
/>
</SettingsSectionScreen>
);
}