From 68b7551190c1957d03bd6865b0e3c79e56d1c3f4 Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Thu, 16 May 2024 18:14:36 +0200 Subject: [PATCH 01/11] add error message in case of sync failure --- src/components/MenuItem.tsx | 12 ++- src/languages/en.ts | 11 +++ src/libs/actions/connections/index.ts | 9 ++- .../accounting/PolicyAccountingPage.tsx | 80 ++++++++++--------- 4 files changed, 72 insertions(+), 40 deletions(-) diff --git a/src/components/MenuItem.tsx b/src/components/MenuItem.tsx index ad06f9fcb78c..f0bcf7b1f062 100644 --- a/src/components/MenuItem.tsx +++ b/src/components/MenuItem.tsx @@ -153,9 +153,15 @@ type MenuItemBaseProps = { /** Error to display at the bottom of the component */ errorText?: MaybePhraseKey; + /** Any additional styles to pass to error text. */ + errorTextStyle?: StyleProp; + /** Hint to display at the bottom of the component */ hintText?: MaybePhraseKey; + /** Should the description be shown above the title (instead of the other way around) */ + shouldShowErrorTextRedDot?: boolean; + /** A boolean flag that gives the icon a green fill if true */ success?: boolean; @@ -308,6 +314,8 @@ function MenuItem( helperText, helperTextStyle, errorText, + errorTextStyle, + shouldShowErrorTextRedDot, hintText, success = false, focused = false, @@ -683,9 +691,9 @@ function MenuItem( {!!errorText && ( )} {!!hintText && ( diff --git a/src/languages/en.ts b/src/languages/en.ts index f8d122f3b69a..05359df253d2 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2338,6 +2338,17 @@ export default { } } }, + syncError: (integration?: ConnectionName): string => { + switch (integration) { + case CONST.POLICY.CONNECTIONS.NAME.QBO: + return "Couldn't connect to QuickBooks Online due to incorrect credentials."; + case CONST.POLICY.CONNECTIONS.NAME.XERO: + return "Couldn't connect to Xero due to incorrect credentials."; + default: { + return "Couldn't connect to integration due to incorrect credentials."; + } + } + }, accounts: 'Chart of accounts', taxes: 'Taxes', imported: 'Imported', diff --git a/src/libs/actions/connections/index.ts b/src/libs/actions/connections/index.ts index 4a501910548b..8d0eda8e5c88 100644 --- a/src/libs/actions/connections/index.ts +++ b/src/libs/actions/connections/index.ts @@ -1,5 +1,5 @@ import Onyx from 'react-native-onyx'; -import type {OnyxUpdate} from 'react-native-onyx'; +import type {OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import * as API from '@libs/API'; import type {RemovePolicyConnectionParams, UpdateManyPolicyConnectionConfigurationsParams, UpdatePolicyConnectionConfigParams} from '@libs/API/parameters'; import {WRITE_COMMANDS} from '@libs/API/types'; @@ -7,6 +7,7 @@ import * as ErrorUtils from '@libs/ErrorUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {ConnectionName, Connections, PolicyConnectionName} from '@src/types/onyx/Policy'; +import type Policy from '@src/types/onyx/Policy'; function removePolicyConnection(policyID: string, connectionName: PolicyConnectionName) { const optimisticData: OnyxUpdate[] = [ @@ -183,4 +184,8 @@ function updateManyPolicyConnectionConfigs, connectionName: PolicyConnectionName): boolean { + return policy?.connections?.[connectionName].lastSync?.isSuccessful === false; +} + +export {removePolicyConnection, updatePolicyConnectionConfig, updateManyPolicyConnectionConfigs, hasSynchronizationError}; diff --git a/src/pages/workspace/accounting/PolicyAccountingPage.tsx b/src/pages/workspace/accounting/PolicyAccountingPage.tsx index d6ef27c7b00d..4bfa1299a69c 100644 --- a/src/pages/workspace/accounting/PolicyAccountingPage.tsx +++ b/src/pages/workspace/accounting/PolicyAccountingPage.tsx @@ -2,7 +2,6 @@ import React, {useMemo, useRef, useState} from 'react'; import {ActivityIndicator, View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; -import AccountingListSkeletonView from '@components/AccountingListSkeletonView'; import CollapsibleSection from '@components/CollapsibleSection'; import ConfirmModal from '@components/ConfirmModal'; import ConnectToQuickbooksOnlineButton from '@components/ConnectToQuickbooksOnlineButton'; @@ -12,7 +11,10 @@ import * as Expensicons from '@components/Icon/Expensicons'; import * as Illustrations from '@components/Icon/Illustrations'; import type {LocaleContextProps} from '@components/LocaleContextProvider'; import type {MenuItemProps} from '@components/MenuItem'; +import MenuItem from '@components/MenuItem'; import MenuItemList from '@components/MenuItemList'; +import type {OfflineWithFeedbackProps} from '@components/OfflineWithFeedback'; +import OfflineWithFeedback from '@components/OfflineWithFeedback'; import ScreenWrapper from '@components/ScreenWrapper'; import ScrollView from '@components/ScrollView'; import Section from '@components/Section'; @@ -24,7 +26,7 @@ import usePermissions from '@hooks/usePermissions'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import {removePolicyConnection} from '@libs/actions/connections'; +import {hasSynchronizationError, removePolicyConnection} from '@libs/actions/connections'; import {syncConnection} from '@libs/actions/connections/QuickBooksOnline'; import {findCurrentXeroOrganization, getCurrentXeroOrganizationName, getXeroTenants} from '@libs/PolicyUtils'; import Navigation from '@navigation/Navigation'; @@ -40,6 +42,8 @@ import type {PolicyConnectionName} from '@src/types/onyx/Policy'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import type IconAsset from '@src/types/utils/IconAsset'; +type MenuItemData = MenuItemProps & {pendingAction?: OfflineWithFeedbackProps['pendingAction']; errors?: OfflineWithFeedbackProps['errors']}; + type PolicyAccountingPageOnyxProps = { connectionSyncProgress: OnyxEntry; }; @@ -101,7 +105,7 @@ function accountingIntegrationData( } } -function PolicyAccountingPage({policy, connectionSyncProgress, isConnectionDataFetchNeeded}: PolicyAccountingPageProps) { +function PolicyAccountingPage({policy, connectionSyncProgress}: PolicyAccountingPageProps) { const theme = useTheme(); const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -141,7 +145,7 @@ function PolicyAccountingPage({policy, connectionSyncProgress, isConnectionDataF [translate, policyID, isOffline], ); - const connectionsMenuItems: MenuItemProps[] = useMemo(() => { + const connectionsMenuItems: MenuItemData[] = useMemo(() => { if (isEmptyObject(policy?.connections) && !isSyncInProgress) { return accountingIntegrations.map((integration) => { const integrationData = accountingIntegrationData(integration, policyID, translate); @@ -169,7 +173,9 @@ function PolicyAccountingPage({policy, connectionSyncProgress, isConnectionDataF wrapperStyle: [styles.sectionMenuItemTopDescription], shouldShowRightComponent: true, title: integrationData?.title, - + errorText: hasSynchronizationError(policy, connectedIntegration) ? translate('workspace.accounting.syncError', connectedIntegration) : undefined, + errorTextStyle: {marginTop: 8}, + shouldShowErrorTextRedDot: true, description: isSyncInProgress ? translate('workspace.accounting.connections.syncStageName', connectionSyncProgress.stageInProgress) : translate('workspace.accounting.lastSync'), @@ -212,6 +218,8 @@ function PolicyAccountingPage({policy, connectionSyncProgress, isConnectionDataF } Navigation.navigate(ROUTES.POLICY_ACCOUNTING_XERO_ORGANIZATION.getRoute(policyID, currentXeroOrganization?.id ?? '')); }, + pendingAction: policy?.connections?.xero?.config?.pendingFields?.tenantID, + brickRoadIndicator: policy?.connections?.xero?.config?.errorFields?.tenantID ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined, }, ] : []), @@ -245,21 +253,23 @@ function PolicyAccountingPage({policy, connectionSyncProgress, isConnectionDataF ]), ]; }, [ - connectedIntegration, - connectionSyncProgress?.stageInProgress, - currentXeroOrganization, - currentXeroOrganizationName, - tenants, + policy, isSyncInProgress, - overflowMenu, - policy?.connections, - policyConnectedToXero, + connectedIntegration, policyID, - styles, + translate, + styles.sectionMenuItemTopDescription, + styles.popoverMenuIcon, + styles.fontWeightNormal, + connectionSyncProgress?.stageInProgress, theme.spinner, + overflowMenu, threeDotsMenuPosition, - translate, + policyConnectedToXero, + currentXeroOrganizationName, + tenants.length, accountingIntegrations, + currentXeroOrganization?.id, ]); const otherIntegrationsItems = useMemo(() => { @@ -336,30 +346,28 @@ function PolicyAccountingPage({policy, connectionSyncProgress, isConnectionDataF titleStyles={styles.accountSettingsSectionTitle} childrenStyles={styles.pt5} > - {isConnectionDataFetchNeeded ? ( - - - - ) : ( - <> + {connectionsMenuItems.map((menuItem) => ( + + + + ))} + {otherIntegrationsItems && ( + - {otherIntegrationsItems && ( - - - - )} - + )} From ecdeaf425ff8bc1639828422ce168149de1fa399 Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Mon, 20 May 2024 10:47:41 +0200 Subject: [PATCH 02/11] add translations --- src/languages/es.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/languages/es.ts b/src/languages/es.ts index 536a6182f393..20450b43d647 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2342,6 +2342,17 @@ export default { } } }, + syncError: (integration?: ConnectionName): string => { + switch (integration) { + case CONST.POLICY.CONNECTIONS.NAME.QBO: + return 'No se pudo conectar a QuickBooks Online debido a credenciales incorrectas.'; + case CONST.POLICY.CONNECTIONS.NAME.XERO: + return 'No se pudo conectar a Xero debido a credenciales incorrectas.'; + default: { + return 'No se pudo conectar a la integración debido a credenciales incorrectas.'; + } + } + }, accounts: 'Plan de cuentas', taxes: 'Impuestos', imported: 'Importado', From e87d711b2e5f47e886a18b66b7dd6b777f60680c Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Mon, 20 May 2024 10:56:39 +0200 Subject: [PATCH 03/11] delete unrelated changes --- src/pages/workspace/accounting/PolicyAccountingPage.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/pages/workspace/accounting/PolicyAccountingPage.tsx b/src/pages/workspace/accounting/PolicyAccountingPage.tsx index 4bfa1299a69c..f0868ad4e15d 100644 --- a/src/pages/workspace/accounting/PolicyAccountingPage.tsx +++ b/src/pages/workspace/accounting/PolicyAccountingPage.tsx @@ -218,8 +218,6 @@ function PolicyAccountingPage({policy, connectionSyncProgress}: PolicyAccounting } Navigation.navigate(ROUTES.POLICY_ACCOUNTING_XERO_ORGANIZATION.getRoute(policyID, currentXeroOrganization?.id ?? '')); }, - pendingAction: policy?.connections?.xero?.config?.pendingFields?.tenantID, - brickRoadIndicator: policy?.connections?.xero?.config?.errorFields?.tenantID ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined, }, ] : []), From 27972bffa735dec62973c7164e5d3530cf0fb4f9 Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Mon, 20 May 2024 11:12:17 +0200 Subject: [PATCH 04/11] fix PR comments --- src/components/MenuItem.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/MenuItem.tsx b/src/components/MenuItem.tsx index f0bcf7b1f062..1365824e8eb0 100644 --- a/src/components/MenuItem.tsx +++ b/src/components/MenuItem.tsx @@ -159,7 +159,7 @@ type MenuItemBaseProps = { /** Hint to display at the bottom of the component */ hintText?: MaybePhraseKey; - /** Should the description be shown above the title (instead of the other way around) */ + /** Should the error text red dot indicator be shown */ shouldShowErrorTextRedDot?: boolean; /** A boolean flag that gives the icon a green fill if true */ From ef5d777a0a3ae95f6923998605cee9f11e767e7b Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Mon, 20 May 2024 17:31:20 +0200 Subject: [PATCH 05/11] fix connections lastSync unsafe access --- src/libs/actions/connections/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/connections/index.ts b/src/libs/actions/connections/index.ts index 2882d9449ccc..0882d1a4ed67 100644 --- a/src/libs/actions/connections/index.ts +++ b/src/libs/actions/connections/index.ts @@ -233,7 +233,7 @@ function updateManyPolicyConnectionConfigs, connectionName: PolicyConnectionName): boolean { - return policy?.connections?.[connectionName].lastSync?.isSuccessful === false; + return policy?.connections?.[connectionName]?.lastSync?.isSuccessful === false; } export {removePolicyConnection, updatePolicyConnectionConfig, updateManyPolicyConnectionConfigs, hasSynchronizationError, syncConnection}; From 5979a35a1405c53f90d06b8938a9b3fe31201017 Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Thu, 23 May 2024 17:25:13 +0200 Subject: [PATCH 06/11] change translations --- src/languages/en.ts | 6 +++--- src/languages/es.ts | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index d769f5222368..ebf51413924d 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2343,11 +2343,11 @@ export default { syncError: (integration?: ConnectionName): string => { switch (integration) { case CONST.POLICY.CONNECTIONS.NAME.QBO: - return "Couldn't connect to QuickBooks Online due to incorrect credentials."; + return "Couldn't connect to QuickBooks Online."; case CONST.POLICY.CONNECTIONS.NAME.XERO: - return "Couldn't connect to Xero due to incorrect credentials."; + return "Couldn't connect to Xero."; default: { - return "Couldn't connect to integration due to incorrect credentials."; + return "Couldn't connect to integration."; } } }, diff --git a/src/languages/es.ts b/src/languages/es.ts index 183af720f09c..9440381c6c6a 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2347,11 +2347,11 @@ export default { syncError: (integration?: ConnectionName): string => { switch (integration) { case CONST.POLICY.CONNECTIONS.NAME.QBO: - return 'No se pudo conectar a QuickBooks Online debido a credenciales incorrectas.'; + return 'No se puede conectar a QuickBooks Online.'; case CONST.POLICY.CONNECTIONS.NAME.XERO: - return 'No se pudo conectar a Xero debido a credenciales incorrectas.'; + return 'No se puede conectar a Xero'; default: { - return 'No se pudo conectar a la integración debido a credenciales incorrectas.'; + return 'No se ha podido conectar a la integración.'; } } }, From 25db9b76ea092a55a876cf6917dc2b36412ccc6f Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Mon, 27 May 2024 11:20:58 +0200 Subject: [PATCH 07/11] fix style --- .../workspace/accounting/PolicyAccountingPage.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/pages/workspace/accounting/PolicyAccountingPage.tsx b/src/pages/workspace/accounting/PolicyAccountingPage.tsx index 7a5cdc1b027b..05c0711a4eb8 100644 --- a/src/pages/workspace/accounting/PolicyAccountingPage.tsx +++ b/src/pages/workspace/accounting/PolicyAccountingPage.tsx @@ -163,6 +163,7 @@ function PolicyAccountingPage({policy, connectionSyncProgress}: PolicyAccounting if (!connectedIntegration) { return []; } + const shouldShowSynchronizationError = hasSynchronizationError(policy, connectedIntegration); const integrationData = accountingIntegrationData(connectedIntegration, policyID, translate); const iconProps = integrationData?.icon ? {icon: integrationData.icon, iconType: CONST.ICON_TYPE_AVATAR} : {}; return [ @@ -172,8 +173,8 @@ function PolicyAccountingPage({policy, connectionSyncProgress}: PolicyAccounting wrapperStyle: [styles.sectionMenuItemTopDescription], shouldShowRightComponent: true, title: integrationData?.title, - errorText: hasSynchronizationError(policy, connectedIntegration) ? translate('workspace.accounting.syncError', connectedIntegration) : undefined, - errorTextStyle: {marginTop: 8}, + errorText: shouldShowSynchronizationError ? translate('workspace.accounting.syncError', connectedIntegration) : undefined, + errorTextStyle: [styles.mt5], shouldShowErrorTextRedDot: true, description: isSyncInProgress ? translate('workspace.accounting.connections.syncStageName', connectionSyncProgress.stageInProgress) @@ -201,7 +202,7 @@ function PolicyAccountingPage({policy, connectionSyncProgress}: PolicyAccounting ), }, - ...(policyConnectedToXero + ...(policyConnectedToXero && !shouldShowSynchronizationError ? [ { description: translate('workspace.xero.organization'), @@ -222,7 +223,7 @@ function PolicyAccountingPage({policy, connectionSyncProgress}: PolicyAccounting }, ] : []), - ...(isEmptyObject(policy?.connections) + ...(isEmptyObject(policy?.connections) || shouldShowSynchronizationError ? [] : [ { @@ -258,6 +259,7 @@ function PolicyAccountingPage({policy, connectionSyncProgress}: PolicyAccounting policyID, translate, styles.sectionMenuItemTopDescription, + styles.mt5, styles.popoverMenuIcon, styles.fontWeightNormal, connectionSyncProgress?.stageInProgress, From b779dffebb1502dfcb8dff13b8628f526be387d4 Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Mon, 27 May 2024 11:49:11 +0200 Subject: [PATCH 08/11] fix styles --- src/pages/workspace/accounting/PolicyAccountingPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/workspace/accounting/PolicyAccountingPage.tsx b/src/pages/workspace/accounting/PolicyAccountingPage.tsx index 05c0711a4eb8..3e3bc48e55a3 100644 --- a/src/pages/workspace/accounting/PolicyAccountingPage.tsx +++ b/src/pages/workspace/accounting/PolicyAccountingPage.tsx @@ -170,7 +170,7 @@ function PolicyAccountingPage({policy, connectionSyncProgress}: PolicyAccounting { ...iconProps, interactive: false, - wrapperStyle: [styles.sectionMenuItemTopDescription], + wrapperStyle: [styles.sectionMenuItemTopDescription, shouldShowSynchronizationError && styles.pb0], shouldShowRightComponent: true, title: integrationData?.title, errorText: shouldShowSynchronizationError ? translate('workspace.accounting.syncError', connectedIntegration) : undefined, From 86f509f800e67910a3641d1434beac5c55cba9f2 Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Mon, 27 May 2024 17:11:03 +0200 Subject: [PATCH 09/11] fix lint errors --- src/pages/workspace/accounting/PolicyAccountingPage.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/workspace/accounting/PolicyAccountingPage.tsx b/src/pages/workspace/accounting/PolicyAccountingPage.tsx index 3e3bc48e55a3..a9d62451365e 100644 --- a/src/pages/workspace/accounting/PolicyAccountingPage.tsx +++ b/src/pages/workspace/accounting/PolicyAccountingPage.tsx @@ -259,10 +259,11 @@ function PolicyAccountingPage({policy, connectionSyncProgress}: PolicyAccounting policyID, translate, styles.sectionMenuItemTopDescription, + styles.pb0, styles.mt5, styles.popoverMenuIcon, styles.fontWeightNormal, - connectionSyncProgress?.stageInProgress, + connectionSyncProgress.stageInProgress, theme.spinner, overflowMenu, threeDotsMenuPosition, From adddbcd036eb417e45b6127c3b305b565df07098 Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Mon, 27 May 2024 17:25:23 +0200 Subject: [PATCH 10/11] fix type error --- src/pages/workspace/accounting/PolicyAccountingPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/workspace/accounting/PolicyAccountingPage.tsx b/src/pages/workspace/accounting/PolicyAccountingPage.tsx index a9d62451365e..6a5fdd4b6b80 100644 --- a/src/pages/workspace/accounting/PolicyAccountingPage.tsx +++ b/src/pages/workspace/accounting/PolicyAccountingPage.tsx @@ -263,7 +263,7 @@ function PolicyAccountingPage({policy, connectionSyncProgress}: PolicyAccounting styles.mt5, styles.popoverMenuIcon, styles.fontWeightNormal, - connectionSyncProgress.stageInProgress, + connectionSyncProgress?.stageInProgress, theme.spinner, overflowMenu, threeDotsMenuPosition, From 4ce01d2bf7a326c43388ded16717ee4817916f64 Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Tue, 28 May 2024 10:10:55 +0200 Subject: [PATCH 11/11] fix PR comments --- src/components/MenuItem.tsx | 6 +++--- src/pages/workspace/accounting/PolicyAccountingPage.tsx | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/MenuItem.tsx b/src/components/MenuItem.tsx index 1365824e8eb0..c10da1800694 100644 --- a/src/components/MenuItem.tsx +++ b/src/components/MenuItem.tsx @@ -160,7 +160,7 @@ type MenuItemBaseProps = { hintText?: MaybePhraseKey; /** Should the error text red dot indicator be shown */ - shouldShowErrorTextRedDot?: boolean; + shouldShowRedDotIndicator?: boolean; /** A boolean flag that gives the icon a green fill if true */ success?: boolean; @@ -315,7 +315,7 @@ function MenuItem( helperTextStyle, errorText, errorTextStyle, - shouldShowErrorTextRedDot, + shouldShowRedDotIndicator, hintText, success = false, focused = false, @@ -691,7 +691,7 @@ function MenuItem( {!!errorText && ( diff --git a/src/pages/workspace/accounting/PolicyAccountingPage.tsx b/src/pages/workspace/accounting/PolicyAccountingPage.tsx index 6a5fdd4b6b80..d33611ec09f2 100644 --- a/src/pages/workspace/accounting/PolicyAccountingPage.tsx +++ b/src/pages/workspace/accounting/PolicyAccountingPage.tsx @@ -175,7 +175,7 @@ function PolicyAccountingPage({policy, connectionSyncProgress}: PolicyAccounting title: integrationData?.title, errorText: shouldShowSynchronizationError ? translate('workspace.accounting.syncError', connectedIntegration) : undefined, errorTextStyle: [styles.mt5], - shouldShowErrorTextRedDot: true, + shouldShowRedDotIndicator: true, description: isSyncInProgress ? translate('workspace.accounting.connections.syncStageName', connectionSyncProgress.stageInProgress) : translate('workspace.accounting.lastSync'),