4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Vuetify】埋め込みツイート風コンポーネント

Last updated at Posted at 2020-05-08

はじめに

埋め込みツイートの表示をするにあたり、公式の埋め込みツイートを使おうと思ったのですが、なぜかVueで<script>が動きませんでした。原因を調べてもいまいちわからなかったので、自作したほうが早いのではないかと思いました。
一応、Vuetifyにも「Twitter card」というものはあったのですが……なんか違う。
image.png
というわけで、v-cardを基本にそれっぽいレイアウトにしてみました。

つくるもの

こんな感じのツイート風コンポーネントです。
tweet

See the Pen Oembed Tweet by END (@aiandrox) on CodePen.

環境

  • Vue.js 2.6.11
  • Vuetify 2.2.21
  • Material Design Icon 5.1.45

アイコンは別のものでも代用可能だと思います。Twitterアイコン、リプライ、リツイート、いいねで使用しています。

使用データ

サンプルコードでは以下のようにデータをネストしています。

  • tweet
    • tweetId(ツイートのID)
    • html(本文部分のHTML)
    • tweetedAt(ツイート日時)
    • user
      • name(表示名)
      • screenName(@screen_nameのユーザー名)
      • avatarUrl(アイコンの画像URL)

htmlはGET statuses/oembedで取得した埋め込みツイート用のHTMLから<p>タグの内側を抜き出して利用しました。
日時はDay.jsなどを使ってフォーマットを整えることをおすすめします。

ツイート表示の規定

Twitterの埋め込みツイートには表示の規定があるので、それに従ってカードを作成します。
こちらのDisplay requirements – Twitter DevelopersをざっくりGoogle意訳しました。間違っていたらご指摘をお願いします。

  • 実際のアカウントによる変更を加えられていないツイートを表示すること。
  • 公式ツイッターロゴを表示すること。
    • 個々のツイートの右上隅に表示するか、タイムラインに直接添付すること。
    • ロゴは、画像と同じ高さにすること。
  • 作成者のプロフィール画像、@ユーザーネーム、表示名を常に表示し、Twitterプロフィールページにリンクさせること。
  • @ユーザーネームは@を用いて表示すること。
  • プロフィール画像は、表示名と@ユーザーネームの左側に配置すること(ただし右から左に読む言語のツイートは例外)
  • ツイートのテキストは作成者の表示名と@ユーザーネームの下の行に表示すること。
  • ツイートのテキストと作成者の周りのスペースはツイートのパーマリンクにリンクさせること。
  • テキスト内のツイートエンティティは、Twitter上の適切なホームに適切にリンクすること。
    • @ユーザーネームによるメンションは言及されたユーザーのプロフィールページにリンクすること。
    • ハッシュタグは、ハッシュタグをクエリとしてTwitter検索にリンクすること。
    • テキスト内のリンクは、URL entities APIにおけるdisplay_urlを表示し、元のt.co urlにリンクすること(詳細はt.co best practices articleを参照)
  • ツイートのタイムスタンプを表示し、ツイートのパーマリンクにリンクすること。
  • プラットフォームに存在しないツイートのモックアップを使用しないこと。

コード

CodePenの方はそれだけで完結するようにしています。
こちらのコードはコンポーネント用のコードです。

tweet.vue
<template>
  <div>
    <v-card flat outlined max-width="500" class="mt-3" :href="tweetUrl">
      <v-card-title>
        <v-list-item class="pl-0">
          <v-list-item :href="userUrl">
            <v-list-item-avatar color="grey" size="40">
              <v-img :src="tweet.user.avatarUrl" />
            </v-list-item-avatar>
            <v-list-item-content>
              <v-list-item-title>{{ tweet.user.name }}</v-list-item-title>
              <v-list-item-subtitle class="font-weight-light">@{{ tweet.user.screenName }}</v-list-item-subtitle>
            </v-list-item-content>
          </v-list-item>
          <v-spacer />
          <v-list-item-action>
            <v-icon color="blue">mdi-twitter</v-icon>
          </v-list-item-action>
        </v-list-item>
      </v-card-title>

      <v-card-text class="text--primary" v-html="tweet.html" />
        <v-card-actions>
          <v-btn
            v-for="button in buttons"
            :key="button.icon"
            :href="button.url"
            :color="button.color"
            icon
          >
            <v-icon>{{ button.icon }}</v-icon>
          </v-btn>
          <v-spacer />
          <span class="body-2 font-weight-light">{{ tweet.tweetedAt }}</span>
        </v-card-actions>
      </v-card>
    </v-card>
  </div>
</template>

<script>
export default {
  data() {
    return {
      tweet: {
        tweetId: "0000000000",
        html: "テキスト<br>改行するとこんな感じ",
        tweetedAt: "2020-12-31 10:51",
        user: {
          name: "ユーザー",
          screenName: "screen_name",
          avatarUrl: "https://avataaars.io/?avatarStyle=Transparent&topType=ShortHairShortCurly&accessoriesType=Prescription02&hairColor=Black&facialHairType=Blank&clotheType=Hoodie&clotheColor=White&eyeType=Default&eyebrowType=DefaultNatural&mouthType=Default&skinColor=Light"
        },
      }
    }
  },
  computed: {
    userUrl() {
      return `https://twitter.com/${this.tweet.user.screenName}`
    },
    tweetUrl() {
      return `https://twitter.com/${this.tweet.user.screenName}/status/${this.tweet.tweetId}`
    },
    replyUrl() {
      return `https://twitter.com/intent/tweet?in_reply_to=${this.tweet.tweetId}`
    },
    retweetUrl() {
      return `https://twitter.com/intent/retweet?tweet_id=${this.tweet.tweetId}`
    },
    likeUrl() {
      return `https://twitter.com/intent/like?tweet_id=${this.tweet.tweetId}`
    },
    buttons() {
      return [
        { url: this.replyUrl, color: "gray", icon: "mdi-chat-outline" },
        { url: this.retweetUrl, color: "green", icon: "mdi-twitter-retweet" },
        { url: this.likeUrl, color: "pink", icon: "mdi-heart-outline" }
      ]
    },
  },
}
</script>

おわりに

プロフィールのリンクの範囲が気になりますが、これで妥協点とします。
もっといい感じにできそうなら踏み台にしていただきたいです。リンクをしていただければ見に行きますので……。

ご意見、ご指摘などございましたらコメントを頂けると幸いです。

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?