feat: basic chat

This commit is contained in:
Vlad Stan 2023-03-07 18:17:46 +02:00
parent b7c099c935
commit 240da5277f
8 changed files with 202 additions and 13 deletions

View file

@ -418,10 +418,10 @@ async def create_direct_message(
dm_id = urlsafe_short_hash()
await db.execute(
f"""
INSERT INTO nostrmarket.direct_messages (merchant_id, id, event_id, message, public_key, incomming)
INSERT INTO nostrmarket.direct_messages (merchant_id, id, event_id, message, public_key, incoming)
VALUES (?, ?, ?, ?, ?, ?)
""",
(merchant_id, dm_id, dm.event_id, dm.message, dm.public_key, dm.incomming),
(merchant_id, dm_id, dm.event_id, dm.message, dm.public_key, dm.incoming),
)
msg = await get_direct_message(merchant_id, dm_id)
@ -442,7 +442,7 @@ async def get_direct_message(merchant_id: str, dm_id: str) -> Optional[DirectMes
async def get_direct_messages(merchant_id: str, public_key: str) -> List[DirectMessage]:
rows = await db.fetchall(
"SELECT * FROM nostrmarket.zones WHERE merchant_id = ? AND public_ley = ?",
"SELECT * FROM nostrmarket.direct_messages WHERE merchant_id = ? AND public_key = ? ORDER BY time DESC",
(merchant_id, public_key),
)
return [DirectMessage.from_row(row) for row in rows]

View file

@ -104,7 +104,7 @@ async def m001_initial(db):
event_id TEXT,
message TEXT NOT NULL,
public_key TEXT NOT NULL,
incomming BOOLEAN NOT NULL DEFAULT false,
incoming BOOLEAN NOT NULL DEFAULT false,
time TIMESTAMP NOT NULL DEFAULT {db.timestamp_now}
);
"""

View file

@ -368,11 +368,11 @@ class PartialDirectMessage(BaseModel):
event_id: Optional[str]
message: str
public_key: str
incomming: bool = False
incoming: bool = False
time: Optional[int]
class DirectMessage(BaseModel):
class DirectMessage(PartialDirectMessage):
id: str
@classmethod

View file

@ -1 +1,58 @@
<div>dmmms</div>
<div>
<q-card>
<q-card-section>
<h6 class="text-subtitle1 q-my-none">Messages</h6>
</q-card-section>
<q-card-section class="q-pa-none">
<q-separator></q-separator>
</q-card-section>
<q-card-section>
<!-- <q-select
v-model="customerKey"
:options="Object.keys(messages).map(k => ({label: `${k.slice(0, 25)}...`, value: k}))"
label="Customers"
@input="chatRoom(customerKey)"
emit-value
></q-select> -->
</q-card-section>
<q-card-section>
<div class="chat-container" ref="chatCard">
<div class="chat-box">
<div class="chat-messages" style="height: 45vh">
<q-chat-message
v-for="(dm, index) in messages"
:key="index"
:name="dm.incoming ? 'customer': 'me'"
:text="[dm.message]"
:sent="!dm.incoming"
:bg-color="dm.incoming ? 'white' : 'light-green-2'"
/>
</div>
</div>
<q-card-section>
<q-form @submit="sendDirectMesage" class="full-width chat-input">
<q-input
ref="newMessage"
v-model="newMessage"
placeholder="Message"
class="full-width"
dense
outlined
>
<template>
<q-btn
round
dense
flat
type="submit"
icon="send"
color="primary"
/>
</template>
</q-input>
</q-form>
</q-card-section>
</div>
</q-card-section>
</q-card>
</div>

View file

@ -6,9 +6,51 @@ async function directMessages(path) {
template,
data: function () {
return {}
return {
activePublicKey:
'83d07a79496f4cbdc50ca585741a79a2df1fd938cfa449f0fccb0ab7352115dd',
messages: [],
newMessage: ''
}
},
methods: {},
created: async function () {}
methods: {
sendMessage: async function () {},
getDirectMessages: async function () {
if (!this.activePublicKey) {
return
}
try {
const {data} = await LNbits.api.request(
'GET',
'/nostrmarket/api/v1/message/' + this.activePublicKey,
this.inkey
)
this.messages = data
console.log('### this.messages', this.messages)
} catch (error) {
LNbits.utils.notifyApiError(error)
}
},
sendDirectMesage: async function () {
try {
const {data} = await LNbits.api.request(
'POST',
'/nostrmarket/api/v1/message',
this.adminkey,
{
message: this.newMessage,
public_key: this.activePublicKey
}
)
this.messages.push(data)
this.newMessage = ''
} catch (error) {
LNbits.utils.notifyApiError(error)
}
},
},
created: async function () {
await this.getDirectMessages()
}
})
}

View file

@ -149,7 +149,7 @@ async def handle_dirrect_message(
event_id=event_id,
message=text_msg,
public_key=from_pubkey,
incomming=True,
incoming=True,
)
await create_direct_message(merchant_id, dm)
return None

View file

@ -101,7 +101,6 @@
</div>
<div class="col-12 col-md-5 q-gutter-y-md">
<!-- <div class="row q-col-gutter-md"> -->
<div class="col-12">
<q-card>
<q-card-section>
@ -115,7 +114,13 @@
</q-card-section>
</q-card>
</div>
<div class="col-12">x111</div>
<div class="col-12">
<direct-messages
:inkey="g.user.wallets[0].inkey"
:adminkey="g.user.wallets[0].adminkey"
>
</direct-messages>
</div>
</div>
<div>
@ -146,6 +151,43 @@
</div>
</div>
{% endblock%}{% block scripts %} {{ window_vars(user) }}
<style scoped>
.q-field__native span {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.chat-container {
position: relative;
display: grid;
grid-template-rows: 1fr auto;
/*height: calc(100vh - 200px);*/
height: 50vh;
}
.chat-box {
display: flex;
flex-direction: column-reverse;
padding: 1rem;
overflow-y: auto;
margin-left: auto;
width: 50%;
}
.chat-messages {
width: auto;
}
.chat-input {
position: relative;
display: flex;
align-items: end;
margin-top: 1rem;
}
</style>
<!-- todo: serve locally -->
<script src="https://unpkg.com/nostr-tools/lib/nostr.bundle.js"></script>
<script src="https://cdn.jsdelivr.net/npm/pica@6.1.1/dist/pica.min.js"></script>
@ -159,4 +201,5 @@
<script src="{{ url_for('nostrmarket_static', path='components/direct-messages/direct-messages.js') }}"></script>
<script src="{{ url_for('nostrmarket_static', path='js/index.js') }}"></script>
{% endblock %}

View file

@ -18,6 +18,7 @@ from lnbits.utils.exchange_rates import currencies
from . import nostrmarket_ext, scheduled_tasks
from .crud import (
create_direct_message,
create_merchant,
create_order,
create_product,
@ -26,6 +27,7 @@ from .crud import (
delete_product,
delete_stall,
delete_zone,
get_direct_messages,
get_merchant_for_user,
get_order,
get_order_by_event_id,
@ -45,11 +47,13 @@ from .crud import (
update_zone,
)
from .models import (
DirectMessage,
Merchant,
Nostrable,
Order,
OrderExtra,
OrderStatusUpdate,
PartialDirectMessage,
PartialMerchant,
PartialOrder,
PartialProduct,
@ -576,6 +580,49 @@ async def api_update_order_status(
)
######################################## DIRECT MESSAGES ########################################
@nostrmarket_ext.get("/api/v1/message/{public_key}")
async def api_get_messages(
public_key: str, wallet: WalletTypeInfo = Depends(get_key_type)
) -> List[DirectMessage]:
try:
merchant = await get_merchant_for_user(wallet.wallet.user)
assert merchant, f"Merchant cannot be found"
messages = await get_direct_messages(merchant.id, public_key)
return messages
except Exception as ex:
logger.warning(ex)
raise HTTPException(
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
detail="Cannot get zone",
)
@nostrmarket_ext.post("/api/v1/message")
async def api_create_message(
data: PartialDirectMessage, wallet: WalletTypeInfo = Depends(require_admin_key)
) -> DirectMessage:
try:
merchant = await get_merchant_for_user(wallet.wallet.user)
assert merchant, f"Merchant cannot be found"
dm_event = merchant.build_dm_event(data.message, data.public_key)
data.event_id = dm_event.id
dm = await create_direct_message(merchant.id, data)
await publish_nostr_event(dm_event)
return dm
except Exception as ex:
logger.warning(ex)
raise HTTPException(
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
detail="Cannot create message",
)
######################################## OTHER ########################################