import constants from '../constants' ; import { IListRecord , imageDB } from '../image-db' ; import pubsub from '../pubsub' ; import { user } from './auth' ; import { createFileMeta , driveRequest , folderList , getFileContent , updateFileContent , updateFileMeta } from './google-drive' ; let snapshotFolder = null ; export enum ChangeType { ADD , REMOVE , UPDATE } export interface IChangeEvent { type : ChangeType ; id : number ; } function syncStart ( ) { syncFiles ( ) ; setInterval ( syncFiles , constants . SYNC_FREQUENCY + 1000 ) ; } if ( 'window' in self ) { pubsub . subscribe ( 'login' , syncStart ) ; } export async function getSnapshotFolder ( ) { if ( snapshotFolder ) { return snapshotFolder ; } const files = await driveRequest ( 'files' , { corpus : 'user' , q : `name = '${ constants . DRIVE_FOLDER }' and 'root' in parents` , spaces : 'drive' , } ) ; if ( files && files . files . length === 1 ) { snapshotFolder = files . files [ 0 ] ; return snapshotFolder ; } const bodyOptions = { mimeType : 'application/vnd.google-apps.folder' , name : constants . DRIVE_FOLDER , parents : [ 'root' ] , } ; snapshotFolder = await createFileMeta ( bodyOptions ) as DriveFile ; return snapshotFolder ; } export async function syncFiles ( ) { if ( ! user . token ) { return ; } const lastSyncTime = Number ( await imageDB . getMeta ( 'lastSyncTime' ) ) ; if ( lastSyncTime + constants . SYNC_FREQUENCY > Date . now ( ) ) { return ; } let registration ; if ( constants . SUPPORTS_BGSYNC ) { registration = await navigator . serviceWorker . getRegistration ( ) ; } const folder = await getSnapshotFolder ( ) ; const files = await folderList ( folder . id ! ) ; const remoteOnly = new Map ( ) ; const localOnly = new Set ( ) ; const links = new Map ( ) ; for ( const file of files ) { if ( ! file . id ) { continue ; } remoteOnly . set ( file . id , file ) ; } for ( const record of await imageDB . all ( ) ) { if ( remoteOnly . has ( record . guid ) ) { links . set ( remoteOnly . get ( record . guid ) ! , record ) ; remoteOnly . delete ( record . guid ) ; } else { localOnly . add ( record ) ; } } if ( registration ) { for ( const remote of remoteOnly . values ( ) ) { if ( remote . trashed ) { continue ; } imageDB . addSync ( { id : 0 , guid : remote . id ! , upload : false , includeMedia : true } ) ; } for ( const local of localOnly ) { imageDB . addSync ( { id : local . id ! , guid : '' , upload : true , includeMedia : true } ) ; } for ( const [ remote , local ] of links ) { if ( remote . trashed ) { pubsub . publish ( { channel : 'sync' , data : { type : ChangeType . REMOVE , id : local . id ! } } ) ; deleteLocal ( local ) ; } else if ( local . localFilterChanges || local . localImageChanges ) { imageDB . addSync ( { guid : remote . id ! , id : local . id ! , includeMedia : local . localImageChanges , upload : true , } ) ; } else if ( local . lastSyncVersion < remote . version ! ) { imageDB . addSync ( { id : local . id ! , guid : remote . id ! , upload : false , includeMedia : true } ) ; } } registration . sync . register ( 'sync' ) ; } else { for ( const remote of remoteOnly . values ( ) ) { if ( remote . trashed ) { continue ; } downloadRemote ( remote , true ) ; } for ( const local of localOnly ) { uploadLocal ( local , local . localImageChanges ) ; } for ( const [ remote , local ] of links ) { if ( remote . trashed ) { pubsub . publish ( { channel : 'sync' , data : { type : ChangeType . REMOVE , id : local . id ! } } ) ; deleteLocal ( local ) ; } else if ( local . localImageChanges || local . localFilterChanges ) { uploadLocal ( local , local . localImageChanges , remote ) ; } else if ( local . lastSyncVersion < remote . version ! ) { downloadRemote ( remote , true , local ) ; } } } imageDB . setMeta ( 'lastSyncTime' , Date . now ( ) ) ; } export async function deleteLocal ( local ) { const mediaIds = [ ] ; if ( local . originalId ) { mediaIds . push ( local . originalId ) ; } if ( local . editedId ) { mediaIds . push ( local . editedId ) ; } if ( local . thumbnailId ) { mediaIds . push ( local . thumbnailId ) ; } imageDB . deleteRecord ( local . id ! , mediaIds ) ; } export async function downloadRemote ( remote , includeMedia , local ? ) { if ( ! remote . id ) { return ; } const original = await getFileContent ( remote . id ) ; if ( original ) { const record = local || { editedId : null , guid : '' , id : null , lastSyncVersion : - 1 , localFilterChanges : false , localImageChanges : false , originalId : null , thumbnailId : null , transform : { } , } ; record . guid = remote . id ; if ( remote . appProperties ) { const data = remote . appProperties ; const transform = record . transform || { } ; transform . saturation = Number ( data . saturation ) || transform . saturation ; transform . warmth = Number ( data . warmth ) || transform . warmth ; transform . sharpen = Number ( data . sharpen ) || transform . sharpen ; transform . blur = Number ( data . blur ) || transform . blur ; transform . brightness = Number ( data . brightness ) || transform . brightness ; transform . contrast = Number ( data . contrast ) || transform . contrast ; transform . grey = Number ( data . grey ) || transform . grey ; transform . vignette = Number ( data . vignette ) || transform . vignette ; record . transform = transform ; } record . localFilterChanges = false ; record . localImageChanges = false ; record . lastSyncVersion = remote . version ! ; record . originalId = await imageDB . storeMedia ( original ) ; await imageDB . storeRecord ( record ) ; pubsub . publish ( { channel : 'sync' , data : { type : ChangeType . ADD , id : record . id ! } } ) ; return record . id ! ; } else { console . log ( `Could not fetch original image` ) ; } return ; } export async function uploadLocal ( local , includeMedia , remote ? ) { if ( ! local . originalId ) { return ; } const original = await imageDB . retrieveMedia ( local . originalId ) ; if ( ! original ) { return ; } if ( ! remote ) { const parent = await getSnapshotFolder ( ) ; if ( ! parent || ! parent . id ) { return ; } remote = { mimeType : original . type , name : `${ local . id }_${ Date . now ( ) }` , parents : [ parent . id ] , } ; } if ( local . transform ) { remote . appProperties = { } ; for ( const key in local . transform ) { remote . appProperties [ key ] = String ( local . transform [ key ] ) ; } } if ( remote . id ) { const result = await updateFileMeta ( remote ) ; local . lastSyncVersion = result . version ! ; } else { const file = await createFileMeta ( remote ) ; if ( ! file . id ) { return ; } remote . id = file . id ; local . guid = file . id ; } if ( includeMedia ) { const updated = await updateFileContent ( remote . id , original ) ; local . lastSyncVersion = updated . version ! ; local . localImageChanges = false ; } local . localFilterChanges = false ; return imageDB . storeRecord ( local ) ; }