Merge branch 'develop' into fix-msg-room
This commit is contained in:
		
						commit
						dd0d86cbb6
					
				
					 6 changed files with 109 additions and 152 deletions
				
			
		| 
						 | 
				
			
			@ -20,52 +20,32 @@
 | 
			
		|||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import { defineComponent } from 'vue';
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { watch } from 'vue';
 | 
			
		||||
import * as misskey from 'misskey-js';
 | 
			
		||||
import { getStaticImageUrl } from '@/scripts/get-static-image-url';
 | 
			
		||||
import ImgWithBlurhash from '@/components/img-with-blurhash.vue';
 | 
			
		||||
import * as os from '@/os';
 | 
			
		||||
import { defaultStore } from '@/store';
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
	components: {
 | 
			
		||||
		ImgWithBlurhash
 | 
			
		||||
	},
 | 
			
		||||
	props: {
 | 
			
		||||
		image: {
 | 
			
		||||
			type: Object,
 | 
			
		||||
			required: true
 | 
			
		||||
		},
 | 
			
		||||
		raw: {
 | 
			
		||||
			default: false
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			hide: true,
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
	computed: {
 | 
			
		||||
		url(): any {
 | 
			
		||||
			let url = this.$store.state.disableShowingAnimatedImages
 | 
			
		||||
				? getStaticImageUrl(this.image.thumbnailUrl)
 | 
			
		||||
				: this.image.thumbnailUrl;
 | 
			
		||||
const props = defineProps<{
 | 
			
		||||
	image: misskey.entities.DriveFile;
 | 
			
		||||
	raw?: boolean;
 | 
			
		||||
}>();
 | 
			
		||||
 | 
			
		||||
			if (this.raw || this.$store.state.loadRawImages) {
 | 
			
		||||
				url = this.image.url;
 | 
			
		||||
			}
 | 
			
		||||
let hide = $ref(true);
 | 
			
		||||
 | 
			
		||||
			return url;
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	created() {
 | 
			
		||||
		// Plugin:register_note_view_interruptor を使って書き換えられる可能性があるためwatchする
 | 
			
		||||
		this.$watch('image', () => {
 | 
			
		||||
			this.hide = (this.$store.state.nsfw === 'force') ? true : this.image.isSensitive && (this.$store.state.nsfw !== 'ignore');
 | 
			
		||||
		}, {
 | 
			
		||||
			deep: true,
 | 
			
		||||
			immediate: true,
 | 
			
		||||
		});
 | 
			
		||||
	},
 | 
			
		||||
const url = (props.raw || defaultStore.state.loadRawImages)
 | 
			
		||||
	? props.image.url
 | 
			
		||||
	: defaultStore.state.disableShowingAnimatedImages
 | 
			
		||||
			? getStaticImageUrl(props.image.thumbnailUrl)
 | 
			
		||||
			: props.image.thumbnailUrl;
 | 
			
		||||
 | 
			
		||||
// Plugin:register_note_view_interruptor を使って書き換えられる可能性があるためwatchする
 | 
			
		||||
watch(() => props.image, () => {
 | 
			
		||||
	hide = (defaultStore.state.nsfw === 'force') ? true : props.image.isSensitive && (defaultStore.state.nsfw !== 'ignore');
 | 
			
		||||
}, {
 | 
			
		||||
	deep: true,
 | 
			
		||||
	immediate: true,
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,8 +12,8 @@
 | 
			
		|||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import { defineComponent, onMounted, PropType, ref } from 'vue';
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { onMounted, ref } from 'vue';
 | 
			
		||||
import * as misskey from 'misskey-js';
 | 
			
		||||
import PhotoSwipeLightbox from 'photoswipe/dist/photoswipe-lightbox.esm.js';
 | 
			
		||||
import PhotoSwipe from 'photoswipe/dist/photoswipe.esm.js';
 | 
			
		||||
| 
						 | 
				
			
			@ -25,98 +25,80 @@ import * as os from '@/os';
 | 
			
		|||
import { FILE_TYPE_BROWSERSAFE } from '@/const';
 | 
			
		||||
import { defaultStore } from '@/store';
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
	components: {
 | 
			
		||||
		XBanner,
 | 
			
		||||
		XImage,
 | 
			
		||||
		XVideo,
 | 
			
		||||
	},
 | 
			
		||||
	props: {
 | 
			
		||||
		mediaList: {
 | 
			
		||||
			type: Array as PropType<misskey.entities.DriveFile[]>,
 | 
			
		||||
			required: true,
 | 
			
		||||
		},
 | 
			
		||||
		raw: {
 | 
			
		||||
			default: false
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	setup(props) {
 | 
			
		||||
		const gallery = ref(null);
 | 
			
		||||
const props = defineProps<{
 | 
			
		||||
	mediaList: misskey.entities.DriveFile[];
 | 
			
		||||
	raw?: boolean;
 | 
			
		||||
}>();
 | 
			
		||||
 | 
			
		||||
		onMounted(() => {
 | 
			
		||||
			const lightbox = new PhotoSwipeLightbox({
 | 
			
		||||
				dataSource: props.mediaList
 | 
			
		||||
					.filter(media => {
 | 
			
		||||
						if (media.type === 'image/svg+xml') return true; // svgのwebpublicはpngなのでtrue
 | 
			
		||||
						return media.type.startsWith('image') && FILE_TYPE_BROWSERSAFE.includes(media.type);
 | 
			
		||||
					})
 | 
			
		||||
					.map(media => {
 | 
			
		||||
						const item = {
 | 
			
		||||
							src: media.url,
 | 
			
		||||
							w: media.properties.width,
 | 
			
		||||
							h: media.properties.height,
 | 
			
		||||
							alt: media.name,
 | 
			
		||||
						};
 | 
			
		||||
						if (media.properties.orientation != null && media.properties.orientation >= 5) {
 | 
			
		||||
							[item.w, item.h] = [item.h, item.w];
 | 
			
		||||
						}
 | 
			
		||||
						return item;
 | 
			
		||||
					}),
 | 
			
		||||
				gallery: gallery.value,
 | 
			
		||||
				children: '.image',
 | 
			
		||||
				thumbSelector: '.image',
 | 
			
		||||
				loop: false,
 | 
			
		||||
				padding: window.innerWidth > 500 ? {
 | 
			
		||||
					top: 32,
 | 
			
		||||
					bottom: 32,
 | 
			
		||||
					left: 32,
 | 
			
		||||
					right: 32,
 | 
			
		||||
				} : {
 | 
			
		||||
					top: 0,
 | 
			
		||||
					bottom: 0,
 | 
			
		||||
					left: 0,
 | 
			
		||||
					right: 0,
 | 
			
		||||
				},
 | 
			
		||||
				imageClickAction: 'close',
 | 
			
		||||
				tapAction: 'toggle-controls',
 | 
			
		||||
				pswpModule: PhotoSwipe,
 | 
			
		||||
			});
 | 
			
		||||
const gallery = ref(null);
 | 
			
		||||
const pswpZIndex = os.claimZIndex('middle');
 | 
			
		||||
 | 
			
		||||
			lightbox.on('itemData', (e) => {
 | 
			
		||||
				const { itemData } = e;
 | 
			
		||||
 | 
			
		||||
				// element is children
 | 
			
		||||
				const { element } = itemData;
 | 
			
		||||
 | 
			
		||||
				const id = element.dataset.id;
 | 
			
		||||
				const file = props.mediaList.find(media => media.id === id);
 | 
			
		||||
 | 
			
		||||
				itemData.src = file.url;
 | 
			
		||||
				itemData.w = Number(file.properties.width);
 | 
			
		||||
				itemData.h = Number(file.properties.height);
 | 
			
		||||
				if (file.properties.orientation != null && file.properties.orientation >= 5) {
 | 
			
		||||
					[itemData.w, itemData.h] = [itemData.h, itemData.w];
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
	const lightbox = new PhotoSwipeLightbox({
 | 
			
		||||
		dataSource: props.mediaList
 | 
			
		||||
			.filter(media => {
 | 
			
		||||
				if (media.type === 'image/svg+xml') return true; // svgのwebpublicはpngなのでtrue
 | 
			
		||||
				return media.type.startsWith('image') && FILE_TYPE_BROWSERSAFE.includes(media.type);
 | 
			
		||||
			})
 | 
			
		||||
			.map(media => {
 | 
			
		||||
				const item = {
 | 
			
		||||
					src: media.url,
 | 
			
		||||
					w: media.properties.width,
 | 
			
		||||
					h: media.properties.height,
 | 
			
		||||
					alt: media.name,
 | 
			
		||||
				};
 | 
			
		||||
				if (media.properties.orientation != null && media.properties.orientation >= 5) {
 | 
			
		||||
					[item.w, item.h] = [item.h, item.w];
 | 
			
		||||
				}
 | 
			
		||||
				itemData.msrc = file.thumbnailUrl;
 | 
			
		||||
				itemData.thumbCropped = true;
 | 
			
		||||
			});
 | 
			
		||||
				return item;
 | 
			
		||||
			}),
 | 
			
		||||
		gallery: gallery.value,
 | 
			
		||||
		children: '.image',
 | 
			
		||||
		thumbSelector: '.image',
 | 
			
		||||
		loop: false,
 | 
			
		||||
		padding: window.innerWidth > 500 ? {
 | 
			
		||||
			top: 32,
 | 
			
		||||
			bottom: 32,
 | 
			
		||||
			left: 32,
 | 
			
		||||
			right: 32,
 | 
			
		||||
		} : {
 | 
			
		||||
			top: 0,
 | 
			
		||||
			bottom: 0,
 | 
			
		||||
			left: 0,
 | 
			
		||||
			right: 0,
 | 
			
		||||
		},
 | 
			
		||||
		imageClickAction: 'close',
 | 
			
		||||
		tapAction: 'toggle-controls',
 | 
			
		||||
		pswpModule: PhotoSwipe,
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
			lightbox.init();
 | 
			
		||||
		});
 | 
			
		||||
	lightbox.on('itemData', (ev) => {
 | 
			
		||||
		const { itemData } = ev;
 | 
			
		||||
 | 
			
		||||
		const previewable = (file: misskey.entities.DriveFile): boolean => {
 | 
			
		||||
			if (file.type === 'image/svg+xml') return true; // svgのwebpublic/thumbnailはpngなのでtrue
 | 
			
		||||
			// FILE_TYPE_BROWSERSAFEに適合しないものはブラウザで表示するのに不適切
 | 
			
		||||
			return (file.type.startsWith('video') || file.type.startsWith('image')) && FILE_TYPE_BROWSERSAFE.includes(file.type);
 | 
			
		||||
		};
 | 
			
		||||
		// element is children
 | 
			
		||||
		const { element } = itemData;
 | 
			
		||||
 | 
			
		||||
		return {
 | 
			
		||||
			previewable,
 | 
			
		||||
			gallery,
 | 
			
		||||
			pswpZIndex: os.claimZIndex('middle'),
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
		const id = element.dataset.id;
 | 
			
		||||
		const file = props.mediaList.find(media => media.id === id);
 | 
			
		||||
 | 
			
		||||
		itemData.src = file.url;
 | 
			
		||||
		itemData.w = Number(file.properties.width);
 | 
			
		||||
		itemData.h = Number(file.properties.height);
 | 
			
		||||
		if (file.properties.orientation != null && file.properties.orientation >= 5) {
 | 
			
		||||
			[itemData.w, itemData.h] = [itemData.h, itemData.w];
 | 
			
		||||
		}
 | 
			
		||||
		itemData.msrc = file.thumbnailUrl;
 | 
			
		||||
		itemData.thumbCropped = true;
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	lightbox.init();
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const previewable = (file: misskey.entities.DriveFile): boolean => {
 | 
			
		||||
	if (file.type === 'image/svg+xml') return true; // svgのwebpublic/thumbnailはpngなのでtrue
 | 
			
		||||
	// FILE_TYPE_BROWSERSAFEに適合しないものはブラウザで表示するのに不適切
 | 
			
		||||
	return (file.type.startsWith('video') || file.type.startsWith('image')) && FILE_TYPE_BROWSERSAFE.includes(file.type);
 | 
			
		||||
};
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,7 +19,7 @@ import MkPagination from '@/components/ui/pagination.vue';
 | 
			
		|||
import MkButton from '@/components/ui/button.vue';
 | 
			
		||||
import * as os from '@/os';
 | 
			
		||||
import * as symbols from '@/symbols';
 | 
			
		||||
import i18n from '@/components/global/i18n';
 | 
			
		||||
import { i18n } from '@/i18n';
 | 
			
		||||
 | 
			
		||||
const pagination = {
 | 
			
		||||
	endpoint: 'clips/list' as const,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,51 +19,41 @@ export function useNoteCapture(props: {
 | 
			
		|||
			case 'reacted': {
 | 
			
		||||
				const reaction = body.reaction;
 | 
			
		||||
 | 
			
		||||
				const updated = JSON.parse(JSON.stringify(appearNote.value));
 | 
			
		||||
 | 
			
		||||
				if (body.emoji) {
 | 
			
		||||
					const emojis = appearNote.value.emojis || [];
 | 
			
		||||
					if (!emojis.includes(body.emoji)) {
 | 
			
		||||
						updated.emojis = [...emojis, body.emoji];
 | 
			
		||||
						appearNote.value.emojis = [...emojis, body.emoji];
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				// TODO: reactionsプロパティがない場合ってあったっけ? なければ || {} は消せる
 | 
			
		||||
				const currentCount = (appearNote.value.reactions || {})[reaction] || 0;
 | 
			
		||||
 | 
			
		||||
				updated.reactions[reaction] = currentCount + 1;
 | 
			
		||||
				appearNote.value.reactions[reaction] = currentCount + 1;
 | 
			
		||||
 | 
			
		||||
				if ($i && (body.userId === $i.id)) {
 | 
			
		||||
					updated.myReaction = reaction;
 | 
			
		||||
					appearNote.value.myReaction = reaction;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				appearNote.value = updated;
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			case 'unreacted': {
 | 
			
		||||
				const reaction = body.reaction;
 | 
			
		||||
 | 
			
		||||
				const updated = JSON.parse(JSON.stringify(appearNote.value));
 | 
			
		||||
 | 
			
		||||
				// TODO: reactionsプロパティがない場合ってあったっけ? なければ || {} は消せる
 | 
			
		||||
				const currentCount = (appearNote.value.reactions || {})[reaction] || 0;
 | 
			
		||||
 | 
			
		||||
				updated.reactions[reaction] = Math.max(0, currentCount - 1);
 | 
			
		||||
				appearNote.value.reactions[reaction] = Math.max(0, currentCount - 1);
 | 
			
		||||
 | 
			
		||||
				if ($i && (body.userId === $i.id)) {
 | 
			
		||||
					updated.myReaction = null;
 | 
			
		||||
					appearNote.value.myReaction = null;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				appearNote.value = updated;
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			case 'pollVoted': {
 | 
			
		||||
				const choice = body.choice;
 | 
			
		||||
 | 
			
		||||
				const updated = JSON.parse(JSON.stringify(appearNote.value));
 | 
			
		||||
 | 
			
		||||
				const choices = [...appearNote.value.poll.choices];
 | 
			
		||||
				choices[choice] = {
 | 
			
		||||
					...choices[choice],
 | 
			
		||||
| 
						 | 
				
			
			@ -73,16 +63,12 @@ export function useNoteCapture(props: {
 | 
			
		|||
					} : {})
 | 
			
		||||
				};
 | 
			
		||||
 | 
			
		||||
				updated.poll.choices = choices;
 | 
			
		||||
 | 
			
		||||
				appearNote.value = updated;
 | 
			
		||||
				appearNote.value.poll.choices = choices;
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			case 'deleted': {
 | 
			
		||||
				const updated = JSON.parse(JSON.stringify(appearNote.value));
 | 
			
		||||
				updated.value = true;
 | 
			
		||||
				appearNote.value = updated;
 | 
			
		||||
				appearNote.value.deletedAt = new Date();
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -52,8 +52,8 @@ const stats = ref([]);
 | 
			
		|||
const fetching = ref(true);
 | 
			
		||||
 | 
			
		||||
const fetch = () => {
 | 
			
		||||
	os.api('hashtags/trend').then(stats => {
 | 
			
		||||
		stats.value = stats;
 | 
			
		||||
	os.api('hashtags/trend').then(res => {
 | 
			
		||||
		stats.value = res;
 | 
			
		||||
		fetching.value = false;
 | 
			
		||||
	});
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue