diff --git a/locales/ja.yml b/locales/ja.yml
index 436a97f1eb..052dd59971 100644
--- a/locales/ja.yml
+++ b/locales/ja.yml
@@ -50,6 +50,7 @@ common:
   delete: "削除"
   loading: "読み込み中"
   ok: "わかった"
+  update-available-title: "更新があります"
   update-available: "Misskeyの新しいバージョンがあります({newer}。現在{current}を利用中)。ページを再度読み込みすると更新が適用されます。"
   my-token-regenerated: "あなたのトークンが更新されたのでサインアウトします。"
   i-like-sushi: "私は(プリンよりむしろ)寿司が好き"
diff --git a/src/client/app/common/scripts/check-for-update.ts b/src/client/app/common/scripts/check-for-update.ts
index b5ba6916d1..e2d5b7923b 100644
--- a/src/client/app/common/scripts/check-for-update.ts
+++ b/src/client/app/common/scripts/check-for-update.ts
@@ -23,11 +23,18 @@ export default async function(mios: MiOS, force = false, silent = false) {
 		}
 
 		if (!silent) {
-			alert('%i18n:common.update-available%'.replace('{newer}', newer).replace('{current}', current));
+			mios.apis.dialog({
+				title: '%i18n:common.update-available-title%',
+				text: '%i18n:common.update-available%'.replace('{newer}', newer).replace('{current}', current)
+			});
 		}
 
 		return newer;
 	} else {
+		mios.apis.dialog({
+			title: '%i18n:common.update-available-title%',
+			text: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
+		});
 		return null;
 	}
 }
diff --git a/src/client/app/desktop/views/pages/user/user.vue b/src/client/app/desktop/views/pages/user/user.vue
index 0e0d4f5768..7f05c695af 100644
--- a/src/client/app/desktop/views/pages/user/user.vue
+++ b/src/client/app/desktop/views/pages/user/user.vue
@@ -1,8 +1,8 @@
 <template>
 <mk-ui>
 	<div class="zwwan0di1v4356rmdbjmwnn32tptpdp2" v-if="!fetching" :data-darkmode="$store.state.device.darkmode">
-		<div class="is-suspended" v-if="user.isSuspended || true">%fa:exclamation-triangle% %i18n:@is-suspended%</div>
-		<div class="is-remote" v-if="user.host != null || true">%fa:exclamation-triangle% %i18n:@is-remote%<a :href="user.url || user.uri" target="_blank">%i18n:@view-remote%</a></div>
+		<div class="is-suspended" v-if="user.isSuspended">%fa:exclamation-triangle% %i18n:@is-suspended%</div>
+		<div class="is-remote" v-if="user.host != null">%fa:exclamation-triangle% %i18n:@is-remote%<a :href="user.url || user.uri" target="_blank">%i18n:@view-remote%</a></div>
 		<main>
 			<div class="main">
 				<x-header :user="user"/>
