mirror of
				https://codeberg.org/yeentown/barkey.git
				synced 2025-11-03 23:14:13 +00:00 
			
		
		
		
	feat(client): MFM関数構文のサジェストを実装
This commit is contained in:
		
							parent
							
								
									a75f3fb87c
								
							
						
					
					
						commit
						a70dbb7e74
					
				
					 3 changed files with 58 additions and 19 deletions
				
			
		| 
						 | 
				
			
			@ -11,6 +11,7 @@
 | 
			
		|||
 | 
			
		||||
### Improvements
 | 
			
		||||
- クライアント: アニメーションを減らす設定をメニューのアニメーションにも適用するように
 | 
			
		||||
- クライアント: MFM関数構文のサジェストを実装
 | 
			
		||||
- ActivityPub: HTML -> MFMの変換を強化
 | 
			
		||||
 | 
			
		||||
### Bugfixes
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,12 +10,12 @@
 | 
			
		|||
		</li>
 | 
			
		||||
		<li @click="chooseUser()" @keydown="onKeydown" tabindex="-1" class="choose">{{ $ts.selectUser }}</li>
 | 
			
		||||
	</ol>
 | 
			
		||||
	<ol class="hashtags" ref="suggests" v-if="hashtags.length > 0">
 | 
			
		||||
	<ol class="hashtags" ref="suggests" v-else-if="hashtags.length > 0">
 | 
			
		||||
		<li v-for="hashtag in hashtags" @click="complete(type, hashtag)" @keydown="onKeydown" tabindex="-1">
 | 
			
		||||
			<span class="name">{{ hashtag }}</span>
 | 
			
		||||
		</li>
 | 
			
		||||
	</ol>
 | 
			
		||||
	<ol class="emojis" ref="suggests" v-if="emojis.length > 0">
 | 
			
		||||
	<ol class="emojis" ref="suggests" v-else-if="emojis.length > 0">
 | 
			
		||||
		<li v-for="emoji in emojis" @click="complete(type, emoji.emoji)" @keydown="onKeydown" tabindex="-1">
 | 
			
		||||
			<span class="emoji" v-if="emoji.isCustomEmoji"><img :src="$store.state.disableShowingAnimatedImages ? getStaticImageUrl(emoji.url) : emoji.url" :alt="emoji.emoji"/></span>
 | 
			
		||||
			<span class="emoji" v-else-if="!$store.state.useOsNativeEmojis"><img :src="emoji.url" :alt="emoji.emoji"/></span>
 | 
			
		||||
| 
						 | 
				
			
			@ -24,6 +24,11 @@
 | 
			
		|||
			<span class="alias" v-if="emoji.aliasOf">({{ emoji.aliasOf }})</span>
 | 
			
		||||
		</li>
 | 
			
		||||
	</ol>
 | 
			
		||||
	<ol class="mfmTags" ref="suggests" v-else-if="mfmTags.length > 0">
 | 
			
		||||
		<li v-for="tag in mfmTags" @click="complete(type, tag)" @keydown="onKeydown" tabindex="-1">
 | 
			
		||||
			<span class="tag">{{ tag }}</span>
 | 
			
		||||
		</li>
 | 
			
		||||
	</ol>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -106,6 +111,8 @@ emojiDefinitions.sort((a, b) => a.name.length - b.name.length);
 | 
			
		|||
const emojiDb = markRaw(emojiDefinitions.concat(emjdb));
 | 
			
		||||
//#endregion
 | 
			
		||||
 | 
			
		||||
