diff --git a/api/models/order.py b/api/models/order.py index d1f3113b..7e825bce 100644 --- a/api/models/order.py +++ b/api/models/order.py @@ -173,6 +173,14 @@ class Order(models.Model): blank=True, ) + # optionally makers can set a description to give more details about the contract terms + description = models.TextField( + max_length=240, + null=True, + default=None, + blank=True, + ) + # how many sats at creation and at last check (relevant for marked to market) t0_satoshis = models.PositiveBigIntegerField( null=True, diff --git a/api/oas_schemas.py b/api/oas_schemas.py index a7133020..bf0739d3 100644 --- a/api/oas_schemas.py +++ b/api/oas_schemas.py @@ -99,6 +99,7 @@ class OrderViewSchema: - `maker_status` - `taker_status` - `price_now` + - `description` ### Order Status diff --git a/api/serializers.py b/api/serializers.py index d51fd5bd..8309f24f 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -406,6 +406,11 @@ class OrderDetailSerializer(serializers.ModelSerializer): required=False, help_text="The index of the last message sent in the trade chatroom", ) + description = serializers.CharField( + required=False, + allow_null=True, + help_text="Order description", + ) class Meta: model = Order @@ -491,6 +496,7 @@ class OrderDetailSerializer(serializers.ModelSerializer): "latitude", "longitude", "chat_last_index", + "description", ) @@ -590,6 +596,7 @@ class MakeOrderSerializer(serializers.ModelSerializer): "latitude", "longitude", "password", + "description", ) diff --git a/api/views.py b/api/views.py index 81a71785..19738eb2 100644 --- a/api/views.py +++ b/api/views.py @@ -119,6 +119,7 @@ class MakerView(CreateAPIView): latitude = serializer.data.get("latitude") longitude = serializer.data.get("longitude") password = serializer.data.get("password") + description = serializer.data.get("description") # Optional params if public_duration is None: @@ -166,6 +167,7 @@ class MakerView(CreateAPIView): latitude=latitude, longitude=longitude, password=password, + description=description, ) order.last_satoshis = order.t0_satoshis = Logics.satoshis_now(order) @@ -282,6 +284,7 @@ class OrderView(viewsets.ViewSet): data["is_disputed"] = order.is_disputed data["ur_nick"] = request.user.username data["satoshis_now"] = order.last_satoshis + data["description"] = order.description # Add whether hold invoices are LOCKED (ACCEPTED) # Is there a maker bond? If so, True if locked, False otherwise diff --git a/frontend/src/components/MakerForm/MakerForm.tsx b/frontend/src/components/MakerForm/MakerForm.tsx index f2806a86..3486b2dc 100644 --- a/frontend/src/components/MakerForm/MakerForm.tsx +++ b/frontend/src/components/MakerForm/MakerForm.tsx @@ -253,6 +253,7 @@ const MakerForm = ({ longitude: maker.longitude, shortAlias: maker.coordinator, password: maker.password ? sha256(maker.password) : null, + description: maker.description ? maker.description : null, }; void slot @@ -294,6 +295,14 @@ const MakerForm = ({ }); }; + const handleDescriptionChange = function (event: React.ChangeEvent): void { + setMaker({ + ...maker, + description: event.target.value, + badDescription: event.target.value.length > 240, + }); + }; + const handleChangeEscrowDuration = function (date: Date): void { const d = new Date(date); const hours: number = d.getHours(); @@ -395,7 +404,8 @@ const MakerForm = ({ maker.badPremiumText !== '' || federation.getCoordinator(maker.coordinator)?.limits === undefined || typeof maker.premium !== 'number' || - maker.paymentMethods.length === 0 + maker.paymentMethods.length === 0 || + maker.badDescription ); }, [maker, maker.premium, amountLimits, federationUpdatedAt, fav.type, makerHasAmountRange]); @@ -866,6 +876,30 @@ const MakerForm = ({ /> + + + + + {maker.badDescription && ( + + {t('Must be equal to or shorter than 240 characters')} + + )} + + {currentOrder?.description !== undefined ? ( + <> + + + + + + + + + + ) : null} + + {/* If there is live Price and Premium data, show it. Otherwise show the order maker settings */} diff --git a/frontend/src/models/Maker.model.ts b/frontend/src/models/Maker.model.ts index 1497604a..f0267f45 100644 --- a/frontend/src/models/Maker.model.ts +++ b/frontend/src/models/Maker.model.ts @@ -22,6 +22,8 @@ export interface Maker { latitude: number | null; longitude: number | null; password: string | null; + description: string | null; + badDescription: boolean; } export const defaultMaker: Maker = { @@ -49,6 +51,8 @@ export const defaultMaker: Maker = { latitude: 0, longitude: 0, password: null, + description: null, + badDescription: false, }; export default Maker; diff --git a/frontend/src/models/Order.model.ts b/frontend/src/models/Order.model.ts index c405d7b4..21b28ae3 100644 --- a/frontend/src/models/Order.model.ts +++ b/frontend/src/models/Order.model.ts @@ -86,6 +86,7 @@ class Order { latitude: number = 0; longitude: number = 0; password: string | undefined = undefined; + description: string | undefined = undefined; premium_now: number | undefined = undefined; tg_enabled: boolean = false; // deprecated tg_token: string = ''; @@ -201,6 +202,7 @@ class Order { latitude: this.latitude, longitude: this.longitude, password: this.password, + description: this.description, }; if (slot) { diff --git a/tests/test_trade_pipeline.py b/tests/test_trade_pipeline.py index f2b6a449..7f4db15f 100644 --- a/tests/test_trade_pipeline.py +++ b/tests/test_trade_pipeline.py @@ -479,6 +479,32 @@ class TradeTest(BaseAPITestCase): # Cancel order to avoid leaving pending HTLCs after a successful test trade.cancel_order() + + def test_make_and_take_description_order(self): + """ + Tests a trade with a description from order creation to taken. + """ + description = "Test" + description_maker_form = maker_form_buy_with_range.copy() + description_maker_form["description"] = description + + trade = Trade( + self.client, + # Add description to order + maker_form=description_maker_form, + ) + trade.publish_order() + trade.take_order() + data = trade.response.json() + + self.assertEqual(trade.response.status_code, 200) + self.assertResponse(trade.response) + + self.assertEqual(data["status_message"], Order.Status(Order.Status.PUB).label) + self.assertEqual(data["description"], description) + + # Cancel order to avoid leaving pending HTLCs after a successful test + trade.cancel_order() def test_make_and_take_password_order(self): """