Skip to content
This repository has been archived by the owner on Sep 20, 2023. It is now read-only.

Cleanup DApp notifications #1025

Merged
merged 10 commits into from
Dec 3, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
90 changes: 59 additions & 31 deletions origin-dapp/src/components/notification-message.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,49 +9,77 @@ class NotificationMessage extends Component {
constructor(props) {
super(props)

this.intlMessages = defineMessages({
offerMade: {
id: 'notification.offerMade',
const intlMessages = defineMessages({
//
// Notifications received by the seller.
//
sellerOfferCreated: {
id: 'notification.sellerOfferCreated',
defaultMessage: 'You have a new offer.'
},
offerAccepted: {
id: 'notification.purchaseSent',
defaultMessage: 'Your offer has been accepted.'
sellerOfferFinalized: {
id: 'notification.sellerOfferFinalized',
defaultMessage: 'Your transaction has been completed.'
},
saleConfirmed: {
id: 'notification.saleConfirmed',
defaultMessage: 'Your sale has been confirmed.'
sellerOfferDisputed: {
id: 'notification.sellerOfferDisputed',
defaultMessage: 'A problem has been reported with your transaction.'
},
sellerReviewed: {
id: 'notification.sellerReviewed',
defaultMessage: 'You have a new review.'
sellerOfferWithdrawn: {
id: 'notification.sellerOfferWithdrawn',
defaultMessage: 'An offer on your listing has been withdrawn.'
},
sellerOfferRuling: {
id: 'notification.sellerOfferRuling',
defaultMessage: 'A ruling has been issued on your disputed transaction.'
},
//
// Notifications received by the buyer.
//
buyerOfferAccepted: {
id: 'notification.buyerOfferAccepted',
defaultMessage: 'An offer you made has been accepted.'
},
buyerOfferDisputed: {
id: 'notification.buyerOfferDisputed',
defaultMessage: 'A problem has been reported with your transaction.'
},
buyerOfferRuling: {
id: 'notification.buyerOfferRuling',
defaultMessage: 'A ruling has been issued on your disputed transaction.'
},
buyerOfferReview: {
id: 'notification.buyerOfferReview',
defaultMessage: 'A review has been left on your transaction.'
},
buyerOfferWithdrawn: {
id: 'notification.buyerOfferWithdrawn',
defaultMessage: 'An offer you made has been rejected.'
}

})

this.notificationTypeToMessage = {
'seller_offer_created': intlMessages.sellerOfferCreated,
'seller_offer_finalized': intlMessages.sellerOfferFinalized,
'seller_offer_disputed': intlMessages.sellerOfferDisputed,
'seller_offer_ruling': intlMessages.sellerOfferRuling,
'seller_offer_withdrawn': intlMessages.sellerOfferWithdrawn,
'buyer_offer_accepted': intlMessages.buyerOfferAccepted,
'buyer_offer_disputed': intlMessages.sellerOfferDisputed,
'buyer_offer_ruling': intlMessages.buyerOfferRuling,
'buyer_offer_review': intlMessages.buyerOfferReview,
'buyer_offer_withdrawn': intlMessages.buyerOfferWithdrawn,
}
}

render() {
const { className, type } = this.props
let message

switch (type) {
case 'buyer_review_received':
message = this.props.intl.formatMessage(
this.intlMessages.sellerReviewed
)
break
case 'seller_review_received':
message = this.props.intl.formatMessage(this.intlMessages.saleConfirmed)
break
case 'buyer_listing_shipped':
message = this.props.intl.formatMessage(this.intlMessages.offerAccepted)
break
case 'seller_listing_purchased':
message = this.props.intl.formatMessage(this.intlMessages.offerMade)
break
default:
let message = this.notificationTypeToMessage[type]
if (!message) {
return <p className={className || ''}>{NON_PURCHASE_RELATED_MESSAGE}</p>
}

message = this.props.intl.formatMessage(message)
return (
<div className={`message${className ? ` ${className}` : ''}`}>
{message}
Expand Down
10 changes: 5 additions & 5 deletions origin-dapp/src/components/notification.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ class Notification extends Component {
super(props)

const { notification, wallet } = this.props
const { listing, purchase } = notification.resources
const counterpartyAddress = [listing.seller, purchase.buyer].find(
const { listing, offer } = notification.resources
const counterpartyAddress = [listing.seller, offer.buyer].find(
addr => formattedAddress(addr) !== formattedAddress(wallet.address)
)

Expand All @@ -27,7 +27,7 @@ class Notification extends Component {
counterpartyAddress,
counterpartyName: '',
listing,
purchase
offer
}
}

Expand Down Expand Up @@ -58,15 +58,15 @@ class Notification extends Component {
counterpartyAddress,
counterpartyName,
listing,
purchase
offer
} = this.state

const listingImageURL =
listing.media && listing.media.length && listing.media[0].url

return (
<li className="list-group-item notification">
<Link to={`/purchases/${purchase.id}`} onClick={this.handleClick}>
<Link to={`/purchases/${offer.id}`} onClick={this.handleClick}>
<div className="d-flex align-items-stretch">
<div className="image-container d-flex align-items-center justify-content-center">
{!listing.id && (
Expand Down
14 changes: 10 additions & 4 deletions origin-dapp/translations/all-messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -287,10 +287,16 @@
"navbar.addListing": "Add a Listing",
"not-found.heading": "How did I get here?",
"not-found.content": "The page you’re looking for is no longer here, maybe it was never here in the first place. In any case, we sincerely apologize if it’s us and we forgive you if it’s you :)",
"notification.offerMade": "You have a new offer.",
"notification.purchaseSent": "Your offer has been accepted.",
"notification.saleConfirmed": "Your sale has been confirmed.",
"notification.sellerReviewed": "You have a new review.",
"notification.sellerOfferCreated": "You have a new offer.",
"notification.sellerOfferFinalized": "Your transaction has been completed.",
"notification.sellerOfferDisputed": "A problem has been reported with your transaction.",
"notification.sellerOfferWithdrawn": "An offer on your listing has been withdrawn.",
"notification.sellerOfferRuling": "A ruling has been issued on your disputed transaction.",
"notification.buyerOfferAccepted": "An offer you made has been accepted.",
"notification.buyerOfferDisputed": "A problem has been reported with your transaction.",
"notification.buyerOfferRuling": "A ruling has been issued on your disputed transaction.",
"notification.buyerOfferReview": "A review has been left on your transaction.",
"notification.buyerOfferWithdrawn": "An offer you made has been rejected.",
"notification.buyer": "Buyer",
"notification.seller": "Seller",
"notificationsComponent.notificationsHeading": "Notifications",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,42 @@
[
{
"id": "notification.offerMade",
"id": "notification.sellerOfferCreated",
"defaultMessage": "You have a new offer."
},
{
"id": "notification.purchaseSent",
"defaultMessage": "Your offer has been accepted."
"id": "notification.sellerOfferFinalized",
"defaultMessage": "Your transaction has been completed."
},
{
"id": "notification.saleConfirmed",
"defaultMessage": "Your sale has been confirmed."
"id": "notification.sellerOfferDisputed",
"defaultMessage": "A problem has been reported with your transaction."
},
{
"id": "notification.sellerReviewed",
"defaultMessage": "You have a new review."
"id": "notification.sellerOfferWithdrawn",
"defaultMessage": "An offer on your listing has been withdrawn."
},
{
"id": "notification.sellerOfferRuling",
"defaultMessage": "A ruling has been issued on your disputed transaction."
},
{
"id": "notification.buyerOfferAccepted",
"defaultMessage": "An offer you made has been accepted."
},
{
"id": "notification.buyerOfferDisputed",
"defaultMessage": "A problem has been reported with your transaction."
},
{
"id": "notification.buyerOfferRuling",
"defaultMessage": "A ruling has been issued on your disputed transaction."
},
{
"id": "notification.buyerOfferReview",
"defaultMessage": "A review has been left on your transaction."
},
{
"id": "notification.buyerOfferWithdrawn",
"defaultMessage": "An offer you made has been rejected."
}
]
17 changes: 7 additions & 10 deletions origin-docs/source/includes/resources/marketplace.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,21 +154,18 @@ const listingId = "927-832"

## getNotifications

Each Notification corresponds to the status of a Listing. Notifications are currently generated for each of the following Listing statuses:
Each Notification corresponds to the status of an Offer. Notifications are currently generated for each of the following Offer statuses:

- ListingCreated
- ListingUpdated
- ListingWithdrawn
- ListingArbitrated
- OfferCreated
- OfferAccepted
- OfferFinalized
- OfferWithdrawn
- OfferFundsAdded
- OfferDisputed
- OfferRuling
- OfferFinalized
- OfferData

Notifications do not exist on the blockchain nor are they read from a database. They are derived from the blockchain transaction logs of the Listing statuses at the time of the API request. Because of this, there is no central record of a notification's status as "read" or "unread". When a client first interacts with the notifications API, Origin.js will record a timestamp in local storage. All notifications resulting from blockchain events that happen prior to this timestamp will be considered to be "read". This ensures that when the same user interacts with the notifications API from a different client for the first time, they will not receive a large number of "unread" notifications that they have previously read from their original client.
Notifications do not exist on the blockchain nor are they read from a database. They are derived from the blockchain transaction logs of the Offer statuses at the time of the API request. Because of this, there is no central record of a notification's status as "read" or "unread". When a client first interacts with the notifications API, Origin.js will record a timestamp in local storage. All notifications resulting from blockchain events that happen prior to this timestamp will be considered to be "read". This ensures that when the same user interacts with the notifications API from a different client for the first time, they will not receive a large number of "unread" notifications that they have previously read from their original client.

> Example: getNotifications

Expand All @@ -180,10 +177,10 @@ Notifications do not exist on the blockchain nor are they read from a database.

[{
"id": "2984803-23433",
"type": "buyer_listing_shipped",
"type": "buyer_offer_accepted",
"status": "unread",
"event": {},
"resources": { listingId: "927-832", offerId: "183", listing: { title: "Whirlpool Microwave" } }
"event": {...},
"resources": { listingId: "1-000-832", offerId: "183", listing: { title: "Whirlpool Microwave" }, offer: {...} }
]}
```

Expand Down
19 changes: 19 additions & 0 deletions origin-js/src/contractInterface/marketplace/resolver.js
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,24 @@ export default class MarketplaceResolver {
)
}

/**
* Returns all notifications relevant to a user.
* The notification status is set to 'read' if either
* - The notification was previously marked as read in local storage.
* - The event occurred prior to the subscription start time (e.g. date at which
* the notification component was initialized for the first time).
*
* @param {string} party - User's ETH address.
* @return {Promise<Array{
* id: string,
* status: read | unread
* event: web3 event,
* type: seller_listing_purchased | seller_review_received | buyer_listing_shipped,
* resources: {listingId: string, offerId: string} (Note: those are index, not global ids)
* network: eth network ID,
* version: version of marketplace contract that emitted the event
* }>}
**/
async getNotifications(party) {
const network = await this.contractService.web3.eth.net.getId()
let notifications = []
Expand All @@ -277,6 +295,7 @@ export default class MarketplaceResolver {
version,
transactionHash: notification.event.transactionHash
})
// Check if the event occurred prior to the subscription start time.
const timestamp = await this.contractService.getTimestamp(
notification.event
)
Expand Down
Loading