diff --git a/src/browser-commands/network-request/NetworkRequestCommand.ts b/src/browser-commands/network-request/NetworkRequestCommand.ts index e9fb499..5a2d2a5 100644 --- a/src/browser-commands/network-request/NetworkRequestCommand.ts +++ b/src/browser-commands/network-request/NetworkRequestCommand.ts @@ -1,6 +1,6 @@ import { Command } from "../../commands-setup/Command"; -type SupportedResponseTypes = "text" | "arraybuffer"; +export type SupportedResponseTypes = "application/json" | "arraybuffer"; export class NetworkRequestCommand implements Command { readonly type = "networkRequest"; diff --git a/src/commands-setup/CommandsMapping.ts b/src/commands-setup/CommandsMapping.ts index a63356e..e27d574 100644 --- a/src/commands-setup/CommandsMapping.ts +++ b/src/commands-setup/CommandsMapping.ts @@ -1,4 +1,5 @@ import { NetworkRequestCommandHandler } from "../browser-commands/network-request/NetworkRequestCommandHandler"; +import { OfficialFigmaPluginApi } from "../infrastructure/OfficialFigmaPluginApi"; import { CancelCommandHandler } from "../scene-commands/cancel/CancelCommandHandler"; import { CreateShapesCommandHandler } from "../scene-commands/create-shapes/CreateShapesCommandHandler"; import { PaintCurrentUserAvatarCommandHandler } from "../scene-commands/paint-current-user-avatar/PaintCurrentUserAvatarCommandHandler"; @@ -8,8 +9,11 @@ import { CommandHandler } from "./CommandHandler"; // πŸ‘‹ Add below your new commands. // Define its arbitrary key and its corresponding Handler class. // Tip: Declare your Command and CommandHandler classes creating a folder inside the `src/scene-commands` or `src/browser-commands` ones depending on the things you need to get access to (see the README explanation) 😊 +const officialFigmaPluginApi = new OfficialFigmaPluginApi(figma); +const cancelCommandHandler = new CancelCommandHandler(officialFigmaPluginApi); + export const CommandsMapping: Record CommandHandler> = { - cancel: () => new CancelCommandHandler(figma), + cancel: () => cancelCommandHandler, createShapes: () => new CreateShapesCommandHandler(figma), paintCurrentUserAvatar: () => new PaintCurrentUserAvatarCommandHandler(figma), networkRequest: () => new NetworkRequestCommandHandler(), diff --git a/src/domain/FigmaPluginApi.ts b/src/domain/FigmaPluginApi.ts new file mode 100644 index 0000000..2a869f3 --- /dev/null +++ b/src/domain/FigmaPluginApi.ts @@ -0,0 +1,21 @@ +import { SupportedResponseTypes } from "../browser-commands/network-request/NetworkRequestCommand"; + +export type FigmaUser = { + photoUrl: string; + name: string; +}; + +export interface FigmaPluginApi { + currentUser(): FigmaUser; + + notify(message: string): void; + + select(message: string): void; + + request( + url: string, + responseType: ResponseType + ): Promise< + ResponseType extends "arraybuffer" ? ArrayBuffer : Record + >; +} diff --git a/src/infrastructure/OfficialFigmaPluginApi.ts b/src/infrastructure/OfficialFigmaPluginApi.ts new file mode 100644 index 0000000..840befc --- /dev/null +++ b/src/infrastructure/OfficialFigmaPluginApi.ts @@ -0,0 +1,63 @@ +import { + NetworkRequestCommand, + SupportedResponseTypes, +} from "../browser-commands/network-request/NetworkRequestCommand"; +import { executeCommand } from "../commands-setup/executeCommand"; +import { FigmaPluginApi, FigmaUser } from "../domain/FigmaPluginApi"; + +export class OfficialFigmaPluginApi implements FigmaPluginApi { + constructor(private readonly figma: PluginAPI) {} + + currentUser(): FigmaUser { + const currentUserAvatarUrl = this.figma.currentUser?.photoUrl; + const currentUserName = this.figma.currentUser?.name; + + if (currentUserAvatarUrl === undefined || currentUserAvatarUrl === null) { + throw new Error("Current user does not have a photo"); + } + + if (currentUserName === undefined || currentUserName === null) { + throw new Error("Current user does not have a name"); + } + + return { photoUrl: currentUserAvatarUrl, name: currentUserName }; + } + + notify(message: string): void { + this.figma.notify(message); + } + + // 🚩🚩🚩 🚩🚩🚩 🚩🚩🚩 🚩🚩🚩 🚩🚩🚩 + // 🚩🚩🚩 Gente que se flipa desacoplando 🚩🚩🚩 + // 🚩🚩🚩 TendrΓ­amos que modelar nuestro SceneNode 🚩🚩🚩 + // 🚩🚩🚩 🚩🚩🚩 🚩🚩🚩 🚩🚩🚩 🚩🚩🚩 + select(elements: ReadonlyArray): void { + this.figma.currentPage.selection = elements; + } + + request( + url: string, + responseType: ResponseType + ): Promise< + ResponseType extends "arraybuffer" ? ArrayBuffer : Record + > { + executeCommand(new NetworkRequestCommand(url, responseType)); + + return new Promise((resolve) => { + this.figma.ui.onmessage = async (command) => { + this.ensureToOnlyReceiveNetworkRequestResponse(command); + + resolve(command.payload); + }; + }); + } + + private ensureToOnlyReceiveNetworkRequestResponse(command: { type: string }) { + if (command.type !== "networkRequestResponse") { + const errorMessage = + "Unexpected command received while performing the request for painting the user avatar."; + + throw new Error(errorMessage); + } + } +} diff --git a/src/scene-commands/cancel/CancelCommandHandler.ts b/src/scene-commands/cancel/CancelCommandHandler.ts index a032ef3..dc48f20 100644 --- a/src/scene-commands/cancel/CancelCommandHandler.ts +++ b/src/scene-commands/cancel/CancelCommandHandler.ts @@ -1,8 +1,9 @@ import { CommandHandler } from "../../commands-setup/CommandHandler"; +import { FigmaPluginApi } from "../../domain/FigmaPluginApi"; import { CancelCommand } from "./CancelCommand"; export class CancelCommandHandler implements CommandHandler { - constructor(private readonly figma: PluginAPI) {} + constructor(private readonly figma: FigmaPluginApi) {} // `command` argument needed due to polymorphism. // eslint-disable-next-line @typescript-eslint/no-unused-vars diff --git a/src/scene-commands/paint-current-user-avatar/PaintCurrentUserAvatarCommandHandler.ts b/src/scene-commands/paint-current-user-avatar/PaintCurrentUserAvatarCommandHandler.ts index b4cf5dc..af3cc34 100644 --- a/src/scene-commands/paint-current-user-avatar/PaintCurrentUserAvatarCommandHandler.ts +++ b/src/scene-commands/paint-current-user-avatar/PaintCurrentUserAvatarCommandHandler.ts @@ -1,6 +1,5 @@ -import { NetworkRequestCommand } from "../../browser-commands/network-request/NetworkRequestCommand"; import { CommandHandler } from "../../commands-setup/CommandHandler"; -import { executeCommand } from "../../commands-setup/executeCommand"; +import { FigmaPluginApi, FigmaUser } from "../../domain/FigmaPluginApi"; import { PaintCurrentUserAvatarCommand } from "./PaintCurrentUserAvatarCommand"; export class PaintCurrentUserAvatarCommandHandler @@ -8,45 +7,27 @@ export class PaintCurrentUserAvatarCommandHandler { private readonly avatarImageSize = 100; - constructor(private readonly figma: PluginAPI) {} + constructor(private readonly figma: FigmaPluginApi) {} // `command` argument needed due to polymorphism. // eslint-disable-next-line @typescript-eslint/no-unused-vars - handle(command: PaintCurrentUserAvatarCommand): Promise { - const currentUserAvatarUrl = this.figma.currentUser?.photoUrl; - const currentUserName = this.figma.currentUser?.name; + async handle(command: PaintCurrentUserAvatarCommand): Promise { + let currentUser: FigmaUser; - if (currentUserAvatarUrl === undefined || currentUserAvatarUrl === null) { + try { + currentUser = this.figma.currentUser(); + } catch (e) { this.figma.notify("Sorry but you do not have an avatar to add πŸ˜…"); return Promise.resolve(); } - const responseType = "arraybuffer"; - executeCommand( - new NetworkRequestCommand(currentUserAvatarUrl, responseType) + const response = await this.figma.request( + currentUser.photoUrl, + "arraybuffer" ); - return new Promise((resolve) => { - this.figma.ui.onmessage = async (command) => { - this.ensureToOnlyReceiveNetworkRequestResponse(command); - - await this.createAvatarBadge( - command.payload as ArrayBuffer, - currentUserName as string - ); - resolve(); - }; - }); - } - - private ensureToOnlyReceiveNetworkRequestResponse(command: { type: string }) { - if (command.type !== "networkRequestResponse") { - const errorMessage = - "Unexpected command received while performing the request for painting the user avatar."; - - throw new Error(errorMessage); - } + await this.createAvatarBadge(response, currentUser.name); } private async createAvatarBadge( diff --git a/tests/scene-commands/CancelCommandHandler.test.ts b/tests/scene-commands/CancelCommandHandler.test.ts index af94aa9..dfa6eaa 100644 --- a/tests/scene-commands/CancelCommandHandler.test.ts +++ b/tests/scene-commands/CancelCommandHandler.test.ts @@ -1,11 +1,12 @@ import { mock } from "jest-mock-extended"; +import { FigmaPluginApi } from "../../src/domain/FigmaPluginApi"; import { CancelCommand } from "../../src/scene-commands/cancel/CancelCommand"; import { CancelCommandHandler } from "../../src/scene-commands/cancel/CancelCommandHandler"; describe("CancelCommandHandler", () => { it("can be instantiated without throwing errors", () => { - const figmaPluginApiMock = mock(); + const figmaPluginApiMock = mock(); const cancelCommandHandlerInstantiator = () => { new CancelCommandHandler(figmaPluginApiMock); @@ -15,7 +16,7 @@ describe("CancelCommandHandler", () => { }); it("notifies the end used with a farewell message", () => { - const figmaPluginApiMock = mock(); + const figmaPluginApiMock = mock(); const cancelCommandHandler = new CancelCommandHandler(figmaPluginApiMock); const randomCancelCommand = new CancelCommand();