-
Notifications
You must be signed in to change notification settings - Fork 367
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: ComposedAttachmentAdapter (#781)
- Loading branch information
Showing
1 changed file
with
98 additions
and
0 deletions.
There are no files selected for viewing
98 changes: 98 additions & 0 deletions
98
packages/react/src/runtimes/attachment/ComposedAttachmentAdapter.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
import { | ||
ComposerAttachment, | ||
MessageAttachment, | ||
} from "../../context/stores/Attachment"; | ||
import { AttachmentAdapter } from "./AttachmentAdapter"; | ||
|
||
function fileMatchesAccept(file: File, acceptString: string) { | ||
// Check if the accept string is "*", which allows any file | ||
if (acceptString === "*") { | ||
return true; | ||
} | ||
|
||
// Split the accept string into an array of allowed types | ||
const allowedTypes = acceptString | ||
.split(",") | ||
.map((type) => type.trim().toLowerCase()); | ||
|
||
// Get the file's extension and MIME type | ||
const fileExtension = "." + file.name.split(".").pop()!.toLowerCase(); | ||
const fileMimeType = file.type.toLowerCase(); | ||
|
||
for (const type of allowedTypes) { | ||
// Check for file extension match | ||
if (type.startsWith(".") && type === fileExtension) { | ||
return true; | ||
} | ||
|
||
// Check for exact MIME type match | ||
if (type.includes("/") && type === fileMimeType) { | ||
return true; | ||
} | ||
|
||
if (type === "image/*" || type === "video/*" || type === "audio/*") { | ||
// Check for wildcard MIME type match | ||
if (type.endsWith("/*")) { | ||
const generalType = type.split("/")[0]!; | ||
if (fileMimeType.startsWith(generalType + "/")) { | ||
return true; | ||
} | ||
} | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
|
||
export class ComposedAttachmentAdapter implements AttachmentAdapter { | ||
private _adapters: AttachmentAdapter[]; | ||
|
||
public accept: string; | ||
|
||
constructor(adapters: AttachmentAdapter[]) { | ||
this._adapters = adapters; | ||
|
||
const wildcardIdx = adapters.findIndex((a) => a.accept === "*"); | ||
if (wildcardIdx !== -1) { | ||
if (wildcardIdx !== adapters.length - 1) | ||
throw new Error( | ||
"A wildcard adapter (handling all files) can only be specified as the last adapter.", | ||
); | ||
|
||
this.accept = "*"; | ||
} else { | ||
this.accept = adapters.map((a) => a.accept).join(","); | ||
} | ||
} | ||
|
||
public async add(state: { file: File }): Promise<ComposerAttachment> { | ||
for (const adapter of this._adapters) { | ||
if (fileMatchesAccept(state.file, adapter.accept)) { | ||
return adapter.add(state); | ||
} | ||
} | ||
throw new Error("No matching adapter found for file"); | ||
} | ||
|
||
public async send( | ||
attachment: ComposerAttachment, | ||
): Promise<MessageAttachment> { | ||
const adapters = this._adapters.slice(); | ||
for (const adapter of adapters) { | ||
if (fileMatchesAccept(attachment.file, adapter.accept)) { | ||
return adapter.send(attachment); | ||
} | ||
} | ||
throw new Error("No matching adapter found for attachment"); | ||
} | ||
|
||
public async remove(attachment: ComposerAttachment): Promise<void> { | ||
const adapters = this._adapters.slice(); | ||
for (const adapter of adapters) { | ||
if (fileMatchesAccept(attachment.file, adapter.accept)) { | ||
return adapter.remove(attachment); | ||
} | ||
} | ||
throw new Error("No matching adapter found for attachment"); | ||
} | ||
} |