# ニックネームとメッセージの装飾

> 基本スキンで、ニックネームの色・画像、メッセージの背景・行間、ニックネームの非表示を変えるサンプルです。

基本スキン(basic-v2)では、CSSを上書きする代わりに決められた方法(resolver)でニックネームとメッセージの見た目を変えます。スキンが変わっても崩れず、ほかのスクリプトと一緒に使ってもお互いに上書きし合いません。

チャットルーム全体の色・文字サイズ・見た目は、コードを書かずに[管理ページ](/manage)の**スキン**メニューで変更できます。

## コード {#code}

次のコード1つに、このページの装飾がすべて入っています。画像のURLは自分の画像に置き換えてください。

```html
<script>U=window.U=window.U||{},U.events=U.events||[],U.chat=function(n){return{on:function(e,t){U.events.push([n,e,t])},off:function(e){for(var t=U.events.length;t>0;)U.events[--t][0]==n&&U.events[t][1]==e&&U.events.splice(t,1)}}};</script>
<script async src="https://client.uchat.io/uchat.js"></script>
<u-chat room="my-room" style="display:inline-block;width:100%;max-width:400px;height:500px"></u-chat>

<script>
U.chat('my-room').on('after.create#decorate', function (room) {
	var ui = room.skin;

	// 1. 管理者のニックネーム: 色と太さ、管理者はニックネームの代わりに画像
	ui.nickname.resolver.add({
		id: 'decorate.admin',
		resolve: function (c) {
			var auth = c.user ? Number(c.user.auth) : 0;
			if (auth < 2) return null;
			var patch = { style: { color: auth >= 3 ? '#e11d48' : '#2563eb', fontWeight: 'bold' } };
			if (auth >= 3) patch.nickcon = { src: 'https://example.com/img/owner-name.png', alt: c.user.nick };
			return patch;
		},
	});

	// 2. レベル 10 以上: ニックネームの前にバッジアイコンを追加する(既存のアイコンは維持)
	ui.nickname.resolver.add({
		id: 'decorate.level',
		resolve: function (c) {
			if (!c.user || Number(c.user.level) < 10) return null;
			return {
				iconsBefore: c.current.iconsBefore.concat([
					{ src: 'https://example.com/img/badge.png', alt: 'レベル10' },
				]),
			};
		},
	});

	// 3. 自分の名前を含むメッセージ: 背景色と広めの行間
	ui.message.resolver.add({
		id: 'decorate.mention',
		resolve: function (c) {
			var me = room.my.nick;
			if (!me || c.author.nick === me || c.message.content.indexOf(me) < 0) return null;
			return { style: { backgroundColor: '#fff4cc' }, row: { spacing: 'relaxed' } };
		},
	});

	// 4. 自分が送った通常のメッセージ: ニックネームを隠し、文字色だけを変える
	ui.message.resolver.add({
		id: 'decorate.mine',
		resolve: function (c) {
			if (!room.my.nick || c.author.nick !== room.my.nick) return null;
			return { style: { color: '#1d4ed8' }, row: { hideNickname: true } };
		},
	});

	// 5. メッセージの後ろに送信時刻(時:分)
	ui.message.extend({
		id: 'decorate.time',
		position: 'after',
		mount: function (host, c) {
			host.textContent = ' ' + String(c.message.created || '').slice(11, 16);
			host.style.color = '#9ca3af';
			host.style.fontSize = '11px';
		},
	});
});

// 自分のニックネームを使うルール(3、4)は、入室後やニックネーム変更後に再計算する
function refreshDecorate(room) {
	room.skin.message.resolver.refresh();
}
U.chat('my-room').on('after.join#decorate', refreshDecorate);
U.chat('my-room').on('after.user.modi#decorate', refreshDecorate);
</script>
```

## ニックネームの色と画像 {#nickname}

`nickname.resolver.add` の `resolve(c)` は、ニックネームが描画されるたびに呼ばれます。変えたいものだけを入れたオブジェクトを返し、変えるものがなければ `null` を返します。

