feat: add conversation image previews and sharing
This commit is contained in:
1 parent
488145340d
commit
b573b88b08
20 files changed
+634
-26
No files matched your search
@@ -28,6 +28,11 @@ export type MediaExchangeResult =
|
||||
| { kind: 'retryable'; assetId: string; requestKind: MediaRequestKind }
|
||||
| { kind: 'human-required'; reason: string; remoteSent?: boolean };
|
||||
|
||||
export interface ShareableMediaLibraryAssets {
|
||||
requestKind: MediaRequestKind | null;
|
||||
assets: MediaLibraryAsset[];
|
||||
}
|
||||
|
||||
/** Detects inbound requests, reserves an unused asset, then sends image before reciprocal text. */
|
||||
export class MediaLibraryExchangeService {
|
||||
constructor(
|
||||
@@ -59,6 +64,35 @@ export class MediaLibraryExchangeService {
|
||||
conversationId: string,
|
||||
incomingKey: string,
|
||||
incomingText: string,
|
||||
): Promise<MediaExchangeResult> {
|
||||
return this.handleRequest(conversationId, incomingKey, incomingText);
|
||||
}
|
||||
|
||||
listShareable(conversationId: string, incomingText: string): ShareableMediaLibraryAssets {
|
||||
const prior = this.repository.listConversationSends(this.accountId, conversationId);
|
||||
const requestKind = detectMediaRequest(incomingText, prior);
|
||||
if (!requestKind) return { requestKind: null, assets: [] };
|
||||
const used = new Set(prior.map((item) => item.assetId));
|
||||
return {
|
||||
requestKind,
|
||||
assets: this.pickEligible(this.repository.listEligible(), used, requestKind, prior),
|
||||
};
|
||||
}
|
||||
|
||||
async handleSelected(
|
||||
conversationId: string,
|
||||
incomingKey: string,
|
||||
incomingText: string,
|
||||
assetId: string,
|
||||
): Promise<MediaExchangeResult> {
|
||||
return this.handleRequest(conversationId, incomingKey, incomingText, assetId);
|
||||
}
|
||||
|
||||
private async handleRequest(
|
||||
conversationId: string,
|
||||
incomingKey: string,
|
||||
incomingText: string,
|
||||
selectedAssetId?: string,
|
||||
): Promise<MediaExchangeResult> {
|
||||
const prior = this.repository.listConversationSends(this.accountId, conversationId);
|
||||
const requestKind = detectMediaRequest(incomingText, prior);
|
||||
@@ -66,6 +100,12 @@ export class MediaLibraryExchangeService {
|
||||
if (this.dryRun) return { kind: 'dry-run', requestKind };
|
||||
|
||||
let send = this.repository.getSend(this.accountId, conversationId, incomingKey);
|
||||
if (send && selectedAssetId && send.assetId !== selectedAssetId) {
|
||||
return { kind: 'human-required', reason: 'A different library image is already reserved for this request.' };
|
||||
}
|
||||
if (send?.status === 'sent' && send.matrixTextEventId === 'unconfirmed') {
|
||||
return { kind: 'human-required', reason: 'The image was sent, but the reciprocal text was not confirmed.', remoteSent: true };
|
||||
}
|
||||
if (send?.status === 'sent') return { kind: 'sent', assetId: send.assetId, requestKind: send.requestKind };
|
||||
if (send?.status === 'failed' || send?.status === 'superseded') {
|
||||
return { kind: 'human-required', reason: 'A previous media send for this request failed or became uncertain.' };
|
||||
@@ -73,7 +113,13 @@ export class MediaLibraryExchangeService {
|
||||
if (!send) {
|
||||
const used = new Set(prior.map((item) => item.assetId));
|
||||
const eligible = this.pickEligible(this.repository.listEligible(), used, requestKind, prior);
|
||||
for (const asset of shuffled(eligible, this.random)) {
|
||||
const candidates = selectedAssetId
|
||||
? eligible.filter((asset) => asset.id === selectedAssetId)
|
||||
: shuffled(eligible, this.random);
|
||||
if (selectedAssetId && candidates.length === 0) {
|
||||
return { kind: 'human-required', reason: 'That library image is disabled, already used, or does not match the current request.' };
|
||||
}
|
||||
for (const asset of candidates) {
|
||||
send = this.repository.tryReserve(asset.id, this.accountId, conversationId, incomingKey, requestKind) ?? undefined;
|
||||
if (send) break;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user