1import { createTemporaryProjectFile } from './dotExpo'; 2 3const debug = require('debug')('expo:start:project:devices') as typeof console.log; 4 5export type DeviceInfo = { 6 installationId: string; 7 lastUsed: number; 8}; 9 10export type DevicesInfo = { 11 devices: DeviceInfo[]; 12}; 13 14const DEVICES_FILE_NAME = 'devices.json'; 15 16const MILLISECONDS_IN_30_DAYS = 30 * 24 * 60 * 60 * 1000; 17 18export const DevicesFile = createTemporaryProjectFile<DevicesInfo>(DEVICES_FILE_NAME, { 19 devices: [], 20}); 21 22let devicesInfo: DevicesInfo | null = null; 23 24export async function getDevicesInfoAsync(projectRoot: string): Promise<DevicesInfo> { 25 if (devicesInfo) { 26 return devicesInfo; 27 } 28 return readDevicesInfoAsync(projectRoot); 29} 30 31export async function readDevicesInfoAsync(projectRoot: string): Promise<DevicesInfo> { 32 try { 33 devicesInfo = await DevicesFile.readAsync(projectRoot); 34 35 // if the file on disk has old devices, filter them out here before we use them 36 const filteredDevices = filterOldDevices(devicesInfo.devices); 37 if (filteredDevices.length < devicesInfo.devices.length) { 38 devicesInfo = { 39 ...devicesInfo, 40 devices: filteredDevices, 41 }; 42 // save the newly filtered list for consistency 43 try { 44 await setDevicesInfoAsync(projectRoot, devicesInfo); 45 } catch { 46 // do nothing here, we'll just keep using the filtered list in memory for now 47 } 48 } 49 50 return devicesInfo; 51 } catch { 52 return await DevicesFile.setAsync(projectRoot, { devices: [] }); 53 } 54} 55 56export async function setDevicesInfoAsync( 57 projectRoot: string, 58 json: DevicesInfo 59): Promise<DevicesInfo> { 60 devicesInfo = json; 61 return await DevicesFile.setAsync(projectRoot, json); 62} 63 64export async function saveDevicesAsync( 65 projectRoot: string, 66 deviceIds: string | string[] 67): Promise<void> { 68 const currentTime = Date.now(); 69 const newDeviceIds = typeof deviceIds === 'string' ? [deviceIds] : deviceIds; 70 71 debug(`Saving devices: ${newDeviceIds}`); 72 const { devices } = await getDevicesInfoAsync(projectRoot); 73 const newDevicesJson = devices 74 .filter((device) => !newDeviceIds.includes(device.installationId)) 75 .concat(newDeviceIds.map((deviceId) => ({ installationId: deviceId, lastUsed: currentTime }))); 76 await setDevicesInfoAsync(projectRoot, { devices: filterOldDevices(newDevicesJson) }); 77} 78 79function filterOldDevices(devices: DeviceInfo[]) { 80 const currentTime = Date.now(); 81 return ( 82 devices 83 // filter out any devices that haven't been used to open this project in 30 days 84 .filter((device) => currentTime - device.lastUsed <= MILLISECONDS_IN_30_DAYS) 85 // keep only the 10 most recently used devices 86 .sort((a, b) => b.lastUsed - a.lastUsed) 87 .slice(0, 10) 88 ); 89} 90