Back

73.46% Statements 36/49
52.63% Branches 10/19
84.84% Functions 28/33
70.45% Lines 31/44

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257                              44x                             2x                 29x   2x   3x   2x                     29x   5x   61x                 4x                   29x   2x 2x 3x 1x   2x                   29x   3x   2x                   29x   3x   2x                       4x                       8x                   153x                             2x                       16x 5x                         45x                                                                   9x           29x                                                      
import { getScriptData } from '@automattic/jetpack-script-data';
import { store as coreStore } from '@wordpress/core-data';
import { createRegistrySelector, createSelector } from '@wordpress/data';
import { REQUEST_TYPE_DEFAULT } from '../actions/constants';
import { EMPTY_ARRAY } from '../constants';
import type { Connection, ConnectionData, SocialStoreState } from '../types';
 
/**
 * Returns the connections list from the store.
 *
 * @param state - State object.
 *
 * @return The connections list
 */
export function getConnections( state: SocialStoreState ): Array< Connection > {
	return state.connectionData?.connections ?? EMPTY_ARRAY;
}
 
/**
 * Return a connection by its ID.
 *
 * @param state        - State object.
 * @param connectionId - The connection ID.
 *
 * @return The connection.
 */
export function getConnectionById(
	state: SocialStoreState,
	connectionId: string
): Connection | undefined {
	return getConnections( state ).find( connection => connection.connection_id === connectionId );
}
 
/**
 * Returns the broken connections.
 *
 * @param state - State object.
 * @return List of broken connections.
 */
export const getBrokenConnections = createSelector(
	( state: SocialStoreState ) => {
		const connections = getConnections( state );
 
		return connections.filter( connection => 'broken' === connection.status );
	},
	( state: SocialStoreState ) => [ state.connectionData?.connections ]
);
 
/**
 * Returns connections by service name/ID.
 *
 * @param state       - State object.
 * @param serviceName - The service name.
 *
 * @return  The connections.
 */
export const getConnectionsByService = createSelector(
	( state: SocialStoreState, serviceName: string ) => {
		return getConnections( state ).filter( ( { service_name } ) => service_name === serviceName );
	},
	( state: SocialStoreState ) => [ state.connectionData?.connections ]
);
 
/**
 * Returns whether there are connections in the store.
 * @param state - State object.
 * @return Whether there are connections.
 */
export function hasConnections( state: SocialStoreState ) {
	return getConnections( state ).length > 0;
}
 
/**
 * Returns a list of Publicize connection service names that require reauthentication from users.
 * For example, when LinkedIn switched its API from v1 to v2.
 *
 * @param state - State object.
 * @return List of service names that need reauthentication.
 */
export const getMustReauthConnections = createSelector(
	( state: SocialStoreState ) => {
		const connections = getConnections( state );
		return connections
			.filter( connection => 'must_reauth' === connection.status )
			.map( connection => connection.service_name );
	},
	( state: SocialStoreState ) => [ state.connectionData?.connections ]
);
 
/**
 * Returns the Publicize connections that are enabled.
 *
 * @param state - State object.
 *
 * @return List of enabled connections.
 */
export const getEnabledConnections = createSelector(
	( state: SocialStoreState ) => {
		return getConnections( state ).filter( connection => connection.enabled );
	},
	( state: SocialStoreState ) => [ state.connectionData?.connections ]
);
 
/**
 * Returns the Publicize connections that are disabled.
 *
 * @param state - State object.
 *
 * @return List of disabled connections.
 */
export const getDisabledConnections = createSelector(
	( state: SocialStoreState ) => {
		return getConnections( state ).filter( connection => ! connection.enabled );
	},
	( state: SocialStoreState ) => [ state.connectionData?.connections ]
);
 
/**
 * Get the connections being deleted.
 *
 * @param state - State object.
 * @return The connection being deleted.
 */
export function getDeletingConnections(
	state: SocialStoreState
): ConnectionData[ 'deletingConnections' ] {
	return state.connectionData?.deletingConnections ?? EMPTY_ARRAY;
}
 
/**
 * Get the connections being updated.
 *
 * @param state - State object.
 * @return The connection being updated.
 */
export function getUpdatingConnections(
	state: SocialStoreState
): ConnectionData[ 'updatingConnections' ] {
	return state.connectionData?.updatingConnections ?? EMPTY_ARRAY;
}
 
/**
 * Get the account being reconnected
 *
 * @param state - State object.
 * @return The account being reconnected.
 */
export function getReconnectingAccount( state: SocialStoreState ) {
	return state.connectionData?.reconnectingAccount;
}
 
/**
 * Get the abort controllers for a specific request type.
 *
 * @param state       - State object.
 * @param requestType - The request type.
 *
 * @return  The abort controllers.
 */
export function getAbortControllers(
	state: SocialStoreState,
	requestType = REQUEST_TYPE_DEFAULT
): Array< AbortController > {
	return state.connectionData?.abortControllers?.[ requestType ] ?? EMPTY_ARRAY;
}
 
/**
 * Whether a mastodon account is already connected.
 *
 * @param state  - State object.
 * @param handle - The mastodon handle.
 *
 * @return Whether the mastodon account is already connected.
 */
export function isMastodonAccountAlreadyConnected( state: SocialStoreState, handle: string ) {
	return getConnectionsByService( state, 'mastodon' ).some( connection => {
		return connection.external_handle === handle;
	} );
}
 
/**
 * Whether a Bluesky account is already connected.
 *
 * @param state  - State object.
 * @param handle - The Bluesky handle.
 *
 * @return Whether the Bluesky account is already connected.
 */
export function isBlueskyAccountAlreadyConnected( state: SocialStoreState, handle: string ) {
	return getConnectionsByService( state, 'bluesky' ).some( connection => {
		return connection.external_handle === handle;
	} );
}
 
/**
 * Returns the latest KeyringResult from the store.
 *
 * @param state - State object.
 *
 * @return The KeyringResult
 */
export function getKeyringResult( state: SocialStoreState ) {
	return state.connectionData?.keyringResult;
}
 
/**
 * Whether the keyring result for a completed connect request is being fetched.
 *
 * @param state - State object.
 *
 * @return Whether the keyring result is being fetched.
 */
export function isFetchingKeyringResult( state: SocialStoreState ) {
	return Boolean( state.connectionData?.fetchingKeyringResult );
}
 
/**
 * Whether the connections modal is open.
 * @param state - State object.
 *
 * @return Whether the connections modal is open.
 */
export function isConnectionsModalOpen( state: SocialStoreState ) {
	return state.connectionData?.isConnectionsModalOpen ?? false;
}
 
/**
 * Whether the current user can manage the connection.
 */
export const canUserManageConnection = createRegistrySelector(
	select =>
		( state: SocialStoreState, connectionOrId: Connection | string ): boolean => {
			const connection =
				typeof connectionOrId === 'string'
					? getConnectionById( state, connectionOrId )
					: connectionOrId;
 
			const { current_user } = getScriptData().user;
 
			// If the current user is the connection owner.
			if ( current_user.wpcom?.ID === connection.wpcom_user_id ) {
				return true;
			}
 
			const isEditorOrAbove = current_user.capabilities?.edit_others_posts;
 
			if ( undefined !== isEditorOrAbove ) {
				return isEditorOrAbove;
			}
 
			const { getUser } = select( coreStore );
 
			// The user has to be at least an editor to manage the connection.
			return getUser( current_user.id )?.capabilities?.edit_others_posts ?? false;
		}
);