Skip to content

Commit

Permalink
✨ [twitter] 画像右下に番組情報を埋め込めるように
Browse files Browse the repository at this point in the history
  • Loading branch information
ci7lus committed Jan 12, 2022
1 parent 0c1eded commit 3c94cfb
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 22 deletions.
51 changes: 42 additions & 9 deletions src/miraktest-twitter/components/Tweet.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import clsx from "clsx"
import dayjs from "dayjs"
import React, { useEffect, useRef, useState } from "react"
import { RotateCw } from "react-feather"
import {
Expand All @@ -9,11 +10,17 @@ import {
import type { Status } from "twitter-d"
import twitterText from "twitter-text"
import { ContentPlayerPlayingContent } from "../../@types/plugin"
import { embedInfoInImage } from "../embedInfo"
import { imageToCanvas } from "../imageToCanvas"
import { SayaDefinition, TwitterSetting } from "../types"
import { blobToBase64Uri } from "../utils"

type ImageDatum = {
imageUrl: string
uri: string
}

export const TweetComponent: React.FC<{
setting: Required<Omit<TwitterSetting, "isContentInfoEmbedInImageEnabled">>
setting: Required<TwitterSetting>
playingContent: ContentPlayerPlayingContent | null
sayaDefinition: SayaDefinition
imageUrl: string | null
Expand All @@ -31,7 +38,7 @@ export const TweetComponent: React.FC<{
setRemaining(280 - weightedLength)
}, [text, hashtag])
const [isPosting, setIsPosting] = useState(false)
const [images, setImages] = useState<string[]>([])
const [images, setImages] = useState<ImageDatum[]>([])
const [selectedImages, setSelectedImages] = useState<string[]>([])
const [failed, setFailed] = useState("")
const [serviceTags, setServiceTags] = useState<string[]>([])
Expand Down Expand Up @@ -79,7 +86,29 @@ export const TweetComponent: React.FC<{
if (!imageUrl) {
return
}
setImages((images) => [imageUrl, ...images.slice(0, 30)])
imageToCanvas(imageUrl)
.then((canvas) => {
if (setting.isContentInfoEmbedInImageEnabled === false) {
const image = canvas.toDataURL("image/jpeg", 0.9).split(",")[1]
setImages((images) => [
{ imageUrl, uri: image },
...images.slice(0, 30),
])
} else {
const label = [
playingContent?.service?.name,
playingContent?.program?.name,
dayjs().format("HH:mm"),
]
.filter((s) => s)
.join(" - ")
setImages((images) => [
{ imageUrl, uri: embedInfoInImage(canvas, label).split(",")[1] },
...images.slice(0, 30),
])
}
})
.catch(console.error)
}, [imageUrl])
const formRef = useRef<HTMLFormElement>(null)

Expand Down Expand Up @@ -235,11 +264,15 @@ export const TweetComponent: React.FC<{
setIsPosting(true)
setFailed("")
Promise.all(
selectedImages.map(async (image) => {
const response = await fetch(image)
const uri = await blobToBase64Uri(await response.blob())
selectedImages.map(async (targetImageUrl) => {
const media = images.find(
({ imageUrl }) => imageUrl === targetImageUrl
)?.uri
if (!media) {
return
}
const uploadResult = await twitter.media.mediaUpload({
media: uri.split(",")[1],
media,
})
return uploadResult.media_id_string
})
Expand Down Expand Up @@ -388,7 +421,7 @@ export const TweetComponent: React.FC<{
"gap-2"
)}
>
{images.map((imageUrl) => (
{images.map(({ imageUrl }) => (
<div
onClick={() => {
setSelectedImages((selectedImages) =>
Expand Down
6 changes: 3 additions & 3 deletions src/miraktest-twitter/components/TwitterRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export const TwitterRenderer: InitPlugin["renderer"] = ({
)
const [
isContentInfoEmbedInImageEnabled,
// TODO: setIsContentInfoEmbedInImageEnabled,
setIsContentInfoEmbedInImageEnabled,
] = useState(setting.isContentInfoEmbedInImageEnabled)
return (
<>
Expand Down Expand Up @@ -114,7 +114,7 @@ export const TwitterRenderer: InitPlugin["renderer"] = ({
value={accessTokenSecret || ""}
onChange={(e) => setAccessTokenSecret(e.target.value)}
/>
{/*<label className="block mt-4">
<label className="block mt-4">
<span>画像に番組情報を埋め込む</span>
<input
type="checkbox"
Expand All @@ -126,7 +126,7 @@ export const TwitterRenderer: InitPlugin["renderer"] = ({
)
}
/>
</label>*/}
</label>
</label>
<button
type="submit"
Expand Down
29 changes: 29 additions & 0 deletions src/miraktest-twitter/embedInfo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
export const embedInfoInImage = (
imageCanvas: HTMLCanvasElement,
info: string
) => {
const infoCanvas = document.createElement("canvas")
infoCanvas.width = imageCanvas.width * 2
infoCanvas.height = imageCanvas.height * 2
const ctx = infoCanvas.getContext("2d")
if (!ctx) {
throw new Error("info ctx context error")
}
ctx.scale(2, 2)
ctx.font = `24px "Hiragino Maru Gothic ProN"`
ctx.fillStyle = "rgba(255, 255, 255, 0.5)"
ctx.textAlign = "right"
ctx.textBaseline = "middle"
ctx.shadowBlur = 5
ctx.shadowColor = "rgba(0, 0, 0, .5)"
ctx.shadowOffsetX = 5
ctx.shadowOffsetY = 5
ctx.fillText(info, imageCanvas.width - 10, imageCanvas.height - 20)
const imCtx = imageCanvas.getContext("2d")
if (!imCtx) {
throw new Error("info ctx context error")
}
imCtx.drawImage(infoCanvas, 0, 0, imageCanvas.width, imageCanvas.height)
const datauri = imageCanvas.toDataURL("image/jpeg", 0.9)
return datauri
}
19 changes: 19 additions & 0 deletions src/miraktest-twitter/imageToCanvas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export const imageToCanvas = async (url: string) => {
const canvas = document.createElement("canvas")
const imCtx = canvas.getContext("2d")
if (!imCtx) {
throw new Error("ctx context error")
}
await new Promise<void>((res, rej) => {
const image = new Image()
image.onload = () => {
canvas.width = image.width
canvas.height = image.height
imCtx.drawImage(image, 0, 0)
res()
}
image.onerror = rej
image.src = url
})
return canvas
}
10 changes: 0 additions & 10 deletions src/miraktest-twitter/utils.ts

This file was deleted.

0 comments on commit 3c94cfb

Please sign in to comment.