mirror of
https://github.com/sky-systems/sky_phone.git
synced 2026-08-29 01:08:59 +00:00
ADD - separate Home and Lock wallpapers
Store independent Home Screen and Lock Screen wallpaper selections, preserve legacy preferences, and route gallery or camera picks to the selected destination. Add the Settings segmented selector and contract coverage.
This commit is contained in:
@@ -6,8 +6,18 @@ const styles = readFileSync(
|
||||
new URL('../assets/main.css', import.meta.url),
|
||||
'utf8',
|
||||
)
|
||||
const source = readFileSync(
|
||||
new URL('./PhoneLockScreen.vue', import.meta.url),
|
||||
'utf8',
|
||||
)
|
||||
|
||||
describe('PhoneLockScreen notification theme contract', () => {
|
||||
it('uses the dedicated Lock Screen wallpaper preference', () => {
|
||||
expect(source).toContain('settings.lockWallpaperImageUrl')
|
||||
expect(source).toContain('settings.lockWallpaper')
|
||||
expect(source).not.toContain('settings.wallpaperImageUrl')
|
||||
})
|
||||
|
||||
it('uses explicit light and dark notification material tokens', () => {
|
||||
expect(styles).toMatch(
|
||||
/\.lock-screen\s*\{[^}]*--lock-notification-background:\s*rgb\(38 36 40 \/ 76%\)[^}]*--lock-notification-color:\s*#fff/s,
|
||||
|
||||
@@ -74,10 +74,10 @@ const time = computed(() =>
|
||||
.trim(),
|
||||
)
|
||||
const dragStyle = computed<Record<string, string>>(() => {
|
||||
const imageUrl = preferences.value.settings.wallpaperImageUrl
|
||||
const imageUrl = preferences.value.settings.lockWallpaperImageUrl
|
||||
return {
|
||||
'--lock-drag': `${dragOffset.value}px`,
|
||||
...(preferences.value.settings.wallpaper === 'custom' && imageUrl
|
||||
...(preferences.value.settings.lockWallpaper === 'custom' && imageUrl
|
||||
? { '--phone-wallpaper-image': `url(${JSON.stringify(imageUrl)})` }
|
||||
: {}),
|
||||
}
|
||||
@@ -289,7 +289,7 @@ onBeforeUnmount(() => {
|
||||
<section
|
||||
class="lock-screen"
|
||||
:class="[
|
||||
`wallpaper--${preferences.settings.wallpaper}`,
|
||||
`wallpaper--${preferences.settings.lockWallpaper}`,
|
||||
{
|
||||
'lock-screen--dragging': dragging,
|
||||
'lock-screen--preview': preview,
|
||||
|
||||
@@ -115,7 +115,7 @@ function choosePerformance(mode: GraphicsMode): void {
|
||||
}
|
||||
|
||||
function chooseWallpaper(wallpaper: Exclude<WallpaperId, 'custom'>): void {
|
||||
phone.setWallpaper(wallpaper)
|
||||
phone.setWallpaper(wallpaper, null, 'both')
|
||||
}
|
||||
|
||||
function toggleApp(appId: BuiltinPhoneAppId): void {
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
type AppNotificationPreferences,
|
||||
type PhonePreferencesV1,
|
||||
type WallpaperId,
|
||||
type WallpaperTarget,
|
||||
} from '@/utils/preferences'
|
||||
|
||||
type LocaleTree = Record<string, unknown>
|
||||
@@ -4062,6 +4063,9 @@ const defaultLocales: LocaleTree = {
|
||||
wallpaperFromCameraDescription: 'Create a new wallpaper',
|
||||
wallpaperHistory: 'Recent',
|
||||
wallpaperCustom: 'Photo wallpaper',
|
||||
wallpaperTarget: 'Wallpaper destination',
|
||||
wallpaperHomeScreen: 'Home Screen',
|
||||
wallpaperLockScreen: 'Lock Screen',
|
||||
toggle: {
|
||||
airplaneMode: 'Toggle Airplane Mode',
|
||||
streamerMode: 'Toggle Streamer Mode',
|
||||
@@ -4654,15 +4658,26 @@ export const usePhoneStore = defineStore('phone', {
|
||||
setSystemDarkMode(value: boolean): void {
|
||||
this.systemDarkMode = value
|
||||
},
|
||||
setWallpaper(wallpaper: WallpaperId, imageUrl: string | null = null): void {
|
||||
setWallpaper(
|
||||
wallpaper: WallpaperId,
|
||||
imageUrl: string | null = null,
|
||||
target: WallpaperTarget | 'both' = 'home',
|
||||
): void {
|
||||
const normalizedImageUrl =
|
||||
wallpaper === 'custom' ? imageUrl?.trim() : null
|
||||
if (wallpaper === 'custom' && !normalizedImageUrl) {
|
||||
throw new Error('Custom wallpapers require a photo URL.')
|
||||
}
|
||||
|
||||
this.preferences.settings.wallpaper = wallpaper
|
||||
this.preferences.settings.wallpaperImageUrl = normalizedImageUrl ?? null
|
||||
if (target === 'home' || target === 'both') {
|
||||
this.preferences.settings.wallpaper = wallpaper
|
||||
this.preferences.settings.wallpaperImageUrl = normalizedImageUrl ?? null
|
||||
}
|
||||
if (target === 'lock' || target === 'both') {
|
||||
this.preferences.settings.lockWallpaper = wallpaper
|
||||
this.preferences.settings.lockWallpaperImageUrl =
|
||||
normalizedImageUrl ?? null
|
||||
}
|
||||
this.preferences.settings.wallpaperHistory = [
|
||||
{ imageUrl: normalizedImageUrl ?? null, wallpaper },
|
||||
...this.preferences.settings.wallpaperHistory.filter((entry) =>
|
||||
|
||||
@@ -196,6 +196,10 @@ describe('preferences', () => {
|
||||
expect(value.settings.wallpaperImageUrl).toBe(
|
||||
'https://media.example/current.jpg',
|
||||
)
|
||||
expect(value.settings.lockWallpaper).toBe('custom')
|
||||
expect(value.settings.lockWallpaperImageUrl).toBe(
|
||||
'https://media.example/current.jpg',
|
||||
)
|
||||
expect(value.settings.wallpaperHistory).toHaveLength(4)
|
||||
expect(
|
||||
value.settings.wallpaperHistory.map((entry) => entry.wallpaper),
|
||||
@@ -213,4 +217,24 @@ describe('preferences', () => {
|
||||
expect(value.settings.wallpaper).toBe('midnight')
|
||||
expect(value.settings.wallpaperImageUrl).toBeNull()
|
||||
})
|
||||
|
||||
it('restores independent Home Screen and Lock Screen wallpapers', () => {
|
||||
const value = parsePhonePreferences(
|
||||
JSON.stringify({
|
||||
version: 1,
|
||||
settings: {
|
||||
wallpaper: 'ocean',
|
||||
lockWallpaper: 'custom',
|
||||
lockWallpaperImageUrl: 'https://media.example/lock.jpg',
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
expect(value.settings.wallpaper).toBe('ocean')
|
||||
expect(value.settings.wallpaperImageUrl).toBeNull()
|
||||
expect(value.settings.lockWallpaper).toBe('custom')
|
||||
expect(value.settings.lockWallpaperImageUrl).toBe(
|
||||
'https://media.example/lock.jpg',
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -48,6 +48,7 @@ export type RingtoneId = (typeof RINGTONE_IDS)[number]
|
||||
export type NotificationSoundId = (typeof NOTIFICATION_SOUND_IDS)[number]
|
||||
export type BuiltInWallpaperId = (typeof WALLPAPER_IDS)[number]
|
||||
export type WallpaperId = BuiltInWallpaperId | 'custom'
|
||||
export type WallpaperTarget = 'home' | 'lock'
|
||||
export type WallpaperHistoryEntry = {
|
||||
imageUrl: string | null
|
||||
wallpaper: WallpaperId
|
||||
@@ -72,6 +73,8 @@ export type PhonePreferencesV1 = {
|
||||
focusMode: boolean
|
||||
frame: PhoneFrameId
|
||||
graphicsMode: GraphicsMode
|
||||
lockWallpaper: WallpaperId
|
||||
lockWallpaperImageUrl: string | null
|
||||
notificationSound: NotificationSoundId
|
||||
notificationDurationSeconds: number
|
||||
notificationVolume: number
|
||||
@@ -145,6 +148,8 @@ export const DEFAULT_PHONE_PREFERENCES: PhonePreferencesV1 = {
|
||||
focusMode: false,
|
||||
frame: 'black',
|
||||
graphicsMode: 'ultimate',
|
||||
lockWallpaper: 'midnight',
|
||||
lockWallpaperImageUrl: null,
|
||||
notificationSound: 'chime',
|
||||
notificationDurationSeconds: 10,
|
||||
notificationVolume: 70,
|
||||
@@ -280,6 +285,20 @@ export function parsePhonePreferences(raw: string | null): PhonePreferencesV1 {
|
||||
settings.wallpaper === 'custom' && wallpaperImageUrl
|
||||
? 'custom'
|
||||
: readChoice(settings.wallpaper, WALLPAPER_IDS, defaults.wallpaper)
|
||||
const requestedLockWallpaperImageUrl = readWallpaperImageUrl(
|
||||
settings.lockWallpaperImageUrl,
|
||||
)
|
||||
const lockWallpaper =
|
||||
settings.lockWallpaper === 'custom' && requestedLockWallpaperImageUrl
|
||||
? 'custom'
|
||||
: typeof settings.lockWallpaper === 'string' &&
|
||||
WALLPAPER_IDS.includes(settings.lockWallpaper as BuiltInWallpaperId)
|
||||
? (settings.lockWallpaper as BuiltInWallpaperId)
|
||||
: wallpaper
|
||||
const lockWallpaperImageUrl =
|
||||
lockWallpaper === 'custom'
|
||||
? (requestedLockWallpaperImageUrl ?? wallpaperImageUrl)
|
||||
: null
|
||||
const wallpaperHistory = readWallpaperHistory(settings.wallpaperHistory)
|
||||
|
||||
return {
|
||||
@@ -305,6 +324,8 @@ export function parsePhonePreferences(raw: string | null): PhonePreferencesV1 {
|
||||
GRAPHICS_MODE_IDS,
|
||||
defaults.graphicsMode,
|
||||
),
|
||||
lockWallpaper,
|
||||
lockWallpaperImageUrl,
|
||||
notificationSound: readChoice(
|
||||
settings.notificationSound,
|
||||
NOTIFICATION_SOUND_IDS,
|
||||
|
||||
@@ -39,7 +39,9 @@ describe('SettingsApp Sky UI contract', () => {
|
||||
it('offers photo, camera, recent, and two-column built-in wallpaper choices', () => {
|
||||
expect(source).toContain("openWallpaperMedia('photos')")
|
||||
expect(source).toContain("openWallpaperMedia('camera')")
|
||||
expect(source).toContain("mediaPicker.consume('settings:wallpaper')")
|
||||
expect(source).toContain('`settings:wallpaper:${wallpaperTarget.value}`')
|
||||
expect(source).toContain("wallpaperTarget === 'home'")
|
||||
expect(source).toContain("wallpaperTarget === 'lock'")
|
||||
expect(source).toContain('class="settings-wallpaper-history"')
|
||||
expect(source).toContain('class="settings-wallpaper-grid"')
|
||||
expect(source).toMatch(
|
||||
|
||||
@@ -89,6 +89,8 @@ import {
|
||||
type PhoneFrameId,
|
||||
type RingtoneId,
|
||||
type WallpaperHistoryEntry,
|
||||
type WallpaperId,
|
||||
type WallpaperTarget,
|
||||
} from '@/utils/preferences'
|
||||
|
||||
type SettingsView =
|
||||
@@ -156,6 +158,7 @@ const resetOpened = ref(false)
|
||||
const simEjectOpened = ref(false)
|
||||
const factoryResetting = ref(false)
|
||||
const factoryResetProgress = ref(0)
|
||||
const wallpaperTarget = ref<WallpaperTarget>('home')
|
||||
const selectedFrameColor = computed(
|
||||
() => PHONE_FRAME_COLORS[phone.preferences.settings.frame],
|
||||
)
|
||||
@@ -201,6 +204,16 @@ let factoryResetAnimationFrame: number | undefined
|
||||
const wallpaperHistory = computed(
|
||||
() => phone.preferences.settings.wallpaperHistory,
|
||||
)
|
||||
const selectedWallpaper = computed<WallpaperId>(() =>
|
||||
wallpaperTarget.value === 'home'
|
||||
? phone.preferences.settings.wallpaper
|
||||
: phone.preferences.settings.lockWallpaper,
|
||||
)
|
||||
const selectedWallpaperImageUrl = computed(() =>
|
||||
wallpaperTarget.value === 'home'
|
||||
? phone.preferences.settings.wallpaperImageUrl
|
||||
: phone.preferences.settings.lockWallpaperImageUrl,
|
||||
)
|
||||
|
||||
const toggleRows = [
|
||||
{
|
||||
@@ -273,10 +286,11 @@ function openSkyUiKitchenSink(): void {
|
||||
}
|
||||
|
||||
function openWallpaperMedia(app: 'photos' | 'camera'): void {
|
||||
const target = wallpaperTarget.value
|
||||
mediaPicker.begin(
|
||||
'settings:wallpaper',
|
||||
`settings:wallpaper:${target}`,
|
||||
'photo',
|
||||
'/apps/settings?wallpaper=1',
|
||||
`/apps/settings?wallpaper=1&wallpaperTarget=${target}`,
|
||||
1,
|
||||
)
|
||||
void router.push({
|
||||
@@ -293,7 +307,18 @@ function wallpaperPreviewStyle(
|
||||
}
|
||||
|
||||
function selectWallpaperHistory(entry: WallpaperHistoryEntry): void {
|
||||
phone.setWallpaper(entry.wallpaper, entry.imageUrl)
|
||||
phone.setWallpaper(entry.wallpaper, entry.imageUrl, wallpaperTarget.value)
|
||||
}
|
||||
|
||||
function isWallpaperSelected(entry: WallpaperHistoryEntry): boolean {
|
||||
return (
|
||||
selectedWallpaper.value === entry.wallpaper &&
|
||||
selectedWallpaperImageUrl.value === entry.imageUrl
|
||||
)
|
||||
}
|
||||
|
||||
function selectBuiltInWallpaper(wallpaper: WallpaperId): void {
|
||||
phone.setWallpaper(wallpaper, null, wallpaperTarget.value)
|
||||
}
|
||||
|
||||
const connectivityRows = [
|
||||
@@ -671,10 +696,18 @@ async function confirmSimEject(): Promise<void> {
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (route.query.wallpaper === '1') activeView.value = 'wallpaper'
|
||||
if (route.query.wallpaper === '1') {
|
||||
activeView.value = 'wallpaper'
|
||||
wallpaperTarget.value =
|
||||
route.query.wallpaperTarget === 'lock' ? 'lock' : 'home'
|
||||
}
|
||||
|
||||
const selectedPhoto = mediaPicker.consume('settings:wallpaper')
|
||||
if (selectedPhoto) phone.setWallpaper('custom', selectedPhoto.url)
|
||||
const selectedPhoto = mediaPicker.consume(
|
||||
`settings:wallpaper:${wallpaperTarget.value}`,
|
||||
)
|
||||
if (selectedPhoto) {
|
||||
phone.setWallpaper('custom', selectedPhoto.url, wallpaperTarget.value)
|
||||
}
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
@@ -1459,6 +1492,28 @@ onBeforeUnmount(() => {
|
||||
</template>
|
||||
|
||||
<template v-else-if="activeView === 'wallpaper'">
|
||||
<SkySegmented
|
||||
class="settings-wallpaper-target"
|
||||
strong
|
||||
rounded
|
||||
:active-index="wallpaperTarget === 'home' ? 0 : 1"
|
||||
:item-count="2"
|
||||
:aria-label="phone.t('Apps.settings.wallpaperTarget')"
|
||||
>
|
||||
<SkySegmentedButton
|
||||
:active="wallpaperTarget === 'home'"
|
||||
@click="wallpaperTarget = 'home'"
|
||||
>
|
||||
{{ phone.t('Apps.settings.wallpaperHomeScreen') }}
|
||||
</SkySegmentedButton>
|
||||
<SkySegmentedButton
|
||||
:active="wallpaperTarget === 'lock'"
|
||||
@click="wallpaperTarget = 'lock'"
|
||||
>
|
||||
{{ phone.t('Apps.settings.wallpaperLockScreen') }}
|
||||
</SkySegmentedButton>
|
||||
</SkySegmented>
|
||||
|
||||
<section class="settings-wallpaper-actions">
|
||||
<button type="button" @click="openWallpaperMedia('photos')">
|
||||
<span class="settings-wallpaper-actions__icon" aria-hidden="true">
|
||||
@@ -1502,9 +1557,7 @@ onBeforeUnmount(() => {
|
||||
`wallpaper--${entry.wallpaper}`,
|
||||
{
|
||||
'settings-wallpaper-history__item--selected':
|
||||
phone.preferences.settings.wallpaper === entry.wallpaper &&
|
||||
phone.preferences.settings.wallpaperImageUrl ===
|
||||
entry.imageUrl,
|
||||
isWallpaperSelected(entry),
|
||||
},
|
||||
]"
|
||||
:style="wallpaperPreviewStyle(entry)"
|
||||
@@ -1515,14 +1568,7 @@ onBeforeUnmount(() => {
|
||||
"
|
||||
@click="selectWallpaperHistory(entry)"
|
||||
>
|
||||
<Check
|
||||
v-if="
|
||||
phone.preferences.settings.wallpaper === entry.wallpaper &&
|
||||
phone.preferences.settings.wallpaperImageUrl ===
|
||||
entry.imageUrl
|
||||
"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<Check v-if="isWallpaperSelected(entry)" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
@@ -1537,10 +1583,10 @@ onBeforeUnmount(() => {
|
||||
class="settings-wallpaper-choice"
|
||||
:class="{
|
||||
'settings-wallpaper-choice--selected':
|
||||
phone.preferences.settings.wallpaper === wallpaper,
|
||||
selectedWallpaper === wallpaper,
|
||||
}"
|
||||
:aria-pressed="phone.preferences.settings.wallpaper === wallpaper"
|
||||
@click="phone.setWallpaper(wallpaper)"
|
||||
:aria-pressed="selectedWallpaper === wallpaper"
|
||||
@click="selectBuiltInWallpaper(wallpaper)"
|
||||
>
|
||||
<span
|
||||
class="settings-wallpaper-choice__preview"
|
||||
@@ -1548,9 +1594,7 @@ onBeforeUnmount(() => {
|
||||
aria-hidden="true"
|
||||
>
|
||||
<span class="settings-wallpaper-choice__screen"></span>
|
||||
<Check
|
||||
v-if="phone.preferences.settings.wallpaper === wallpaper"
|
||||
/>
|
||||
<Check v-if="selectedWallpaper === wallpaper" />
|
||||
</span>
|
||||
<strong>{{
|
||||
phone.t('Apps.settings.wallpapers.' + wallpaper)
|
||||
@@ -1855,6 +1899,10 @@ onBeforeUnmount(() => {
|
||||
box-shadow: 0 1px 3px rgb(0 0 0 / 18%);
|
||||
}
|
||||
|
||||
.settings-wallpaper-target {
|
||||
margin-bottom: var(--sky-space-4);
|
||||
}
|
||||
|
||||
.settings-wallpaper-actions {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
|
||||
@@ -1856,6 +1856,7 @@ Locales["en"] = {
|
||||
wallpaperFromPhotos = "Choose from Photos", wallpaperFromPhotosDescription = "Use one of your photos",
|
||||
wallpaperFromCamera = "Take Photo", wallpaperFromCameraDescription = "Create a new wallpaper",
|
||||
wallpaperHistory = "Recent", wallpaperCustom = "Photo wallpaper",
|
||||
wallpaperTarget = "Wallpaper destination", wallpaperHomeScreen = "Home Screen", wallpaperLockScreen = "Lock Screen",
|
||||
imei = "IMEI", linkedDevices = "Linked Devices", thisDevice = "This Phone", removeDevice = "Remove Device",
|
||||
simCard = "SIM Card", simNumber = "Phone Number", simType = "SIM Type", registeredSim = "Registered",
|
||||
anonymousSim = "Anonymous", noSim = "No SIM", ejectSim = "Eject SIM", ejectSimBody = "Return this SIM card to your inventory?",
|
||||
|
||||
Reference in New Issue
Block a user