const MFM_TAGS = ['tada', 'jelly', 'twitch', 'shake', 'spin', 'jump', 'bounce', 'flip', 'x2', 'x3', 'x4', 'font', 'blur', 'rainbow', 'sparkle'];
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
	props: {
 | 
			
		||||
		type: {
 | 
			
		||||
| 
						 | 
				
			
			@ -137,11 +144,6 @@ export default defineComponent({
 | 
			
		|||
			type: Number,
 | 
			
		||||
			required: true,
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		showing: {
 | 
			
		||||
			type: Boolean,
 | 
			
		||||
			required: true
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	emits: ['done', 'closed'],
 | 
			
		||||
| 
						 | 
				
			
			@ -154,18 +156,11 @@ export default defineComponent({
 | 
			
		|||
			hashtags: [],
 | 
			
		||||
			emojis: [],
 | 
			
		||||
			items: [],
 | 
			
		||||
			mfmTags: [],
 | 
			
		||||
			select: -1,
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	watch: {
 | 
			
		||||
		showing() {
 | 
			
		||||
			if (!this.showing) {
 | 
			
		||||
				this.$emit('closed');
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	updated() {
 | 
			
		||||
		this.setPosition();
 | 
			
		||||
		this.items = (this.$refs.suggests as Element | undefined)?.children || [];
 | 
			
		||||
| 
						 | 
				
			
			@ -236,7 +231,9 @@ export default defineComponent({
 | 
			
		|||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (this.type == 'user') {
 | 
			
		||||
			console.log(this.type);
 | 
			
		||||
 | 
			
		||||
			if (this.type === 'user') {
 | 
			
		||||
				if (this.q == null) {
 | 
			
		||||
					this.users = [];
 | 
			
		||||
					this.fetching = false;
 | 
			
		||||
| 
						 | 
				
			
			@ -262,7 +259,7 @@ export default defineComponent({
 | 
			
		|||
						sessionStorage.setItem(cacheKey, JSON.stringify(users));
 | 
			
		||||
					});
 | 
			
		||||
				}
 | 
			
		||||
			} else if (this.type == 'hashtag') {
 | 
			
		||||
			} else if (this.type === 'hashtag') {
 | 
			
		||||
				if (this.q == null || this.q == '') {
 | 
			
		||||
					this.hashtags = JSON.parse(localStorage.getItem('hashtags') || '[]');
 | 
			
		||||
					this.fetching = false;
 | 
			
		||||
| 
						 | 
				
			
			@ -286,7 +283,7 @@ export default defineComponent({
 | 
			
		|||
						});
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			} else if (this.type == 'emoji') {
 | 
			
		||||
			} else if (this.type === 'emoji') {
 | 
			
		||||
				if (this.q == null || this.q == '') {
 | 
			
		||||
					// 最近使った絵文字をサジェスト
 | 
			
		||||
					this.emojis = this.$store.state.recentlyUsedEmojis.map(emoji => emojiDb.find(e => e.emoji == emoji)).filter(x => x != null);
 | 
			
		||||
| 
						 | 
				
			
			@ -314,6 +311,14 @@ export default defineComponent({
 | 
			
		|||
				}
 | 
			
		||||
 | 
			
		||||
				this.emojis = matched;
 | 
			
		||||
			} else if (this.type === 'mfmTag') {
 | 
			
		||||
				console.log(this.q);
 | 
			
		||||
				if (this.q == null || this.q == '') {
 | 
			
		||||
					this.mfmTags = MFM_TAGS;
 | 
			
		||||
					return;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				this.mfmTags = MFM_TAGS.filter(tag => tag.startsWith(this.q));
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -490,5 +495,11 @@ export default defineComponent({
 | 
			
		|||
			margin: 0 0 0 8px;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	> .mfmTags > li {
 | 
			
		||||
 | 
			
		||||
		.name {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -70,11 +70,13 @@ export class Autocomplete {
 | 
			
		|||
		const mentionIndex = text.lastIndexOf('@');
 | 
			
		||||
		const hashtagIndex = text.lastIndexOf('#');
 | 
			
		||||
		const emojiIndex = text.lastIndexOf(':');
 | 
			
		||||
		const mfmTagIndex = text.lastIndexOf('$');
 | 
			
		||||
 | 
			
		||||
		const max = Math.max(
 | 
			
		||||
			mentionIndex,
 | 
			
		||||
			hashtagIndex,
 | 
			
		||||
			emojiIndex);
 | 
			
		||||
			emojiIndex,
 | 
			
		||||
			mfmTagIndex);
 | 
			
		||||
 | 
			
		||||
		if (max == -1) {
 | 
			
		||||
			this.close();
 | 
			
		||||
| 
						 | 
				
			
			@ -83,6 +85,7 @@ export class Autocomplete {
 | 
			
		|||
 | 
			
		||||
		const isMention = mentionIndex != -1;
 | 
			
		||||
		const isHashtag = hashtagIndex != -1;
 | 
			
		||||
		const isMfmTag = mfmTagIndex != -1;
 | 
			
		||||
		const isEmoji = emojiIndex != -1 && text.split(/:[a-z0-9_+\-]+:/).pop()!.includes(':');
 | 
			
		||||
 | 
			
		||||
		let opened = false;
 | 
			
		||||
| 
						 | 
				
			
			@ -114,6 +117,14 @@ export class Autocomplete {
 | 
			
		|||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (isMfmTag && !opened) {
 | 
			
		||||
			const mfmTag = text.substr(mfmTagIndex + 1);
 | 
			
		||||
			if (!mfmTag.includes(' ')) {
 | 
			
		||||
				this.open('mfmTag', mfmTag);
 | 
			
		||||
				opened = true;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!opened) {
 | 
			
		||||
			this.close();
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -244,6 +255,22 @@ export class Autocomplete {
 | 
			
		|||
				const pos = trimmedBefore.length + value.length;
 | 
			
		||||
				this.textarea.setSelectionRange(pos, pos);
 | 
			
		||||
			});
 | 
			
		||||
		} else if (type == 'mfmTag') {
 | 
			
		||||
			const source = this.text;
 | 
			
		||||
 | 
			
		||||
			const before = source.substr(0, caret);
 | 
			
		||||
			const trimmedBefore = before.substring(0, before.lastIndexOf('$'));
 | 
			
		||||
			const after = source.substr(caret);
 | 
			
		||||
 | 
			
		||||
			// 挿入
 | 
			
		||||
			this.text = `${trimmedBefore}$[${value} ]${after}`;
 | 
			
		||||
 | 
			
		||||
			// キャレットを戻す
 | 
			
		||||
			this.vm.$nextTick(() => {
 | 
			
		||||
				this.textarea.focus();
 | 
			
		||||
				const pos = trimmedBefore.length + (value.length + 3);
 | 
			
		||||
				this.textarea.setSelectionRange(pos, pos);
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue