Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
2f20722
fix: retry requests without final status
S-FrontendDev May 17, 2025
4d0d2a0
fix: types
S-FrontendDev May 17, 2025
3e044af
fix: code split
S-FrontendDev May 17, 2025
310710b
fix: test log
S-FrontendDev May 17, 2025
7148e40
fix: remove test log
S-FrontendDev May 17, 2025
86038eb
fix: add additional checkers
S-FrontendDev May 17, 2025
475c25d
fix: remove log
S-FrontendDev May 17, 2025
7b2742f
fix: add util for check error
S-FrontendDev May 20, 2025
4835cc6
fix: check new transaction
S-FrontendDev May 22, 2025
008e443
fix: change isNew params
S-FrontendDev May 22, 2025
b69b428
fix: sync statuses
S-FrontendDev May 31, 2025
a5b3546
chore: code style
S-FrontendDev Jun 4, 2025
1c5c228
Merge branch 'dev' into fix/senging-algo-sync
S-FrontendDev Jun 4, 2025
524b1fd
chore: rename store
S-FrontendDev Jun 4, 2025
7a889ed
chore: early return for condition
S-FrontendDev Jun 4, 2025
04d8587
fix: refetch interval fix
S-FrontendDev Jun 4, 2025
9d161c8
Merge branch 'dev' into fix/senging-algo-sync
S-FrontendDev Jun 17, 2025
6bbfebe
fix: add CONFIRMED status for sync
S-FrontendDev Jun 17, 2025
47bda55
fix: check finalStatus
S-FrontendDev Jun 17, 2025
107d782
fix: wrong coin status icon
S-FrontendDev Jun 17, 2025
2832039
fix: add reset handler
S-FrontendDev Jun 17, 2025
4765d8a
Merge branch 'dev' into fix/senging-algo-sync
S-FrontendDev Jun 19, 2025
3451eec
fix: clear
S-FrontendDev Jun 19, 2025
7eb7774
Merge branch 'dev' into fix/senging-algo-sync
S-FrontendDev Jun 22, 2025
7c5f418
chore: conflict resolve
S-FrontendDev Jun 22, 2025
33c4381
fix: isTransferType in ChatPreview
S-FrontendDev Jun 22, 2025
c7564b1
Merge branch 'dev' into fix/senging-algo-sync
S-FrontendDev Jun 29, 2025
6b15b1b
fix: check status in tx info when mounted
S-FrontendDev Jun 29, 2025
e4a3340
fix: clear
S-FrontendDev Jun 29, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
306 changes: 278 additions & 28 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
"path-browserify": "^1.0.1",
"pbkdf2": "^3.1.2",
"pinia": "^3.0.0",
"pinia-plugin-persistedstate": "^4.3.0",
"popmotion": "^11.0.5",
"promise-queue": "^2.2.5",
"qrcode": "^1.5.4",
Expand Down
2 changes: 1 addition & 1 deletion src/components/AChat/AChatAttachment/AChatAttachment.vue
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ export default defineComponent({
}
const showAvatar = computed(() => !isWelcomeChat(partnerId.value))

const statusIcon = computed(() => tsIcon(props.transaction.status))
const statusIcon = computed(() => tsIcon(props.transaction.status, true))
const isOutgoingMessage = computed(() =>
isStringEqualCI(props.transaction.senderId, userId.value)
)
Expand Down
2 changes: 1 addition & 1 deletion src/components/AChat/AChatMessage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ export default defineComponent({

const showAvatar = computed(() => !isWelcomeChat(partnerId.value))

const statusIcon = computed(() => tsIcon(props.transaction.status))
const statusIcon = computed(() => tsIcon(props.transaction.status, true))
const isOutgoingMessage = computed(() =>
isStringEqualCI(props.transaction.senderId, userId.value)
)
Expand Down
2 changes: 1 addition & 1 deletion src/components/ChatPreview.vue
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ const numOfNewMessages = computed(() => store.getters['chat/numOfNewMessages'](c
const createdAt = computed(() => props.transaction.timestamp)

const status = computed(() => props.transaction.status)
const admStatusIcon = computed(() => tsIcon(status.value))
const admStatusIcon = computed(() => tsIcon(status.value, isTransferType.value))
const isConfirmed = computed(() => status.value === TS.CONFIRMED)
</script>

Expand Down
25 changes: 19 additions & 6 deletions src/components/TransactionListItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@

<v-list-item-subtitle :class="`${className}__date`" class="a-text-explanation-small">
<span v-if="!isStatusVisibleTransaction">{{ formatDate(createdAt) }}</span>
<span v-else-if="status" :class="`${className}__status ${className}__status--${status}`">{{
$t(`transaction.statuses.${status}`)
<span v-else-if="txStatus" :class="`${className}__status ${className}__status--${txStatus}`">{{
$t(`transaction.statuses.${txStatus}`)
}}</span>
</v-list-item-subtitle>

Expand Down Expand Up @@ -69,6 +69,9 @@ import currencyAmount from '@/filters/currencyAmount'
import { timestampInSec } from '@/filters/helpers'
import currency from '@/filters/currencyAmountWithSymbol'
import { mdiAirplaneLanding, mdiAirplaneTakeoff, mdiMessageOutline, mdiMessageText } from '@mdi/js'
import { computed } from 'vue'
import { useFinalTransactions } from '@/stores/final-transactions.js'
import { storeToRefs } from 'pinia'

export default {
mixins: [partnerName],
Expand Down Expand Up @@ -109,8 +112,18 @@ export default {
}
},
emits: ['click:transaction', 'click:icon'],
setup() {
setup(props) {
const finalTransactionsStore = useFinalTransactions()
const { list } = storeToRefs(finalTransactionsStore)

const finalStatus = computed(() => list.value[props.id])

const txStatus = computed(() => {
return finalStatus.value ?? props.status
})

return {
txStatus,
mdiAirplaneLanding,
mdiAirplaneTakeoff,
mdiMessageOutline,
Expand Down Expand Up @@ -201,9 +214,9 @@ export default {
},
isStatusVisibleTransaction() {
return (
this.status === TransactionStatus.PENDING ||
this.status === TransactionStatus.REGISTERED ||
this.status === TransactionStatus.REJECTED
this.txStatus === TransactionStatus.PENDING ||
this.txStatus === TransactionStatus.REGISTERED ||
this.txStatus === TransactionStatus.REJECTED
)
}
},
Expand Down
34 changes: 32 additions & 2 deletions src/components/transactions/TransactionTemplate.vue
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,11 @@
/>
</v-list-item-title>
</template>

<div
:class="[
`${className}__inconsistent-status`,
`${className}__inconsistent-status--${transactionStatus}`
`${className}__inconsistent-status--${finalStatus || transactionStatus}`
]"
>
<v-icon
Expand All @@ -51,7 +52,6 @@
}}<span v-if="inconsistentStatus">{{
': ' + t(`transaction.inconsistent_reasons.${inconsistentStatus}`, { crypto })
}}</span>
<!-- <span v-if="status.addStatus">{{ ': ' + status.addDescription }}</span>-->
</div>
</v-list-item>

Expand Down Expand Up @@ -164,6 +164,8 @@ import {
mdiRefresh
} from '@mdi/js'
import { useFormattedDate } from '@/hooks/useFormattedDate'
import { useFinalTransactions } from '@/stores/final-transactions'
import { storeToRefs } from 'pinia'

const className = 'transaction-view'

Expand Down Expand Up @@ -223,6 +225,11 @@ const props = defineProps({

const emit = defineEmits(['refetch-status'])
const store = useStore()
const finalTransactionsStore = useFinalTransactions()

const { addTransaction, removeTransaction } = finalTransactionsStore
const { list } = storeToRefs(finalTransactionsStore)

const router = useRouter()
const route = useRoute()
const { t } = useI18n()
Expand Down Expand Up @@ -254,7 +261,15 @@ const isPendingQuery = computed(() => props.queryStatus === 'pending')

const isRejectedTransaction = computed(() => props.transactionStatus === TransactionStatus.REJECTED)

const txId = computed(() => route.params.txId as string)

const finalStatus = computed(() => list.value[txId.value])

const formattedTransactionStatus = computed(() => {
if (finalStatus.value) {
return t(`transaction.statuses.${finalStatus.value}`)
}

if (isPendingQuery.value) return Symbols.HOURGLASS

return t(`transaction.statuses.${props.transactionStatus}`)
Expand Down Expand Up @@ -359,6 +374,21 @@ watch(
{ immediate: true }
)

watch(
() => props.transactionStatus,
(value) => {
if (value === TransactionStatus.PENDING) {
removeTransaction(txId.value)
return
}

if (value === TransactionStatus.REJECTED || value === TransactionStatus.CONFIRMED) {
addTransaction(txId.value, value)
}
},
{ immediate: true}
)

const formatAmount = (amount: number, decimals = CryptosInfo[props.crypto].decimals) => {
return BigNumber(amount).decimalPlaces(decimals, BigNumber.ROUND_DOWN).toFixed()
}
Expand Down
5 changes: 4 additions & 1 deletion src/components/transactions/hooks/useTransactionStatus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ export function useTransactionStatus(
inconsistentStatus?: Ref<InconsistentStatus>
) {
return computed(() => {
if (transactionStatus?.value) {
return transactionStatus?.value
}
if (queryStatus.value === 'error') return TransactionStatus.REJECTED
if (queryStatus.value === 'success') {
if (inconsistentStatus?.value) return TransactionStatus.INVALID
Expand All @@ -18,6 +21,6 @@ export function useTransactionStatus(
}
if (isFetching.value) return TransactionStatus.PENDING

return TransactionStatus.UNKNOWN
return TransactionStatus.PENDING
})
}
34 changes: 31 additions & 3 deletions src/hooks/queries/transaction/useTransactionStatusQuery.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,47 @@
import { computed, MaybeRef, unref } from 'vue'
import { computed, MaybeRef, unref, watch } from 'vue'
import { useInconsistentStatus } from '@/components/transactions/hooks/useInconsistentStatus'
import { useTransactionStatus } from '@/components/transactions/hooks/useTransactionStatus'
import { CryptoSymbol } from '@/lib/constants'
import { CryptoSymbol, TransactionStatus } from '@/lib/constants'
import { useTransactionQuery } from './useTransactionQuery'
import { UseTransactionQueryParams } from './types'
import { useFinalTransactions } from '@/stores/final-transactions'
import { storeToRefs } from 'pinia'

export function useTransactionStatusQuery(
transactionId: MaybeRef<string>,
crypto: MaybeRef<CryptoSymbol>,
params: UseTransactionQueryParams = {}
) {
const finalTransactionsStore = useFinalTransactions()

const { addTransaction, removeTransaction } = finalTransactionsStore

const { list } = storeToRefs(finalTransactionsStore)

const {
status: queryStatus,
isFetching,
data: transaction,
refetch
} = useTransactionQuery(transactionId, unref(crypto), params)

const refetcher = () => {
removeTransaction(unref(transactionId))

refetch()
}

const finalStatus = computed(() => list.value[unref(transactionId)])
const inconsistentStatus = useInconsistentStatus(transaction, unref(crypto))
const transactionStatus = computed(() => {
if (finalStatus.value) {
return finalStatus.value
}

if (transaction.value && 'status' in transaction.value) {
return transaction.value.status
}

return undefined
})
const status = useTransactionStatus(
Expand All @@ -30,10 +51,17 @@ export function useTransactionStatusQuery(
inconsistentStatus
)

watch(status, (value) => {
if (value === TransactionStatus.REJECTED || value === TransactionStatus.CONFIRMED) {
addTransaction(unref(transactionId), value)
}
})

return {
queryStatus,
inconsistentStatus,
status,
refetch
refetch: refetcher,
finalStatus
}
}
70 changes: 56 additions & 14 deletions src/hooks/queries/transaction/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,34 +11,69 @@ import {
INSTANT_SEND_INTERVAL,
INSTANT_SEND_TIME
} from '@/lib/transactionsFetching'
import {
isAllNodesDisabledError,
isAllNodesOfflineError,
isNodeOfflineError
} from '@/lib/nodes/utils/errors'
import { isAxiosError } from 'axios'

const isNew = ({
newPendingAttempts,
newPendingInterval,
transaction
}: {
newPendingAttempts: number
newPendingInterval: number
transaction?: {
timestamp?: number
}
}) => {
return (
transaction?.timestamp &&
Date.now() - transaction.timestamp < newPendingAttempts * newPendingInterval
)
}

export function retryFactory(crypto: CryptoSymbol, transactionId: string) {
const txFetchInfo = getTxFetchInfo(crypto)
const { newPendingAttempts, oldPendingAttempts, newPendingInterval } = getTxFetchInfo(crypto)

return (failureCount: number, error: unknown): boolean => {
if (
isAllNodesDisabledError(error as Error) ||
isNodeOfflineError(error as Error) ||
isAllNodesOfflineError(error as Error) ||
isAxiosError(error)
) {
return true
}

return (failureCount: number): boolean => {
const pendingTransaction = PendingTxStore.get(crypto)

const isPendingTransaction = pendingTransaction?.id === transactionId

const attempts = isPendingTransaction
? txFetchInfo.newPendingAttempts
: txFetchInfo.oldPendingAttempts
const attempts =
isPendingTransaction &&
isNew({ newPendingAttempts, newPendingInterval, transaction: pendingTransaction })
? newPendingAttempts
: oldPendingAttempts

return failureCount + 1 < attempts
}
}

export function retryDelayFactory(crypto: CryptoSymbol, transactionId: string) {
const txFetchInfo = getTxFetchInfo(crypto)
const { newPendingInterval, oldPendingInterval, newPendingAttempts } = getTxFetchInfo(crypto)

return (): number => {
const pendingTransaction = PendingTxStore.get(crypto)
const isPendingTransaction = pendingTransaction?.id === transactionId

const delay = isPendingTransaction
? txFetchInfo.newPendingInterval
: txFetchInfo.oldPendingInterval
const isPendingTransaction = pendingTransaction?.id === transactionId

return delay
return isPendingTransaction &&
isNew({ newPendingInterval, newPendingAttempts, transaction: pendingTransaction })
? newPendingInterval
: oldPendingInterval
}
}

Expand All @@ -47,7 +82,8 @@ export function refetchIntervalFactory(
queryStatus: QueryStatus,
transaction?: { status: TransactionStatusType; timestamp?: number }
) {
const txFetchInfo = getTxFetchInfo(crypto)
const { registeredInterval, newPendingInterval, oldPendingInterval, newPendingAttempts } =
getTxFetchInfo(crypto)

if (
queryStatus === 'error' ||
Expand All @@ -61,10 +97,16 @@ export function refetchIntervalFactory(
if (isInstantSendPossible(crypto) && transaction?.timestamp) {
return Date.now() - transaction.timestamp < INSTANT_SEND_TIME
? INSTANT_SEND_INTERVAL
: txFetchInfo.registeredInterval
: registeredInterval
}

if (transaction?.status === TransactionStatus.PENDING) {
return isNew({ newPendingInterval, newPendingAttempts, transaction })
? newPendingInterval
: oldPendingInterval
}

return txFetchInfo.registeredInterval
return registeredInterval
}

export function refetchOnMountFn(transaction?: { status: TransactionStatusType }) {
Expand Down
6 changes: 3 additions & 3 deletions src/lib/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,12 +166,12 @@ export const TransactionAdditionalStatus = {
ADM_REGISTERED: 'adm_registered' // ADM tx, registered in a blockchain, but has 0 confirmations yet
}

export const tsIcon = function (status: TransactionStatusType) {
if (status === TransactionStatus.CONFIRMED || status === TransactionStatus.REGISTERED) {
export const tsIcon = function (status: TransactionStatusType, isMessage = false) {
if (status === TransactionStatus.CONFIRMED || (isMessage && status === TransactionStatus.REGISTERED)) {
return mdiCheck
}

if (status === TransactionStatus.PENDING) {
if (status === TransactionStatus.PENDING || status === TransactionStatus.REGISTERED) {
return mdiClockOutline
}

Expand Down
2 changes: 2 additions & 0 deletions src/plugins/pinia.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { createPinia, defineStore, getActivePinia } from 'pinia'
import piniaPluginPersistedState from 'pinia-plugin-persistedstate'

export const pinia = createPinia()
pinia.use(piniaPluginPersistedState)

export function resetPinia() {
const activePinia = getActivePinia()
Expand Down
Loading