wip
This commit is contained in:
		
							parent
							
								
									61b95e0c26
								
							
						
					
					
						commit
						99b3499364
					
				
					 103 changed files with 878 additions and 790 deletions
				
			
		| 
						 | 
				
			
			@ -62,7 +62,7 @@ export default Vue.extend({
 | 
			
		|||
 | 
			
		||||
		send() {
 | 
			
		||||
			this.sending = true;
 | 
			
		||||
			this.$root.$data.os.api('messaging/messages/create', {
 | 
			
		||||
			(this as any).api('messaging/messages/create', {
 | 
			
		||||
				user_id: this.user.id,
 | 
			
		||||
				text: this.text
 | 
			
		||||
			}).then(message => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,7 +8,7 @@
 | 
			
		|||
			<p class="read" v-if="message.is_me && message.is_read">%i18n:common.tags.mk-messaging-message.is-read%</p>
 | 
			
		||||
			<button class="delete-button" v-if="message.is_me" title="%i18n:common.delete%"><img src="/assets/desktop/messaging/delete.png" alt="Delete"/></button>
 | 
			
		||||
			<div class="content" v-if="!message.is_deleted">
 | 
			
		||||
				<mk-post-html v-if="message.ast" :ast="message.ast" :i="$root.$data.os.i"/>
 | 
			
		||||
				<mk-post-html v-if="message.ast" :ast="message.ast" :i="os.i"/>
 | 
			
		||||
				<mk-url-preview v-for="url in urls" :url="url" :key="url"/>
 | 
			
		||||
				<div class="image" v-if="message.file"><img src={ message.file.url } alt="image" title={ message.file.name }/></div>
 | 
			
		||||
			</div>
 | 
			
		||||
| 
						 | 
				
			
			@ -30,7 +30,7 @@ export default Vue.extend({
 | 
			
		|||
	props: ['message'],
 | 
			
		||||
	computed: {
 | 
			
		||||
		isMe(): boolean {
 | 
			
		||||
			return this.message.user_id == this.$root.$data.os.i.id;
 | 
			
		||||
			return this.message.user_id == (this as any).os.i.id;
 | 
			
		||||
		},
 | 
			
		||||
		urls(): string[] {
 | 
			
		||||
			if (this.message.ast) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -48,7 +48,7 @@ export default Vue.extend({
 | 
			
		|||
	},
 | 
			
		||||
 | 
			
		||||
	mounted() {
 | 
			
		||||
		this.connection = new MessagingStreamConnection(this.$root.$data.os.i, this.user.id);
 | 
			
		||||
		this.connection = new MessagingStreamConnection((this as any).os.i, this.user.id);
 | 
			
		||||
 | 
			
		||||
		this.connection.on('message', this.onMessage);
 | 
			
		||||
		this.connection.on('read', this.onRead);
 | 
			
		||||
| 
						 | 
				
			
			@ -72,7 +72,7 @@ export default Vue.extend({
 | 
			
		|||
			return new Promise((resolve, reject) => {
 | 
			
		||||
				const max = this.existMoreMessages ? 20 : 10;
 | 
			
		||||
 | 
			
		||||
				this.$root.$data.os.api('messaging/messages', {
 | 
			
		||||
				(this as any).api('messaging/messages', {
 | 
			
		||||
					user_id: this.user.id,
 | 
			
		||||
					limit: max + 1,
 | 
			
		||||
					until_id: this.existMoreMessages ? this.messages[0].id : undefined
 | 
			
		||||
| 
						 | 
				
			
			@ -99,7 +99,7 @@ export default Vue.extend({
 | 
			
		|||
			const isBottom = this.isBottom();
 | 
			
		||||
 | 
			
		||||
			this.messages.push(message);
 | 
			
		||||
			if (message.user_id != this.$root.$data.os.i.id && !document.hidden) {
 | 
			
		||||
			if (message.user_id != (this as any).os.i.id && !document.hidden) {
 | 
			
		||||
				this.connection.send({
 | 
			
		||||
					type: 'read',
 | 
			
		||||
					id: message.id
 | 
			
		||||
| 
						 | 
				
			
			@ -109,7 +109,7 @@ export default Vue.extend({
 | 
			
		|||
			if (isBottom) {
 | 
			
		||||
				// Scroll to bottom
 | 
			
		||||
				this.scrollToBottom();
 | 
			
		||||
			} else if (message.user_id != this.$root.$data.os.i.id) {
 | 
			
		||||
			} else if (message.user_id != (this as any).os.i.id) {
 | 
			
		||||
				// Notify
 | 
			
		||||
				this.notify('%i18n:common.tags.mk-messaging-room.new-message%');
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -157,7 +157,7 @@ export default Vue.extend({
 | 
			
		|||
		onVisibilitychange() {
 | 
			
		||||
			if (document.hidden) return;
 | 
			
		||||
			this.messages.forEach(message => {
 | 
			
		||||
				if (message.user_id !== this.$root.$data.os.i.id && !message.is_read) {
 | 
			
		||||
				if (message.user_id !== (this as any).os.i.id && !message.is_read) {
 | 
			
		||||
					this.connection.send({
 | 
			
		||||
						type: 'read',
 | 
			
		||||
						id: message.id
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -71,13 +71,13 @@ export default Vue.extend({
 | 
			
		|||
		};
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		this.connection = this.$root.$data.os.streams.messagingIndexStream.getConnection();
 | 
			
		||||
		this.connectionId = this.$root.$data.os.streams.messagingIndexStream.use();
 | 
			
		||||
		this.connection = (this as any).os.streams.messagingIndexStream.getConnection();
 | 
			
		||||
		this.connectionId = (this as any).os.streams.messagingIndexStream.use();
 | 
			
		||||
 | 
			
		||||
		this.connection.on('message', this.onMessage);
 | 
			
		||||
		this.connection.on('read', this.onRead);
 | 
			
		||||
 | 
			
		||||
		this.$root.$data.os.api('messaging/history').then(messages => {
 | 
			
		||||
		(this as any).api('messaging/history').then(messages => {
 | 
			
		||||
			this.fetching = false;
 | 
			
		||||
			this.messages = messages;
 | 
			
		||||
		});
 | 
			
		||||
| 
						 | 
				
			
			@ -85,11 +85,11 @@ export default Vue.extend({
 | 
			
		|||
	beforeDestroy() {
 | 
			
		||||
		this.connection.off('message', this.onMessage);
 | 
			
		||||
		this.connection.off('read', this.onRead);
 | 
			
		||||
		this.$root.$data.os.stream.dispose(this.connectionId);
 | 
			
		||||
		(this as any).os.stream.dispose(this.connectionId);
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
		isMe(message) {
 | 
			
		||||
			return message.user_id == this.$root.$data.os.i.id;
 | 
			
		||||
			return message.user_id == (this as any).os.i.id;
 | 
			
		||||
		},
 | 
			
		||||
		onMessage(message) {
 | 
			
		||||
			this.messages = this.messages.filter(m => !(
 | 
			
		||||
| 
						 | 
				
			
			@ -109,7 +109,7 @@ export default Vue.extend({
 | 
			
		|||
				this.result = [];
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
			this.$root.$data.os.api('users/search', {
 | 
			
		||||
			(this as any).api('users/search', {
 | 
			
		||||
				query: this.q,
 | 
			
		||||
				max: 5
 | 
			
		||||
			}).then(users => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -47,7 +47,7 @@
 | 
			
		|||
			},
 | 
			
		||||
			vote(id) {
 | 
			
		||||
				if (this.poll.choices.some(c => c.is_voted)) return;
 | 
			
		||||
				this.$root.$data.os.api('posts/polls/vote', {
 | 
			
		||||
				(this as any).api('posts/polls/vote', {
 | 
			
		||||
					post_id: this.post.id,
 | 
			
		||||
					choice: id
 | 
			
		||||
				}).then(() => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -48,7 +48,7 @@ export default Vue.extend({
 | 
			
		|||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
		pin() {
 | 
			
		||||
			this.$root.$data.os.api('i/pin', {
 | 
			
		||||
			(this as any).api('i/pin', {
 | 
			
		||||
				post_id: this.post.id
 | 
			
		||||
			}).then(() => {
 | 
			
		||||
				this.$destroy();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -68,7 +68,7 @@ export default Vue.extend({
 | 
			
		|||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
		react(reaction) {
 | 
			
		||||
			this.$root.$data.os.api('posts/reactions/create', {
 | 
			
		||||
			(this as any).api('posts/reactions/create', {
 | 
			
		||||
				post_id: this.post.id,
 | 
			
		||||
				reaction: reaction
 | 
			
		||||
			}).then(() => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,7 +28,7 @@ export default Vue.extend({
 | 
			
		|||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
		onUsernameChange() {
 | 
			
		||||
			this.$root.$data.os.api('users/show', {
 | 
			
		||||
			(this as any).api('users/show', {
 | 
			
		||||
				username: this.username
 | 
			
		||||
			}).then(user => {
 | 
			
		||||
				this.user = user;
 | 
			
		||||
| 
						 | 
				
			
			@ -37,7 +37,7 @@ export default Vue.extend({
 | 
			
		|||
		onSubmit() {
 | 
			
		||||
			this.signing = true;
 | 
			
		||||
 | 
			
		||||
			this.$root.$data.os.api('signin', {
 | 
			
		||||
			(this as any).api('signin', {
 | 
			
		||||
				username: this.username,
 | 
			
		||||
				password: this.password,
 | 
			
		||||
				token: this.user && this.user.two_factor_enabled ? this.token : undefined
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -88,7 +88,7 @@ export default Vue.extend({
 | 
			
		|||
 | 
			
		||||
			this.usernameState = 'wait';
 | 
			
		||||
 | 
			
		||||
			this.$root.$data.os.api('username/available', {
 | 
			
		||||
			(this as any).api('username/available', {
 | 
			
		||||
				username: this.username
 | 
			
		||||
			}).then(result => {
 | 
			
		||||
				this.usernameState = result.available ? 'ok' : 'unavailable';
 | 
			
		||||
| 
						 | 
				
			
			@ -115,12 +115,12 @@ export default Vue.extend({
 | 
			
		|||
			this.passwordRetypeState = this.password == this.retypedPassword ? 'match' : 'not-match';
 | 
			
		||||
		},
 | 
			
		||||
		onSubmit() {
 | 
			
		||||
			this.$root.$data.os.api('signup', {
 | 
			
		||||
			(this as any).api('signup', {
 | 
			
		||||
				username: this.username,
 | 
			
		||||
				password: this.password,
 | 
			
		||||
				'g-recaptcha-response': (window as any).grecaptcha.getResponse()
 | 
			
		||||
			}).then(() => {
 | 
			
		||||
				this.$root.$data.os.api('signin', {
 | 
			
		||||
				(this as any).api('signin', {
 | 
			
		||||
					username: this.username,
 | 
			
		||||
					password: this.password
 | 
			
		||||
				}).then(() => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,10 +26,10 @@ export default Vue.extend({
 | 
			
		|||
		};
 | 
			
		||||
	},
 | 
			
		||||
	created() {
 | 
			
		||||
		this.stream = this.$root.$data.os.stream.borrow();
 | 
			
		||||
		this.stream = (this as any).os.stream.borrow();
 | 
			
		||||
 | 
			
		||||
		this.$root.$data.os.stream.on('connected', this.onConnected);
 | 
			
		||||
		this.$root.$data.os.stream.on('disconnected', this.onDisconnected);
 | 
			
		||||
		(this as any).os.stream.on('connected', this.onConnected);
 | 
			
		||||
		(this as any).os.stream.on('disconnected', this.onDisconnected);
 | 
			
		||||
 | 
			
		||||
		this.$nextTick(() => {
 | 
			
		||||
			if (this.stream.state == 'connected') {
 | 
			
		||||
| 
						 | 
				
			
			@ -38,12 +38,12 @@ export default Vue.extend({
 | 
			
		|||
		});
 | 
			
		||||
	},
 | 
			
		||||
	beforeDestroy() {
 | 
			
		||||
		this.$root.$data.os.stream.off('connected', this.onConnected);
 | 
			
		||||
		this.$root.$data.os.stream.off('disconnected', this.onDisconnected);
 | 
			
		||||
		(this as any).os.stream.off('connected', this.onConnected);
 | 
			
		||||
		(this as any).os.stream.off('disconnected', this.onDisconnected);
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
		onConnected() {
 | 
			
		||||
			this.stream = this.$root.$data.os.stream.borrow();
 | 
			
		||||
			this.stream = (this as any).os.stream.borrow();
 | 
			
		||||
 | 
			
		||||
			setTimeout(() => {
 | 
			
		||||
				anime({
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -50,7 +50,7 @@ export default Vue.extend({
 | 
			
		|||
			reader.readAsDataURL(file);
 | 
			
		||||
 | 
			
		||||
			const data = new FormData();
 | 
			
		||||
			data.append('i', this.$root.$data.os.i.token);
 | 
			
		||||
			data.append('i', (this as any).os.i.token);
 | 
			
		||||
			data.append('file', file);
 | 
			
		||||
 | 
			
		||||
			if (folder) data.append('folder_id', folder);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,12 +26,12 @@ export default define({
 | 
			
		|||
		};
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		this.connection = this.$root.$data.os.stream.getConnection();
 | 
			
		||||
		this.connectionId = this.$root.$data.os.stream.use();
 | 
			
		||||
		this.connection = (this as any).os.stream.getConnection();
 | 
			
		||||
		this.connectionId = (this as any).os.stream.use();
 | 
			
		||||
 | 
			
		||||
		this.connection.on('drive_file_created', this.onDriveFileCreated);
 | 
			
		||||
 | 
			
		||||
		this.$root.$data.os.api('drive/stream', {
 | 
			
		||||
		(this as any).api('drive/stream', {
 | 
			
		||||
			type: 'image/*',
 | 
			
		||||
			limit: 9
 | 
			
		||||
		}).then(images => {
 | 
			
		||||
| 
						 | 
				
			
			@ -41,7 +41,7 @@ export default define({
 | 
			
		|||
	},
 | 
			
		||||
	beforeDestroy() {
 | 
			
		||||
		this.connection.off('drive_file_created', this.onDriveFileCreated);
 | 
			
		||||
		this.$root.$data.os.stream.dispose(this.connectionId);
 | 
			
		||||
		(this as any).os.stream.dispose(this.connectionId);
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
		onDriveFileCreated(file) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -89,7 +89,7 @@ export default define({
 | 
			
		|||
		fetch() {
 | 
			
		||||
			this.fetching = true;
 | 
			
		||||
 | 
			
		||||
			this.$root.$data.os.api('drive/files', {
 | 
			
		||||
			(this as any).api('drive/files', {
 | 
			
		||||
				folder_id: this.props.folder,
 | 
			
		||||
				type: 'image/*',
 | 
			
		||||
				limit: 100
 | 
			
		||||
| 
						 | 
				
			
			@ -102,7 +102,7 @@ export default define({
 | 
			
		|||
			});
 | 
			
		||||
		},
 | 
			
		||||
		choose() {
 | 
			
		||||
			this.$root.$data.api.chooseDriveFolder().then(folder => {
 | 
			
		||||
			(this as any).apis.chooseDriveFolder().then(folder => {
 | 
			
		||||
				this.props.folder = folder ? folder.id : null;
 | 
			
		||||
				this.fetch();
 | 
			
		||||
			});
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,60 +0,0 @@
 | 
			
		|||
<mk-drive-browser-window>
 | 
			
		||||
	<mk-window ref="window" is-modal={ false } width={ '800px' } height={ '500px' } popout={ popout }>
 | 
			
		||||
		<yield to="header">
 | 
			
		||||
			<p class="info" v-if="parent.usage"><b>{ parent.usage.toFixed(1) }%</b> %i18n:desktop.tags.mk-drive-browser-window.used%</p>
 | 
			
		||||
			%fa:cloud%%i18n:desktop.tags.mk-drive-browser-window.drive%
 | 
			
		||||
		</yield>
 | 
			
		||||
		<yield to="content">
 | 
			
		||||
			<mk-drive-browser multiple={ true } folder={ parent.folder } ref="browser"/>
 | 
			
		||||
		</yield>
 | 
			
		||||
	</mk-window>
 | 
			
		||||
	<style lang="stylus" scoped>
 | 
			
		||||
		:scope
 | 
			
		||||
			> mk-window
 | 
			
		||||
				[data-yield='header']
 | 
			
		||||
					> .info
 | 
			
		||||
						position absolute
 | 
			
		||||
						top 0
 | 
			
		||||
						left 16px
 | 
			
		||||
						margin 0
 | 
			
		||||
						font-size 80%
 | 
			
		||||
 | 
			
		||||
					> [data-fa]
 | 
			
		||||
						margin-right 4px
 | 
			
		||||
 | 
			
		||||
				[data-yield='content']
 | 
			
		||||
					> mk-drive-browser
 | 
			
		||||
						height 100%
 | 
			
		||||
 | 
			
		||||
	</style>
 | 
			
		||||
	<script lang="typescript">
 | 
			
		||||
		this.mixin('api');
 | 
			
		||||
 | 
			
		||||
		this.folder = this.opts.folder ? this.opts.folder : null;
 | 
			
		||||
 | 
			
		||||
		this.popout = () => {
 | 
			
		||||
			const folder = this.$refs.window.refs.browser.folder;
 | 
			
		||||
			if (folder) {
 | 
			
		||||
				return `${_URL_}/i/drive/folder/${folder.id}`;
 | 
			
		||||
			} else {
 | 
			
		||||
				return `${_URL_}/i/drive`;
 | 
			
		||||
			}
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		this.on('mount', () => {
 | 
			
		||||
			this.$refs.window.on('closed', () => {
 | 
			
		||||
				this.$destroy();
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			this.$root.$data.os.api('drive').then(info => {
 | 
			
		||||
				this.update({
 | 
			
		||||
					usage: info.usage / info.capacity * 100
 | 
			
		||||
				});
 | 
			
		||||
			});
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		this.close = () => {
 | 
			
		||||
			this.$refs.window.close();
 | 
			
		||||
		};
 | 
			
		||||
	</script>
 | 
			
		||||
</mk-drive-browser-window>
 | 
			
		||||
| 
						 | 
				
			
			@ -1,99 +0,0 @@
 | 
			
		|||
<mk-drive-browser-file-contextmenu>
 | 
			
		||||
	<mk-contextmenu ref="ctx">
 | 
			
		||||
		<ul>
 | 
			
		||||
			<li @click="parent.rename">
 | 
			
		||||
				<p>%fa:i-cursor%%i18n:desktop.tags.mk-drive-browser-file-contextmenu.rename%</p>
 | 
			
		||||
			</li>
 | 
			
		||||
			<li @click="parent.copyUrl">
 | 
			
		||||
				<p>%fa:link%%i18n:desktop.tags.mk-drive-browser-file-contextmenu.copy-url%</p>
 | 
			
		||||
			</li>
 | 
			
		||||
			<li><a href={ parent.file.url + '?download' } download={ parent.file.name } @click="parent.download">%fa:download%%i18n:desktop.tags.mk-drive-browser-file-contextmenu.download%</a></li>
 | 
			
		||||
			<li class="separator"></li>
 | 
			
		||||
			<li @click="parent.delete">
 | 
			
		||||
				<p>%fa:R trash-alt%%i18n:common.delete%</p>
 | 
			
		||||
			</li>
 | 
			
		||||
			<li class="separator"></li>
 | 
			
		||||
			<li class="has-child">
 | 
			
		||||
				<p>%i18n:desktop.tags.mk-drive-browser-file-contextmenu.else-files%%fa:caret-right%</p>
 | 
			
		||||
				<ul>
 | 
			
		||||
					<li @click="parent.setAvatar">
 | 
			
		||||
						<p>%i18n:desktop.tags.mk-drive-browser-file-contextmenu.set-as-avatar%</p>
 | 
			
		||||
					</li>
 | 
			
		||||
					<li @click="parent.setBanner">
 | 
			
		||||
						<p>%i18n:desktop.tags.mk-drive-browser-file-contextmenu.set-as-banner%</p>
 | 
			
		||||
					</li>
 | 
			
		||||
				</ul>
 | 
			
		||||
			</li>
 | 
			
		||||
			<li class="has-child">
 | 
			
		||||
				<p>%i18n:desktop.tags.mk-drive-browser-file-contextmenu.open-in-app%...%fa:caret-right%</p>
 | 
			
		||||
				<ul>
 | 
			
		||||
					<li @click="parent.addApp">
 | 
			
		||||
						<p>%i18n:desktop.tags.mk-drive-browser-file-contextmenu.add-app%...</p>
 | 
			
		||||
					</li>
 | 
			
		||||
				</ul>
 | 
			
		||||
			</li>
 | 
			
		||||
		</ul>
 | 
			
		||||
	</mk-contextmenu>
 | 
			
		||||
	<script lang="typescript">
 | 
			
		||||
		import copyToClipboard from '../../../common/scripts/copy-to-clipboard';
 | 
			
		||||
		import dialog from '../../scripts/dialog';
 | 
			
		||||
		import inputDialog from '../../scripts/input-dialog';
 | 
			
		||||
		import updateAvatar from '../../scripts/update-avatar';
 | 
			
		||||
		import NotImplementedException from '../../scripts/not-implemented-exception';
 | 
			
		||||
 | 
			
		||||
		this.mixin('i');
 | 
			
		||||
		this.mixin('api');
 | 
			
		||||
 | 
			
		||||
		this.browser = this.opts.browser;
 | 
			
		||||
		this.file = this.opts.file;
 | 
			
		||||
 | 
			
		||||
		this.on('mount', () => {
 | 
			
		||||
			this.$refs.ctx.on('closed', () => {
 | 
			
		||||
				this.$emit('closed');
 | 
			
		||||
				this.$destroy();
 | 
			
		||||
			});
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		this.open = pos => {
 | 
			
		||||
			this.$refs.ctx.open(pos);
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		this.rename = () => {
 | 
			
		||||
			this.$refs.ctx.close();
 | 
			
		||||
 | 
			
		||||
			inputDialog('%i18n:desktop.tags.mk-drive-browser-file-contextmenu.rename-file%', '%i18n:desktop.tags.mk-drive-browser-file-contextmenu.input-new-file-name%', this.file.name, name => {
 | 
			
		||||
				this.$root.$data.os.api('drive/files/update', {
 | 
			
		||||
					file_id: this.file.id,
 | 
			
		||||
					name: name
 | 
			
		||||
				})
 | 
			
		||||
			});
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		this.copyUrl = () => {
 | 
			
		||||
			copyToClipboard(this.file.url);
 | 
			
		||||
			this.$refs.ctx.close();
 | 
			
		||||
			dialog('%fa:check%%i18n:desktop.tags.mk-drive-browser-file-contextmenu.copied%',
 | 
			
		||||
				'%i18n:desktop.tags.mk-drive-browser-file-contextmenu.copied-url-to-clipboard%', [{
 | 
			
		||||
				text: '%i18n:common.ok%'
 | 
			
		||||
			}]);
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		this.download = () => {
 | 
			
		||||
			this.$refs.ctx.close();
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		this.setAvatar = () => {
 | 
			
		||||
			this.$refs.ctx.close();
 | 
			
		||||
			updateAvatar(this.I, null, this.file);
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		this.setBanner = () => {
 | 
			
		||||
			this.$refs.ctx.close();
 | 
			
		||||
			updateBanner(this.I, null, this.file);
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		this.addApp = () => {
 | 
			
		||||
			NotImplementedException();
 | 
			
		||||
		};
 | 
			
		||||
	</script>
 | 
			
		||||
</mk-drive-browser-file-contextmenu>
 | 
			
		||||
| 
						 | 
				
			
			@ -1,63 +0,0 @@
 | 
			
		|||
<mk-drive-browser-folder-contextmenu>
 | 
			
		||||
	<mk-contextmenu ref="ctx">
 | 
			
		||||
		<ul>
 | 
			
		||||
			<li @click="parent.move">
 | 
			
		||||
				<p>%fa:arrow-right%%i18n:desktop.tags.mk-drive-browser-folder-contextmenu.move-to-this-folder%</p>
 | 
			
		||||
			</li>
 | 
			
		||||
			<li @click="parent.newWindow">
 | 
			
		||||
				<p>%fa:R window-restore%%i18n:desktop.tags.mk-drive-browser-folder-contextmenu.show-in-new-window%</p>
 | 
			
		||||
			</li>
 | 
			
		||||
			<li class="separator"></li>
 | 
			
		||||
			<li @click="parent.rename">
 | 
			
		||||
				<p>%fa:i-cursor%%i18n:desktop.tags.mk-drive-browser-folder-contextmenu.rename%</p>
 | 
			
		||||
			</li>
 | 
			
		||||
			<li class="separator"></li>
 | 
			
		||||
			<li @click="parent.delete">
 | 
			
		||||
				<p>%fa:R trash-alt%%i18n:common.delete%</p>
 | 
			
		||||
			</li>
 | 
			
		||||
		</ul>
 | 
			
		||||
	</mk-contextmenu>
 | 
			
		||||
	<script lang="typescript">
 | 
			
		||||
		import inputDialog from '../../scripts/input-dialog';
 | 
			
		||||
 | 
			
		||||
		this.mixin('api');
 | 
			
		||||
 | 
			
		||||
		this.browser = this.opts.browser;
 | 
			
		||||
		this.folder = this.opts.folder;
 | 
			
		||||
 | 
			
		||||
		this.open = pos => {
 | 
			
		||||
			this.$refs.ctx.open(pos);
 | 
			
		||||
 | 
			
		||||
			this.$refs.ctx.on('closed', () => {
 | 
			
		||||
				this.$emit('closed');
 | 
			
		||||
				this.$destroy();
 | 
			
		||||
			});
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		this.move = () => {
 | 
			
		||||
			this.browser.move(this.folder.id);
 | 
			
		||||
			this.$refs.ctx.close();
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		this.newWindow = () => {
 | 
			
		||||
			this.browser.newWindow(this.folder.id);
 | 
			
		||||
			this.$refs.ctx.close();
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		this.createFolder = () => {
 | 
			
		||||
			this.browser.createFolder();
 | 
			
		||||
			this.$refs.ctx.close();
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		this.rename = () => {
 | 
			
		||||
			this.$refs.ctx.close();
 | 
			
		||||
 | 
			
		||||
			inputDialog('%i18n:desktop.tags.mk-drive-browser-folder-contextmenu.rename-folder%', '%i18n:desktop.tags.mk-drive-browser-folder-contextmenu.input-new-folder-name%', this.folder.name, name => {
 | 
			
		||||
				this.$root.$data.os.api('drive/folders/update', {
 | 
			
		||||
					folder_id: this.folder.id,
 | 
			
		||||
					name: name
 | 
			
		||||
				});
 | 
			
		||||
			});
 | 
			
		||||
		};
 | 
			
		||||
	</script>
 | 
			
		||||
</mk-drive-browser-folder-contextmenu>
 | 
			
		||||
							
								
								
									
										18
									
								
								src/web/app/desktop/api/choose-drive-file.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/web/app/desktop/api/choose-drive-file.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,18 @@
 | 
			
		|||
import MkChooseFileFromDriveWindow from '../views/components/choose-file-from-drive-window.vue';
 | 
			
		||||
 | 
			
		||||
export default function(opts) {
 | 
			
		||||
	return new Promise((res, rej) => {
 | 
			
		||||
		const o = opts || {};
 | 
			
		||||
		const w = new MkChooseFileFromDriveWindow({
 | 
			
		||||
			propsData: {
 | 
			
		||||
				title: o.title,
 | 
			
		||||
				multiple: o.multiple,
 | 
			
		||||
				initFolder: o.currentFolder
 | 
			
		||||
			}
 | 
			
		||||
		}).$mount();
 | 
			
		||||
		w.$once('selected', file => {
 | 
			
		||||
			res(file);
 | 
			
		||||
		});
 | 
			
		||||
		document.body.appendChild(w.$el);
 | 
			
		||||
	});
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,12 +1,12 @@
 | 
			
		|||
import MkChooseFolderFromDriveWindow from '../../../common/views/components/choose-folder-from-drive-window.vue';
 | 
			
		||||
import MkChooseFolderFromDriveWindow from '../views/components/choose-folder-from-drive-window.vue';
 | 
			
		||||
 | 
			
		||||
export default function(this: any, opts) {
 | 
			
		||||
export default function(opts) {
 | 
			
		||||
	return new Promise((res, rej) => {
 | 
			
		||||
		const o = opts || {};
 | 
			
		||||
		const w = new MkChooseFolderFromDriveWindow({
 | 
			
		||||
			parent: this,
 | 
			
		||||
			propsData: {
 | 
			
		||||
				title: o.title
 | 
			
		||||
				title: o.title,
 | 
			
		||||
				initFolder: o.currentFolder
 | 
			
		||||
			}
 | 
			
		||||
		}).$mount();
 | 
			
		||||
		w.$once('selected', folder => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										16
									
								
								src/web/app/desktop/api/contextmenu.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/web/app/desktop/api/contextmenu.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,16 @@
 | 
			
		|||
import Ctx from '../views/components/context-menu.vue';
 | 
			
		||||
 | 
			
		||||
export default function(e, menu, opts?) {
 | 
			
		||||
	const o = opts || {};
 | 
			
		||||
	const vm = new Ctx({
 | 
			
		||||
		propsData: {
 | 
			
		||||
			menu,
 | 
			
		||||
			x: e.pageX - window.pageXOffset,
 | 
			
		||||
			y: e.pageY - window.pageYOffset,
 | 
			
		||||
		}
 | 
			
		||||
	}).$mount();
 | 
			
		||||
	vm.$once('closed', () => {
 | 
			
		||||
		if (o.closed) o.closed();
 | 
			
		||||
	});
 | 
			
		||||
	document.body.appendChild(vm.$el);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										19
									
								
								src/web/app/desktop/api/dialog.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/web/app/desktop/api/dialog.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,19 @@
 | 
			
		|||
import Dialog from '../views/components/dialog.vue';
 | 
			
		||||
 | 
			
		||||
export default function(opts) {
 | 
			
		||||
	return new Promise<string>((res, rej) => {
 | 
			
		||||
		const o = opts || {};
 | 
			
		||||
		const d = new Dialog({
 | 
			
		||||
			propsData: {
 | 
			
		||||
				title: o.title,
 | 
			
		||||
				text: o.text,
 | 
			
		||||
				modal: o.modal,
 | 
			
		||||
				buttons: o.actions
 | 
			
		||||
			}
 | 
			
		||||
		}).$mount();
 | 
			
		||||
		d.$once('clicked', id => {
 | 
			
		||||
			res(id);
 | 
			
		||||
		});
 | 
			
		||||
		document.body.appendChild(d.$el);
 | 
			
		||||
	});
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										19
									
								
								src/web/app/desktop/api/input.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/web/app/desktop/api/input.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,19 @@
 | 
			
		|||
import InputDialog from '../views/components/input-dialog.vue';
 | 
			
		||||
 | 
			
		||||
export default function(opts) {
 | 
			
		||||
	return new Promise<string>((res, rej) => {
 | 
			
		||||
		const o = opts || {};
 | 
			
		||||
		const d = new InputDialog({
 | 
			
		||||
			propsData: {
 | 
			
		||||
				title: o.title,
 | 
			
		||||
				placeholder: o.placeholder,
 | 
			
		||||
				default: o.default,
 | 
			
		||||
				type: o.type || 'text'
 | 
			
		||||
			}
 | 
			
		||||
		}).$mount();
 | 
			
		||||
		d.$once('done', text => {
 | 
			
		||||
			res(text);
 | 
			
		||||
		});
 | 
			
		||||
		document.body.appendChild(d.$el);
 | 
			
		||||
	});
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -11,6 +11,9 @@ import HomeStreamManager from '../common/scripts/streaming/home-stream-manager';
 | 
			
		|||
import composeNotification from '../common/scripts/compose-notification';
 | 
			
		||||
 | 
			
		||||
import chooseDriveFolder from './api/choose-drive-folder';
 | 
			
		||||
import chooseDriveFile from './api/choose-drive-file';
 | 
			
		||||
import dialog from './api/dialog';
 | 
			
		||||
import input from './api/input';
 | 
			
		||||
 | 
			
		||||
import MkIndex from './views/pages/index.vue';
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -30,7 +33,10 @@ init(async (launch) => {
 | 
			
		|||
	require('./views/components');
 | 
			
		||||
 | 
			
		||||
	const app = launch({
 | 
			
		||||
		chooseDriveFolder
 | 
			
		||||
		chooseDriveFolder,
 | 
			
		||||
		chooseDriveFile,
 | 
			
		||||
		dialog,
 | 
			
		||||
		input
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -36,7 +36,7 @@ export default Vue.extend({
 | 
			
		|||
	methods: {
 | 
			
		||||
		register() {
 | 
			
		||||
			passwordDialog('%i18n:desktop.tags.mk-2fa-setting.enter-password%', password => {
 | 
			
		||||
				this.$root.$data.os.api('i/2fa/register', {
 | 
			
		||||
				(this as any).api('i/2fa/register', {
 | 
			
		||||
					password: password
 | 
			
		||||
				}).then(data => {
 | 
			
		||||
					this.data = data;
 | 
			
		||||
| 
						 | 
				
			
			@ -46,21 +46,21 @@ export default Vue.extend({
 | 
			
		|||
 | 
			
		||||
		unregister() {
 | 
			
		||||
			passwordDialog('%i18n:desktop.tags.mk-2fa-setting.enter-password%', password => {
 | 
			
		||||
				this.$root.$data.os.api('i/2fa/unregister', {
 | 
			
		||||
				(this as any).api('i/2fa/unregister', {
 | 
			
		||||
					password: password
 | 
			
		||||
				}).then(() => {
 | 
			
		||||
					notify('%i18n:desktop.tags.mk-2fa-setting.unregistered%');
 | 
			
		||||
					this.$root.$data.os.i.two_factor_enabled = false;
 | 
			
		||||
					(this as any).os.i.two_factor_enabled = false;
 | 
			
		||||
				});
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		submit() {
 | 
			
		||||
			this.$root.$data.os.api('i/2fa/done', {
 | 
			
		||||
			(this as any).api('i/2fa/done', {
 | 
			
		||||
				token: this.token
 | 
			
		||||
			}).then(() => {
 | 
			
		||||
				notify('%i18n:desktop.tags.mk-2fa-setting.success%');
 | 
			
		||||
				this.$root.$data.os.i.two_factor_enabled = true;
 | 
			
		||||
				(this as any).os.i.two_factor_enabled = true;
 | 
			
		||||
			}).catch(() => {
 | 
			
		||||
				notify('%i18n:desktop.tags.mk-2fa-setting.failed%');
 | 
			
		||||
			});
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
<template>
 | 
			
		||||
<div class="mk-api-setting">
 | 
			
		||||
	<p>Token: <code>{{ $root.$data.os.i.token }}</code></p>
 | 
			
		||||
	<p>Token: <code>{{ os.i.token }}</code></p>
 | 
			
		||||
	<p>%i18n:desktop.tags.mk-api-info.intro%</p>
 | 
			
		||||
	<div class="ui info warn"><p>%fa:exclamation-triangle%%i18n:desktop.tags.mk-api-info.caution%</p></div>
 | 
			
		||||
	<p>%i18n:desktop.tags.mk-api-info.regeneration-of-token%</p>
 | 
			
		||||
| 
						 | 
				
			
			@ -16,7 +16,7 @@ export default Vue.extend({
 | 
			
		|||
	methods: {
 | 
			
		||||
		regenerateToken() {
 | 
			
		||||
			passwordDialog('%i18n:desktop.tags.mk-api-info.enter-password%', password => {
 | 
			
		||||
				this.$root.$data.os.api('i/regenerate_token', {
 | 
			
		||||
				(this as any).api('i/regenerate_token', {
 | 
			
		||||
					password: password
 | 
			
		||||
				});
 | 
			
		||||
			});
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										113
									
								
								src/web/app/desktop/views/components/context-menu-menu.vue
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								src/web/app/desktop/views/components/context-menu-menu.vue
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,113 @@
 | 
			
		|||
<template>
 | 
			
		||||
<ul class="me-nu">
 | 
			
		||||
	<li v-for="(item, i) in menu" :key="i" :class="item.type">
 | 
			
		||||
		<template v-if="item.type == 'item'">
 | 
			
		||||
			<p @click="click(item)"><span class="icon" v-if="item.icon" v-html="item.icon"></span>{{ item.text }}</p>
 | 
			
		||||
		</template>
 | 
			
		||||
		<template v-else-if="item.type == 'nest'">
 | 
			
		||||
			<p><span class="icon" v-if="item.icon" v-html="item.icon"></span>{{ item.text }}...<span class="caret">%fa:caret-right%</span></p>
 | 
			
		||||
			<me-nu :menu="item.menu" @x="click"/>
 | 
			
		||||
		</template>
 | 
			
		||||
	</li>
 | 
			
		||||
</ul>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	name: 'me-nu',
 | 
			
		||||
	props: ['menu'],
 | 
			
		||||
	methods: {
 | 
			
		||||
		click(item) {
 | 
			
		||||
			this.$emit('x', item);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" scoped>
 | 
			
		||||
.me-nu
 | 
			
		||||
	$width = 240px
 | 
			
		||||
	$item-height = 38px
 | 
			
		||||
	$padding = 10px
 | 
			
		||||
 | 
			
		||||
	ul
 | 
			
		||||
		display block
 | 
			
		||||
		margin 0
 | 
			
		||||
		padding $padding 0
 | 
			
		||||
		list-style none
 | 
			
		||||
 | 
			
		||||
	li
 | 
			
		||||
		display block
 | 
			
		||||
 | 
			
		||||
		&:empty
 | 
			
		||||
			margin-top $padding
 | 
			
		||||
			padding-top $padding
 | 
			
		||||
			border-top solid 1px #eee
 | 
			
		||||
 | 
			
		||||
		&.nest
 | 
			
		||||
			> p
 | 
			
		||||
				cursor default
 | 
			
		||||
 | 
			
		||||
				> .caret
 | 
			
		||||
					> *
 | 
			
		||||
						position absolute
 | 
			
		||||
						top 0
 | 
			
		||||
						right 8px
 | 
			
		||||
						line-height $item-height
 | 
			
		||||
 | 
			
		||||
			&:hover > ul
 | 
			
		||||
				visibility visible
 | 
			
		||||
 | 
			
		||||
			&:active
 | 
			
		||||
				> p, a
 | 
			
		||||
					background $theme-color
 | 
			
		||||
 | 
			
		||||
		> p, a
 | 
			
		||||
			display block
 | 
			
		||||
			z-index 1
 | 
			
		||||
			margin 0
 | 
			
		||||
			padding 0 32px 0 38px
 | 
			
		||||
			line-height $item-height
 | 
			
		||||
			color #868C8C
 | 
			
		||||
			text-decoration none
 | 
			
		||||
			cursor pointer
 | 
			
		||||
 | 
			
		||||
			&:hover
 | 
			
		||||
				text-decoration none
 | 
			
		||||
 | 
			
		||||
			*
 | 
			
		||||
				pointer-events none
 | 
			
		||||
 | 
			
		||||
			> .icon
 | 
			
		||||
				> *
 | 
			
		||||
					width 28px
 | 
			
		||||
					margin-left -28px
 | 
			
		||||
					text-align center
 | 
			
		||||
 | 
			
		||||
		&:hover
 | 
			
		||||
			> p, a
 | 
			
		||||
				text-decoration none
 | 
			
		||||
				background $theme-color
 | 
			
		||||
				color $theme-color-foreground
 | 
			
		||||
 | 
			
		||||
		&:active
 | 
			
		||||
			> p, a
 | 
			
		||||
				text-decoration none
 | 
			
		||||
				background darken($theme-color, 10%)
 | 
			
		||||
				color $theme-color-foreground
 | 
			
		||||
 | 
			
		||||
	li > ul
 | 
			
		||||
		visibility hidden
 | 
			
		||||
		position absolute
 | 
			
		||||
		top 0
 | 
			
		||||
		left $width
 | 
			
		||||
		margin-top -($padding)
 | 
			
		||||
		width $width
 | 
			
		||||
		background #fff
 | 
			
		||||
		border-radius 0 4px 4px 4px
 | 
			
		||||
		box-shadow 2px 2px 8px rgba(0, 0, 0, 0.2)
 | 
			
		||||
		transition visibility 0s linear 0.2s
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										74
									
								
								src/web/app/desktop/views/components/context-menu.vue
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								src/web/app/desktop/views/components/context-menu.vue
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,74 @@
 | 
			
		|||
<template>
 | 
			
		||||
<div class="context-menu" :style="{ x: `${x}px`, y: `${y}px` }" @contextmenu.prevent="() => {}">
 | 
			
		||||
	<me-nu :menu="menu" @x="click"/>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
import * as anime from 'animejs';
 | 
			
		||||
import contains from '../../../common/scripts/contains';
 | 
			
		||||
import meNu from './context-menu-menu.vue';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	components: {
 | 
			
		||||
		'me-nu': meNu
 | 
			
		||||
	},
 | 
			
		||||
	props: ['x', 'y', 'menu'],
 | 
			
		||||
	mounted() {
 | 
			
		||||
		this.$nextTick(() => {
 | 
			
		||||
			Array.from(document.querySelectorAll('body *')).forEach(el => {
 | 
			
		||||
				el.addEventListener('mousedown', this.onMousedown);
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			this.$el.style.display = 'block';
 | 
			
		||||
 | 
			
		||||
			anime({
 | 
			
		||||
				targets: this.$el,
 | 
			
		||||
				opacity: [0, 1],
 | 
			
		||||
				duration: 100,
 | 
			
		||||
				easing: 'linear'
 | 
			
		||||
			});
 | 
			
		||||
		});
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
		onMousedown(e) {
 | 
			
		||||
			e.preventDefault();
 | 
			
		||||
			if (!contains(this.$el, e.target) && (this.$el != e.target)) this.close();
 | 
			
		||||
			return false;
 | 
			
		||||
		},
 | 
			
		||||
		click(item) {
 | 
			
		||||
			if (item.onClick) item.onClick();
 | 
			
		||||
			this.close();
 | 
			
		||||
		},
 | 
			
		||||
		close() {
 | 
			
		||||
			Array.from(document.querySelectorAll('body *')).forEach(el => {
 | 
			
		||||
				el.removeEventListener('mousedown', this.onMousedown);
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			this.$emit('closed');
 | 
			
		||||
			this.$destroy();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" scoped>
 | 
			
		||||
.context-menu
 | 
			
		||||
	$width = 240px
 | 
			
		||||
	$item-height = 38px
 | 
			
		||||
	$padding = 10px
 | 
			
		||||
 | 
			
		||||
	display none
 | 
			
		||||
	position fixed
 | 
			
		||||
	top 0
 | 
			
		||||
	left 0
 | 
			
		||||
	z-index 4096
 | 
			
		||||
	width $width
 | 
			
		||||
	font-size 0.8em
 | 
			
		||||
	background #fff
 | 
			
		||||
	border-radius 0 4px 4px 4px
 | 
			
		||||
	box-shadow 2px 2px 8px rgba(0, 0, 0, 0.2)
 | 
			
		||||
	opacity 0
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
| 
						 | 
				
			
			@ -1,142 +0,0 @@
 | 
			
		|||
<template>
 | 
			
		||||
<div class="mk-contextmenu" @contextmenu.prevent="() => {}">
 | 
			
		||||
	<slot></slot>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
import * as anime from 'animejs';
 | 
			
		||||
import contains from '../../../common/scripts/contains';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	props: ['x', 'y'],
 | 
			
		||||
	mounted() {
 | 
			
		||||
		document.querySelectorAll('body *').forEach(el => {
 | 
			
		||||
			el.addEventListener('mousedown', this.onMousedown);
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		this.$el.style.display = 'block';
 | 
			
		||||
		this.$el.style.left = this.x + 'px';
 | 
			
		||||
		this.$el.style.top = this.y + 'px';
 | 
			
		||||
 | 
			
		||||
		anime({
 | 
			
		||||
			targets: this.$el,
 | 
			
		||||
			opacity: [0, 1],
 | 
			
		||||
			duration: 100,
 | 
			
		||||
			easing: 'linear'
 | 
			
		||||
		});
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
		onMousedown(e) {
 | 
			
		||||
			e.preventDefault();
 | 
			
		||||
			if (!contains(this.$el, e.target) && (this.$el != e.target)) this.close();
 | 
			
		||||
			return false;
 | 
			
		||||
		},
 | 
			
		||||
		close() {
 | 
			
		||||
			Array.from(document.querySelectorAll('body *')).forEach(el => {
 | 
			
		||||
				el.removeEventListener('mousedown', this.onMousedown);
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			this.$emit('closed');
 | 
			
		||||
			this.$destroy();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" scoped>
 | 
			
		||||
.mk-contextmenu
 | 
			
		||||
	$width = 240px
 | 
			
		||||
	$item-height = 38px
 | 
			
		||||
	$padding = 10px
 | 
			
		||||
 | 
			
		||||
	display none
 | 
			
		||||
	position fixed
 | 
			
		||||
	top 0
 | 
			
		||||
	left 0
 | 
			
		||||
	z-index 4096
 | 
			
		||||
	width $width
 | 
			
		||||
	font-size 0.8em
 | 
			
		||||
	background #fff
 | 
			
		||||
	border-radius 0 4px 4px 4px
 | 
			
		||||
	box-shadow 2px 2px 8px rgba(0, 0, 0, 0.2)
 | 
			
		||||
	opacity 0
 | 
			
		||||
 | 
			
		||||
	ul
 | 
			
		||||
		display block
 | 
			
		||||
		margin 0
 | 
			
		||||
		padding $padding 0
 | 
			
		||||
		list-style none
 | 
			
		||||
 | 
			
		||||
	li
 | 
			
		||||
		display block
 | 
			
		||||
 | 
			
		||||
		&.separator
 | 
			
		||||
			margin-top $padding
 | 
			
		||||
			padding-top $padding
 | 
			
		||||
			border-top solid 1px #eee
 | 
			
		||||
 | 
			
		||||
		&.has-child
 | 
			
		||||
			> p
 | 
			
		||||
				cursor default
 | 
			
		||||
 | 
			
		||||
				> [data-fa]:last-child
 | 
			
		||||
					position absolute
 | 
			
		||||
					top 0
 | 
			
		||||
					right 8px
 | 
			
		||||
					line-height $item-height
 | 
			
		||||
 | 
			
		||||
			&:hover > ul
 | 
			
		||||
				visibility visible
 | 
			
		||||
 | 
			
		||||
			&:active
 | 
			
		||||
				> p, a
 | 
			
		||||
					background $theme-color
 | 
			
		||||
 | 
			
		||||
		> p, a
 | 
			
		||||
			display block
 | 
			
		||||
			z-index 1
 | 
			
		||||
			margin 0
 | 
			
		||||
			padding 0 32px 0 38px
 | 
			
		||||
			line-height $item-height
 | 
			
		||||
			color #868C8C
 | 
			
		||||
			text-decoration none
 | 
			
		||||
			cursor pointer
 | 
			
		||||
 | 
			
		||||
			&:hover
 | 
			
		||||
				text-decoration none
 | 
			
		||||
 | 
			
		||||
			*
 | 
			
		||||
				pointer-events none
 | 
			
		||||
 | 
			
		||||
			> i
 | 
			
		||||
				width 28px
 | 
			
		||||
				margin-left -28px
 | 
			
		||||
				text-align center
 | 
			
		||||
 | 
			
		||||
		&:hover
 | 
			
		||||
			> p, a
 | 
			
		||||
				text-decoration none
 | 
			
		||||
				background $theme-color
 | 
			
		||||
				color $theme-color-foreground
 | 
			
		||||
 | 
			
		||||
		&:active
 | 
			
		||||
			> p, a
 | 
			
		||||
				text-decoration none
 | 
			
		||||
				background darken($theme-color, 10%)
 | 
			
		||||
				color $theme-color-foreground
 | 
			
		||||
 | 
			
		||||
	li > ul
 | 
			
		||||
		visibility hidden
 | 
			
		||||
		position absolute
 | 
			
		||||
		top 0
 | 
			
		||||
		left $width
 | 
			
		||||
		margin-top -($padding)
 | 
			
		||||
		width $width
 | 
			
		||||
		background #fff
 | 
			
		||||
		border-radius 0 4px 4px 4px
 | 
			
		||||
		box-shadow 2px 2px 8px rgba(0, 0, 0, 0.2)
 | 
			
		||||
		transition visibility 0s linear 0.2s
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
| 
						 | 
				
			
			@ -5,7 +5,7 @@
 | 
			
		|||
		<header v-html="title"></header>
 | 
			
		||||
		<div class="body" v-html="text"></div>
 | 
			
		||||
		<div class="buttons">
 | 
			
		||||
			<button v-for="(button, i) in buttons" @click="click(button)" :key="i">{{ button.text }}</button>
 | 
			
		||||
			<button v-for="button in buttons" @click="click(button)" :key="button.id">{{ button.text }}</button>
 | 
			
		||||
		</div>
 | 
			
		||||
	</div>
 | 
			
		||||
</div>
 | 
			
		||||
| 
						 | 
				
			
			@ -26,13 +26,9 @@ export default Vue.extend({
 | 
			
		|||
		buttons: {
 | 
			
		||||
			type: Array
 | 
			
		||||
		},
 | 
			
		||||
		canThrough: {
 | 
			
		||||
		modal: {
 | 
			
		||||
			type: Boolean,
 | 
			
		||||
			default: true
 | 
			
		||||
		},
 | 
			
		||||
		onThrough: {
 | 
			
		||||
			type: Function,
 | 
			
		||||
			required: false
 | 
			
		||||
			default: false
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
| 
						 | 
				
			
			@ -54,7 +50,7 @@ export default Vue.extend({
 | 
			
		|||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
		click(button) {
 | 
			
		||||
			if (button.onClick) button.onClick();
 | 
			
		||||
			this.$emit('clicked', button.id);
 | 
			
		||||
			this.close();
 | 
			
		||||
		},
 | 
			
		||||
		close() {
 | 
			
		||||
| 
						 | 
				
			
			@ -77,8 +73,7 @@ export default Vue.extend({
 | 
			
		|||
			});
 | 
			
		||||
		},
 | 
			
		||||
		onBgClick() {
 | 
			
		||||
			if (this.canThrough) {
 | 
			
		||||
				if (this.onThrough) this.onThrough();
 | 
			
		||||
			if (!this.modal) {
 | 
			
		||||
				this.close();
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,46 +0,0 @@
 | 
			
		|||
<template>
 | 
			
		||||
<mk-contextmenu ref="menu" @closed="onClosed">
 | 
			
		||||
	<ul>
 | 
			
		||||
		<li @click="createFolder">
 | 
			
		||||
			<p>%fa:R folder%%i18n:desktop.tags.mk-drive-browser-base-contextmenu.create-folder%</p>
 | 
			
		||||
		</li>
 | 
			
		||||
		<li @click="upload">
 | 
			
		||||
			<p>%fa:upload%%i18n:desktop.tags.mk-drive-browser-base-contextmenu.upload%</p>
 | 
			
		||||
		</li>
 | 
			
		||||
		<li @click="urlUpload">
 | 
			
		||||
			<p>%fa:cloud-upload-alt%%i18n:desktop.tags.mk-drive-browser-base-contextmenu.url-upload%</p>
 | 
			
		||||
		</li>
 | 
			
		||||
	</ul>
 | 
			
		||||
</mk-contextmenu>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	props: ['browser'],
 | 
			
		||||
	mounted() {
 | 
			
		||||
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
		close() {
 | 
			
		||||
			(this.$refs.menu as any).close();
 | 
			
		||||
		},
 | 
			
		||||
		onClosed() {
 | 
			
		||||
			this.$emit('closed');
 | 
			
		||||
			this.$destroy();
 | 
			
		||||
		},
 | 
			
		||||
		createFolder() {
 | 
			
		||||
			this.browser.createFolder();
 | 
			
		||||
			this.close();
 | 
			
		||||
		},
 | 
			
		||||
		upload() {
 | 
			
		||||
			this.browser.selectLocalFile();
 | 
			
		||||
			this.close();
 | 
			
		||||
		},
 | 
			
		||||
		urlUpload() {
 | 
			
		||||
			this.browser.urlUpload();
 | 
			
		||||
			this.close();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
| 
						 | 
				
			
			@ -3,24 +3,24 @@
 | 
			
		|||
	:data-is-selected="isSelected"
 | 
			
		||||
	:data-is-contextmenu-showing="isContextmenuShowing"
 | 
			
		||||
	@click="onClick"
 | 
			
		||||
	@contextmenu.prevent.stop="onContextmenu"
 | 
			
		||||
	draggable="true"
 | 
			
		||||
	@dragstart="onDragstart"
 | 
			
		||||
	@dragend="onDragend"
 | 
			
		||||
	@contextmenu.prevent.stop="onContextmenu"
 | 
			
		||||
	:title="title"
 | 
			
		||||
>
 | 
			
		||||
	<div class="label" v-if="I.avatar_id == file.id"><img src="/assets/label.svg"/>
 | 
			
		||||
	<div class="label" v-if="os.i.avatar_id == file.id"><img src="/assets/label.svg"/>
 | 
			
		||||
		<p>%i18n:desktop.tags.mk-drive-browser-file.avatar%</p>
 | 
			
		||||
	</div>
 | 
			
		||||
	<div class="label" v-if="I.banner_id == file.id"><img src="/assets/label.svg"/>
 | 
			
		||||
	<div class="label" v-if="os.i.banner_id == file.id"><img src="/assets/label.svg"/>
 | 
			
		||||
		<p>%i18n:desktop.tags.mk-drive-browser-file.banner%</p>
 | 
			
		||||
	</div>
 | 
			
		||||
	<div class="thumbnail" ref="thumbnail" style="background-color:{ file.properties.average_color ? 'rgb(' + file.properties.average_color.join(',') + ')' : 'transparent' }">
 | 
			
		||||
		<img src={ file.url + '?thumbnail&size=128' } alt="" @load="onThumbnailLoaded"/>
 | 
			
		||||
	<div class="thumbnail" ref="thumbnail" :style="`background-color: ${ background }`">
 | 
			
		||||
		<img :src="`${file.url}?thumbnail&size=128`" alt="" @load="onThumbnailLoaded"/>
 | 
			
		||||
	</div>
 | 
			
		||||
	<p class="name">
 | 
			
		||||
		<span>{ file.name.lastIndexOf('.') != -1 ? file.name.substr(0, file.name.lastIndexOf('.')) : file.name }</span>
 | 
			
		||||
		<span class="ext" v-if="file.name.lastIndexOf('.') != -1">{ file.name.substr(file.name.lastIndexOf('.')) }</span>
 | 
			
		||||
		<span>{{ file.name.lastIndexOf('.') != -1 ? file.name.substr(0, file.name.lastIndexOf('.')) : file.name }}</span>
 | 
			
		||||
		<span class="ext" v-if="file.name.lastIndexOf('.') != -1">{{ file.name.substr(file.name.lastIndexOf('.')) }}</span>
 | 
			
		||||
	</p>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
| 
						 | 
				
			
			@ -28,10 +28,12 @@
 | 
			
		|||
<script lang="ts">
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
import * as anime from 'animejs';
 | 
			
		||||
import contextmenu from '../../api/contextmenu';
 | 
			
		||||
import copyToClipboard from '../../../common/scripts/copy-to-clipboard';
 | 
			
		||||
import bytesToSize from '../../../common/scripts/bytes-to-size';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	props: ['file', 'browser'],
 | 
			
		||||
	props: ['file'],
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			isContextmenuShowing: false,
 | 
			
		||||
| 
						 | 
				
			
			@ -39,11 +41,19 @@ export default Vue.extend({
 | 
			
		|||
		};
 | 
			
		||||
	},
 | 
			
		||||
	computed: {
 | 
			
		||||
		browser(): any {
 | 
			
		||||
			return this.$parent;
 | 
			
		||||
		},
 | 
			
		||||
		isSelected(): boolean {
 | 
			
		||||
			return this.browser.selectedFiles.some(f => f.id == this.file.id);
 | 
			
		||||
		},
 | 
			
		||||
		title(): string {
 | 
			
		||||
			return `${this.file.name}\n${this.file.type} ${bytesToSize(this.file.datasize)}`;
 | 
			
		||||
		},
 | 
			
		||||
		background(): string {
 | 
			
		||||
			return this.file.properties.average_color
 | 
			
		||||
				? `rgb(${this.file.properties.average_color.join(',')})'`
 | 
			
		||||
				: 'transparent';
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
| 
						 | 
				
			
			@ -53,18 +63,55 @@ export default Vue.extend({
 | 
			
		|||
 | 
			
		||||
		onContextmenu(e) {
 | 
			
		||||
			this.isContextmenuShowing = true;
 | 
			
		||||
			const ctx = new MkDriveFileContextmenu({
 | 
			
		||||
				parent: this,
 | 
			
		||||
				propsData: {
 | 
			
		||||
					browser: this.browser,
 | 
			
		||||
					x: e.pageX - window.pageXOffset,
 | 
			
		||||
					y: e.pageY - window.pageYOffset
 | 
			
		||||
			contextmenu(e, [{
 | 
			
		||||
				type: 'item',
 | 
			
		||||
				text: '%i18n:desktop.tags.mk-drive-browser-file-contextmenu.rename%',
 | 
			
		||||
				icon: '%fa:i-cursor%',
 | 
			
		||||
				onClick: this.rename
 | 
			
		||||
			}, {
 | 
			
		||||
				type: 'item',
 | 
			
		||||
				text: '%i18n:desktop.tags.mk-drive-browser-file-contextmenu.copy-url%',
 | 
			
		||||
				icon: '%fa:link%',
 | 
			
		||||
				onClick: this.copyUrl
 | 
			
		||||
			}, {
 | 
			
		||||
				type: 'link',
 | 
			
		||||
				href: `${this.file.url}?download`,
 | 
			
		||||
				text: '%i18n:desktop.tags.mk-drive-browser-file-contextmenu.download%',
 | 
			
		||||
				icon: '%fa:download%',
 | 
			
		||||
			}, {
 | 
			
		||||
				type: 'divider',
 | 
			
		||||
			}, {
 | 
			
		||||
				type: 'item',
 | 
			
		||||
				text: '%i18n:common.delete%',
 | 
			
		||||
				icon: '%fa:R trash-alt%',
 | 
			
		||||
				onClick: this.deleteFile
 | 
			
		||||
			}, {
 | 
			
		||||
				type: 'divider',
 | 
			
		||||
			}, {
 | 
			
		||||
				type: 'nest',
 | 
			
		||||
				text: '%i18n:desktop.tags.mk-drive-browser-file-contextmenu.else-files%',
 | 
			
		||||
				menu: [{
 | 
			
		||||
					type: 'item',
 | 
			
		||||
					text: '%i18n:desktop.tags.mk-drive-browser-file-contextmenu.set-as-avatar%',
 | 
			
		||||
					onClick: this.setAsAvatar
 | 
			
		||||
				}, {
 | 
			
		||||
					type: 'item',
 | 
			
		||||
					text: '%i18n:desktop.tags.mk-drive-browser-file-contextmenu.set-as-banner%',
 | 
			
		||||
					onClick: this.setAsBanner
 | 
			
		||||
				}]
 | 
			
		||||
			}, {
 | 
			
		||||
				type: 'nest',
 | 
			
		||||
				text: '%i18n:desktop.tags.mk-drive-browser-file-contextmenu.open-in-app%',
 | 
			
		||||
				menu: [{
 | 
			
		||||
					type: 'item',
 | 
			
		||||
					text: '%i18n:desktop.tags.mk-drive-browser-file-contextmenu.add-app%...',
 | 
			
		||||
					onClick: this.addApp
 | 
			
		||||
				}]
 | 
			
		||||
			}], {
 | 
			
		||||
				closed: () => {
 | 
			
		||||
					this.isContextmenuShowing = false;
 | 
			
		||||
				}
 | 
			
		||||
			}).$mount();
 | 
			
		||||
			ctx.$once('closed', () => {
 | 
			
		||||
				this.isContextmenuShowing = false;
 | 
			
		||||
			});
 | 
			
		||||
			document.body.appendChild(ctx.$el);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onDragstart(e) {
 | 
			
		||||
| 
						 | 
				
			
			@ -95,6 +142,46 @@ export default Vue.extend({
 | 
			
		|||
					easing: 'linear'
 | 
			
		||||
				});
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		rename() {
 | 
			
		||||
			(this as any).apis.input({
 | 
			
		||||
				title: '%i18n:desktop.tags.mk-drive-browser-file-contextmenu.rename-file%',
 | 
			
		||||
				placeholder: '%i18n:desktop.tags.mk-drive-browser-file-contextmenu.input-new-file-name%',
 | 
			
		||||
				default: this.file.name
 | 
			
		||||
			}).then(name => {
 | 
			
		||||
				(this as any).api('drive/files/update', {
 | 
			
		||||
					file_id: this.file.id,
 | 
			
		||||
					name: name
 | 
			
		||||
				})
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		copyUrl() {
 | 
			
		||||
			copyToClipboard(this.file.url);
 | 
			
		||||
			(this as any).apis.dialog({
 | 
			
		||||
				title: '%fa:check%%i18n:desktop.tags.mk-drive-browser-file-contextmenu.copied%',
 | 
			
		||||
				text: '%i18n:desktop.tags.mk-drive-browser-file-contextmenu.copied-url-to-clipboard%',
 | 
			
		||||
				actions: [{
 | 
			
		||||
					text: '%i18n:common.ok%'
 | 
			
		||||
				}]
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		setAsAvatar() {
 | 
			
		||||
			(this as any).apis.updateAvatar(this.file);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		setAsBanner() {
 | 
			
		||||
			(this as any).apis.updateBanner(this.file);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		addApp() {
 | 
			
		||||
			alert('not implemented yet');
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		deleteFile() {
 | 
			
		||||
			alert('not implemented yet');
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,10 +9,10 @@
 | 
			
		|||
	@dragenter.prevent="onDragenter"
 | 
			
		||||
	@dragleave="onDragleave"
 | 
			
		||||
	@drop.prevent.stop="onDrop"
 | 
			
		||||
	@contextmenu.prevent.stop="onContextmenu"
 | 
			
		||||
	draggable="true"
 | 
			
		||||
	@dragstart="onDragstart"
 | 
			
		||||
	@dragend="onDragend"
 | 
			
		||||
	@contextmenu.prevent.stop="onContextmenu"
 | 
			
		||||
	:title="title"
 | 
			
		||||
>
 | 
			
		||||
	<p class="name">
 | 
			
		||||
| 
						 | 
				
			
			@ -25,10 +25,10 @@
 | 
			
		|||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
import dialog from '../../scripts/dialog';
 | 
			
		||||
import contextmenu from '../../api/contextmenu';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	props: ['folder', 'browser'],
 | 
			
		||||
	props: ['folder'],
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			hover: false,
 | 
			
		||||
| 
						 | 
				
			
			@ -38,6 +38,9 @@ export default Vue.extend({
 | 
			
		|||
		};
 | 
			
		||||
	},
 | 
			
		||||
	computed: {
 | 
			
		||||
		browser(): any {
 | 
			
		||||
			return this.$parent;
 | 
			
		||||
		},
 | 
			
		||||
		title(): string {
 | 
			
		||||
			return this.folder.name;
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -47,6 +50,39 @@ export default Vue.extend({
 | 
			
		|||
			this.browser.move(this.folder);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onContextmenu(e) {
 | 
			
		||||
			this.isContextmenuShowing = true;
 | 
			
		||||
			contextmenu(e, [{
 | 
			
		||||
				type: 'item',
 | 
			
		||||
				text: '%i18n:desktop.tags.mk-drive-browser-folder-contextmenu.move-to-this-folder%',
 | 
			
		||||
				icon: '%fa:arrow-right%',
 | 
			
		||||
				onClick: this.go
 | 
			
		||||
			}, {
 | 
			
		||||
				type: 'item',
 | 
			
		||||
				text: '%i18n:desktop.tags.mk-drive-browser-folder-contextmenu.show-in-new-window%',
 | 
			
		||||
				icon: '%fa:R window-restore%',
 | 
			
		||||
				onClick: this.newWindow
 | 
			
		||||
			}, {
 | 
			
		||||
				type: 'divider',
 | 
			
		||||
			}, {
 | 
			
		||||
				type: 'item',
 | 
			
		||||
				text: '%i18n:desktop.tags.mk-drive-browser-folder-contextmenu.rename%',
 | 
			
		||||
				icon: '%fa:i-cursor%',
 | 
			
		||||
				onClick: this.rename
 | 
			
		||||
			}, {
 | 
			
		||||
				type: 'divider',
 | 
			
		||||
			}, {
 | 
			
		||||
				type: 'item',
 | 
			
		||||
				text: '%i18n:common.delete%',
 | 
			
		||||
				icon: '%fa:R trash-alt%',
 | 
			
		||||
				onClick: this.deleteFolder
 | 
			
		||||
			}], {
 | 
			
		||||
				closed: () => {
 | 
			
		||||
					this.isContextmenuShowing = false;
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onMouseover() {
 | 
			
		||||
			this.hover = true;
 | 
			
		||||
		},
 | 
			
		||||
| 
						 | 
				
			
			@ -102,7 +138,7 @@ export default Vue.extend({
 | 
			
		|||
			if (obj.type == 'file') {
 | 
			
		||||
				const file = obj.id;
 | 
			
		||||
				this.browser.removeFile(file);
 | 
			
		||||
				this.$root.$data.os.api('drive/files/update', {
 | 
			
		||||
				(this as any).api('drive/files/update', {
 | 
			
		||||
					file_id: file,
 | 
			
		||||
					folder_id: this.folder.id
 | 
			
		||||
				});
 | 
			
		||||
| 
						 | 
				
			
			@ -112,7 +148,7 @@ export default Vue.extend({
 | 
			
		|||
				// 移動先が自分自身ならreject
 | 
			
		||||
				if (folder == this.folder.id) return false;
 | 
			
		||||
				this.browser.removeFolder(folder);
 | 
			
		||||
				this.$root.$data.os.api('drive/folders/update', {
 | 
			
		||||
				(this as any).api('drive/folders/update', {
 | 
			
		||||
					folder_id: folder,
 | 
			
		||||
					parent_id: this.folder.id
 | 
			
		||||
				}).then(() => {
 | 
			
		||||
| 
						 | 
				
			
			@ -120,10 +156,13 @@ export default Vue.extend({
 | 
			
		|||
				}).catch(err => {
 | 
			
		||||
					switch (err) {
 | 
			
		||||
						case 'detected-circular-definition':
 | 
			
		||||
							dialog('%fa:exclamation-triangle%%i18n:desktop.tags.mk-drive-browser-folder.unable-to-process%',
 | 
			
		||||
								'%i18n:desktop.tags.mk-drive-browser-folder.circular-reference-detected%', [{
 | 
			
		||||
								text: '%i18n:common.ok%'
 | 
			
		||||
							}]);
 | 
			
		||||
							(this as any).apis.dialog({
 | 
			
		||||
								title: '%fa:exclamation-triangle%%i18n:desktop.tags.mk-drive-browser-folder.unable-to-process%',
 | 
			
		||||
								text: '%i18n:desktop.tags.mk-drive-browser-folder.circular-reference-detected%',
 | 
			
		||||
								actions: [{
 | 
			
		||||
									text: '%i18n:common.ok%'
 | 
			
		||||
								}]
 | 
			
		||||
							});
 | 
			
		||||
							break;
 | 
			
		||||
						default:
 | 
			
		||||
							alert('%i18n:desktop.tags.mk-drive-browser-folder.unhandled-error% ' + err);
 | 
			
		||||
| 
						 | 
				
			
			@ -152,21 +191,29 @@ export default Vue.extend({
 | 
			
		|||
			this.browser.isDragSource = false;
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onContextmenu(e) {
 | 
			
		||||
			this.isContextmenuShowing = true;
 | 
			
		||||
			const ctx = new MkDriveFolderContextmenu({
 | 
			
		||||
				parent: this,
 | 
			
		||||
				propsData: {
 | 
			
		||||
					browser: this.browser,
 | 
			
		||||
					x: e.pageX - window.pageXOffset,
 | 
			
		||||
					y: e.pageY - window.pageYOffset
 | 
			
		||||
				}
 | 
			
		||||
			}).$mount();
 | 
			
		||||
			ctx.$once('closed', () => {
 | 
			
		||||
				this.isContextmenuShowing = false;
 | 
			
		||||
		go() {
 | 
			
		||||
			this.browser.move(this.folder.id);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		newWindow() {
 | 
			
		||||
			this.browser.newWindow(this.folder.id);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		rename() {
 | 
			
		||||
			(this as any).apis.input({
 | 
			
		||||
				title: '%i18n:desktop.tags.mk-drive-browser-folder-contextmenu.rename-folder%',
 | 
			
		||||
				placeholder: '%i18n:desktop.tags.mk-drive-browser-folder-contextmenu.input-new-folder-name%',
 | 
			
		||||
				default: this.folder.name
 | 
			
		||||
			}).then(name => {
 | 
			
		||||
				(this as any).api('drive/folders/update', {
 | 
			
		||||
					folder_id: this.folder.id,
 | 
			
		||||
					name: name
 | 
			
		||||
				});
 | 
			
		||||
			});
 | 
			
		||||
			document.body.appendChild(ctx.$el);
 | 
			
		||||
			return false;
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		deleteFolder() {
 | 
			
		||||
			alert('not implemented yet');
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -73,7 +73,7 @@ export default Vue.extend({
 | 
			
		|||
			if (obj.type == 'file') {
 | 
			
		||||
				const file = obj.id;
 | 
			
		||||
				this.browser.removeFile(file);
 | 
			
		||||
				this.$root.$data.os.api('drive/files/update', {
 | 
			
		||||
				(this as any).api('drive/files/update', {
 | 
			
		||||
					file_id: file,
 | 
			
		||||
					folder_id: this.folder ? this.folder.id : null
 | 
			
		||||
				});
 | 
			
		||||
| 
						 | 
				
			
			@ -83,7 +83,7 @@ export default Vue.extend({
 | 
			
		|||
				// 移動先が自分自身ならreject
 | 
			
		||||
				if (this.folder && folder == this.folder.id) return false;
 | 
			
		||||
				this.browser.removeFolder(folder);
 | 
			
		||||
				this.$root.$data.os.api('drive/folders/update', {
 | 
			
		||||
				(this as any).api('drive/folders/update', {
 | 
			
		||||
					folder_id: folder,
 | 
			
		||||
					parent_id: this.folder ? this.folder.id : null
 | 
			
		||||
				});
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										53
									
								
								src/web/app/desktop/views/components/drive-window.vue
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								src/web/app/desktop/views/components/drive-window.vue
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,53 @@
 | 
			
		|||
<template>
 | 
			
		||||
<mk-window ref="window" @closed="$destroy" width="800px" height="500px" :popout="popout">
 | 
			
		||||
	<span slot="header" :class="$style.header">
 | 
			
		||||
		<p class="info" v-if="usage" :class="$style.info"><b>{{ usage.toFixed(1) }}%</b> %i18n:desktop.tags.mk-drive-browser-window.used%</p>
 | 
			
		||||
		%fa:cloud%%i18n:desktop.tags.mk-drive-browser-window.drive%
 | 
			
		||||
	</span>
 | 
			
		||||
	<mk-drive-browser multiple :folder="folder" ref="browser"/>
 | 
			
		||||
</mk-window>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
import { url } from '../../../config';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	props: ['folder'],
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			usage: null
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		(this as any).api('drive').then(info => {
 | 
			
		||||
			this.usage = info.usage / info.capacity * 100;
 | 
			
		||||
		});
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
		popout() {
 | 
			
		||||
			const folder = (this.$refs.browser as any).folder;
 | 
			
		||||
			if (folder) {
 | 
			
		||||
				return `${url}/i/drive/folder/${folder.id}`;
 | 
			
		||||
			} else {
 | 
			
		||||
				return `${url}/i/drive`;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" module>
 | 
			
		||||
.header
 | 
			
		||||
	> [data-fa]
 | 
			
		||||
		margin-right 4px
 | 
			
		||||
 | 
			
		||||
.info
 | 
			
		||||
	position absolute
 | 
			
		||||
	top 0
 | 
			
		||||
	left 16px
 | 
			
		||||
	margin 0
 | 
			
		||||
	font-size 80%
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -2,17 +2,17 @@
 | 
			
		|||
<div class="mk-drive">
 | 
			
		||||
	<nav>
 | 
			
		||||
		<div class="path" @contextmenu.prevent.stop="() => {}">
 | 
			
		||||
			<mk-drive-browser-nav-folder :class="{ current: folder == null }" folder={ null }/>
 | 
			
		||||
			<template each={ folder in hierarchyFolders }>
 | 
			
		||||
				<span class="separator">%fa:angle-right%</span>
 | 
			
		||||
				<mk-drive-browser-nav-folder folder={ folder }/>
 | 
			
		||||
			<mk-drive-nav-folder :class="{ current: folder == null }"/>
 | 
			
		||||
			<template v-for="folder in hierarchyFolders">
 | 
			
		||||
				<span class="separator" :key="folder.id + '>'">%fa:angle-right%</span>
 | 
			
		||||
				<mk-drive-nav-folder :folder="folder" :key="folder.id"/>
 | 
			
		||||
			</template>
 | 
			
		||||
			<span class="separator" v-if="folder != null">%fa:angle-right%</span>
 | 
			
		||||
			<span class="folder current" v-if="folder != null">{ folder.name }</span>
 | 
			
		||||
			<span class="folder current" v-if="folder != null">{{ folder.name }}</span>
 | 
			
		||||
		</div>
 | 
			
		||||
		<input class="search" type="search" placeholder=" %i18n:desktop.tags.mk-drive-browser.search%"/>
 | 
			
		||||
	</nav>
 | 
			
		||||
	<div class="main { uploading: uploads.length > 0, fetching: fetching }"
 | 
			
		||||
	<div class="main" :class="{ uploading: uploadings.length > 0, fetching }"
 | 
			
		||||
		ref="main"
 | 
			
		||||
		@mousedown="onMousedown"
 | 
			
		||||
		@dragover.prevent.stop="onDragover"
 | 
			
		||||
| 
						 | 
				
			
			@ -24,19 +24,15 @@
 | 
			
		|||
		<div class="selection" ref="selection"></div>
 | 
			
		||||
		<div class="contents" ref="contents">
 | 
			
		||||
			<div class="folders" ref="foldersContainer" v-if="folders.length > 0">
 | 
			
		||||
				<template each={ folder in folders }>
 | 
			
		||||
					<mk-drive-browser-folder class="folder" folder={ folder }/>
 | 
			
		||||
				</template>
 | 
			
		||||
				<mk-drive-folder v-for="folder in folders" :key="folder.id" class="folder" :folder="folder"/>
 | 
			
		||||
				<!-- SEE: https://stackoverflow.com/questions/18744164/flex-box-align-last-row-to-grid -->
 | 
			
		||||
				<div class="padding" each={ Array(10).fill(16) }></div>
 | 
			
		||||
				<div class="padding" v-for="n in 16" :key="n"></div>
 | 
			
		||||
				<button v-if="moreFolders">%i18n:desktop.tags.mk-drive-browser.load-more%</button>
 | 
			
		||||
			</div>
 | 
			
		||||
			<div class="files" ref="filesContainer" v-if="files.length > 0">
 | 
			
		||||
				<template each={ file in files }>
 | 
			
		||||
					<mk-drive-browser-file class="file" file={ file }/>
 | 
			
		||||
				</template>
 | 
			
		||||
				<mk-drive-file v-for="file in files" :key="file.id" class="file" :file="file"/>
 | 
			
		||||
				<!-- SEE: https://stackoverflow.com/questions/18744164/flex-box-align-last-row-to-grid -->
 | 
			
		||||
				<div class="padding" each={ Array(10).fill(16) }></div>
 | 
			
		||||
				<div class="padding" v-for="n in 16" :key="n"></div>
 | 
			
		||||
				<button v-if="moreFiles" @click="fetchMoreFiles">%i18n:desktop.tags.mk-drive-browser.load-more%</button>
 | 
			
		||||
			</div>
 | 
			
		||||
			<div class="empty" v-if="files.length == 0 && folders.length == 0 && !fetching">
 | 
			
		||||
| 
						 | 
				
			
			@ -60,16 +56,18 @@
 | 
			
		|||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
import MkDriveWindow from './drive-window.vue';
 | 
			
		||||
import contains from '../../../common/scripts/contains';
 | 
			
		||||
import dialog from '../../scripts/dialog';
 | 
			
		||||
import inputDialog from '../../scripts/input-dialog';
 | 
			
		||||
import contextmenu from '../../api/contextmenu';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	props: {
 | 
			
		||||
		initFolder: {
 | 
			
		||||
			type: Object,
 | 
			
		||||
			required: false
 | 
			
		||||
		},
 | 
			
		||||
		multiple: {
 | 
			
		||||
			type: Boolean,
 | 
			
		||||
			default: false
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
| 
						 | 
				
			
			@ -106,8 +104,8 @@ export default Vue.extend({
 | 
			
		|||
		};
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		this.connection = this.$root.$data.os.streams.driveStream.getConnection();
 | 
			
		||||
		this.connectionId = this.$root.$data.os.streams.driveStream.use();
 | 
			
		||||
		this.connection = (this as any).os.streams.driveStream.getConnection();
 | 
			
		||||
		this.connectionId = (this as any).os.streams.driveStream.use();
 | 
			
		||||
 | 
			
		||||
		this.connection.on('file_created', this.onStreamDriveFileCreated);
 | 
			
		||||
		this.connection.on('file_updated', this.onStreamDriveFileUpdated);
 | 
			
		||||
| 
						 | 
				
			
			@ -125,12 +123,32 @@ export default Vue.extend({
 | 
			
		|||
		this.connection.off('file_updated', this.onStreamDriveFileUpdated);
 | 
			
		||||
		this.connection.off('folder_created', this.onStreamDriveFolderCreated);
 | 
			
		||||
		this.connection.off('folder_updated', this.onStreamDriveFolderUpdated);
 | 
			
		||||
		this.$root.$data.os.streams.driveStream.dispose(this.connectionId);
 | 
			
		||||
		(this as any).os.streams.driveStream.dispose(this.connectionId);
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
		onContextmenu(e) {
 | 
			
		||||
			contextmenu(e, [{
 | 
			
		||||
				type: 'item',
 | 
			
		||||
				text: '%i18n:desktop.tags.mk-drive-browser-base-contextmenu.create-folder%',
 | 
			
		||||
				icon: '%fa:R folder%',
 | 
			
		||||
				onClick: this.createFolder
 | 
			
		||||
			}, {
 | 
			
		||||
				type: 'item',
 | 
			
		||||
				text: '%i18n:desktop.tags.mk-drive-browser-base-contextmenu.upload%',
 | 
			
		||||
				icon: '%fa:upload%',
 | 
			
		||||
				onClick: this.selectLocalFile
 | 
			
		||||
			}, {
 | 
			
		||||
				type: 'item',
 | 
			
		||||
				text: '%i18n:desktop.tags.mk-drive-browser-base-contextmenu.url-upload%',
 | 
			
		||||
				icon: '%fa:cloud-upload-alt%',
 | 
			
		||||
				onClick: this.urlUpload
 | 
			
		||||
			}]);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onStreamDriveFileCreated(file) {
 | 
			
		||||
			this.addFile(file, true);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onStreamDriveFileUpdated(file) {
 | 
			
		||||
			const current = this.folder ? this.folder.id : null;
 | 
			
		||||
			if (current != file.folder_id) {
 | 
			
		||||
| 
						 | 
				
			
			@ -139,9 +157,11 @@ export default Vue.extend({
 | 
			
		|||
				this.addFile(file, true);
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onStreamDriveFolderCreated(folder) {
 | 
			
		||||
			this.addFolder(folder, true);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onStreamDriveFolderUpdated(folder) {
 | 
			
		||||
			const current = this.folder ? this.folder.id : null;
 | 
			
		||||
			if (current != folder.parent_id) {
 | 
			
		||||
| 
						 | 
				
			
			@ -150,12 +170,15 @@ export default Vue.extend({
 | 
			
		|||
				this.addFolder(folder, true);
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onChangeUploaderUploads(uploads) {
 | 
			
		||||
			this.uploadings = uploads;
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onUploaderUploaded(file) {
 | 
			
		||||
			this.addFile(file, true);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onMousedown(e): any {
 | 
			
		||||
			if (contains(this.$refs.foldersContainer, e.target) || contains(this.$refs.filesContainer, e.target)) return true;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -202,6 +225,7 @@ export default Vue.extend({
 | 
			
		|||
			document.documentElement.addEventListener('mousemove', move);
 | 
			
		||||
			document.documentElement.addEventListener('mouseup', up);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onDragover(e): any {
 | 
			
		||||
			// ドラッグ元が自分自身の所有するアイテムかどうか
 | 
			
		||||
			if (!this.isDragSource) {
 | 
			
		||||
| 
						 | 
				
			
			@ -214,12 +238,15 @@ export default Vue.extend({
 | 
			
		|||
				return false;
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onDragenter(e) {
 | 
			
		||||
			if (!this.isDragSource) this.draghover = true;
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onDragleave(e) {
 | 
			
		||||
			this.draghover = false;
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onDrop(e): any {
 | 
			
		||||
			this.draghover = false;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -244,7 +271,7 @@ export default Vue.extend({
 | 
			
		|||
				const file = obj.id;
 | 
			
		||||
				if (this.files.some(f => f.id == file)) return false;
 | 
			
		||||
				this.removeFile(file);
 | 
			
		||||
				this.$root.$data.os.api('drive/files/update', {
 | 
			
		||||
				(this as any).api('drive/files/update', {
 | 
			
		||||
					file_id: file,
 | 
			
		||||
					folder_id: this.folder ? this.folder.id : null
 | 
			
		||||
				});
 | 
			
		||||
| 
						 | 
				
			
			@ -255,7 +282,7 @@ export default Vue.extend({
 | 
			
		|||
				if (this.folder && folder == this.folder.id) return false;
 | 
			
		||||
				if (this.folders.some(f => f.id == folder)) return false;
 | 
			
		||||
				this.removeFolder(folder);
 | 
			
		||||
				this.$root.$data.os.api('drive/folders/update', {
 | 
			
		||||
				(this as any).api('drive/folders/update', {
 | 
			
		||||
					folder_id: folder,
 | 
			
		||||
					parent_id: this.folder ? this.folder.id : null
 | 
			
		||||
				}).then(() => {
 | 
			
		||||
| 
						 | 
				
			
			@ -263,10 +290,13 @@ export default Vue.extend({
 | 
			
		|||
				}).catch(err => {
 | 
			
		||||
					switch (err) {
 | 
			
		||||
						case 'detected-circular-definition':
 | 
			
		||||
							dialog('%fa:exclamation-triangle%%i18n:desktop.tags.mk-drive-browser.unable-to-process%',
 | 
			
		||||
								'%i18n:desktop.tags.mk-drive-browser.circular-reference-detected%', [{
 | 
			
		||||
								text: '%i18n:common.ok%'
 | 
			
		||||
							}]);
 | 
			
		||||
							(this as any).apis.dialog({
 | 
			
		||||
								title: '%fa:exclamation-triangle%%i18n:desktop.tags.mk-drive-browser.unable-to-process%',
 | 
			
		||||
								text: '%i18n:desktop.tags.mk-drive-browser.circular-reference-detected%',
 | 
			
		||||
								actions: [{
 | 
			
		||||
									text: '%i18n:common.ok%'
 | 
			
		||||
								}]
 | 
			
		||||
							});
 | 
			
		||||
							break;
 | 
			
		||||
						default:
 | 
			
		||||
							alert('%i18n:desktop.tags.mk-drive-browser.unhandled-error% ' + err);
 | 
			
		||||
| 
						 | 
				
			
			@ -276,40 +306,37 @@ export default Vue.extend({
 | 
			
		|||
 | 
			
		||||
			return false;
 | 
			
		||||
		},
 | 
			
		||||
		onContextmenu(e) {
 | 
			
		||||
			document.body.appendChild(new MkDriveContextmenu({
 | 
			
		||||
				propsData: {
 | 
			
		||||
					browser: this,
 | 
			
		||||
					x: e.pageX - window.pageXOffset,
 | 
			
		||||
					y: e.pageY - window.pageYOffset
 | 
			
		||||
				}
 | 
			
		||||
			}).$mount().$el);
 | 
			
		||||
 | 
			
		||||
			return false;
 | 
			
		||||
		},
 | 
			
		||||
		selectLocalFile() {
 | 
			
		||||
			(this.$refs.fileInput as any).click();
 | 
			
		||||
		},
 | 
			
		||||
		urlUpload() {
 | 
			
		||||
			inputDialog('%i18n:desktop.tags.mk-drive-browser.url-upload%',
 | 
			
		||||
				'%i18n:desktop.tags.mk-drive-browser.url-of-file%', null, url => {
 | 
			
		||||
 | 
			
		||||
				this.$root.$data.os.api('drive/files/upload_from_url', {
 | 
			
		||||
		urlUpload() {
 | 
			
		||||
			(this as any).apis.input({
 | 
			
		||||
				title: '%i18n:desktop.tags.mk-drive-browser.url-upload%',
 | 
			
		||||
				placeholder: '%i18n:desktop.tags.mk-drive-browser.url-of-file%'
 | 
			
		||||
			}).then(url => {
 | 
			
		||||
				(this as any).api('drive/files/upload_from_url', {
 | 
			
		||||
					url: url,
 | 
			
		||||
					folder_id: this.folder ? this.folder.id : undefined
 | 
			
		||||
				});
 | 
			
		||||
 | 
			
		||||
				dialog('%fa:check%%i18n:desktop.tags.mk-drive-browser.url-upload-requested%',
 | 
			
		||||
					'%i18n:desktop.tags.mk-drive-browser.may-take-time%', [{
 | 
			
		||||
					text: '%i18n:common.ok%'
 | 
			
		||||
				}]);
 | 
			
		||||
				(this as any).apis.dialog({
 | 
			
		||||
					title: '%fa:check%%i18n:desktop.tags.mk-drive-browser.url-upload-requested%',
 | 
			
		||||
					text: '%i18n:desktop.tags.mk-drive-browser.may-take-time%',
 | 
			
		||||
					actions: [{
 | 
			
		||||
						text: '%i18n:common.ok%'
 | 
			
		||||
					}]
 | 
			
		||||
				});
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
		createFolder() {
 | 
			
		||||
			inputDialog('%i18n:desktop.tags.mk-drive-browser.create-folder%',
 | 
			
		||||
				'%i18n:desktop.tags.mk-drive-browser.folder-name%', null, name => {
 | 
			
		||||
 | 
			
		||||
				this.$root.$data.os.api('drive/folders/create', {
 | 
			
		||||
		createFolder() {
 | 
			
		||||
			(this as any).apis.input({
 | 
			
		||||
				title: '%i18n:desktop.tags.mk-drive-browser.create-folder%',
 | 
			
		||||
				placeholder: '%i18n:desktop.tags.mk-drive-browser.folder-name%'
 | 
			
		||||
			}).then(name => {
 | 
			
		||||
				(this as any).api('drive/folders/create', {
 | 
			
		||||
					name: name,
 | 
			
		||||
					folder_id: this.folder ? this.folder.id : undefined
 | 
			
		||||
				}).then(folder => {
 | 
			
		||||
| 
						 | 
				
			
			@ -317,15 +344,18 @@ export default Vue.extend({
 | 
			
		|||
				});
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onChangeFileInput() {
 | 
			
		||||
			Array.from((this.$refs.fileInput as any).files).forEach(file => {
 | 
			
		||||
				this.upload(file, this.folder);
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		upload(file, folder) {
 | 
			
		||||
			if (folder && typeof folder == 'object') folder = folder.id;
 | 
			
		||||
			(this.$refs.uploader as any).upload(file, folder);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		chooseFile(file) {
 | 
			
		||||
			const isAlreadySelected = this.selectedFiles.some(f => f.id == file.id);
 | 
			
		||||
			if (this.multiple) {
 | 
			
		||||
| 
						 | 
				
			
			@ -344,6 +374,7 @@ export default Vue.extend({
 | 
			
		|||
				}
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		newWindow(folderId) {
 | 
			
		||||
			document.body.appendChild(new MkDriveWindow({
 | 
			
		||||
				propsData: {
 | 
			
		||||
| 
						 | 
				
			
			@ -351,6 +382,7 @@ export default Vue.extend({
 | 
			
		|||
				}
 | 
			
		||||
			}).$mount().$el);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		move(target) {
 | 
			
		||||
			if (target == null) {
 | 
			
		||||
				this.goRoot();
 | 
			
		||||
| 
						 | 
				
			
			@ -361,7 +393,7 @@ export default Vue.extend({
 | 
			
		|||
 | 
			
		||||
			this.fetching = true;
 | 
			
		||||
 | 
			
		||||
			this.$root.$data.os.api('drive/folders/show', {
 | 
			
		||||
			(this as any).api('drive/folders/show', {
 | 
			
		||||
				folder_id: target
 | 
			
		||||
			}).then(folder => {
 | 
			
		||||
				this.folder = folder;
 | 
			
		||||
| 
						 | 
				
			
			@ -378,6 +410,7 @@ export default Vue.extend({
 | 
			
		|||
				this.fetch();
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		addFolder(folder, unshift = false) {
 | 
			
		||||
			const current = this.folder ? this.folder.id : null;
 | 
			
		||||
			if (current != folder.parent_id) return;
 | 
			
		||||
| 
						 | 
				
			
			@ -394,6 +427,7 @@ export default Vue.extend({
 | 
			
		|||
				this.folders.push(folder);
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		addFile(file, unshift = false) {
 | 
			
		||||
			const current = this.folder ? this.folder.id : null;
 | 
			
		||||
			if (current != file.folder_id) return;
 | 
			
		||||
| 
						 | 
				
			
			@ -410,26 +444,33 @@ export default Vue.extend({
 | 
			
		|||
				this.files.push(file);
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		removeFolder(folder) {
 | 
			
		||||
			if (typeof folder == 'object') folder = folder.id;
 | 
			
		||||
			this.folders = this.folders.filter(f => f.id != folder);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		removeFile(file) {
 | 
			
		||||
			if (typeof file == 'object') file = file.id;
 | 
			
		||||
			this.files = this.files.filter(f => f.id != file);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		appendFile(file) {
 | 
			
		||||
			this.addFile(file);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		appendFolder(folder) {
 | 
			
		||||
			this.addFolder(folder);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		prependFile(file) {
 | 
			
		||||
			this.addFile(file, true);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		prependFolder(folder) {
 | 
			
		||||
			this.addFolder(folder, true);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		goRoot() {
 | 
			
		||||
			// 既にrootにいるなら何もしない
 | 
			
		||||
			if (this.folder == null) return;
 | 
			
		||||
| 
						 | 
				
			
			@ -439,6 +480,7 @@ export default Vue.extend({
 | 
			
		|||
			this.$emit('move-root');
 | 
			
		||||
			this.fetch();
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		fetch() {
 | 
			
		||||
			this.folders = [];
 | 
			
		||||
			this.files = [];
 | 
			
		||||
| 
						 | 
				
			
			@ -453,7 +495,7 @@ export default Vue.extend({
 | 
			
		|||
			const filesMax = 30;
 | 
			
		||||
 | 
			
		||||
			// フォルダ一覧取得
 | 
			
		||||
			this.$root.$data.os.api('drive/folders', {
 | 
			
		||||
			(this as any).api('drive/folders', {
 | 
			
		||||
				folder_id: this.folder ? this.folder.id : null,
 | 
			
		||||
				limit: foldersMax + 1
 | 
			
		||||
			}).then(folders => {
 | 
			
		||||
| 
						 | 
				
			
			@ -466,7 +508,7 @@ export default Vue.extend({
 | 
			
		|||
			});
 | 
			
		||||
 | 
			
		||||
			// ファイル一覧取得
 | 
			
		||||
			this.$root.$data.os.api('drive/files', {
 | 
			
		||||
			(this as any).api('drive/files', {
 | 
			
		||||
				folder_id: this.folder ? this.folder.id : null,
 | 
			
		||||
				limit: filesMax + 1
 | 
			
		||||
			}).then(files => {
 | 
			
		||||
| 
						 | 
				
			
			@ -489,13 +531,14 @@ export default Vue.extend({
 | 
			
		|||
				}
 | 
			
		||||
			};
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		fetchMoreFiles() {
 | 
			
		||||
			this.fetching = true;
 | 
			
		||||
 | 
			
		||||
			const max = 30;
 | 
			
		||||
 | 
			
		||||
			// ファイル一覧取得
 | 
			
		||||
			this.$root.$data.os.api('drive/files', {
 | 
			
		||||
			(this as any).api('drive/files', {
 | 
			
		||||
				folder_id: this.folder ? this.folder.id : null,
 | 
			
		||||
				limit: max + 1
 | 
			
		||||
			}).then(files => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -29,8 +29,8 @@ export default Vue.extend({
 | 
			
		|||
		};
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		this.connection = this.$root.$data.os.stream.getConnection();
 | 
			
		||||
		this.connectionId = this.$root.$data.os.stream.use();
 | 
			
		||||
		this.connection = (this as any).os.stream.getConnection();
 | 
			
		||||
		this.connectionId = (this as any).os.stream.use();
 | 
			
		||||
 | 
			
		||||
		this.connection.on('follow', this.onFollow);
 | 
			
		||||
		this.connection.on('unfollow', this.onUnfollow);
 | 
			
		||||
| 
						 | 
				
			
			@ -38,7 +38,7 @@ export default Vue.extend({
 | 
			
		|||
	beforeDestroy() {
 | 
			
		||||
		this.connection.off('follow', this.onFollow);
 | 
			
		||||
		this.connection.off('unfollow', this.onUnfollow);
 | 
			
		||||
		this.$root.$data.os.stream.dispose(this.connectionId);
 | 
			
		||||
		(this as any).os.stream.dispose(this.connectionId);
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -57,7 +57,7 @@ export default Vue.extend({
 | 
			
		|||
		onClick() {
 | 
			
		||||
			this.wait = true;
 | 
			
		||||
			if (this.user.is_following) {
 | 
			
		||||
				this.$root.$data.os.api('following/delete', {
 | 
			
		||||
				(this as any).api('following/delete', {
 | 
			
		||||
					user_id: this.user.id
 | 
			
		||||
				}).then(() => {
 | 
			
		||||
					this.user.is_following = false;
 | 
			
		||||
| 
						 | 
				
			
			@ -67,7 +67,7 @@ export default Vue.extend({
 | 
			
		|||
					this.wait = false;
 | 
			
		||||
				});
 | 
			
		||||
			} else {
 | 
			
		||||
				this.$root.$data.os.api('following/create', {
 | 
			
		||||
				(this as any).api('following/create', {
 | 
			
		||||
					user_id: this.user.id
 | 
			
		||||
				}).then(() => {
 | 
			
		||||
					this.user.is_following = true;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -39,7 +39,7 @@ export default Vue.extend({
 | 
			
		|||
			this.fetching = true;
 | 
			
		||||
			this.users = [];
 | 
			
		||||
 | 
			
		||||
			this.$root.$data.os.api('users/recommendation', {
 | 
			
		||||
			(this as any).api('users/recommendation', {
 | 
			
		||||
				limit: this.limit,
 | 
			
		||||
				offset: this.limit * this.page
 | 
			
		||||
			}).then(users => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -101,7 +101,7 @@ export default Vue.extend({
 | 
			
		|||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
		bakeHomeData() {
 | 
			
		||||
			return JSON.stringify(this.$root.$data.os.i.client_settings.home);
 | 
			
		||||
			return JSON.stringify((this as any).os.i.client_settings.home);
 | 
			
		||||
		},
 | 
			
		||||
		onTlLoaded() {
 | 
			
		||||
			this.$emit('loaded');
 | 
			
		||||
| 
						 | 
				
			
			@ -123,7 +123,7 @@ export default Vue.extend({
 | 
			
		|||
				data: {}
 | 
			
		||||
			};
 | 
			
		||||
 | 
			
		||||
			this.$root.$data.os.i.client_settings.home.unshift(widget);
 | 
			
		||||
			(this as any).os.i.client_settings.home.unshift(widget);
 | 
			
		||||
 | 
			
		||||
			this.saveHome();
 | 
			
		||||
		},
 | 
			
		||||
| 
						 | 
				
			
			@ -132,48 +132,48 @@ export default Vue.extend({
 | 
			
		|||
 | 
			
		||||
			Array.from((this.$refs.left as Element).children).forEach(el => {
 | 
			
		||||
				const id = el.getAttribute('data-widget-id');
 | 
			
		||||
				const widget = this.$root.$data.os.i.client_settings.home.find(w => w.id == id);
 | 
			
		||||
				const widget = (this as any).os.i.client_settings.home.find(w => w.id == id);
 | 
			
		||||
				widget.place = 'left';
 | 
			
		||||
				data.push(widget);
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			Array.from((this.$refs.right as Element).children).forEach(el => {
 | 
			
		||||
				const id = el.getAttribute('data-widget-id');
 | 
			
		||||
				const widget = this.$root.$data.os.i.client_settings.home.find(w => w.id == id);
 | 
			
		||||
				const widget = (this as any).os.i.client_settings.home.find(w => w.id == id);
 | 
			
		||||
				widget.place = 'right';
 | 
			
		||||
				data.push(widget);
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			Array.from((this.$refs.maintop as Element).children).forEach(el => {
 | 
			
		||||
				const id = el.getAttribute('data-widget-id');
 | 
			
		||||
				const widget = this.$root.$data.os.i.client_settings.home.find(w => w.id == id);
 | 
			
		||||
				const widget = (this as any).os.i.client_settings.home.find(w => w.id == id);
 | 
			
		||||
				widget.place = 'main';
 | 
			
		||||
				data.push(widget);
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			this.$root.$data.os.api('i/update_home', {
 | 
			
		||||
			(this as any).api('i/update_home', {
 | 
			
		||||
				home: data
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	computed: {
 | 
			
		||||
		leftWidgets(): any {
 | 
			
		||||
			return this.$root.$data.os.i.client_settings.home.filter(w => w.place == 'left');
 | 
			
		||||
			return (this as any).os.i.client_settings.home.filter(w => w.place == 'left');
 | 
			
		||||
		},
 | 
			
		||||
		centerWidgets(): any {
 | 
			
		||||
			return this.$root.$data.os.i.client_settings.home.filter(w => w.place == 'center');
 | 
			
		||||
			return (this as any).os.i.client_settings.home.filter(w => w.place == 'center');
 | 
			
		||||
		},
 | 
			
		||||
		rightWidgets(): any {
 | 
			
		||||
			return this.$root.$data.os.i.client_settings.home.filter(w => w.place == 'right');
 | 
			
		||||
			return (this as any).os.i.client_settings.home.filter(w => w.place == 'right');
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	created() {
 | 
			
		||||
		this.bakedHomeData = this.bakeHomeData();
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		this.$root.$data.os.i.on('refreshed', this.onMeRefreshed);
 | 
			
		||||
		(this as any).os.i.on('refreshed', this.onMeRefreshed);
 | 
			
		||||
 | 
			
		||||
		this.home = this.$root.$data.os.i.client_settings.home;
 | 
			
		||||
		this.home = (this as any).os.i.client_settings.home;
 | 
			
		||||
 | 
			
		||||
		if (!this.customize) {
 | 
			
		||||
			if ((this.$refs.left as Element).children.length == 0) {
 | 
			
		||||
| 
						 | 
				
			
			@ -214,14 +214,14 @@ export default Vue.extend({
 | 
			
		|||
					const el = evt.item;
 | 
			
		||||
					const id = el.getAttribute('data-widget-id');
 | 
			
		||||
					el.parentNode.removeChild(el);
 | 
			
		||||
					this.$root.$data.os.i.client_settings.home = this.$root.$data.os.i.client_settings.home.filter(w => w.id != id);
 | 
			
		||||
					(this as any).os.i.client_settings.home = (this as any).os.i.client_settings.home.filter(w => w.id != id);
 | 
			
		||||
					this.saveHome();
 | 
			
		||||
				}
 | 
			
		||||
			}));
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	beforeDestroy() {
 | 
			
		||||
		this.$root.$data.os.i.off('refreshed', this.onMeRefreshed);
 | 
			
		||||
		(this as any).os.i.off('refreshed', this.onMeRefreshed);
 | 
			
		||||
 | 
			
		||||
		this.home.forEach(widget => {
 | 
			
		||||
			widget.unmount();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,6 +27,10 @@ import postForm from './post-form.vue';
 | 
			
		|||
import repostForm from './repost-form.vue';
 | 
			
		||||
import followButton from './follow-button.vue';
 | 
			
		||||
import postPreview from './post-preview.vue';
 | 
			
		||||
import drive from './drive.vue';
 | 
			
		||||
import driveFile from './drive-file.vue';
 | 
			
		||||
import driveFolder from './drive-folder.vue';
 | 
			
		||||
import driveNavFolder from './drive-nav-folder.vue';
 | 
			
		||||
 | 
			
		||||
Vue.component('mk-ui', ui);
 | 
			
		||||
Vue.component('mk-ui-header', uiHeader);
 | 
			
		||||
| 
						 | 
				
			
			@ -55,3 +59,7 @@ Vue.component('mk-post-form', postForm);
 | 
			
		|||
Vue.component('mk-repost-form', repostForm);
 | 
			
		||||
Vue.component('mk-follow-button', followButton);
 | 
			
		||||
Vue.component('mk-post-preview', postPreview);
 | 
			
		||||
Vue.component('mk-drive', drive);
 | 
			
		||||
Vue.component('mk-drive-file', driveFile);
 | 
			
		||||
Vue.component('mk-drive-folder', driveFolder);
 | 
			
		||||
Vue.component('mk-drive-nav-folder', driveNavFolder);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -33,12 +33,6 @@ export default Vue.extend({
 | 
			
		|||
		},
 | 
			
		||||
		type: {
 | 
			
		||||
			default: 'text'
 | 
			
		||||
		},
 | 
			
		||||
		onOk: {
 | 
			
		||||
			type: Function
 | 
			
		||||
		},
 | 
			
		||||
		onCancel: {
 | 
			
		||||
			type: Function
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	data() {
 | 
			
		||||
| 
						 | 
				
			
			@ -63,9 +57,9 @@ export default Vue.extend({
 | 
			
		|||
		},
 | 
			
		||||
		beforeClose() {
 | 
			
		||||
			if (this.done) {
 | 
			
		||||
				this.onOk(this.text);
 | 
			
		||||
				this.$emit('done', this.text);
 | 
			
		||||
			} else {
 | 
			
		||||
				if (this.onCancel) this.onCancel();
 | 
			
		||||
				this.$emit('canceled');
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		onKeydown(e) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,7 +11,6 @@ export default Vue.extend({
 | 
			
		|||
	methods: {
 | 
			
		||||
		navigate(user) {
 | 
			
		||||
			document.body.appendChild(new MkMessagingRoomWindow({
 | 
			
		||||
				parent: this,
 | 
			
		||||
				propsData: {
 | 
			
		||||
					user: user
 | 
			
		||||
				}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,7 +22,7 @@ export default Vue.extend({
 | 
			
		|||
		};
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		this.$root.$data.os.api('mute/list').then(x => {
 | 
			
		||||
		(this as any).api('mute/list').then(x => {
 | 
			
		||||
			this.fetching = false;
 | 
			
		||||
			this.users = x.users;
 | 
			
		||||
		});
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -128,14 +128,14 @@ export default Vue.extend({
 | 
			
		|||
		}
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		this.connection = this.$root.$data.os.stream.getConnection();
 | 
			
		||||
		this.connectionId = this.$root.$data.os.stream.use();
 | 
			
		||||
		this.connection = (this as any).os.stream.getConnection();
 | 
			
		||||
		this.connectionId = (this as any).os.stream.use();
 | 
			
		||||
 | 
			
		||||
		this.connection.on('notification', this.onNotification);
 | 
			
		||||
 | 
			
		||||
		const max = 10;
 | 
			
		||||
 | 
			
		||||
		this.$root.$data.os.api('i/notifications', {
 | 
			
		||||
		(this as any).api('i/notifications', {
 | 
			
		||||
			limit: max + 1
 | 
			
		||||
		}).then(notifications => {
 | 
			
		||||
			if (notifications.length == max + 1) {
 | 
			
		||||
| 
						 | 
				
			
			@ -149,7 +149,7 @@ export default Vue.extend({
 | 
			
		|||
	},
 | 
			
		||||
	beforeDestroy() {
 | 
			
		||||
		this.connection.off('notification', this.onNotification);
 | 
			
		||||
		this.$root.$data.os.stream.dispose(this.connectionId);
 | 
			
		||||
		(this as any).os.stream.dispose(this.connectionId);
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
		fetchMoreNotifications() {
 | 
			
		||||
| 
						 | 
				
			
			@ -157,7 +157,7 @@ export default Vue.extend({
 | 
			
		|||
 | 
			
		||||
			const max = 30;
 | 
			
		||||
 | 
			
		||||
			this.$root.$data.os.api('i/notifications', {
 | 
			
		||||
			(this as any).api('i/notifications', {
 | 
			
		||||
				limit: max + 1,
 | 
			
		||||
				until_id: this.notifications[this.notifications.length - 1].id
 | 
			
		||||
			}).then(notifications => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,7 +22,7 @@ export default Vue.extend({
 | 
			
		|||
							}]);
 | 
			
		||||
							return;
 | 
			
		||||
						}
 | 
			
		||||
						this.$root.$data.os.api('i/change_password', {
 | 
			
		||||
						(this as any).api('i/change_password', {
 | 
			
		||||
							current_password: currentPassword,
 | 
			
		||||
							new_password: newPassword
 | 
			
		||||
						}).then(() => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,7 +16,7 @@
 | 
			
		|||
			</div>
 | 
			
		||||
		</header>
 | 
			
		||||
		<div class="body">
 | 
			
		||||
			<mk-post-html v-if="post.ast" :ast="post.ast" :i="$root.$data.os.i"/>
 | 
			
		||||
			<mk-post-html v-if="post.ast" :ast="post.ast" :i="os.i"/>
 | 
			
		||||
			<div class="media" v-if="post.media">
 | 
			
		||||
				<mk-images images={ post.media }/>
 | 
			
		||||
			</div>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -35,7 +35,7 @@
 | 
			
		|||
			</a>
 | 
			
		||||
		</header>
 | 
			
		||||
		<div class="body">
 | 
			
		||||
			<mk-post-html v-if="p.ast" :ast="p.ast" :i="$root.$data.os.i"/>
 | 
			
		||||
			<mk-post-html v-if="p.ast" :ast="p.ast" :i="os.i"/>
 | 
			
		||||
			<mk-url-preview v-for="url in urls" :url="url" :key="url"/>
 | 
			
		||||
			<div class="media" v-if="p.media">
 | 
			
		||||
				<mk-images images={ p.media }/>
 | 
			
		||||
| 
						 | 
				
			
			@ -117,7 +117,7 @@ export default Vue.extend({
 | 
			
		|||
	mounted() {
 | 
			
		||||
		// Get replies
 | 
			
		||||
		if (!this.compact) {
 | 
			
		||||
			this.$root.$data.os.api('posts/replies', {
 | 
			
		||||
			(this as any).api('posts/replies', {
 | 
			
		||||
				post_id: this.p.id,
 | 
			
		||||
				limit: 8
 | 
			
		||||
			}).then(replies => {
 | 
			
		||||
| 
						 | 
				
			
			@ -130,7 +130,7 @@ export default Vue.extend({
 | 
			
		|||
			this.contextFetching = true;
 | 
			
		||||
 | 
			
		||||
			// Fetch context
 | 
			
		||||
			this.$root.$data.os.api('posts/context', {
 | 
			
		||||
			(this as any).api('posts/context', {
 | 
			
		||||
				post_id: this.p.reply_id
 | 
			
		||||
			}).then(context => {
 | 
			
		||||
				this.contextFetching = false;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -113,18 +113,12 @@ export default Vue.extend({
 | 
			
		|||
		chooseFile() {
 | 
			
		||||
			(this.$refs.file as any).click();
 | 
			
		||||
		},
 | 
			
		||||
		chooseFileFromDrive() {/*
 | 
			
		||||
			const w = new MkDriveFileSelectorWindow({
 | 
			
		||||
				propsData: {
 | 
			
		||||
					multiple: true
 | 
			
		||||
				}
 | 
			
		||||
			}).$mount();
 | 
			
		||||
 | 
			
		||||
			document.body.appendChild(w.$el);
 | 
			
		||||
 | 
			
		||||
			w.$once('selected', files => {
 | 
			
		||||
		chooseFileFromDrive() {
 | 
			
		||||
			(this as any).apis.chooseDriveFile({
 | 
			
		||||
				multiple: true
 | 
			
		||||
			}).then(files => {
 | 
			
		||||
				files.forEach(this.attachMedia);
 | 
			
		||||
			});*/
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
		attachMedia(driveFile) {
 | 
			
		||||
			this.files.push(driveFile);
 | 
			
		||||
| 
						 | 
				
			
			@ -196,7 +190,7 @@ export default Vue.extend({
 | 
			
		|||
		post() {
 | 
			
		||||
			this.posting = true;
 | 
			
		||||
 | 
			
		||||
			this.$root.$data.os.api('posts/create', {
 | 
			
		||||
			(this as any).api('posts/create', {
 | 
			
		||||
				text: this.text == '' ? undefined : this.text,
 | 
			
		||||
				media_ids: this.files.length > 0 ? this.files.map(f => f.id) : undefined,
 | 
			
		||||
				reply_id: this.reply ? this.reply.id : undefined,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -32,7 +32,7 @@
 | 
			
		|||
				<div class="text" ref="text">
 | 
			
		||||
					<p class="channel" v-if="p.channel"><a :href="`${_CH_URL_}/${p.channel.id}`" target="_blank">{{ p.channel.title }}</a>:</p>
 | 
			
		||||
					<a class="reply" v-if="p.reply">%fa:reply%</a>
 | 
			
		||||
					<mk-post-html v-if="p.ast" :ast="p.ast" :i="$root.$data.os.i"/>
 | 
			
		||||
					<mk-post-html v-if="p.ast" :ast="p.ast" :i="os.i"/>
 | 
			
		||||
					<a class="quote" v-if="p.repost">RP:</a>
 | 
			
		||||
					<mk-url-preview v-for="url in urls" :url="url" :key="url"/>
 | 
			
		||||
				</div>
 | 
			
		||||
| 
						 | 
				
			
			@ -133,24 +133,24 @@ export default Vue.extend({
 | 
			
		|||
		}
 | 
			
		||||
	},
 | 
			
		||||
	created() {
 | 
			
		||||
		this.connection = this.$root.$data.os.stream.getConnection();
 | 
			
		||||
		this.connectionId = this.$root.$data.os.stream.use();
 | 
			
		||||
		this.connection = (this as any).os.stream.getConnection();
 | 
			
		||||
		this.connectionId = (this as any).os.stream.use();
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		this.capture(true);
 | 
			
		||||
 | 
			
		||||
		if (this.$root.$data.os.isSignedIn) {
 | 
			
		||||
		if ((this as any).os.isSignedIn) {
 | 
			
		||||
			this.connection.on('_connected_', this.onStreamConnected);
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	beforeDestroy() {
 | 
			
		||||
		this.decapture(true);
 | 
			
		||||
		this.connection.off('_connected_', this.onStreamConnected);
 | 
			
		||||
		this.$root.$data.os.stream.dispose(this.connectionId);
 | 
			
		||||
		(this as any).os.stream.dispose(this.connectionId);
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
		capture(withHandler = false) {
 | 
			
		||||
			if (this.$root.$data.os.isSignedIn) {
 | 
			
		||||
			if ((this as any).os.isSignedIn) {
 | 
			
		||||
				this.connection.send({
 | 
			
		||||
					type: 'capture',
 | 
			
		||||
					id: this.post.id
 | 
			
		||||
| 
						 | 
				
			
			@ -159,7 +159,7 @@ export default Vue.extend({
 | 
			
		|||
			}
 | 
			
		||||
		},
 | 
			
		||||
		decapture(withHandler = false) {
 | 
			
		||||
			if (this.$root.$data.os.isSignedIn) {
 | 
			
		||||
			if ((this as any).os.isSignedIn) {
 | 
			
		||||
				this.connection.send({
 | 
			
		||||
					type: 'decapture',
 | 
			
		||||
					id: this.post.id
 | 
			
		||||
| 
						 | 
				
			
			@ -178,7 +178,7 @@ export default Vue.extend({
 | 
			
		|||
		},
 | 
			
		||||
		reply() {
 | 
			
		||||
			document.body.appendChild(new MkPostFormWindow({
 | 
			
		||||
				parent: this,
 | 
			
		||||
 | 
			
		||||
				propsData: {
 | 
			
		||||
					reply: this.p
 | 
			
		||||
				}
 | 
			
		||||
| 
						 | 
				
			
			@ -186,7 +186,7 @@ export default Vue.extend({
 | 
			
		|||
		},
 | 
			
		||||
		repost() {
 | 
			
		||||
			document.body.appendChild(new MkRepostFormWindow({
 | 
			
		||||
				parent: this,
 | 
			
		||||
 | 
			
		||||
				propsData: {
 | 
			
		||||
					post: this.p
 | 
			
		||||
				}
 | 
			
		||||
| 
						 | 
				
			
			@ -194,7 +194,7 @@ export default Vue.extend({
 | 
			
		|||
		},
 | 
			
		||||
		react() {
 | 
			
		||||
			document.body.appendChild(new MkReactionPicker({
 | 
			
		||||
				parent: this,
 | 
			
		||||
 | 
			
		||||
				propsData: {
 | 
			
		||||
					source: this.$refs.reactButton,
 | 
			
		||||
					post: this.p
 | 
			
		||||
| 
						 | 
				
			
			@ -203,7 +203,7 @@ export default Vue.extend({
 | 
			
		|||
		},
 | 
			
		||||
		menu() {
 | 
			
		||||
			document.body.appendChild(new MkPostMenu({
 | 
			
		||||
				parent: this,
 | 
			
		||||
 | 
			
		||||
				propsData: {
 | 
			
		||||
					source: this.$refs.menuButton,
 | 
			
		||||
					post: this.p
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
<template>
 | 
			
		||||
<div class="mk-profile-setting">
 | 
			
		||||
	<label class="avatar ui from group">
 | 
			
		||||
		<p>%i18n:desktop.tags.mk-profile-setting.avatar%</p><img class="avatar" :src="`${$root.$data.os.i.avatar_url}?thumbnail&size=64`" alt="avatar"/>
 | 
			
		||||
		<p>%i18n:desktop.tags.mk-profile-setting.avatar%</p><img class="avatar" :src="`${os.i.avatar_url}?thumbnail&size=64`" alt="avatar"/>
 | 
			
		||||
		<button class="ui" @click="updateAvatar">%i18n:desktop.tags.mk-profile-setting.choice-avatar%</button>
 | 
			
		||||
	</label>
 | 
			
		||||
	<label class="ui from group">
 | 
			
		||||
| 
						 | 
				
			
			@ -32,18 +32,18 @@ import notify from '../../scripts/notify';
 | 
			
		|||
export default Vue.extend({
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			name: this.$root.$data.os.i.name,
 | 
			
		||||
			location: this.$root.$data.os.i.location,
 | 
			
		||||
			description: this.$root.$data.os.i.description,
 | 
			
		||||
			birthday: this.$root.$data.os.i.birthday,
 | 
			
		||||
			name: (this as any).os.i.name,
 | 
			
		||||
			location: (this as any).os.i.location,
 | 
			
		||||
			description: (this as any).os.i.description,
 | 
			
		||||
			birthday: (this as any).os.i.birthday,
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
		updateAvatar() {
 | 
			
		||||
			updateAvatar(this.$root.$data.os.i);
 | 
			
		||||
			updateAvatar((this as any).os.i);
 | 
			
		||||
		},
 | 
			
		||||
		save() {
 | 
			
		||||
			this.$root.$data.os.api('i/update', {
 | 
			
		||||
			(this as any).api('i/update', {
 | 
			
		||||
				name: this.name,
 | 
			
		||||
				location: this.location || null,
 | 
			
		||||
				description: this.description || null,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -29,7 +29,7 @@ export default Vue.extend({
 | 
			
		|||
	methods: {
 | 
			
		||||
		ok() {
 | 
			
		||||
			this.wait = true;
 | 
			
		||||
			this.$root.$data.os.api('posts/create', {
 | 
			
		||||
			(this as any).api('posts/create', {
 | 
			
		||||
				repost_id: this.post.id
 | 
			
		||||
			}).then(data => {
 | 
			
		||||
				this.$emit('posted');
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,7 +2,7 @@
 | 
			
		|||
<div class="mk-sub-post-content">
 | 
			
		||||
	<div class="body">
 | 
			
		||||
		<a class="reply" v-if="post.reply_id">%fa:reply%</a>
 | 
			
		||||
		<mk-post-html :ast="post.ast" :i="$root.$data.os.i"/>
 | 
			
		||||
		<mk-post-html :ast="post.ast" :i="os.i"/>
 | 
			
		||||
		<a class="quote" v-if="post.repost_id" :href="`/post:${post.repost_id}`">RP: ...</a>
 | 
			
		||||
		<mk-url-preview v-for="url in urls" :url="url" :key="url"/>
 | 
			
		||||
	</div>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -30,12 +30,12 @@ export default Vue.extend({
 | 
			
		|||
	},
 | 
			
		||||
	computed: {
 | 
			
		||||
		alone(): boolean {
 | 
			
		||||
			return this.$root.$data.os.i.following_count == 0;
 | 
			
		||||
			return (this as any).os.i.following_count == 0;
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		this.connection = this.$root.$data.os.stream.getConnection();
 | 
			
		||||
		this.connectionId = this.$root.$data.os.stream.use();
 | 
			
		||||
		this.connection = (this as any).os.stream.getConnection();
 | 
			
		||||
		this.connectionId = (this as any).os.stream.use();
 | 
			
		||||
 | 
			
		||||
		this.connection.on('post', this.onPost);
 | 
			
		||||
		this.connection.on('follow', this.onChangeFollowing);
 | 
			
		||||
| 
						 | 
				
			
			@ -50,7 +50,7 @@ export default Vue.extend({
 | 
			
		|||
		this.connection.off('post', this.onPost);
 | 
			
		||||
		this.connection.off('follow', this.onChangeFollowing);
 | 
			
		||||
		this.connection.off('unfollow', this.onChangeFollowing);
 | 
			
		||||
		this.$root.$data.os.stream.dispose(this.connectionId);
 | 
			
		||||
		(this as any).os.stream.dispose(this.connectionId);
 | 
			
		||||
 | 
			
		||||
		document.removeEventListener('keydown', this.onKeydown);
 | 
			
		||||
		window.removeEventListener('scroll', this.onScroll);
 | 
			
		||||
| 
						 | 
				
			
			@ -59,7 +59,7 @@ export default Vue.extend({
 | 
			
		|||
		fetch(cb?) {
 | 
			
		||||
			this.fetching = true;
 | 
			
		||||
 | 
			
		||||
			this.$root.$data.os.api('posts/timeline', {
 | 
			
		||||
			(this as any).api('posts/timeline', {
 | 
			
		||||
				until_date: this.date ? (this.date as any).getTime() : undefined
 | 
			
		||||
			}).then(posts => {
 | 
			
		||||
				this.fetching = false;
 | 
			
		||||
| 
						 | 
				
			
			@ -70,7 +70,7 @@ export default Vue.extend({
 | 
			
		|||
		more() {
 | 
			
		||||
			if (this.moreFetching || this.fetching || this.posts.length == 0) return;
 | 
			
		||||
			this.moreFetching = true;
 | 
			
		||||
			this.$root.$data.os.api('posts/timeline', {
 | 
			
		||||
			(this as any).api('posts/timeline', {
 | 
			
		||||
				until_id: this.posts[this.posts.length - 1].id
 | 
			
		||||
			}).then(posts => {
 | 
			
		||||
				this.moreFetching = false;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,13 +1,13 @@
 | 
			
		|||
<template>
 | 
			
		||||
<div class="mk-ui-header-account">
 | 
			
		||||
	<button class="header" :data-active="isOpen" @click="toggle">
 | 
			
		||||
		<span class="username">{{ $root.$data.os.i.username }}<template v-if="!isOpen">%fa:angle-down%</template><template v-if="isOpen">%fa:angle-up%</template></span>
 | 
			
		||||
		<img class="avatar" :src="`${ $root.$data.os.i.avatar_url }?thumbnail&size=64`" alt="avatar"/>
 | 
			
		||||
		<span class="username">{{ os.i.username }}<template v-if="!isOpen">%fa:angle-down%</template><template v-if="isOpen">%fa:angle-up%</template></span>
 | 
			
		||||
		<img class="avatar" :src="`${ os.i.avatar_url }?thumbnail&size=64`" alt="avatar"/>
 | 
			
		||||
	</button>
 | 
			
		||||
	<div class="menu" v-if="isOpen">
 | 
			
		||||
		<ul>
 | 
			
		||||
			<li>
 | 
			
		||||
				<a :href="`/${ $root.$data.os.i.username }`">%fa:user%%i18n:desktop.tags.mk-ui-header-account.profile%%fa:angle-right%</a>
 | 
			
		||||
				<a :href="`/${ os.i.username }`">%fa:user%%i18n:desktop.tags.mk-ui-header-account.profile%%fa:angle-right%</a>
 | 
			
		||||
			</li>
 | 
			
		||||
			<li @click="drive">
 | 
			
		||||
				<p>%fa:cloud%%i18n:desktop.tags.mk-ui-header-account.drive%%fa:angle-right%</p>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
<template>
 | 
			
		||||
<div class="mk-ui-header-nav">
 | 
			
		||||
	<ul>
 | 
			
		||||
		<template v-if="$root.$data.os.isSignedIn">
 | 
			
		||||
		<template v-if="os.isSignedIn">
 | 
			
		||||
			<li class="home" :class="{ active: page == 'home' }">
 | 
			
		||||
				<a href="/">
 | 
			
		||||
					%fa:home%
 | 
			
		||||
| 
						 | 
				
			
			@ -44,15 +44,15 @@ export default Vue.extend({
 | 
			
		|||
		};
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		if (this.$root.$data.os.isSignedIn) {
 | 
			
		||||
			this.connection = this.$root.$data.os.stream.getConnection();
 | 
			
		||||
			this.connectionId = this.$root.$data.os.stream.use();
 | 
			
		||||
		if ((this as any).os.isSignedIn) {
 | 
			
		||||
			this.connection = (this as any).os.stream.getConnection();
 | 
			
		||||
			this.connectionId = (this as any).os.stream.use();
 | 
			
		||||
 | 
			
		||||
			this.connection.on('read_all_messaging_messages', this.onReadAllMessagingMessages);
 | 
			
		||||
			this.connection.on('unread_messaging_message', this.onUnreadMessagingMessage);
 | 
			
		||||
 | 
			
		||||
			// Fetch count of unread messaging messages
 | 
			
		||||
			this.$root.$data.os.api('messaging/unread').then(res => {
 | 
			
		||||
			(this as any).api('messaging/unread').then(res => {
 | 
			
		||||
				if (res.count > 0) {
 | 
			
		||||
					this.hasUnreadMessagingMessages = true;
 | 
			
		||||
				}
 | 
			
		||||
| 
						 | 
				
			
			@ -60,10 +60,10 @@ export default Vue.extend({
 | 
			
		|||
		}
 | 
			
		||||
	},
 | 
			
		||||
	beforeDestroy() {
 | 
			
		||||
		if (this.$root.$data.os.isSignedIn) {
 | 
			
		||||
		if ((this as any).os.isSignedIn) {
 | 
			
		||||
			this.connection.off('read_all_messaging_messages', this.onReadAllMessagingMessages);
 | 
			
		||||
			this.connection.off('unread_messaging_message', this.onUnreadMessagingMessage);
 | 
			
		||||
			this.$root.$data.os.stream.dispose(this.connectionId);
 | 
			
		||||
			(this as any).os.stream.dispose(this.connectionId);
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,15 +23,15 @@ export default Vue.extend({
 | 
			
		|||
		};
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		if (this.$root.$data.os.isSignedIn) {
 | 
			
		||||
			this.connection = this.$root.$data.os.stream.getConnection();
 | 
			
		||||
			this.connectionId = this.$root.$data.os.stream.use();
 | 
			
		||||
		if ((this as any).os.isSignedIn) {
 | 
			
		||||
			this.connection = (this as any).os.stream.getConnection();
 | 
			
		||||
			this.connectionId = (this as any).os.stream.use();
 | 
			
		||||
 | 
			
		||||
			this.connection.on('read_all_notifications', this.onReadAllNotifications);
 | 
			
		||||
			this.connection.on('unread_notification', this.onUnreadNotification);
 | 
			
		||||
 | 
			
		||||
			// Fetch count of unread notifications
 | 
			
		||||
			this.$root.$data.os.api('notifications/get_unread_count').then(res => {
 | 
			
		||||
			(this as any).api('notifications/get_unread_count').then(res => {
 | 
			
		||||
				if (res.count > 0) {
 | 
			
		||||
					this.hasUnreadNotifications = true;
 | 
			
		||||
				}
 | 
			
		||||
| 
						 | 
				
			
			@ -39,10 +39,10 @@ export default Vue.extend({
 | 
			
		|||
		}
 | 
			
		||||
	},
 | 
			
		||||
	beforeDestroy() {
 | 
			
		||||
		if (this.$root.$data.os.isSignedIn) {
 | 
			
		||||
		if ((this as any).os.isSignedIn) {
 | 
			
		||||
			this.connection.off('read_all_notifications', this.onReadAllNotifications);
 | 
			
		||||
			this.connection.off('unread_notification', this.onUnreadNotification);
 | 
			
		||||
			this.$root.$data.os.stream.dispose(this.connectionId);
 | 
			
		||||
			(this as any).os.stream.dispose(this.connectionId);
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,9 +10,9 @@
 | 
			
		|||
				</div>
 | 
			
		||||
				<div class="right">
 | 
			
		||||
					<mk-ui-header-search/>
 | 
			
		||||
					<mk-ui-header-account v-if="$root.$data.os.isSignedIn"/>
 | 
			
		||||
					<mk-ui-header-notifications v-if="$root.$data.os.isSignedIn"/>
 | 
			
		||||
					<mk-ui-header-post-button v-if="$root.$data.os.isSignedIn"/>
 | 
			
		||||
					<mk-ui-header-account v-if="os.isSignedIn"/>
 | 
			
		||||
					<mk-ui-header-notifications v-if="os.isSignedIn"/>
 | 
			
		||||
					<mk-ui-header-post-button v-if="os.isSignedIn"/>
 | 
			
		||||
					<mk-ui-header-clock/>
 | 
			
		||||
				</div>
 | 
			
		||||
			</div>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,7 +4,7 @@
 | 
			
		|||
	<div class="content">
 | 
			
		||||
		<slot></slot>
 | 
			
		||||
	</div>
 | 
			
		||||
	<mk-stream-indicator v-if="$root.$data.os.isSignedIn"/>
 | 
			
		||||
	<mk-stream-indicator v-if="os.isSignedIn"/>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,7 +14,7 @@ export default Vue.extend({
 | 
			
		|||
	props: ['user'],
 | 
			
		||||
	methods: {
 | 
			
		||||
		fetch(iknow, limit, cursor, cb) {
 | 
			
		||||
			this.$root.$data.os.api('users/followers', {
 | 
			
		||||
			(this as any).api('users/followers', {
 | 
			
		||||
				user_id: this.user.id,
 | 
			
		||||
				iknow: iknow,
 | 
			
		||||
				limit: limit,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,7 +14,7 @@ export default Vue.extend({
 | 
			
		|||
	props: ['user'],
 | 
			
		||||
	methods: {
 | 
			
		||||
		fetch(iknow, limit, cursor, cb) {
 | 
			
		||||
			this.$root.$data.os.api('users/following', {
 | 
			
		||||
			(this as any).api('users/following', {
 | 
			
		||||
				user_id: this.user.id,
 | 
			
		||||
				iknow: iknow,
 | 
			
		||||
				limit: limit,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,7 +21,7 @@
 | 
			
		|||
				<p>フォロワー</p><a>{{ u.followers_count }}</a>
 | 
			
		||||
			</div>
 | 
			
		||||
		</div>
 | 
			
		||||
		<mk-follow-button v-if="$root.$data.os.isSignedIn && user.id != $root.$data.os.i.id" :user="u"/>
 | 
			
		||||
		<mk-follow-button v-if="os.isSignedIn && user.id != os.i.id" :user="u"/>
 | 
			
		||||
	</template>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
| 
						 | 
				
			
			@ -49,7 +49,7 @@ export default Vue.extend({
 | 
			
		|||
				this.open();
 | 
			
		||||
			});
 | 
			
		||||
		} else {
 | 
			
		||||
			this.$root.$data.os.api('users/show', {
 | 
			
		||||
			(this as any).api('users/show', {
 | 
			
		||||
				user_id: this.user[0] == '@' ? undefined : this.user,
 | 
			
		||||
				username: this.user[0] == '@' ? this.user.substr(1) : undefined
 | 
			
		||||
			}).then(user => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -60,7 +60,7 @@ export default Vue.extend({
 | 
			
		|||
			}
 | 
			
		||||
		},
 | 
			
		||||
		fetch(cb?) {
 | 
			
		||||
			this.$root.$data.os.api('users/posts', {
 | 
			
		||||
			(this as any).api('users/posts', {
 | 
			
		||||
				user_id: this.user.id,
 | 
			
		||||
				until_date: this.date ? this.date.getTime() : undefined,
 | 
			
		||||
				with_replies: this.mode == 'with-replies'
 | 
			
		||||
| 
						 | 
				
			
			@ -73,7 +73,7 @@ export default Vue.extend({
 | 
			
		|||
		more() {
 | 
			
		||||
			if (this.moreFetching || this.fetching || this.posts.length == 0) return;
 | 
			
		||||
			this.moreFetching = true;
 | 
			
		||||
			this.$root.$data.os.api('users/posts', {
 | 
			
		||||
			(this as any).api('users/posts', {
 | 
			
		||||
				user_id: this.user.id,
 | 
			
		||||
				with_replies: this.mode == 'with-replies',
 | 
			
		||||
				until_id: this.posts[this.posts.length - 1].id
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,7 +3,7 @@
 | 
			
		|||
	<nav>
 | 
			
		||||
		<div>
 | 
			
		||||
			<span :data-is-active="mode == 'all'" @click="mode = 'all'">すべて<span>{{ count }}</span></span>
 | 
			
		||||
			<span v-if="$root.$data.os.isSignedIn && youKnowCount" :data-is-active="mode == 'iknow'" @click="mode = 'iknow'">知り合い<span>{{ youKnowCount }}</span></span>
 | 
			
		||||
			<span v-if="os.isSignedIn && youKnowCount" :data-is-active="mode == 'iknow'" @click="mode = 'iknow'">知り合い<span>{{ youKnowCount }}</span></span>
 | 
			
		||||
		</div>
 | 
			
		||||
	</nav>
 | 
			
		||||
	<div class="users" v-if="!fetching && users.length != 0">
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -80,7 +80,7 @@ export default Vue.extend({
 | 
			
		|||
 | 
			
		||||
	created() {
 | 
			
		||||
		// ウィンドウをウィンドウシステムに登録
 | 
			
		||||
		this.$root.$data.os.windows.add(this);
 | 
			
		||||
		(this as any).os.windows.add(this);
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	mounted() {
 | 
			
		||||
| 
						 | 
				
			
			@ -97,7 +97,7 @@ export default Vue.extend({
 | 
			
		|||
 | 
			
		||||
	destroyed() {
 | 
			
		||||
		// ウィンドウをウィンドウシステムから削除
 | 
			
		||||
		this.$root.$data.os.windows.remove(this);
 | 
			
		||||
		(this as any).os.windows.remove(this);
 | 
			
		||||
 | 
			
		||||
		window.removeEventListener('resize', this.onBrowserResize);
 | 
			
		||||
	},
 | 
			
		||||
| 
						 | 
				
			
			@ -191,7 +191,7 @@ export default Vue.extend({
 | 
			
		|||
		top() {
 | 
			
		||||
			let z = 0;
 | 
			
		||||
 | 
			
		||||
			this.$root.$data.os.windows.getAll().forEach(w => {
 | 
			
		||||
			(this as any).os.windows.getAll().forEach(w => {
 | 
			
		||||
				if (w == this) return;
 | 
			
		||||
				const m = w.$refs.main;
 | 
			
		||||
				const mz = Number(document.defaultView.getComputedStyle(m, null).zIndex);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,8 +26,8 @@ export default Vue.extend({
 | 
			
		|||
	mounted() {
 | 
			
		||||
		document.title = 'Misskey';
 | 
			
		||||
 | 
			
		||||
		this.connection = this.$root.$data.os.stream.getConnection();
 | 
			
		||||
		this.connectionId = this.$root.$data.os.stream.use();
 | 
			
		||||
		this.connection = (this as any).os.stream.getConnection();
 | 
			
		||||
		this.connectionId = (this as any).os.stream.use();
 | 
			
		||||
 | 
			
		||||
		this.connection.on('post', this.onStreamPost);
 | 
			
		||||
		document.addEventListener('visibilitychange', this.onVisibilitychange, false);
 | 
			
		||||
| 
						 | 
				
			
			@ -36,12 +36,12 @@ export default Vue.extend({
 | 
			
		|||
	},
 | 
			
		||||
	beforeDestroy() {
 | 
			
		||||
		this.connection.off('post', this.onStreamPost);
 | 
			
		||||
		this.$root.$data.os.stream.dispose(this.connectionId);
 | 
			
		||||
		(this as any).os.stream.dispose(this.connectionId);
 | 
			
		||||
		document.removeEventListener('visibilitychange', this.onVisibilitychange);
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
		onStreamPost(post) {
 | 
			
		||||
			if (document.hidden && post.user_id != this.$root.$data.os.i.id) {
 | 
			
		||||
			if (document.hidden && post.user_id != (this as any).os.i.id) {
 | 
			
		||||
				this.unreadCount++;
 | 
			
		||||
				document.title = `(${this.unreadCount}) ${getPostSummary(post)}`;
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
<template>
 | 
			
		||||
	<component v-bind:is="$root.$data.os.isSignedIn ? 'home' : 'welcome'"></component>
 | 
			
		||||
	<component v-bind:is="os.isSignedIn ? 'home' : 'welcome'"></component>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,7 +21,7 @@ export default Vue.extend({
 | 
			
		|||
 | 
			
		||||
		document.documentElement.style.background = '#fff';
 | 
			
		||||
 | 
			
		||||
		this.$root.$data.os.api('users/show', {
 | 
			
		||||
		(this as any).api('users/show', {
 | 
			
		||||
			username: this.username
 | 
			
		||||
		}).then(user => {
 | 
			
		||||
			this.fetching = false;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,7 +23,7 @@ export default Vue.extend({
 | 
			
		|||
	mounted() {
 | 
			
		||||
		Progress.start();
 | 
			
		||||
 | 
			
		||||
		this.$root.$data.os.api('posts/show', {
 | 
			
		||||
		(this as any).api('posts/show', {
 | 
			
		||||
			post_id: this.postId
 | 
			
		||||
		}).then(post => {
 | 
			
		||||
			this.fetching = false;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -44,7 +44,7 @@ export default Vue.extend({
 | 
			
		|||
		document.addEventListener('keydown', this.onDocumentKeydown);
 | 
			
		||||
		window.addEventListener('scroll', this.onScroll);
 | 
			
		||||
 | 
			
		||||
		this.$root.$data.os.api('posts/search', parse(this.query)).then(posts => {
 | 
			
		||||
		(this as any).api('posts/search', parse(this.query)).then(posts => {
 | 
			
		||||
			this.fetching = false;
 | 
			
		||||
			this.posts = posts;
 | 
			
		||||
		});
 | 
			
		||||
| 
						 | 
				
			
			@ -65,7 +65,7 @@ export default Vue.extend({
 | 
			
		|||
			if (this.moreFetching || this.fetching || this.posts.length == 0) return;
 | 
			
		||||
			this.offset += limit;
 | 
			
		||||
			this.moreFetching = true;
 | 
			
		||||
			return this.$root.$data.os.api('posts/search', Object.assign({}, parse(this.query), {
 | 
			
		||||
			return (this as any).api('posts/search', Object.assign({}, parse(this.query), {
 | 
			
		||||
				limit: limit,
 | 
			
		||||
				offset: this.offset
 | 
			
		||||
			})).then(posts => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,7 +22,7 @@ export default Vue.extend({
 | 
			
		|||
		};
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		this.$root.$data.os.api('users/followers', {
 | 
			
		||||
		(this as any).api('users/followers', {
 | 
			
		||||
			user_id: this.user.id,
 | 
			
		||||
			iknow: true,
 | 
			
		||||
			limit: 16
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,7 +27,7 @@ export default Vue.extend({
 | 
			
		|||
		};
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		this.$root.$data.os.api('users/get_frequently_replied_users', {
 | 
			
		||||
		(this as any).api('users/get_frequently_replied_users', {
 | 
			
		||||
			user_id: this.user.id,
 | 
			
		||||
			limit: 4
 | 
			
		||||
		}).then(docs => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -51,9 +51,9 @@ export default Vue.extend({
 | 
			
		|||
		},
 | 
			
		||||
 | 
			
		||||
		onBannerClick() {
 | 
			
		||||
			if (!this.$root.$data.os.isSignedIn || this.$root.$data.os.i.id != this.user.id) return;
 | 
			
		||||
			if (!(this as any).os.isSignedIn || (this as any).os.i.id != this.user.id) return;
 | 
			
		||||
 | 
			
		||||
			updateBanner(this.$root.$data.os.i, i => {
 | 
			
		||||
			updateBanner((this as any).os.i, i => {
 | 
			
		||||
				this.user.banner_url = i.banner_url;
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,7 +4,7 @@
 | 
			
		|||
		<div ref="left">
 | 
			
		||||
			<mk-user-profile :user="user"/>
 | 
			
		||||
			<mk-user-photos :user="user"/>
 | 
			
		||||
			<mk-user-followers-you-know v-if="$root.$data.os.isSignedIn && $root.$data.os.i.id != user.id" :user="user"/>
 | 
			
		||||
			<mk-user-followers-you-know v-if="os.isSignedIn && os.i.id != user.id" :user="user"/>
 | 
			
		||||
			<p>%i18n:desktop.tags.mk-user.last-used-at%: <b><mk-time :time="user.last_used_at"/></b></p>
 | 
			
		||||
		</div>
 | 
			
		||||
	</div>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,7 +23,7 @@ export default Vue.extend({
 | 
			
		|||
		};
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		this.$root.$data.os.api('users/posts', {
 | 
			
		||||
		(this as any).api('users/posts', {
 | 
			
		||||
			user_id: this.user.id,
 | 
			
		||||
			with_media: true,
 | 
			
		||||
			limit: 9
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
<template>
 | 
			
		||||
<div class="mk-user-profile">
 | 
			
		||||
	<div class="friend-form" v-if="$root.$data.os.isSignedIn && $root.$data.os.i.id != user.id">
 | 
			
		||||
	<div class="friend-form" v-if="os.isSignedIn && os.i.id != user.id">
 | 
			
		||||
		<mk-follow-button :user="user" size="big"/>
 | 
			
		||||
		<p class="followed" v-if="user.is_followed">%i18n:desktop.tags.mk-user.follows-you%</p>
 | 
			
		||||
		<p v-if="user.is_muted">%i18n:desktop.tags.mk-user.muted% <a @click="unmute">%i18n:desktop.tags.mk-user.unmute%</a></p>
 | 
			
		||||
| 
						 | 
				
			
			@ -35,7 +35,7 @@ export default Vue.extend({
 | 
			
		|||
	methods: {
 | 
			
		||||
		showFollowing() {
 | 
			
		||||
			document.body.appendChild(new MkUserFollowingWindow({
 | 
			
		||||
				parent: this,
 | 
			
		||||
 | 
			
		||||
				propsData: {
 | 
			
		||||
					user: this.user
 | 
			
		||||
				}
 | 
			
		||||
| 
						 | 
				
			
			@ -44,7 +44,7 @@ export default Vue.extend({
 | 
			
		|||
 | 
			
		||||
		showFollowers() {
 | 
			
		||||
			document.body.appendChild(new MkUserFollowersWindow({
 | 
			
		||||
				parent: this,
 | 
			
		||||
 | 
			
		||||
				propsData: {
 | 
			
		||||
					user: this.user
 | 
			
		||||
				}
 | 
			
		||||
| 
						 | 
				
			
			@ -52,7 +52,7 @@ export default Vue.extend({
 | 
			
		|||
		},
 | 
			
		||||
 | 
			
		||||
		mute() {
 | 
			
		||||
			this.$root.$data.os.api('mute/create', {
 | 
			
		||||
			(this as any).api('mute/create', {
 | 
			
		||||
				user_id: this.user.id
 | 
			
		||||
			}).then(() => {
 | 
			
		||||
				this.user.is_muted = true;
 | 
			
		||||
| 
						 | 
				
			
			@ -62,7 +62,7 @@ export default Vue.extend({
 | 
			
		|||
		},
 | 
			
		||||
 | 
			
		||||
		unmute() {
 | 
			
		||||
			this.$root.$data.os.api('mute/delete', {
 | 
			
		||||
			(this as any).api('mute/delete', {
 | 
			
		||||
				user_id: this.user.id
 | 
			
		||||
			}).then(() => {
 | 
			
		||||
				this.user.is_muted = false;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -29,7 +29,7 @@ export default Vue.extend({
 | 
			
		|||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		Progress.start();
 | 
			
		||||
		this.$root.$data.os.api('users/show', {
 | 
			
		||||
		(this as any).api('users/show', {
 | 
			
		||||
			username: this.username
 | 
			
		||||
		}).then(user => {
 | 
			
		||||
			this.fetching = false;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -78,37 +78,50 @@ if (localStorage.getItem('should-refresh') == 'true') {
 | 
			
		|||
 | 
			
		||||
type API = {
 | 
			
		||||
	chooseDriveFile: (opts: {
 | 
			
		||||
		title: string;
 | 
			
		||||
		currentFolder: any;
 | 
			
		||||
		multiple: boolean;
 | 
			
		||||
		title?: string;
 | 
			
		||||
		currentFolder?: any;
 | 
			
		||||
		multiple?: boolean;
 | 
			
		||||
	}) => Promise<any>;
 | 
			
		||||
 | 
			
		||||
	chooseDriveFolder: (opts: {
 | 
			
		||||
		title: string;
 | 
			
		||||
		currentFolder: any;
 | 
			
		||||
		title?: string;
 | 
			
		||||
		currentFolder?: any;
 | 
			
		||||
	}) => Promise<any>;
 | 
			
		||||
 | 
			
		||||
	dialog: (opts: {
 | 
			
		||||
		title: string;
 | 
			
		||||
		text: string;
 | 
			
		||||
		actions: Array<{
 | 
			
		||||
			text: string;
 | 
			
		||||
			id: string;
 | 
			
		||||
		}>;
 | 
			
		||||
	}) => Promise<string>;
 | 
			
		||||
 | 
			
		||||
	input: (opts: {
 | 
			
		||||
		title: string;
 | 
			
		||||
		placeholder?: string;
 | 
			
		||||
		default?: string;
 | 
			
		||||
	}) => Promise<string>;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// MiOSを初期化してコールバックする
 | 
			
		||||
export default (callback: (launch: (api: API) => Vue) => void, sw = false) => {
 | 
			
		||||
	const mios = new MiOS(sw);
 | 
			
		||||
	const os = new MiOS(sw);
 | 
			
		||||
 | 
			
		||||
	Vue.mixin({
 | 
			
		||||
		data: {
 | 
			
		||||
			$os: mios
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	mios.init(() => {
 | 
			
		||||
	os.init(() => {
 | 
			
		||||
		// アプリ基底要素マウント
 | 
			
		||||
		document.body.innerHTML = '<div id="app"></div>';
 | 
			
		||||
 | 
			
		||||
		const launch = (api: API) => {
 | 
			
		||||
			Vue.mixin({
 | 
			
		||||
				created() {
 | 
			
		||||
					(this as any).os = os;
 | 
			
		||||
					(this as any).api = os.api;
 | 
			
		||||
					(this as any).apis = api;
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			return new Vue({
 | 
			
		||||
				data: {
 | 
			
		||||
					os: mios,
 | 
			
		||||
					api: api
 | 
			
		||||
				},
 | 
			
		||||
				router: new VueRouter({
 | 
			
		||||
					mode: 'history'
 | 
			
		||||
				}),
 | 
			
		||||
| 
						 | 
				
			
			@ -124,7 +137,7 @@ export default (callback: (launch: (api: API) => Vue) => void, sw = false) => {
 | 
			
		|||
 | 
			
		||||
		// 更新チェック
 | 
			
		||||
		setTimeout(() => {
 | 
			
		||||
			checkForUpdate(mios);
 | 
			
		||||
			checkForUpdate(os);
 | 
			
		||||
		}, 3000);
 | 
			
		||||
	});
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -87,8 +87,8 @@ export default Vue.extend({
 | 
			
		|||
		}
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		this.connection = this.$root.$data.os.streams.driveStream.getConnection();
 | 
			
		||||
		this.connectionId = this.$root.$data.os.streams.driveStream.use();
 | 
			
		||||
		this.connection = (this as any).os.streams.driveStream.getConnection();
 | 
			
		||||
		this.connectionId = (this as any).os.streams.driveStream.use();
 | 
			
		||||
 | 
			
		||||
		this.connection.on('file_created', this.onStreamDriveFileCreated);
 | 
			
		||||
		this.connection.on('file_updated', this.onStreamDriveFileUpdated);
 | 
			
		||||
| 
						 | 
				
			
			@ -112,7 +112,7 @@ export default Vue.extend({
 | 
			
		|||
		this.connection.off('file_updated', this.onStreamDriveFileUpdated);
 | 
			
		||||
		this.connection.off('folder_created', this.onStreamDriveFolderCreated);
 | 
			
		||||
		this.connection.off('folder_updated', this.onStreamDriveFolderUpdated);
 | 
			
		||||
		this.$root.$data.os.streams.driveStream.dispose(this.connectionId);
 | 
			
		||||
		(this as any).os.streams.driveStream.dispose(this.connectionId);
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
		onStreamDriveFileCreated(file) {
 | 
			
		||||
| 
						 | 
				
			
			@ -158,7 +158,7 @@ export default Vue.extend({
 | 
			
		|||
 | 
			
		||||
			this.fetching = true;
 | 
			
		||||
 | 
			
		||||
			this.$root.$data.os.api('drive/folders/show', {
 | 
			
		||||
			(this as any).api('drive/folders/show', {
 | 
			
		||||
				folder_id: target
 | 
			
		||||
			}).then(folder => {
 | 
			
		||||
				this.folder = folder;
 | 
			
		||||
| 
						 | 
				
			
			@ -253,7 +253,7 @@ export default Vue.extend({
 | 
			
		|||
			const filesMax = 20;
 | 
			
		||||
 | 
			
		||||
			// フォルダ一覧取得
 | 
			
		||||
			this.$root.$data.os.api('drive/folders', {
 | 
			
		||||
			(this as any).api('drive/folders', {
 | 
			
		||||
				folder_id: this.folder ? this.folder.id : null,
 | 
			
		||||
				limit: foldersMax + 1
 | 
			
		||||
			}).then(folders => {
 | 
			
		||||
| 
						 | 
				
			
			@ -266,7 +266,7 @@ export default Vue.extend({
 | 
			
		|||
			});
 | 
			
		||||
 | 
			
		||||
			// ファイル一覧取得
 | 
			
		||||
			this.$root.$data.os.api('drive/files', {
 | 
			
		||||
			(this as any).api('drive/files', {
 | 
			
		||||
				folder_id: this.folder ? this.folder.id : null,
 | 
			
		||||
				limit: filesMax + 1
 | 
			
		||||
			}).then(files => {
 | 
			
		||||
| 
						 | 
				
			
			@ -296,7 +296,7 @@ export default Vue.extend({
 | 
			
		|||
 | 
			
		||||
			if (this.folder == null) {
 | 
			
		||||
				// Fetch addtional drive info
 | 
			
		||||
				this.$root.$data.os.api('drive').then(info => {
 | 
			
		||||
				(this as any).api('drive').then(info => {
 | 
			
		||||
					this.info = info;
 | 
			
		||||
				});
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -309,7 +309,7 @@ export default Vue.extend({
 | 
			
		|||
			const max = 30;
 | 
			
		||||
 | 
			
		||||
			// ファイル一覧取得
 | 
			
		||||
			this.$root.$data.os.api('drive/files', {
 | 
			
		||||
			(this as any).api('drive/files', {
 | 
			
		||||
				folder_id: this.folder ? this.folder.id : null,
 | 
			
		||||
				limit: max + 1,
 | 
			
		||||
				until_id: this.files[this.files.length - 1].id
 | 
			
		||||
| 
						 | 
				
			
			@ -348,7 +348,7 @@ export default Vue.extend({
 | 
			
		|||
 | 
			
		||||
			this.fetching = true;
 | 
			
		||||
 | 
			
		||||
			this.$root.$data.os.api('drive/files/show', {
 | 
			
		||||
			(this as any).api('drive/files/show', {
 | 
			
		||||
				file_id: file
 | 
			
		||||
			}).then(file => {
 | 
			
		||||
				this.fetching = false;
 | 
			
		||||
| 
						 | 
				
			
			@ -394,7 +394,7 @@ export default Vue.extend({
 | 
			
		|||
		createFolder() {
 | 
			
		||||
			const name = window.prompt('フォルダー名');
 | 
			
		||||
			if (name == null || name == '') return;
 | 
			
		||||
			this.$root.$data.os.api('drive/folders/create', {
 | 
			
		||||
			(this as any).api('drive/folders/create', {
 | 
			
		||||
				name: name,
 | 
			
		||||
				parent_id: this.folder ? this.folder.id : undefined
 | 
			
		||||
			}).then(folder => {
 | 
			
		||||
| 
						 | 
				
			
			@ -409,7 +409,7 @@ export default Vue.extend({
 | 
			
		|||
			}
 | 
			
		||||
			const name = window.prompt('フォルダー名', this.folder.name);
 | 
			
		||||
			if (name == null || name == '') return;
 | 
			
		||||
			this.$root.$data.os.api('drive/folders/update', {
 | 
			
		||||
			(this as any).api('drive/folders/update', {
 | 
			
		||||
				name: name,
 | 
			
		||||
				folder_id: this.folder.id
 | 
			
		||||
			}).then(folder => {
 | 
			
		||||
| 
						 | 
				
			
			@ -424,7 +424,7 @@ export default Vue.extend({
 | 
			
		|||
			}
 | 
			
		||||
			const dialog = riot.mount(document.body.appendChild(document.createElement('mk-drive-folder-selector')))[0];
 | 
			
		||||
			dialog.one('selected', folder => {
 | 
			
		||||
				this.$root.$data.os.api('drive/folders/update', {
 | 
			
		||||
				(this as any).api('drive/folders/update', {
 | 
			
		||||
					parent_id: folder ? folder.id : null,
 | 
			
		||||
					folder_id: this.folder.id
 | 
			
		||||
				}).then(folder => {
 | 
			
		||||
| 
						 | 
				
			
			@ -436,7 +436,7 @@ export default Vue.extend({
 | 
			
		|||
		urlUpload() {
 | 
			
		||||
			const url = window.prompt('アップロードしたいファイルのURL');
 | 
			
		||||
			if (url == null || url == '') return;
 | 
			
		||||
			this.$root.$data.os.api('drive/files/upload_from_url', {
 | 
			
		||||
			(this as any).api('drive/files/upload_from_url', {
 | 
			
		||||
				url: url,
 | 
			
		||||
				folder_id: this.folder ? this.folder.id : undefined
 | 
			
		||||
			});
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,8 +28,8 @@ export default Vue.extend({
 | 
			
		|||
		};
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		this.connection = this.$root.$data.os.stream.getConnection();
 | 
			
		||||
		this.connectionId = this.$root.$data.os.stream.use();
 | 
			
		||||
		this.connection = (this as any).os.stream.getConnection();
 | 
			
		||||
		this.connectionId = (this as any).os.stream.use();
 | 
			
		||||
 | 
			
		||||
		this.connection.on('follow', this.onFollow);
 | 
			
		||||
		this.connection.on('unfollow', this.onUnfollow);
 | 
			
		||||
| 
						 | 
				
			
			@ -37,7 +37,7 @@ export default Vue.extend({
 | 
			
		|||
	beforeDestroy() {
 | 
			
		||||
		this.connection.off('follow', this.onFollow);
 | 
			
		||||
		this.connection.off('unfollow', this.onUnfollow);
 | 
			
		||||
		this.$root.$data.os.stream.dispose(this.connectionId);
 | 
			
		||||
		(this as any).os.stream.dispose(this.connectionId);
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -56,7 +56,7 @@ export default Vue.extend({
 | 
			
		|||
		onClick() {
 | 
			
		||||
			this.wait = true;
 | 
			
		||||
			if (this.user.is_following) {
 | 
			
		||||
				this.$root.$data.os.api('following/delete', {
 | 
			
		||||
				(this as any).api('following/delete', {
 | 
			
		||||
					user_id: this.user.id
 | 
			
		||||
				}).then(() => {
 | 
			
		||||
					this.user.is_following = false;
 | 
			
		||||
| 
						 | 
				
			
			@ -66,7 +66,7 @@ export default Vue.extend({
 | 
			
		|||
					this.wait = false;
 | 
			
		||||
				});
 | 
			
		||||
			} else {
 | 
			
		||||
				this.$root.$data.os.api('following/create', {
 | 
			
		||||
				(this as any).api('following/create', {
 | 
			
		||||
					user_id: this.user.id
 | 
			
		||||
				}).then(() => {
 | 
			
		||||
					this.user.is_following = true;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -32,7 +32,7 @@ export default Vue.extend({
 | 
			
		|||
			this.fetching = true;
 | 
			
		||||
			this.users = [];
 | 
			
		||||
 | 
			
		||||
			this.$root.$data.os.api('users/recommendation', {
 | 
			
		||||
			(this as any).api('users/recommendation', {
 | 
			
		||||
				limit: this.limit,
 | 
			
		||||
				offset: this.limit * this.page
 | 
			
		||||
			}).then(users => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -42,14 +42,14 @@ export default Vue.extend({
 | 
			
		|||
		}
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		this.connection = this.$root.$data.os.stream.getConnection();
 | 
			
		||||
		this.connectionId = this.$root.$data.os.stream.use();
 | 
			
		||||
		this.connection = (this as any).os.stream.getConnection();
 | 
			
		||||
		this.connectionId = (this as any).os.stream.use();
 | 
			
		||||
 | 
			
		||||
		this.connection.on('notification', this.onNotification);
 | 
			
		||||
 | 
			
		||||
		const max = 10;
 | 
			
		||||
 | 
			
		||||
		this.$root.$data.os.api('i/notifications', {
 | 
			
		||||
		(this as any).api('i/notifications', {
 | 
			
		||||
			limit: max + 1
 | 
			
		||||
		}).then(notifications => {
 | 
			
		||||
			if (notifications.length == max + 1) {
 | 
			
		||||
| 
						 | 
				
			
			@ -63,7 +63,7 @@ export default Vue.extend({
 | 
			
		|||
	},
 | 
			
		||||
	beforeDestroy() {
 | 
			
		||||
		this.connection.off('notification', this.onNotification);
 | 
			
		||||
		this.$root.$data.os.stream.dispose(this.connectionId);
 | 
			
		||||
		(this as any).os.stream.dispose(this.connectionId);
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
		fetchMoreNotifications() {
 | 
			
		||||
| 
						 | 
				
			
			@ -71,7 +71,7 @@ export default Vue.extend({
 | 
			
		|||
 | 
			
		||||
			const max = 30;
 | 
			
		||||
 | 
			
		||||
			this.$root.$data.os.api('i/notifications', {
 | 
			
		||||
			(this as any).api('i/notifications', {
 | 
			
		||||
				limit: max + 1,
 | 
			
		||||
				until_id: this.notifications[this.notifications.length - 1].id
 | 
			
		||||
			}).then(notifications => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -33,7 +33,7 @@
 | 
			
		|||
			</div>
 | 
			
		||||
		</header>
 | 
			
		||||
		<div class="body">
 | 
			
		||||
			<mk-post-html v-if="p.ast" :ast="p.ast" :i="$root.$data.os.i"/>
 | 
			
		||||
			<mk-post-html v-if="p.ast" :ast="p.ast" :i="os.i"/>
 | 
			
		||||
			<mk-url-preview v-for="url in urls" :url="url" :key="url"/>
 | 
			
		||||
			<div class="media" v-if="p.media">
 | 
			
		||||
				<mk-images images={ p.media }/>
 | 
			
		||||
| 
						 | 
				
			
			@ -116,7 +116,7 @@ export default Vue.extend({
 | 
			
		|||
	mounted() {
 | 
			
		||||
		// Get replies
 | 
			
		||||
		if (!this.compact) {
 | 
			
		||||
			this.$root.$data.os.api('posts/replies', {
 | 
			
		||||
			(this as any).api('posts/replies', {
 | 
			
		||||
				post_id: this.p.id,
 | 
			
		||||
				limit: 8
 | 
			
		||||
			}).then(replies => {
 | 
			
		||||
| 
						 | 
				
			
			@ -129,7 +129,7 @@ export default Vue.extend({
 | 
			
		|||
			this.contextFetching = true;
 | 
			
		||||
 | 
			
		||||
			// Fetch context
 | 
			
		||||
			this.$root.$data.os.api('posts/context', {
 | 
			
		||||
			(this as any).api('posts/context', {
 | 
			
		||||
				post_id: this.p.reply_id
 | 
			
		||||
			}).then(context => {
 | 
			
		||||
				this.contextFetching = false;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -106,24 +106,24 @@ export default Vue.extend({
 | 
			
		|||
		}
 | 
			
		||||
	},
 | 
			
		||||
	created() {
 | 
			
		||||
		this.connection = this.$root.$data.os.stream.getConnection();
 | 
			
		||||
		this.connectionId = this.$root.$data.os.stream.use();
 | 
			
		||||
		this.connection = (this as any).os.stream.getConnection();
 | 
			
		||||
		this.connectionId = (this as any).os.stream.use();
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		this.capture(true);
 | 
			
		||||
 | 
			
		||||
		if (this.$root.$data.os.isSignedIn) {
 | 
			
		||||
		if ((this as any).os.isSignedIn) {
 | 
			
		||||
			this.connection.on('_connected_', this.onStreamConnected);
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	beforeDestroy() {
 | 
			
		||||
		this.decapture(true);
 | 
			
		||||
		this.connection.off('_connected_', this.onStreamConnected);
 | 
			
		||||
		this.$root.$data.os.stream.dispose(this.connectionId);
 | 
			
		||||
		(this as any).os.stream.dispose(this.connectionId);
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
		capture(withHandler = false) {
 | 
			
		||||
			if (this.$root.$data.os.isSignedIn) {
 | 
			
		||||
			if ((this as any).os.isSignedIn) {
 | 
			
		||||
				this.connection.send({
 | 
			
		||||
					type: 'capture',
 | 
			
		||||
					id: this.post.id
 | 
			
		||||
| 
						 | 
				
			
			@ -132,7 +132,7 @@ export default Vue.extend({
 | 
			
		|||
			}
 | 
			
		||||
		},
 | 
			
		||||
		decapture(withHandler = false) {
 | 
			
		||||
			if (this.$root.$data.os.isSignedIn) {
 | 
			
		||||
			if ((this as any).os.isSignedIn) {
 | 
			
		||||
				this.connection.send({
 | 
			
		||||
					type: 'decapture',
 | 
			
		||||
					id: this.post.id
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,7 +2,7 @@
 | 
			
		|||
<div class="mk-sub-post-content">
 | 
			
		||||
	<div class="body">
 | 
			
		||||
		<a class="reply" v-if="post.reply_id">%fa:reply%</a>
 | 
			
		||||
		<mk-post-html v-if="post.ast" :ast="post.ast" :i="$root.$data.os.i"/>
 | 
			
		||||
		<mk-post-html v-if="post.ast" :ast="post.ast" :i="os.i"/>
 | 
			
		||||
		<a class="quote" v-if="post.repost_id">RP: ...</a>
 | 
			
		||||
	</div>
 | 
			
		||||
	<details v-if="post.media">
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -37,12 +37,12 @@ export default Vue.extend({
 | 
			
		|||
	},
 | 
			
		||||
	computed: {
 | 
			
		||||
		alone(): boolean {
 | 
			
		||||
			return this.$root.$data.os.i.following_count == 0;
 | 
			
		||||
			return (this as any).os.i.following_count == 0;
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		this.connection = this.$root.$data.os.stream.getConnection();
 | 
			
		||||
		this.connectionId = this.$root.$data.os.stream.use();
 | 
			
		||||
		this.connection = (this as any).os.stream.getConnection();
 | 
			
		||||
		this.connectionId = (this as any).os.stream.use();
 | 
			
		||||
 | 
			
		||||
		this.connection.on('post', this.onPost);
 | 
			
		||||
		this.connection.on('follow', this.onChangeFollowing);
 | 
			
		||||
| 
						 | 
				
			
			@ -54,13 +54,13 @@ export default Vue.extend({
 | 
			
		|||
		this.connection.off('post', this.onPost);
 | 
			
		||||
		this.connection.off('follow', this.onChangeFollowing);
 | 
			
		||||
		this.connection.off('unfollow', this.onChangeFollowing);
 | 
			
		||||
		this.$root.$data.os.stream.dispose(this.connectionId);
 | 
			
		||||
		(this as any).os.stream.dispose(this.connectionId);
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
		fetch(cb?) {
 | 
			
		||||
			this.fetching = true;
 | 
			
		||||
 | 
			
		||||
			this.$root.$data.os.api('posts/timeline', {
 | 
			
		||||
			(this as any).api('posts/timeline', {
 | 
			
		||||
				until_date: this.date ? (this.date as any).getTime() : undefined
 | 
			
		||||
			}).then(posts => {
 | 
			
		||||
				this.fetching = false;
 | 
			
		||||
| 
						 | 
				
			
			@ -71,7 +71,7 @@ export default Vue.extend({
 | 
			
		|||
		more() {
 | 
			
		||||
			if (this.moreFetching || this.fetching || this.posts.length == 0) return;
 | 
			
		||||
			this.moreFetching = true;
 | 
			
		||||
			this.$root.$data.os.api('posts/timeline', {
 | 
			
		||||
			(this as any).api('posts/timeline', {
 | 
			
		||||
				until_id: this.posts[this.posts.length - 1].id
 | 
			
		||||
			}).then(posts => {
 | 
			
		||||
				this.moreFetching = false;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,9 +31,9 @@ export default Vue.extend({
 | 
			
		|||
		};
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		if (this.$root.$data.os.isSignedIn) {
 | 
			
		||||
			this.connection = this.$root.$data.os.stream.getConnection();
 | 
			
		||||
			this.connectionId = this.$root.$data.os.stream.use();
 | 
			
		||||
		if ((this as any).os.isSignedIn) {
 | 
			
		||||
			this.connection = (this as any).os.stream.getConnection();
 | 
			
		||||
			this.connectionId = (this as any).os.stream.use();
 | 
			
		||||
 | 
			
		||||
			this.connection.on('read_all_notifications', this.onReadAllNotifications);
 | 
			
		||||
			this.connection.on('unread_notification', this.onUnreadNotification);
 | 
			
		||||
| 
						 | 
				
			
			@ -41,14 +41,14 @@ export default Vue.extend({
 | 
			
		|||
			this.connection.on('unread_messaging_message', this.onUnreadMessagingMessage);
 | 
			
		||||
 | 
			
		||||
			// Fetch count of unread notifications
 | 
			
		||||
			this.$root.$data.os.api('notifications/get_unread_count').then(res => {
 | 
			
		||||
			(this as any).api('notifications/get_unread_count').then(res => {
 | 
			
		||||
				if (res.count > 0) {
 | 
			
		||||
					this.hasUnreadNotifications = true;
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			// Fetch count of unread messaging messages
 | 
			
		||||
			this.$root.$data.os.api('messaging/unread').then(res => {
 | 
			
		||||
			(this as any).api('messaging/unread').then(res => {
 | 
			
		||||
				if (res.count > 0) {
 | 
			
		||||
					this.hasUnreadMessagingMessages = true;
 | 
			
		||||
				}
 | 
			
		||||
| 
						 | 
				
			
			@ -56,12 +56,12 @@ export default Vue.extend({
 | 
			
		|||
		}
 | 
			
		||||
	},
 | 
			
		||||
	beforeDestroy() {
 | 
			
		||||
		if (this.$root.$data.os.isSignedIn) {
 | 
			
		||||
		if ((this as any).os.isSignedIn) {
 | 
			
		||||
			this.connection.off('read_all_notifications', this.onReadAllNotifications);
 | 
			
		||||
			this.connection.off('unread_notification', this.onUnreadNotification);
 | 
			
		||||
			this.connection.off('read_all_messaging_messages', this.onReadAllMessagingMessages);
 | 
			
		||||
			this.connection.off('unread_messaging_message', this.onUnreadMessagingMessage);
 | 
			
		||||
			this.$root.$data.os.stream.dispose(this.connectionId);
 | 
			
		||||
			(this as any).os.stream.dispose(this.connectionId);
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,7 +2,7 @@
 | 
			
		|||
<div class="mk-ui-nav" :style="{ display: isOpen ? 'block' : 'none' }">
 | 
			
		||||
	<div class="backdrop" @click="parent.toggleDrawer"></div>
 | 
			
		||||
	<div class="body">
 | 
			
		||||
		<a class="me" v-if="$root.$data.os.isSignedIn" href={ '/' + I.username }>
 | 
			
		||||
		<a class="me" v-if="os.isSignedIn" href={ '/' + I.username }>
 | 
			
		||||
			<img class="avatar" src={ I.avatar_url + '?thumbnail&size=128' } alt="avatar"/>
 | 
			
		||||
			<p class="name">{ I.name }</p>
 | 
			
		||||
		</a>
 | 
			
		||||
| 
						 | 
				
			
			@ -41,9 +41,9 @@ export default Vue.extend({
 | 
			
		|||
		};
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		if (this.$root.$data.os.isSignedIn) {
 | 
			
		||||
			this.connection = this.$root.$data.os.stream.getConnection();
 | 
			
		||||
			this.connectionId = this.$root.$data.os.stream.use();
 | 
			
		||||
		if ((this as any).os.isSignedIn) {
 | 
			
		||||
			this.connection = (this as any).os.stream.getConnection();
 | 
			
		||||
			this.connectionId = (this as any).os.stream.use();
 | 
			
		||||
 | 
			
		||||
			this.connection.on('read_all_notifications', this.onReadAllNotifications);
 | 
			
		||||
			this.connection.on('unread_notification', this.onUnreadNotification);
 | 
			
		||||
| 
						 | 
				
			
			@ -51,14 +51,14 @@ export default Vue.extend({
 | 
			
		|||
			this.connection.on('unread_messaging_message', this.onUnreadMessagingMessage);
 | 
			
		||||
 | 
			
		||||
			// Fetch count of unread notifications
 | 
			
		||||
			this.$root.$data.os.api('notifications/get_unread_count').then(res => {
 | 
			
		||||
			(this as any).api('notifications/get_unread_count').then(res => {
 | 
			
		||||
				if (res.count > 0) {
 | 
			
		||||
					this.hasUnreadNotifications = true;
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			// Fetch count of unread messaging messages
 | 
			
		||||
			this.$root.$data.os.api('messaging/unread').then(res => {
 | 
			
		||||
			(this as any).api('messaging/unread').then(res => {
 | 
			
		||||
				if (res.count > 0) {
 | 
			
		||||
					this.hasUnreadMessagingMessages = true;
 | 
			
		||||
				}
 | 
			
		||||
| 
						 | 
				
			
			@ -66,12 +66,12 @@ export default Vue.extend({
 | 
			
		|||
		}
 | 
			
		||||
	},
 | 
			
		||||
	beforeDestroy() {
 | 
			
		||||
		if (this.$root.$data.os.isSignedIn) {
 | 
			
		||||
		if ((this as any).os.isSignedIn) {
 | 
			
		||||
			this.connection.off('read_all_notifications', this.onReadAllNotifications);
 | 
			
		||||
			this.connection.off('unread_notification', this.onUnreadNotification);
 | 
			
		||||
			this.connection.off('read_all_messaging_messages', this.onReadAllMessagingMessages);
 | 
			
		||||
			this.connection.off('unread_messaging_message', this.onUnreadMessagingMessage);
 | 
			
		||||
			this.$root.$data.os.stream.dispose(this.connectionId);
 | 
			
		||||
			(this as any).os.stream.dispose(this.connectionId);
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,7 +7,7 @@
 | 
			
		|||
	<div class="content">
 | 
			
		||||
		<slot></slot>
 | 
			
		||||
	</div>
 | 
			
		||||
	<mk-stream-indicator v-if="$root.$data.os.isSignedIn"/>
 | 
			
		||||
	<mk-stream-indicator v-if="os.isSignedIn"/>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -23,17 +23,17 @@ export default Vue.extend({
 | 
			
		|||
		};
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		if (this.$root.$data.os.isSignedIn) {
 | 
			
		||||
			this.connection = this.$root.$data.os.stream.getConnection();
 | 
			
		||||
			this.connectionId = this.$root.$data.os.stream.use();
 | 
			
		||||
		if ((this as any).os.isSignedIn) {
 | 
			
		||||
			this.connection = (this as any).os.stream.getConnection();
 | 
			
		||||
			this.connectionId = (this as any).os.stream.use();
 | 
			
		||||
 | 
			
		||||
			this.connection.on('notification', this.onNotification);
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	beforeDestroy() {
 | 
			
		||||
		if (this.$root.$data.os.isSignedIn) {
 | 
			
		||||
		if ((this as any).os.isSignedIn) {
 | 
			
		||||
			this.connection.off('notification', this.onNotification);
 | 
			
		||||
			this.$root.$data.os.stream.dispose(this.connectionId);
 | 
			
		||||
			(this as any).os.stream.dispose(this.connectionId);
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,7 +14,7 @@ export default Vue.extend({
 | 
			
		|||
	props: ['user'],
 | 
			
		||||
	methods: {
 | 
			
		||||
		fetch(iknow, limit, cursor, cb) {
 | 
			
		||||
			this.$root.$data.os.api('users/followers', {
 | 
			
		||||
			(this as any).api('users/followers', {
 | 
			
		||||
				user_id: this.user.id,
 | 
			
		||||
				iknow: iknow,
 | 
			
		||||
				limit: limit,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,7 +14,7 @@ export default Vue.extend({
 | 
			
		|||
	props: ['user'],
 | 
			
		||||
	methods: {
 | 
			
		||||
		fetch(iknow, limit, cursor, cb) {
 | 
			
		||||
			this.$root.$data.os.api('users/following', {
 | 
			
		||||
			(this as any).api('users/following', {
 | 
			
		||||
				user_id: this.user.id,
 | 
			
		||||
				iknow: iknow,
 | 
			
		||||
				limit: limit,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,7 +27,7 @@ export default Vue.extend({
 | 
			
		|||
		};
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		this.$root.$data.os.api('users/posts', {
 | 
			
		||||
		(this as any).api('users/posts', {
 | 
			
		||||
			user_id: this.user.id,
 | 
			
		||||
			with_media: this.withMedia
 | 
			
		||||
		}).then(posts => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,7 +2,7 @@
 | 
			
		|||
<div class="mk-users-list">
 | 
			
		||||
	<nav>
 | 
			
		||||
		<span :data-is-active="mode == 'all'" @click="mode = 'all'">%i18n:mobile.tags.mk-users-list.all%<span>{{ count }}</span></span>
 | 
			
		||||
		<span v-if="$root.$data.os.isSignedIn && youKnowCount" :data-is-active="mode == 'iknow'" @click="mode = 'iknow'">%i18n:mobile.tags.mk-users-list.known%<span>{{ youKnowCount }}</span></span>
 | 
			
		||||
		<span v-if="os.isSignedIn && youKnowCount" :data-is-active="mode == 'iknow'" @click="mode = 'iknow'">%i18n:mobile.tags.mk-users-list.known%<span>{{ youKnowCount }}</span></span>
 | 
			
		||||
	</nav>
 | 
			
		||||
	<div class="users" v-if="!fetching && users.length != 0">
 | 
			
		||||
		<mk-user-preview v-for="u in users" :user="u" :key="u.id"/>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,7 +23,7 @@ export default Vue.extend({
 | 
			
		|||
	mounted() {
 | 
			
		||||
		Progress.start();
 | 
			
		||||
 | 
			
		||||
		this.$root.$data.os.api('users/show', {
 | 
			
		||||
		(this as any).api('users/show', {
 | 
			
		||||
			username: this.username
 | 
			
		||||
		}).then(user => {
 | 
			
		||||
			this.fetching = false;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,7 +23,7 @@ export default Vue.extend({
 | 
			
		|||
	mounted() {
 | 
			
		||||
		Progress.start();
 | 
			
		||||
 | 
			
		||||
		this.$root.$data.os.api('users/show', {
 | 
			
		||||
		(this as any).api('users/show', {
 | 
			
		||||
			username: this.username
 | 
			
		||||
		}).then(user => {
 | 
			
		||||
			this.fetching = false;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,8 +23,8 @@ export default Vue.extend({
 | 
			
		|||
		document.title = 'Misskey';
 | 
			
		||||
		document.documentElement.style.background = '#313a42';
 | 
			
		||||
 | 
			
		||||
		this.connection = this.$root.$data.os.stream.getConnection();
 | 
			
		||||
		this.connectionId = this.$root.$data.os.stream.use();
 | 
			
		||||
		this.connection = (this as any).os.stream.getConnection();
 | 
			
		||||
		this.connectionId = (this as any).os.stream.use();
 | 
			
		||||
 | 
			
		||||
		this.connection.on('post', this.onStreamPost);
 | 
			
		||||
		document.addEventListener('visibilitychange', this.onVisibilitychange, false);
 | 
			
		||||
| 
						 | 
				
			
			@ -33,7 +33,7 @@ export default Vue.extend({
 | 
			
		|||
	},
 | 
			
		||||
	beforeDestroy() {
 | 
			
		||||
		this.connection.off('post', this.onStreamPost);
 | 
			
		||||
		this.$root.$data.os.stream.dispose(this.connectionId);
 | 
			
		||||
		(this as any).os.stream.dispose(this.connectionId);
 | 
			
		||||
		document.removeEventListener('visibilitychange', this.onVisibilitychange);
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
| 
						 | 
				
			
			@ -44,7 +44,7 @@ export default Vue.extend({
 | 
			
		|||
			Progress.done();
 | 
			
		||||
		},
 | 
			
		||||
		onStreamPost(post) {
 | 
			
		||||
			if (document.hidden && post.user_id !== this.$root.$data.os.i.id) {
 | 
			
		||||
			if (document.hidden && post.user_id !== (this as any).os.i.id) {
 | 
			
		||||
				this.unreadCount++;
 | 
			
		||||
				document.title = `(${this.unreadCount}) ${getPostSummary(post)}`;
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,7 +21,7 @@ export default Vue.extend({
 | 
			
		|||
			const ok = window.confirm('%i18n:mobile.tags.mk-notifications-page.read-all%');
 | 
			
		||||
			if (!ok) return;
 | 
			
		||||
 | 
			
		||||
			this.$root.$data.os.api('notifications/mark_as_read_all');
 | 
			
		||||
			(this as any).api('notifications/mark_as_read_all');
 | 
			
		||||
		},
 | 
			
		||||
		onFetched() {
 | 
			
		||||
			Progress.done();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -29,7 +29,7 @@ export default Vue.extend({
 | 
			
		|||
 | 
			
		||||
		Progress.start();
 | 
			
		||||
 | 
			
		||||
		this.$root.$data.os.api('posts/show', {
 | 
			
		||||
		(this as any).api('posts/show', {
 | 
			
		||||
			post_id: this.postId
 | 
			
		||||
		}).then(post => {
 | 
			
		||||
			this.fetching = false;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -35,7 +35,7 @@ export default Vue.extend({
 | 
			
		|||
 | 
			
		||||
		Progress.start();
 | 
			
		||||
 | 
			
		||||
		this.$root.$data.os.api('posts/search', Object.assign({}, parse(this.query), {
 | 
			
		||||
		(this as any).api('posts/search', Object.assign({}, parse(this.query), {
 | 
			
		||||
			limit: limit
 | 
			
		||||
		})).then(posts => {
 | 
			
		||||
			this.posts = posts;
 | 
			
		||||
| 
						 | 
				
			
			@ -46,7 +46,7 @@ export default Vue.extend({
 | 
			
		|||
	methods: {
 | 
			
		||||
		more() {
 | 
			
		||||
			this.offset += limit;
 | 
			
		||||
			return this.$root.$data.os.api('posts/search', Object.assign({}, parse(this.query), {
 | 
			
		||||
			return (this as any).api('posts/search', Object.assign({}, parse(this.query), {
 | 
			
		||||
				limit: limit,
 | 
			
		||||
				offset: this.offset
 | 
			
		||||
			}));
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,7 +9,7 @@
 | 
			
		|||
					<a class="avatar">
 | 
			
		||||
						<img :src="`${user.avatar_url}?thumbnail&size=200`" alt="avatar"/>
 | 
			
		||||
					</a>
 | 
			
		||||
					<mk-follow-button v-if="$root.$data.os.isSignedIn && $root.$data.os.i.id != user.id" :user="user"/>
 | 
			
		||||
					<mk-follow-button v-if="os.isSignedIn && os.i.id != user.id" :user="user"/>
 | 
			
		||||
				</div>
 | 
			
		||||
				<div class="title">
 | 
			
		||||
					<h1>{{ user.name }}</h1>
 | 
			
		||||
| 
						 | 
				
			
			@ -85,7 +85,7 @@ export default Vue.extend({
 | 
			
		|||
		document.documentElement.style.background = '#313a42';
 | 
			
		||||
		Progress.start();
 | 
			
		||||
 | 
			
		||||
		this.$root.$data.os.api('users/show', {
 | 
			
		||||
		(this as any).api('users/show', {
 | 
			
		||||
			username: this.username
 | 
			
		||||
		}).then(user => {
 | 
			
		||||
			this.fetching = false;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,7 +21,7 @@ export default Vue.extend({
 | 
			
		|||
		};
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		this.$root.$data.os.api('users/followers', {
 | 
			
		||||
		(this as any).api('users/followers', {
 | 
			
		||||
			user_id: this.user.id,
 | 
			
		||||
			iknow: true,
 | 
			
		||||
			limit: 30
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,7 +28,7 @@ export default Vue.extend({
 | 
			
		|||
		};
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		this.$root.$data.os.api('aggregation/users/activity', {
 | 
			
		||||
		(this as any).api('aggregation/users/activity', {
 | 
			
		||||
			user_id: this.user.id,
 | 
			
		||||
			limit: 30
 | 
			
		||||
		}).then(data => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,7 +19,7 @@ export default Vue.extend({
 | 
			
		|||
		};
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		this.$root.$data.os.api('users/get_frequently_replied_users', {
 | 
			
		||||
		(this as any).api('users/get_frequently_replied_users', {
 | 
			
		||||
			user_id: this.user.id
 | 
			
		||||
		}).then(res => {
 | 
			
		||||
			this.fetching = false;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
Some files were not shown because too many files have changed in this diff Show more
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue