--[[----------------------------------------------------------------------------

NmbAlbums.lua

--------------------------------------------------------------------------------

ADOBE CONFIDENTIAL
------------------
Copyright 2015 Adobe Systems Incorporated
All Rights Reserved.

NOTICE: All information contained herein is, and remains the property of
Adobe Systems Incorporated and its suppliers, if any. The intellectual and
technical concepts contained herein are proprietary to Adobe Systems
Incorporated and its suppliers and are protected by all applicable
intellectual property laws, including trade secret and copyright laws.
Dissemination of this information or reproduction of this material is
strictly forbidden unless prior written permission is obtained from
Adobe Systems Incorporated.

----------------------------------------------------------------------------]]--

	-- Rx
local Rx = require 'Rx'

local KSTableUtils = require 'KSTableUtils'
local NmbConnectors = require 'NmbConnectors'
local WFCatAlbums = require 'WFCatAlbums'
local WFCatCollaboration = require 'WFCatCollaboration'
local WFCatDevelopSettings = require 'WFCatDevelopSettings'
local WFCatIdUtils = require 'WFCatIdUtils'
local WFCatSharedWithYouAlbums = require 'WFCatSharedWithYouAlbums'
local WFCatSharedWithYouUtils = require 'WFCatSharedWithYouUtils'

local WFRxRunUntilDone = require 'WFRxRunUntilDone'

local runUntilDone = WFRxRunUntilDone
local derefOpt = KSTableUtils.derefOpt

--------------------------------------------------------------------------------

local NmbAlbums  = {}

--[[
	Index definitions for finding assets in albums.  This includes the SQLite index.
	Index definitions for identifying what asset are in albums.
--]]

--------------------------------------------------------------------------------

function NmbAlbums.generateIndexSchema( indexStoreSchema )
	
	-- The base albums and album_asset_v2 tables can still be found in WFCatSearchByAlbum. Moving it
	-- here causes the table to be built unnecessarily so, for now, we leave those over there until
	-- they're being rebuilt for other reasons
	
	--------------------------------------------------------------------------------

	indexStoreSchema:addTable {
		
		sql = [[CREATE TABLE project_albums( 
					albumId,
					name, 
					updateTimestamp, 
					sourceAlbumId,
					serviceId,
					pendingPublish,
					syncStatus,
					notifyConnect,
					coverId,
					assetSortOrder)]],

		useLocalId = true,
		
		hasOneRowPerDocument = true,
		
		relevantTypeSubtypeTuples = {
			{ 'album', 'project' },
		},
		
		map = function( docValue, emit )

			-- Don't emit index store records for album subtypes we don't understand since the UI may not show
			-- things correctly. The basic album subtype is of type 'collection' which is the classic type of album. 
			-- The full list of subtypes can be found here http://apidocs.adbecrsl.com/v2/data-model/album/index.html
			-- Since the different subtypes tend to represent different concepts they may end up in separate tables
			-- entirely.
			if docValue and docValue.type == 'album' 
					and docValue.subtype == 'project'
					and not docValue._deleted then

				local publishInfo = derefOpt( docValue, 'publishInfo' )
				
				local deletedOnThirdPartyService = derefOpt( publishInfo, 'deleted' )
				if deletedOnThirdPartyService then
					-- if the connection was deleted via the third party service, then it's marked in Oz
					-- with a 'deleted' flag under publishInfo but the actual document still exists on Oz
					-- we should ignore these
					return
				end
				
				local notifyConnect = derefOpt( docValue, '_localOnly', 'publishNotifications') and 1 or 0
				local editUrl = derefOpt( docValue, '_localOnly', 'publishNotifications', 'editUrl' )
				local pendingPublish = editUrl == 'pending' and 1 or 0
				
				local name = docValue.name
				
				local albumId = docValue._id
				local localTimeStamp = derefOpt( docValue, 'userUpdated' ) 
				local ozTimeStamp = derefOpt( docValue, '_serverProvidedMetadata', 'updated' )
				local updateTimestamp = localTimeStamp or ozTimeStamp
				local sourceAlbumId = derefOpt( docValue, 'sourceAlbum', 'id' ) 
				local serviceId = derefOpt( docValue, 'serviceId' )
					or derefOpt( publishInfo, 'serviceId' )
				local coverId = derefOpt( docValue, 'cover', 'id' )
	
				-- low resolution sync status - if the local stamp < oz time stamp and not pending Publish
				local syncStatus = 'syncPending'
				if not localTimeStamp then
					-- early project_album records may be missing a userUpdated field. Consider these synced
					-- even if we can't guarantee it.
					syncStatus = 'synced'
				elseif ozTimeStamp and ozTimeStamp >= localTimeStamp then
					syncStatus = 'synced'
				end
				
				local assetSortOrder = derefOpt( docValue, 'assetSortOrder' )
				
				if docValue._spaceId then
				
					-- For space-feed documents, the asset sort order can be changed by the contributor/viewer
					-- but it is a local-only change; they cannot overwrite the album order set by the album owner.
					
					assetSortOrder = derefOpt( docValue, '_localOnly', 'universal', 'assetSortOrder' ) or assetSortOrder
					
				end
				
				if albumId and name then
		
					emit( albumId, name, updateTimestamp, sourceAlbumId, serviceId, pendingPublish, syncStatus, notifyConnect, coverId, assetSortOrder )
							
				end

			end

		end,
		
	}

		
end

--------------------------------------------------------------------------------

local function createFolderWithCache( wfCatalog, albumsCache, folderName, optParentId )
		
	return WFCatAlbums.createFolder( wfCatalog, folderName, optParentId )
			:flatMap( function( localDocId, fullDocId, docValue )
			
			return albumsCache:addFolder( localDocId, docValue.name, optParentId )
				:map( function()
					return localDocId, fullDocId, docValue
				end )
		end )
end

--------------------------------------------------------------------------------

local function createAlbumWithCache( wfCatalog, albumsCache, albumName, optParentId, optHidden )
	
	return WFCatAlbums.createAlbum( wfCatalog, albumName, optParentId, optHidden )
			:flatMap( function( localDocId, fullDocId, docValue )
			
			return albumsCache:addAlbum( localDocId, docValue.name, optParentId, optHidden )
				:map( function()
					return localDocId, fullDocId, docValue
				end )
		end )
end

--------------------------------------------------------------------------------

local function renameAlbumOrFolderWithCache( wfCatalog, albumsCache, albumId, albumName )
		
	return Rx.Observable.combineLatest(
		albumsCache:renameAlbumOrFolder( albumId, albumName ),
		WFCatAlbums.renameAlbumOrFolder( wfCatalog, albumId, albumName ) )
		:map( function( _, result ) return result end )
	
end

--------------------------------------------------------------------------------

local function setAssetSortOrderWithCache( wfCatalog, albumsCache, albumId, assetSortOrder )
		
	return Rx.Observable.combineLatest(
		albumsCache:setAssetSortOrder( albumId, assetSortOrder ),
		WFCatAlbums.setAssetSortOrder( wfCatalog, albumId, assetSortOrder ) )
		:map( function( _, result ) return result end )
	
end

--------------------------------------------------------------------------------

local function reparentAlbumOrFolderWithCache( wfCatalog, albumsCache, albumId, optNewParentId, optOrder )
	 
	return Rx.Observable.combineLatest(
		albumsCache:reparentAlbumOrFolder( albumId, optNewParentId, optOrder ),
		WFCatAlbums.reparentAlbumOrFolder( wfCatalog, albumId, optNewParentId ) )
		:map( function( _, result ) return result end )
end

--------------------------------------------------------------------------------

local function reparentMultipleAlbumsOrFoldersWithCache( wfCatalog, albumsCache, albumIds, optNewParentId, optOrder )
	
	return Rx.Observable.combineLatest(
		albumsCache:reparentMultipleAlbumsOrFolders( albumIds, optNewParentId, optOrder ),
		WFCatAlbums.reparentMultipleAlbumsOrFolders( wfCatalog, albumIds, optNewParentId, optOrder) )
		:map( function( _, result ) return result end )

end

--------------------------------------------------------------------------------

local function deleteAlbumOrFolderWithCache( wfCatalog, albumsCache, albumId )
	
	-- SPAM [matthewj 2018-01-24]: NMB-7104 Note that deletion is different within the cache
	-- here.  Since the cache is what is displayed to users, we need to make sure that we only
	-- delete from the real doc store those folders/albums we removed from the cache.  Otherwise
	-- we can delete things that the user does not realize they are deleting.
	return albumsCache:getCache()
		:take( 1 )
		:flatMap( function( cachedHierarchy )
			-- SPAM [matthewj 2018-08-14]: I would generally prefer that the client is explicitly choosing 
			-- to call either 'deleteAlbum' or 'deleteProjectAlbum' because different types of albums likely
			-- have different actions that need to happen on delete. For now we'll make that distinction here
			local albumInfo = WFCatAlbums._getAlbumFromHierarchyById( cachedHierarchy, albumId )
			
			if not albumInfo then
				return Rx.Observable.empty()
			end
			
			if albumInfo.subtype == 'project' then
				-- do project deletion stuff
				return albumsCache:deleteProjectAlbum( albumId )
					:flatMap( function( albumIdsRemovedFromCache )
						return WFCatAlbums.deleteProjectAlbums( wfCatalog, albumIdsRemovedFromCache )
					end )
				
			else
				return albumsCache:deleteAlbumOrFolder( albumId )
					:flatMap( function( folderAndAlbumIdsRemovedFromCache )
						return WFCatAlbums.deleteIndividualAlbums( wfCatalog, folderAndAlbumIdsRemovedFromCache )
				end )
			end
			
		end )
		:takeLast( 0 )
end

--------------------------------------------------------------------------------

local function shareAlbumWithCache( wfCatalog, albumsCache, albumId, ... )

	return Rx.Observable.combineLatest(
		albumsCache:shareAlbum( albumId ),
		WFCatCollaboration.shareAlbum( wfCatalog, albumId, ... ) )
		:map( function( _, result ) return result end ) 

end

--------------------------------------------------------------------------------

local function unshareAlbumWithCache( wfCatalog, albumsCache, albumId )

	return Rx.Observable.combineLatest(
		albumsCache:unshareAlbum( albumId ),
		WFCatCollaboration.unshareAlbum( wfCatalog, albumId ) )
		:map( function( _, result ) return result end ) 

end

--------------------------------------------------------------------------------

local function makeCatalogAndCacheBasedObservableIntoSubModel( fn, optObservableType, maybeRunUntilDone )
	
	local deliverOneResult = optObservableType == 'deliverOneResult'
	
	return function( model, ... )
		local params = { ... }
		local nParams = select( '#', ... )
		local value

		local catalogStream = deliverOneResult and model._wfCatalog or model._wfCatalog:take( 1 )
		local subModelStream = catalogStream
                             	:mapSwitch( function( wfCatalog, cache )
									
									if maybeRunUntilDone then
                             			return fn( wfCatalog, cache, unpack( params, 1, nParams ) )
											:call( runUntilDone )
									else
										return fn( wfCatalog, cache, unpack( params, 1, nParams ) )
									end
									
                               	end )
								
								
		value = deliverOneResult and subModelStream:take( 1 ) or subModelStream

		return {
			_wfCatalog = model._wfCatalog,

			value = value,
		}
		
	end
	
end

--------------------------------------------------------------------------------

local function makeCatalogAndCacheBasedObservableIntoRunUntilDoneSubModel( fn, optObservableType )

	return makeCatalogAndCacheBasedObservableIntoSubModel( fn, optObservableType, true )

end

--------------------------------------------------------------------------------

local function makeCatalogBasedObservableIntoSubModel( fn, optObservableType, maybeRunUntilDone )
	
	local deliverOneResult = optObservableType == 'deliverOneResult'
	
	return function( model, ... )
		local params = { ... }
		local nParams = select( '#', ... )
		local value
		
		local catalogStream = deliverOneResult and model._wfCatalog or model._wfCatalog:take( 1 )
		local subModelStream = catalogStream
                             	:mapSwitch( function( wfCatalog )
								
									if maybeRunUntilDone then
                             			return fn( wfCatalog, unpack( params, 1, nParams ) )
											:call( runUntilDone )
									else
										return fn( wfCatalog, unpack( params, 1, nParams ) )
									end
									
                               	end )
								
		value = deliverOneResult and subModelStream:take( 1 ) or subModelStream

		return {
			_wfCatalog = model._wfCatalog,

			value = value,
		}
	end
end

--------------------------------------------------------------------------------

local function makeCatalogBasedObservableIntoRunUntilDoneSubModel( fn, optObservableType )

	return makeCatalogBasedObservableIntoSubModel( fn, optObservableType, true )
	
end

--------------------------------------------------------------------------------

