From 9ba76b3392c4b47c03124ed52c81b11888731194 Mon Sep 17 00:00:00 2001 From: Javier Ferrer Date: Wed, 17 Aug 2022 12:41:43 +0200 Subject: [PATCH] [WIP] feat: add current user avatar badge to the Cove page* *It seems to do not receive the `figma.ui.postMessage` on the invisible iframe UI because it loses the trace there, even before arriving to the `NetworkRequestCommandHandler` --- manifest.json | 1 + src/commands-setup/CommandsMapping.ts | 7 +- .../create-pages/CreatePagesCommandHandler.ts | 46 +++++++++++-- .../CurrentUserAvatarBadgeCreator.ts} | 65 ++++++++++++------- .../PaintCurrentUserAvatarCommand.ts | 5 -- .../CreatePagesCommandHandler.test.ts | 24 ++++++- 6 files changed, 110 insertions(+), 38 deletions(-) rename src/scene-commands/{paint-current-user-avatar/PaintCurrentUserAvatarCommandHandler.ts => create-pages/CurrentUserAvatarBadgeCreator.ts} (64%) delete mode 100644 src/scene-commands/paint-current-user-avatar/PaintCurrentUserAvatarCommand.ts diff --git a/manifest.json b/manifest.json index 3779d76..0f84f5e 100644 --- a/manifest.json +++ b/manifest.json @@ -3,5 +3,6 @@ "id": "1139148181170685187", "api": "1.0.0", "main": "dist/figmaEntrypoint.js", + "permissions": ["currentuser"], "editorType": ["figma"] } diff --git a/src/commands-setup/CommandsMapping.ts b/src/commands-setup/CommandsMapping.ts index 20fa19a..26ba65e 100644 --- a/src/commands-setup/CommandsMapping.ts +++ b/src/commands-setup/CommandsMapping.ts @@ -1,12 +1,17 @@ import { NetworkRequestCommandHandler } from "../browser-commands/network-request/NetworkRequestCommandHandler"; import { CreatePagesCommandHandler } from "../scene-commands/create-pages/CreatePagesCommandHandler"; +import { CurrentUserAvatarBadgeCreator } from "../scene-commands/create-pages/CurrentUserAvatarBadgeCreator"; import { Command } from "./Command"; 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 currentUserAvatarBadgeCreator = new CurrentUserAvatarBadgeCreator(figma); + export const CommandsMapping: Record CommandHandler> = { networkRequest: () => new NetworkRequestCommandHandler(), - createPages: () => new CreatePagesCommandHandler(figma), + createPages: () => + new CreatePagesCommandHandler(currentUserAvatarBadgeCreator, figma), }; diff --git a/src/scene-commands/create-pages/CreatePagesCommandHandler.ts b/src/scene-commands/create-pages/CreatePagesCommandHandler.ts index d284b6d..4b81592 100644 --- a/src/scene-commands/create-pages/CreatePagesCommandHandler.ts +++ b/src/scene-commands/create-pages/CreatePagesCommandHandler.ts @@ -1,10 +1,24 @@ import { CommandHandler } from "../../commands-setup/CommandHandler"; import { CreatePagesCommand } from "./CreatePagesCommand"; +import { + BadgeStyle, + CurrentUserAvatarBadgeCreator, +} from "./CurrentUserAvatarBadgeCreator"; export class CreatePagesCommandHandler implements CommandHandler { - constructor(private readonly figma: PluginAPI) {} + private fontFamily = "Moderat"; + + private frameDimensions = { + width: 1240, + height: 640, + }; + + constructor( + private readonly currentUserAvatarBadgeCreator: CurrentUserAvatarBadgeCreator, + private readonly figma: PluginAPI + ) {} // `command` argument needed due to polymorphism. // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -47,7 +61,7 @@ export class CreatePagesCommandHandler frame.counterAxisAlignItems = "CENTER"; frame.paddingTop = 100; frame.itemSpacing = 30; - frame.resize(1240, 640); + frame.resize(this.frameDimensions.width, this.frameDimensions.height); return frame; }; @@ -58,7 +72,7 @@ export class CreatePagesCommandHandler ): Promise => { const heading = this.figma.createText(); - const font = { family: "Moderat", style: "Bold" }; + const font = { family: this.fontFamily, style: "Bold" }; await this.figma.loadFontAsync(font); heading.fontName = font; heading.characters = name; @@ -79,7 +93,7 @@ export class CreatePagesCommandHandler ): Promise => { const description = this.figma.createText(); - const font = { family: "Moderat", style: "Regular" }; + const font = { family: this.fontFamily, style: "Regular" }; await this.figma.loadFontAsync(font); description.fontName = font; description.fontSize = 64; @@ -92,18 +106,36 @@ export class CreatePagesCommandHandler return description; }; + const frame = await createFrame(); + this.figma.currentPage.appendChild(frame); + const heading = await createHeading(frame, "âœŒī¸ Add your title âœŒī¸"); + frame.appendChild(heading); + const description = await createDescription( frame, heading, "đŸĒŠ Add your description đŸĒŠ" ); - - this.figma.currentPage.appendChild(frame); - frame.appendChild(heading); frame.appendChild(description); + const badgeSize = 100; + const currentUserAvatarBadgeStyle: BadgeStyle = { + avatarImage: { + size: badgeSize, + xAxisPosition: this.frameDimensions.width - badgeSize, + yAxisPosition: this.frameDimensions.height - badgeSize, + }, + userNameText: { + fontSize: 14, + fontFamily: this.fontFamily, + }, + }; + await this.currentUserAvatarBadgeCreator.create( + currentUserAvatarBadgeStyle + ); + this.figma.currentPage.selection = [heading]; return frame; diff --git a/src/scene-commands/paint-current-user-avatar/PaintCurrentUserAvatarCommandHandler.ts b/src/scene-commands/create-pages/CurrentUserAvatarBadgeCreator.ts similarity index 64% rename from src/scene-commands/paint-current-user-avatar/PaintCurrentUserAvatarCommandHandler.ts rename to src/scene-commands/create-pages/CurrentUserAvatarBadgeCreator.ts index b4cf5dc..e16326d 100644 --- a/src/scene-commands/paint-current-user-avatar/PaintCurrentUserAvatarCommandHandler.ts +++ b/src/scene-commands/create-pages/CurrentUserAvatarBadgeCreator.ts @@ -1,18 +1,25 @@ import { NetworkRequestCommand } from "../../browser-commands/network-request/NetworkRequestCommand"; -import { CommandHandler } from "../../commands-setup/CommandHandler"; import { executeCommand } from "../../commands-setup/executeCommand"; -import { PaintCurrentUserAvatarCommand } from "./PaintCurrentUserAvatarCommand"; - -export class PaintCurrentUserAvatarCommandHandler - implements CommandHandler -{ - private readonly avatarImageSize = 100; +export type BadgeStyle = { + avatarImage: { + size: number; + xAxisPosition: number; + yAxisPosition: number; + }; + userNameText: { + fontSize: number; + fontFamily: string; + }; +}; + +export class CurrentUserAvatarBadgeCreator { constructor(private readonly figma: PluginAPI) {} - // `command` argument needed due to polymorphism. - // eslint-disable-next-line @typescript-eslint/no-unused-vars - handle(command: PaintCurrentUserAvatarCommand): Promise { + create(badgeStyle: BadgeStyle): Promise { + console.log("CurrentUserAvatarBadgeCreator.create(badgeStyle): "); + console.log(badgeStyle); + const currentUserAvatarUrl = this.figma.currentUser?.photoUrl; const currentUserName = this.figma.currentUser?.name; @@ -32,6 +39,7 @@ export class PaintCurrentUserAvatarCommandHandler this.ensureToOnlyReceiveNetworkRequestResponse(command); await this.createAvatarBadge( + badgeStyle, command.payload as ArrayBuffer, currentUserName as string ); @@ -50,18 +58,16 @@ export class PaintCurrentUserAvatarCommandHandler } private async createAvatarBadge( + badgeStyle: BadgeStyle, imageBuffer: ArrayBuffer, userName: string ): Promise { - const avatarImage = this.createAvatarImage(imageBuffer, userName); - const userNameText = await this.createAvatarText(userName); - - const elementsToFocus = [avatarImage, userNameText]; - this.figma.currentPage.selection = elementsToFocus; - this.figma.viewport.scrollAndZoomIntoView(elementsToFocus); + this.createAvatarImage(badgeStyle, imageBuffer, userName); + await this.createAvatarText(badgeStyle, userName); } private createAvatarImage( + badgeStyle: BadgeStyle, avatarImage: ArrayBuffer, currentUserName: string ): EllipseNode { @@ -69,9 +75,12 @@ export class PaintCurrentUserAvatarCommandHandler const figmaImage = this.figma.createImage(imageUint8Array); const imageWrapper = this.figma.createEllipse(); - imageWrapper.x = this.figma.viewport.center.x; - imageWrapper.y = this.figma.viewport.center.y; - imageWrapper.resize(this.avatarImageSize, this.avatarImageSize); + imageWrapper.x = badgeStyle.avatarImage.xAxisPosition; + imageWrapper.y = badgeStyle.avatarImage.yAxisPosition; + imageWrapper.resize( + badgeStyle.avatarImage.size, + badgeStyle.avatarImage.size + ); imageWrapper.fills = [ { type: "IMAGE", scaleMode: "FILL", imageHash: figmaImage.hash }, ]; @@ -82,17 +91,25 @@ export class PaintCurrentUserAvatarCommandHandler return imageWrapper; } - private async createAvatarText(userName: string): Promise { + private async createAvatarText( + badgeStyle: BadgeStyle, + userName: string + ): Promise { const userNameText = this.figma.createText(); userNameText.x = this.figma.viewport.center.x - userName.length / 2; userNameText.y = this.figma.viewport.center.y + - this.avatarImageSize + - this.avatarImageSize / 12; + badgeStyle.avatarImage.size + + badgeStyle.avatarImage.size / badgeStyle.userNameText.fontSize; + + const fontName: FontName = { + family: badgeStyle.userNameText.fontFamily, + style: "Regular", + }; + await this.figma.loadFontAsync(fontName); - await this.figma.loadFontAsync(userNameText.fontName as FontName); userNameText.characters = userName; - userNameText.fontSize = 14; + userNameText.fontSize = badgeStyle.userNameText.fontSize; return userNameText; } diff --git a/src/scene-commands/paint-current-user-avatar/PaintCurrentUserAvatarCommand.ts b/src/scene-commands/paint-current-user-avatar/PaintCurrentUserAvatarCommand.ts deleted file mode 100644 index 858827b..0000000 --- a/src/scene-commands/paint-current-user-avatar/PaintCurrentUserAvatarCommand.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { Command } from "../../commands-setup/Command"; - -export class PaintCurrentUserAvatarCommand implements Command { - readonly type = "paintCurrentUserAvatar"; -} diff --git a/tests/scene-commands/CreatePagesCommandHandler.test.ts b/tests/scene-commands/CreatePagesCommandHandler.test.ts index e71d0ff..8688933 100644 --- a/tests/scene-commands/CreatePagesCommandHandler.test.ts +++ b/tests/scene-commands/CreatePagesCommandHandler.test.ts @@ -2,21 +2,33 @@ import { mock } from "jest-mock-extended"; import { CreatePagesCommand } from "../../src/scene-commands/create-pages/CreatePagesCommand"; import { CreatePagesCommandHandler } from "../../src/scene-commands/create-pages/CreatePagesCommandHandler"; +import { CurrentUserAvatarBadgeCreator } from "../../src/scene-commands/create-pages/CurrentUserAvatarBadgeCreator"; import { figmaPluginApiMockForCreatePagesCommand } from "../figma-mocks/figma-mocks"; describe("CreatePagesCommandHandler", () => { it("can be instantiated without throwing errors", () => { const figmaPluginApiMock = mock(); + const currentUserAvatarBadgeCreator = new CurrentUserAvatarBadgeCreator( + figmaPluginApiMock + ); const commandHandlerInstantiator = () => { - new CreatePagesCommandHandler(figmaPluginApiMock); + new CreatePagesCommandHandler( + currentUserAvatarBadgeCreator, + figmaPluginApiMock + ); }; expect(commandHandlerInstantiator).not.toThrow(TypeError); }); it("notifies the end used with a farewell message", async () => { + const currentUserAvatarBadgeCreator = new CurrentUserAvatarBadgeCreator( + figmaPluginApiMockForCreatePagesCommand + ); + const commandHandler = new CreatePagesCommandHandler( + currentUserAvatarBadgeCreator, figmaPluginApiMockForCreatePagesCommand ); const command = new CreatePagesCommand(); @@ -27,7 +39,12 @@ describe("CreatePagesCommandHandler", () => { }); it("rename Cover Page", async () => { + const currentUserAvatarBadgeCreator = new CurrentUserAvatarBadgeCreator( + figmaPluginApiMockForCreatePagesCommand + ); + const commandHandler = new CreatePagesCommandHandler( + currentUserAvatarBadgeCreator, figmaPluginApiMockForCreatePagesCommand ); const command = new CreatePagesCommand(); @@ -38,7 +55,12 @@ describe("CreatePagesCommandHandler", () => { }); it("create secondary pages", async () => { + const currentUserAvatarBadgeCreator = new CurrentUserAvatarBadgeCreator( + figmaPluginApiMockForCreatePagesCommand + ); + const commandHandler = new CreatePagesCommandHandler( + currentUserAvatarBadgeCreator, figmaPluginApiMockForCreatePagesCommand ); const command = new CreatePagesCommand();