@ -267,7 +267,7 @@ Raymer * @version 1.0.0 */
: confirmer - id - list = "record.confirmerIdList"
@ load - claim = "onClickLoadClaim"
@ view - image = "openImageViewer"
@ cache - image = "cacheImageData"
@ cache - image = "(url: string) => cacheImageData(url) "
@ confirm - claim = "confirmClaim"
/ >
< / ul >
@ -429,6 +429,7 @@ export default class HomeView extends Vue {
selectedImageData : Blob | null = null ;
isImageViewerOpen = false ;
imageCache : Map < string , Blob | null > = new Map ( ) ;
loadMoreTimeout : NodeJS . Timeout | null = null ;
/ * *
* Initializes the component on mount
@ -446,13 +447,26 @@ export default class HomeView extends Vue {
* /
async mounted ( ) {
try {
await this . initializeIdentity ( ) ;
await this . loadSettings ( ) ;
await this . loadContacts ( ) ;
/ / P a r a l l e l i z e i n i t i a l i z a t i o n o p e r a t i o n s
const initPromises = [
this . initializeIdentity ( ) ,
this . loadSettings ( ) ,
this . loadContacts ( )
] ;
await Promise . all ( initPromises ) ;
/ / S e q u e n t i a l o p e r a t i o n s t h a t d e p e n d o n t h e a b o v e
await this . checkRegistrationStatus ( ) ;
await this . loadFeedData ( ) ;
await this . loadNewOffers ( ) ;
await this . checkOnboarding ( ) ;
/ / N o n - c r i t i c a l o p e r a t i o n s t h a t c a n r u n a f t e r U I i s r e a d y
this . loadNewOffers ( ) . catch ( err => {
logger . error ( "Error loading new offers:" , err ) ;
} ) ;
this . checkOnboarding ( ) . catch ( err => {
logger . error ( "Error checking onboarding:" , err ) ;
} ) ;
} catch ( err : unknown ) {
this . handleError ( err ) ;
}
@ -471,7 +485,9 @@ export default class HomeView extends Vue {
* /
private async initializeIdentity ( ) {
try {
/ / L o a d D I D s f i r s t a s i t ' s c r i t i c a l
this . allMyDids = await retrieveAccountDids ( ) ;
if ( this . allMyDids . length === 0 ) {
this . isCreatingIdentifier = true ;
const newDid = await generateSaveAndActivateIdentity ( ) ;
@ -479,75 +495,44 @@ export default class HomeView extends Vue {
this . allMyDids = [ newDid ] ;
}
const settings = await retrieveSettingsForActiveAccount ( ) ;
/ / L o a d s e t t i n g s a n d c o n t a c t s i n p a r a l l e l
const [ settings , contacts ] = await Promise . all ( [
retrieveSettingsForActiveAccount ( ) ,
db . contacts . toArray ( )
] ) ;
/ / U p d a t e s t a t e w i t h l o a d e d d a t a
this . apiServer = settings . apiServer || "" ;
this . activeDid = settings . activeDid || "" ;
this . allContacts = await db . contacts . toArray ( ) ;
this . allContacts = contacts ;
this . feedLastViewedClaimId = settings . lastViewedClaimId ;
this . givenName = settings . firstName || "" ;
this . isFeedFilteredByVisible = ! ! settings . filterFeedByVisible ;
this . isFeedFilteredByNearby = ! ! settings . filterFeedByNearby ;
this . isRegistered = ! ! settings . isRegistered ;
this . lastAckedOfferToUserJwtId = settings . lastAckedOfferToUserJwtId ;
this . lastAckedOfferToUserProjectsJwtId =
settings . lastAckedOfferToUserProjectsJwtId ;
this . lastAckedOfferToUserProjectsJwtId = settings . lastAckedOfferToUserProjectsJwtId ;
this . searchBoxes = settings . searchBoxes || [ ] ;
this . showShortcutBvc = ! ! settings . showShortcutBvc ;
this . isAnyFeedFilterOn = checkIsAnyFeedFilterOn ( settings ) ;
/ / S t a r t n o n - c r i t i c a l o p e r a t i o n s
if ( ! settings . finishedOnboarding ) {
( this . $refs . onboardingDialog as OnboardingDialog ) . open (
OnboardPage . Home ,
) ;
( this . $refs . onboardingDialog as OnboardingDialog ) . open ( OnboardPage . Home ) ;
}
/ / s o m e o n e m a y h a v e h a v e r e g i s t e r e d a f t e r s h a r i n g c o n t a c t i n f o , s o r e c h e c k
/ / C h e c k r e g i s t r a t i o n s t a t u s i n b a c k g r o u n d
if ( ! this . isRegistered && this . activeDid ) {
try {
const resp = await fetchEndorserRateLimits (
this . apiServer ,
this . axios ,
this . activeDid ,
) ;
if ( resp . status === 200 ) {
await updateAccountSettings ( this . activeDid , {
isRegistered : true ,
... ( await retrieveSettingsForActiveAccount ( ) ) ,
} ) ;
this . isRegistered = true ;
}
} catch ( e ) {
/ / i g n o r e t h e e r r o r . . . j u s t k e e p u s u n r e g i s t e r e d
}
this . checkRegistrationStatus ( ) . catch ( err => {
logger . error ( "Error checking registration status:" , err ) ;
} ) ;
}
/ / t h i s r e t u r n s a P r o m i s e b u t w e d o n ' t n e e d t o w a i t f o r i t
this . updateAllFeed ( ) ;
if ( this . activeDid ) {
const offersToUserData = await getNewOffersToUser (
this . axios ,
this . apiServer ,
this . activeDid ,
this . lastAckedOfferToUserJwtId ,
) ;
this . numNewOffersToUser = offersToUserData . data . length ;
this . newOffersToUserHitLimit = offersToUserData . hitLimit ;
}
if ( this . activeDid ) {
const offersToUserProjects = await getNewOffersToUserProjects (
this . axios ,
this . apiServer ,
this . activeDid ,
this . lastAckedOfferToUserProjectsJwtId ,
) ;
this . numNewOffersToUserProjects = offersToUserProjects . data . length ;
this . newOffersToUserProjectsHitLimit = offersToUserProjects . hitLimit ;
}
/ / S t a r t f e e d u p d a t e i n b a c k g r o u n d
this . updateAllFeed ( ) . catch ( err => {
logger . error ( "Error updating feed:" , err ) ;
} ) ;
/ / e s l i n t - d i s a b l e - n e x t - l i n e @ t y p e s c r i p t - e s l i n t / n o - e x p l i c i t - a n y
} catch ( err : any ) {
logConsoleAndDb ( "Error retrieving settings or feed: " + err , true ) ;
this . $notify (
@ -555,8 +540,7 @@ export default class HomeView extends Vue {
group : "alert" ,
type : "danger" ,
title : "Error" ,
text :
( err as { userMessage ? : string } ) ? . userMessage ||
text : ( err as { userMessage ? : string } ) ? . userMessage ||
"There was an error retrieving your settings or the latest activity." ,
} ,
5000 ,
@ -767,11 +751,14 @@ export default class HomeView extends Vue {
* @ param payload Boolean indicating if more items should be loaded
* /
async loadMoreGives ( payload : boolean ) {
/ / S i n c e f e e d n o w l o a d s p r o j e c t s a l o n g t h e w a y , i t t a k e s l o n g e r
/ / a n d t h e I n f i n i t e S c r o l l c o m p o n e n t t r i g g e r s a l o a d b e f o r e f i n i s h e d .
/ / O n e a l t e r n a t i v e i s t o t o t a l l y s e p a r a t e t h e p r o j e c t l i n k l o a d i n g .
if ( payload && ! this . isFeedLoading ) {
await this . updateAllFeed ( ) ;
/ / A d d d e b o u n c e t o p r e v e n t m u l t i p l e r a p i d c a l l s
if ( this . loadMoreTimeout ) {
clearTimeout ( this . loadMoreTimeout ) ;
}
this . loadMoreTimeout = setTimeout ( async ( ) => {
await this . updateAllFeed ( ) ;
} , 300 ) ;
}
}
@ -881,11 +868,20 @@ export default class HomeView extends Vue {
* @ param records Array of feed records to process
* /
private async processFeedResults ( records : GiveSummaryRecord [ ] ) {
for ( const record of records ) {
const processedRecord = await this . processRecord ( record ) ;
if ( processedRecord ) {
this . feedData . push ( processedRecord ) ;
}
/ / P r o c e s s r e c o r d s i n c h u n k s t o a v o i d b l o c k i n g m a i n t h r e a d
const CHUNK_SIZE = 5 ;
for ( let i = 0 ; i < records . length ; i += CHUNK_SIZE ) {
const chunk = records . slice ( i , i + CHUNK_SIZE ) ;
await Promise . all (
chunk . map ( async ( record ) => {
const processedRecord = await this . processRecord ( record ) ;
if ( processedRecord ) {
this . feedData . push ( processedRecord ) ;
}
} )
) ;
/ / A l l o w U I t o u p d a t e b e t w e e n c h u n k s
await new Promise ( resolve => setTimeout ( resolve , 0 ) ) ;
}
this . feedPreviousOldestId = records [ records . length - 1 ] . jwtId ;
}
@ -1296,9 +1292,9 @@ export default class HomeView extends Vue {
}
/ * *
* Only show giver and / or receiver info first if they ' re named in your contacts .
* Only show giver and / or recipient info first if they ' re named in your contacts .
* - If only giver is named , show "... gave"
* - If only receiver is named , show "... received"
* - If only recipient is named , show "... received"
* /
const giverInfo = giveRecord . giver ;
@ -1588,15 +1584,15 @@ export default class HomeView extends Vue {
* Caches image data for sharing
*
* @ public
* Called by ActivityListItem component
* @ param event Event object
* Called by ActivityListItem component and openImageViewer
* @ param imageUrl URL of image to cache
* @ param blob Optional blob data to cache
* /
async cacheImageData ( event : Event , imageUrl : string ) {
private async cacheImageData ( imageUrl : string , blob ? : Blob | null ) {
try {
/ / F o r i m a g e s t h a t m i g h t f a i l C O R S , j u s t s t o r e t h e U R L
/ / T h e W e b S h a r e A P I w i l l h a n d l e s h a r i n g t h e U R L a p p r o p r i a t e l y
this . imageCache . set ( imageUrl , null ) ;
this . imageCache . set ( imageUrl , blob || null ) ;
} catch ( error ) {
logger . warn ( "Failed to cache image:" , error ) ;
}
@ -1607,12 +1603,47 @@ export default class HomeView extends Vue {
*
* @ public
* Called by ActivityListItem component
* @ param imageUrl URL of image to display
* @ param eventOrUrl Either an Event object or a direct image URL string
* /
async openImageViewer ( imageUrl : string ) {
this . selectedImageData = this . imageCache . get ( imageUrl ) ? ? null ;
this . selectedImage = imageUrl ;
this . isImageViewerOpen = true ;
private async openImageViewer ( eventOrUrl : Event | string ) {
const imageUrl = typeof eventOrUrl === 'string'
? eventOrUrl
: ( eventOrUrl . target as HTMLElement ) . getAttribute ( 'src' ) || '' ;
if ( ! imageUrl ) return ;
/ / C h e c k c a c h e f i r s t
const cachedData = this . imageCache . get ( imageUrl ) ;
if ( cachedData ) {
this . selectedImageData = cachedData ;
this . selectedImage = imageUrl ;
this . isImageViewerOpen = true ;
return ;
}
/ / I f n o t i n c a c h e , l o a d i t
try {
const response = await fetch ( imageUrl ) ;
if ( ! response . ok ) throw new Error ( 'Failed to load image' ) ;
const blob = await response . blob ( ) ;
await this . cacheImageData ( imageUrl , blob ) ;
this . selectedImageData = blob ;
this . selectedImage = imageUrl ;
this . isImageViewerOpen = true ;
} catch ( error ) {
logger . error ( 'Error loading image:' , error ) ;
this . $notify (
{
group : "alert" ,
type : "danger" ,
title : "Error" ,
text : "Failed to load image. Please try again." ,
} ,
3000 ,
) ;
}
}
/ * *