local function addAssetsToAlbum( wfCatalog, albumId, assetIds, maybeRedactionOptions )

	return WFCatSharedWithYouAlbums.getSpaceIdOfAlbumId( wfCatalog, albumId )
	
		:flatMap( function( spaceId )
		
			if spaceId then
			
				return WFCatDevelopSettings.waitForPendingDevelopSettingsSaveForAssetIds( assetIds )
				
					:flatMap( function()
					
						return WFCatSharedWithYouAlbums.addAssetsToAlbum( wfCatalog, spaceId, albumId, assetIds, maybeRedactionOptions )
					
					end )
					
					:map( function( added, duplicates, invalidAssets )
						return {
							added = added,
							duplicates = duplicates,
							invalidAssets = invalidAssets,
						}
					end )
			
			else
			
				return WFCatAlbums.addAssetsToAlbum( wfCatalog, albumId, assetIds )
			
			end
		
		end )

end

--------------------------------------------------------------------------------

local function removeAssetsFromAlbum( wfCatalog, albumId, assetIds )

	return WFCatSharedWithYouAlbums.getSpaceIdOfAlbumId( wfCatalog, albumId )
	
		:flatMap( function( spaceId )
		
			if spaceId then
			
				return WFCatSharedWithYouAlbums.deleteAssets( wfCatalog, spaceId, assetIds )
			
			else
			
				return WFCatAlbums.removeAssetsFromAlbum( wfCatalog, albumId, assetIds )
			
			end
		
		end )

end

--------------------------------------------------------------------------------

local function albumAssetsAndSubtype( wfCatalog, albumId, albumType )
	
	local albumAssetIdsObservable
	if albumType == 'project' then
		albumAssetIdsObservable = WFCatAlbums.getProjectAlbumAssetIds( wfCatalog, albumId )
	else
		albumAssetIdsObservable = WFCatAlbums.getAlbumAssetIds( wfCatalog, albumId )
	end
	   
	return albumAssetIdsObservable
		:take( 1 )
        :flatMap( function( assetIdsMap )
		
            local assetIds = {}
			
            for assetId, _ in pairs( assetIdsMap ) do
                assetIds[ #assetIds + 1 ] = assetId
            end
			
            return WFCatIdUtils.getOzAssetIdsAndTypeForIds( wfCatalog, assetIds )
					:map( function( assetIdsAndSubtypes ) 
						
						local assetIdsToSubtypes = {}
						for _, row in ipairs( assetIdsAndSubtypes ) do
							assetIdsToSubtypes[ row[ 1 ] ] = row[ 2 ]
						end
						
						local result = {}
						for _, assetId in ipairs( assetIds ) do
							result[ #result + 1 ] = { 
								id = assetId, 
								subtype = assetIdsToSubtypes[ assetId ] 
							}
						end
						return result
						
					end )
        end )
		
end

--------------------------------------------------------------------------------

--[[
	\brief Returns an array of tables that contain the asset ids in an album along with the subtype of the asset
	(image, video, stack, etc.) if we have it. i.e. <code>{ { id="abc123", subtype="image" }</code>. 
	
	There may be some asset subtypes that we don't have a readily accessible subtype for and for these the subtype may 
	be <code>nil</code> or additional assets and subtypes may be added to the album later. For these reasons it 
	is best to check the subtypes affirmatively when using the results of this function. For example,
	
	<code>if row.subtype == 'image' or row.subtype =='video' then ...</code>
	
	\param wfCatalog WFCatalog
	\param albumId the local or full doc id of an album to get the assets
	
	\return <code>Observable<table></code> list of tables containing the <code>id</code> and <code>subtype</code> 
		of each asset in the provided album
--]]
local function getAlbumAssetsAndSubtype( wfCatalog, albumId )

    return albumAssetsAndSubtype( wfCatalog, albumId )
        
end 

--------------------------------------------------------------------------------

--[[
	\brief Returns an array of tables that contain the asset ids in a project album along with the subtype of the asset
	(image, video, stack, etc.) if we have it. i.e. <code>{ { id="abc123", subtype="image" }</code>. 
	
	There may be some asset subtypes that we don't have a readily accessible subtype for and for these the subtype may 
	be <code>nil</code> or additional assets and subtypes may be added to the album later. For these reasons it 
	is best to check the subtypes affirmatively when using the results of this function. For example,
	
	<code>if row.subtype == 'image' or row.subtype =='video' then ...</code>
	
	\param wfCatalog WFCatalog
	\param projectAlbumId the local or full doc id of a project album to get the assets
	
	\return <code>Observable<table></code> list of tables containing the <code>id</code> and <code>subtype</code> 
		of each asset in the project album
--]]
local function getProjectAlbumAssetsAndSubtype( wfCatalog, projectAlbumId )
	 
	 return albumAssetsAndSubtype( wfCatalog, projectAlbumId, 'project' )

end

--------------------------------------------------------------------------------

--[[
	\param wfCatalog an Observable( WFCatalog )
	Returns a model whose value is an Observable array of top-level albums, each containing fields:
		id : (int) the uuid of the album
		name : (user string) the name of the album
		path : (user string) The name of the parents and album separated by /
		parentId : (uuid, or nil) the id of the albums parent
		children : (array) array of child albums
--]]

function NmbAlbums.makeAlbumsModel( wfCatalog )
	
	local wfCatalogAndCache = wfCatalog
		:filter( function( catalog ) return catalog ~= nil end )
		:map( function( nmbCatalog )

			return nmbCatalog, nmbCatalog:albumsCache()
			
		end )
	
	return {
	
		_wfCatalog = wfCatalogAndCache,
		
		priority = 'high',
		
		value = {},

		models = {
		
			-- use cache
			albumsList = makeCatalogAndCacheBasedObservableIntoSubModel( function( catalog, cache )
					return cache:getAlbumsFromCache( )
						:map( function( results, reason )
							return { albums = results }
						end )
							
				end ),
			
			adhocAlbums = makeCatalogAndCacheBasedObservableIntoSubModel( function( catalog, cache )
				 	return cache:getAdhocAlbumsFromCache( )
						:map( function( results )
							return { albums = results }
						end  )
				end ),
					
			projectAlbums = makeCatalogAndCacheBasedObservableIntoSubModel( function( catalog, cache ) 
					return cache:getProjectAlbumsFromCache( catalog )
						:map( function( results )
							return { albums = results } 
						end )
				end ),
			
			sharedAlbums = makeCatalogAndCacheBasedObservableIntoSubModel( function( catalog, cache )
					return cache:getSharedAlbumsFromCache( )
						:map( function( sharedAlbums )
							return { albums = sharedAlbums }
						end ) 
				end ),

			sharedWithYouAlbumsList = makeCatalogAndCacheBasedObservableIntoSubModel( function( catalog, cache )
					return cache:getSharedWithYouAlbumsFromCache()
						:map( function( results )
							return { albums = results }
						end )
				end ),
				
			enableConnectionNotification = makeCatalogAndCacheBasedObservableIntoSubModel( function( catalog, cache, projectAlbumId, serviceId, editUrl )
				return WFCatAlbums.enableProjectAlbumPublishNotification( catalog, projectAlbumId, serviceId, editUrl )
			end ),
			
			removeConnectionNotification = makeCatalogAndCacheBasedObservableIntoSubModel( function( catalog, cache, projectAlbumId )
				return WFCatAlbums.removeProjectAlbumPublishNotification( catalog, projectAlbumId )
			end ),

			ongoingProjectAlbumNotificationStatus = makeCatalogBasedObservableIntoSubModel( function( catalog, projectAlbumId )			
				return WFCatAlbums.ongoingProjectAlbumNotificationStatus( catalog, projectAlbumId )
			end ),
			
			-- album ops
			createFolder = makeCatalogAndCacheBasedObservableIntoRunUntilDoneSubModel( createFolderWithCache, 'deliverOneResult' ),
			createAlbum = makeCatalogAndCacheBasedObservableIntoRunUntilDoneSubModel( createAlbumWithCache, 'deliverOneResult' ),
			deleteAlbum = makeCatalogAndCacheBasedObservableIntoRunUntilDoneSubModel( deleteAlbumOrFolderWithCache, 'deliverOneResult' ),
			renameAlbum = makeCatalogAndCacheBasedObservableIntoRunUntilDoneSubModel( renameAlbumOrFolderWithCache, 'deliverOneResult' ),
			reparentAlbum = makeCatalogAndCacheBasedObservableIntoRunUntilDoneSubModel( reparentAlbumOrFolderWithCache, 'deliverOneResult' ),
			reparentMultipleAlbums = makeCatalogAndCacheBasedObservableIntoRunUntilDoneSubModel( reparentMultipleAlbumsOrFoldersWithCache, 'deliverOneResult' ),
			
			shareAlbum = makeCatalogAndCacheBasedObservableIntoRunUntilDoneSubModel( shareAlbumWithCache, 'deliverOneResult' ),
			unshareAlbum = makeCatalogAndCacheBasedObservableIntoRunUntilDoneSubModel( unshareAlbumWithCache, 'deliverOneResult' ),
			setAssetSortOrder = makeCatalogAndCacheBasedObservableIntoRunUntilDoneSubModel( setAssetSortOrderWithCache, 'deliverOneResult' ),
			
			-- do not use cache
			addAssetsToAlbum = makeCatalogBasedObservableIntoRunUntilDoneSubModel( addAssetsToAlbum, 'deliverOneResult' ),
			removeAssetsFromAlbum = makeCatalogBasedObservableIntoRunUntilDoneSubModel( removeAssetsFromAlbum ),
			setAlbumCover = makeCatalogBasedObservableIntoRunUntilDoneSubModel( WFCatAlbums.setAlbumCover, 'deliverOneResult' ),
			unhideAlbum = makeCatalogBasedObservableIntoRunUntilDoneSubModel( WFCatAlbums.unhideAlbum, 'deliverOneResult' ),
			getAlbumAssets = makeCatalogBasedObservableIntoRunUntilDoneSubModel( WFCatAlbums.getAlbumAssetIds, 'deliverOneResult' ),
			getAlbumAssetsAndSubtype = makeCatalogBasedObservableIntoRunUntilDoneSubModel( getAlbumAssetsAndSubtype, 'deliverOneResult' ),
			getProjectAlbumAssets = makeCatalogBasedObservableIntoRunUntilDoneSubModel( WFCatAlbums.getProjectAlbumAssetIds, 'deliverOneResult' ),
			getProjectAlbumAssetsAndSubtype = makeCatalogBasedObservableIntoRunUntilDoneSubModel( getProjectAlbumAssetsAndSubtype, 'deliverOneResult' ),
			
			sharedAlbumUrl = makeCatalogBasedObservableIntoSubModel( WFCatCollaboration.sharedAlbumUrl ),
			getLrWebShareUrl = makeCatalogBasedObservableIntoSubModel( WFCatCollaboration.getLrWebShareUrl ),
			albumCover = makeCatalogBasedObservableIntoSubModel( WFCatAlbums.albumCover ),
			getSharedAlbumAttributes = makeCatalogBasedObservableIntoSubModel( WFCatCollaboration.getSharedAlbumAttributes ),
			setSharedAlbumAttributes = makeCatalogBasedObservableIntoSubModel( WFCatCollaboration.setSharedAlbumAttributes ),
	
			hasSharedAlbumsWithDisplayFilters = makeCatalogBasedObservableIntoSubModel( WFCatCollaboration.hasSharedAlbumsWithDisplayFilters ),
			deleteDisplayFiltersFromAlbum = makeCatalogBasedObservableIntoSubModel( WFCatCollaboration.deleteDisplayFiltersFromAlbum ),
			getCollaborators = makeCatalogBasedObservableIntoSubModel( WFCatCollaboration.getCollaborators ),
			inviteByEmail = makeCatalogBasedObservableIntoSubModel( WFCatCollaboration.inviteByEmail ),
			getPendingRequests = makeCatalogBasedObservableIntoSubModel( WFCatCollaboration.getPendingRequests ),
			getInvitations = makeCatalogBasedObservableIntoSubModel( WFCatCollaboration.getInvitations ),
			updateCollaborators = makeCatalogBasedObservableIntoSubModel( WFCatCollaboration.updateCollaborators ),
			spaceNeededToContributeAssets = makeCatalogBasedObservableIntoSubModel( WFCatSharedWithYouUtils.spaceNeededToContributeAssets ),
	
			waitForAlbumSync = makeCatalogBasedObservableIntoSubModel( WFCatCollaboration.waitForAlbumSync ),

			createConnectionFromAlbum = makeCatalogBasedObservableIntoRunUntilDoneSubModel( NmbConnectors.createConnectionFromAlbum, 'deliverOneResult' ),
			createConnectionFromPhotos = makeCatalogBasedObservableIntoRunUntilDoneSubModel( NmbConnectors.createConnectionFromPhotos, 'deliverOneResult' ),
			updateConnection = makeCatalogBasedObservableIntoRunUntilDoneSubModel( NmbConnectors.updateConnection, 'deliverOneResult' ),
			getConnectionAttributes = makeCatalogBasedObservableIntoRunUntilDoneSubModel( NmbConnectors.getConnectionAttributes, 'deliverOneResult' ),
			setProjectState = makeCatalogBasedObservableIntoRunUntilDoneSubModel( NmbConnectors.setProjectState, 'deliverOneResult' ),
			
		},

	}

end

--------------------------------------------------------------------------------

return NmbAlbums
