diff --git a/.env-sample b/.env-sample index 1464cb693..33875e1fa 100644 --- a/.env-sample +++ b/.env-sample @@ -181,6 +181,9 @@ SLASHED_BOND_REWARD_SPLIT = 0.5 # Username for HTLCs escrows ESCROW_USERNAME = 'admin' +# Blossom image upload in chat. Requires running a Blossom blob server behind the coordinator. +BLOSSOM_ENABLED = False + #Social NOSTR_NSEC = 'nsec1vxhs2zc4kqe0dhz4z2gfrdyjsrwf8pg3neeqx6w4nl8djfzdp0dqwd6rxh' STRFRY_HOST = 'localhost' diff --git a/api/serializers.py b/api/serializers.py index 220d7bb5b..5e01cf734 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -51,6 +51,9 @@ class InfoSerializer(serializers.Serializer): current_swap_fee_rate = serializers.FloatField( help_text="Swap fees to perform on-chain transaction (percent)" ) + blossom_enabled = serializers.BooleanField( + help_text="Whether the coordinator offers encrypted image uploads via Blossom in chat" + ) version = VersionSerializer() notice_severity = serializers.ChoiceField( choices=[ diff --git a/api/views.py b/api/views.py index d5f7cb2c1..e95f269a5 100644 --- a/api/views.py +++ b/api/views.py @@ -897,6 +897,7 @@ def get(self, request): context["max_order_size"] = config("MAX_ORDER_SIZE", cast=int, default=250000) context["swap_enabled"] = not config("DISABLE_ONCHAIN", cast=bool, default=True) context["max_swap"] = config("MAX_SWAP_AMOUNT", cast=int, default=0) + context["blossom_enabled"] = config("BLOSSOM_ENABLED", cast=bool, default=False) try: context["current_swap_fee_rate"] = Logics.compute_swap_fee_rate( diff --git a/docs/assets/schemas/api-latest.yaml b/docs/assets/schemas/api-latest.yaml index b958904d2..fddc0a80a 100644 --- a/docs/assets/schemas/api-latest.yaml +++ b/docs/assets/schemas/api-latest.yaml @@ -1249,6 +1249,9 @@ components: type: number format: double description: Swap fees to perform on-chain transaction (percent) + blossom_enabled: + type: boolean + description: Whether the coordinator offers encrypted image uploads via Blossom in chat version: $ref: '#/components/schemas/Version' notice_severity: @@ -1285,6 +1288,7 @@ components: - swap_enabled - taker_fee - version + - blossom_enabled ListNotification: type: object properties: diff --git a/frontend/src/components/TradeBox/EncryptedChat/EncryptedApiChat/index.tsx b/frontend/src/components/TradeBox/EncryptedChat/EncryptedApiChat/index.tsx index 78a16ca02..447a81297 100644 --- a/frontend/src/components/TradeBox/EncryptedChat/EncryptedApiChat/index.tsx +++ b/frontend/src/components/TradeBox/EncryptedChat/EncryptedApiChat/index.tsx @@ -1,9 +1,10 @@ import React, { Dispatch, SetStateAction, useContext, useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { Button, TextField, Grid, Paper, Typography } from '@mui/material'; +import { Button, TextField, Grid, Paper, Typography, Tooltip, IconButton } from '@mui/material'; import { decryptMessage } from '../../../../pgp'; // Icons +import CircularProgress from '@mui/material/CircularProgress'; import { useTheme } from '@mui/material'; import MessageCard from '../MessageCard'; import ChatHeader from '../ChatHeader'; @@ -16,7 +17,7 @@ import { import { type UseGarageStoreType, GarageContext } from '../../../../contexts/GarageContext'; import { type Order } from '../../../../models'; import getSettings from '../../../../utils/settings'; -import { Send } from '@mui/icons-material'; +import { AttachFile, Send } from '@mui/icons-material'; import PrivacyWarningDialog from '../PrivacyWarningDialog'; import { ParsedFileMessage, parseImageMetadataJson } from '../../../../utils/nip17File'; @@ -45,6 +46,7 @@ interface Props { setPeerPubKey: (peerPubKey: string) => void; setError: Dispatch>; setLastIndex: Dispatch>; + blossomEnabled: boolean; } const audioPath = @@ -69,6 +71,7 @@ const EncryptedApiChat: React.FC = ({ onSendFile, setError, setLastIndex, + blossomEnabled, }: Props): React.JSX.Element => { const { t } = useTranslation(); const theme = useTheme(); @@ -81,7 +84,7 @@ const EncryptedApiChat: React.FC = ({ const [waitingEcho, setWaitingEcho] = useState(false); const [messageCount, setMessageCount] = useState(0); const [serverMessages, setServerMessages] = useState([]); - const [_uploading, setUploading] = useState(false); + const [uploading, setUploading] = useState(false); const [imageUrls, setImageUrls] = useState>({}); const [privacyWarningOpen, setPrivacyWarningOpen] = useState(false); const fileInputRef = useRef(null); @@ -219,7 +222,7 @@ const EncryptedApiChat: React.FC = ({ } }; - const _handleAttachClick = (): void => { + const handleAttachClick = (): void => { // Clear any previous errors setError(''); setPrivacyWarningOpen(true); @@ -233,7 +236,7 @@ const EncryptedApiChat: React.FC = ({ } }; - const _handleFileChange = (e: React.ChangeEvent): void => { + const handleFileChange = (e: React.ChangeEvent): void => { const file = e.target.files?.[0]; if (!file) { // User cancelled file selection @@ -338,24 +341,32 @@ const EncryptedApiChat: React.FC = ({ }} fullWidth={true} /> - {/* - + {uploading ? : } - */} +