| `c` の値 | 説明 |
| --- | --- |
| `c.user` | ニックネームの持ち主(`nick`、`auth`、`level`、`icons`、`nickcon` など) |
| `c.location` | 描画する場所。メッセージは `message`、ユーザー一覧は `user-list` |
| `c.current` | 前のresolverまでを適用した結果(`style`、`nickcon`、`iconsBefore`、`iconsAfter`) |
| `c.base` | resolverを適用する前の既定値 |

- `nickcon` は、ニックネームの文字の代わりに表示する画像です(高さ16px以内)。画像を読み込めなかった場合はニックネームの文字が表示されます。`nickcon: null` にすると、会員連携で受け取ったニックネーム画像も消します。
- `iconsBefore`、`iconsAfter` は配列全体を置き換えます。既存のアイコンを残すには、上のコードのように `c.current.iconsBefore` に追加します。
- 画像のURLは `https://`、`http://`、`//`、`/` で始まるものを使います。形式が合わないURLは無視されます。

## メッセージの背景と行間 {#message-style}

`message.resolver.add` の `resolve(c)` は、通常のメッセージとウィスパーを描画するときに呼ばれます。`c.message`(`content`、`created`、`kind` など)と送信者の `c.author` を読めます。

`row: { spacing: 'relaxed' }` を返すと、その行の内側の余白が広がり、目立つようになります。既定値は `normal` です。

## 自分のメッセージのニックネームを隠す {#hide-nickname}

`row: { hideNickname: true }` を返すと、その行ではニックネームを描画しません。通常のメッセージにだけ適用され、ウィスパーは誰が送ったかがわかる必要があるため、常にニックネームが表示されます。

入室前は `room.my.nick` が空なので、自分のニックネームを使うルールは `after.join` で `message.resolver.refresh()` を呼んで再計算します。ニックネームのルールで同じことが必要なら `nickname.resolver.refresh()` を呼びます。

## メッセージの横に時刻 {#timestamps}

`message.extend` は、各メッセージの前(`before`)、中(`inline`)、後ろ(`after`)に小さな領域を付け、`mount(host, c)` で自分で描画させます。`c.message.created` は `YYYY-MM-DD HH:MM:SS` 形式です。`match(c)` も一緒に渡すと、`true` を返したメッセージにだけ付きます。

## 使える値 {#values}

| 対象 | キー | 値 |
| --- | --- | --- |
| ニックネーム・メッセージの `style` | `color`, `backgroundColor` | `#rgb`, `#rgba`, `#rrggbb`, `#rrggbbaa`, `rgb()`, `rgba()`, `hsl()`, `hsla()` |
|  | `fontWeight` | `normal`, `bold`, `100`〜`900` |
|  | `fontStyle` | `normal`, `italic`, `oblique` |
|  | `textDecoration` | `none`, `underline`, `overline`, `line-through` |
| ニックネーム | `nickcon` | `{ src, alt }` または `null` |
|  | `iconsBefore`, `iconsAfter` | `[{ src, alt }]` |
| メッセージ | `row.spacing` | `normal`, `relaxed` |
|  | `row.hideNickname` | `true`, `false` |

これ以外のCSSプロパティや値、HTMLはエラーを出さずに無視されます。もっと自由な見た目が必要なら、`message.extend` や `mount.*` で受け取った領域の中で自分で描画してください。

## 注意点 {#notes}

- `resolve`、`match` は同期関数である必要があります。Promiseを返すとエラーとして扱われ、そのルールは無視されます。
- resolverが複数ある場合は、`order` の値の順(同じなら登録順)に重ねて適用されます。後のルールが同じキーを上書きします。
- `add` は解除用の関数を返します。装飾をやめるには、その関数を呼ぶか `remove(id)` を呼びます。全体の一覧は[スキンUI API](/ja/doc/3.4)をご覧ください。
- このAPIは基本スキン(basic-v2)にだけあります。