diff --git a/src/client/app/mobile/api/dialog.ts b/src/client/app/mobile/api/dialog.ts
index a2378767be..23f35b7aa9 100644
--- a/src/client/app/mobile/api/dialog.ts
+++ b/src/client/app/mobile/api/dialog.ts
@@ -1,5 +1,18 @@
-export default function(opts) {
+import OS from '../../mios';
+import Dialog from '../views/components/dialog.vue';
+
+export default (os: OS) => opts => {
 	return new Promise<string>((res, rej) => {
-		alert('dialog not implemented yet');
+		const o = opts || {};
+		const d = os.new(Dialog, {
+			title: o.title,
+			text: o.text,
+			modal: o.modal,
+			buttons: o.actions
+		});
+		d.$once('clicked', id => {
+			res(id);
+		});
+		document.body.appendChild(d.$el);
 	});
-}
+};
diff --git a/src/client/app/mobile/script.ts b/src/client/app/mobile/script.ts
index cc0a8331ba..cfa9654e61 100644
--- a/src/client/app/mobile/script.ts
+++ b/src/client/app/mobile/script.ts
@@ -88,7 +88,7 @@ init((launch) => {
 	launch(router, os => ({
 		chooseDriveFolder,
 		chooseDriveFile,
-		dialog,
+		dialog: dialog(os),
 		input,
 		post: post(os),
 		notify
diff --git a/src/client/app/mobile/views/components/dialog.vue b/src/client/app/mobile/views/components/dialog.vue
new file mode 100644
index 0000000000..9ee01cb782
--- /dev/null
+++ b/src/client/app/mobile/views/components/dialog.vue
@@ -0,0 +1,171 @@
+<template>
+<div class="mk-dialog">
+	<div class="bg" ref="bg" @click="onBgClick"></div>
+	<div class="main" ref="main">
+		<header v-html="title" :class="$style.header"></header>
+		<div class="body" v-html="text"></div>
+		<div class="buttons">
+			<button v-for="button in buttons" @click="click(button)">{{ button.text }}</button>
+		</div>
+	</div>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+import * as anime from 'animejs';
+
+export default Vue.extend({
+	props: {
+		title: {
+			type: String,
+			required: false
+		},
+		text: {
+			type: String,
+			required: true
+		},
+		buttons: {
+			type: Array,
+			default: () => {
+				return [{
+					text: 'OK'
+				}];
+			}
+		},
+		modal: {
+			type: Boolean,
+			default: false
+		}
+	},
+	mounted() {
+		this.$nextTick(() => {
+			(this.$refs.bg as any).style.pointerEvents = 'auto';
+			anime({
+				targets: this.$refs.bg,
+				opacity: 1,
+				duration: 100,
+				easing: 'linear'
+			});
+
+			anime({
+				targets: this.$refs.main,
+				opacity: 1,
+				scale: [1.2, 1],
+				duration: 300,
+				easing: [0, 0.5, 0.5, 1]
+			});
+		});
+	},
+	methods: {
+		click(button) {
+			this.$emit('clicked', button.id);
+			this.close();
+		},
+		close() {
+			(this.$refs.bg as any).style.pointerEvents = 'none';
+			anime({
+				targets: this.$refs.bg,
+				opacity: 0,
+				duration: 300,
+				easing: 'linear'
+			});
+
+			(this.$refs.main as any).style.pointerEvents = 'none';
+			anime({
+				targets: this.$refs.main,
+				opacity: 0,
+				scale: 0.8,
+				duration: 300,
+				easing: [ 0.5, -0.5, 1, 0.5 ],
+				complete: () => this.$destroy()
+			});
+		},
+		onBgClick() {
+			if (!this.modal) {
+				this.close();
+			}
+		}
+	}
+});
+</script>
+
+<style lang="stylus" scoped>
+@import '~const.styl'
+
+.mk-dialog
+	> .bg
+		display block
+		position fixed
+		z-index 8192
+		top 0
+		left 0
+		width 100%
+		height 100%
+		background rgba(#000, 0.7)
+		opacity 0
+		pointer-events none
+
+	> .main
+		display block
+		position fixed
+		z-index 8192
+		top 20%
+		left 0
+		right 0
+		margin 0 auto 0 auto
+		padding 16px
+		width calc(100% - 32px)
+		max-width 300px
+		background #fff
+		opacity 0
+
+		> .body
+			margin 1em 0
+			color #888
+
+		> .buttons
+			> button
+				display inline-block
+				float right
+				margin 0
+				padding 0 10px
+				font-size 1.1em
+				font-weight normal
+				text-decoration none
+				color #888
+				background transparent
+				outline none
+				border none
+				border-radius 0
+				cursor pointer
+				transition color 0.1s ease
+
+				i
+					margin 0 0.375em
+
+				&:hover
+					color $theme-color
+
+				&:active
+					color darken($theme-color, 10%)
+					transition color 0s ease
+
+</style>
+
+<style lang="stylus" module>
+@import '~const.styl'
+
+.header
+	margin 0 0 1em 0
+	color $theme-color
+	// color #43A4EC
+	font-weight bold
+
+	&:empty
+		display none
+
+	> i
+		margin-right 0.5em
+
+</style>