Merge branch 'develop'
This commit is contained in:
		
						commit
						1092532292
					
				
					 47 changed files with 160 additions and 58 deletions
				
			
		
							
								
								
									
										12
									
								
								CHANGELOG.md
									
										
									
									
									
								
							
							
						
						
									
										12
									
								
								CHANGELOG.md
									
										
									
									
									
								
							| 
						 | 
					@ -73,6 +73,18 @@ mongodb:
 | 
				
			||||||
8. master ブランチに戻す
 | 
					8. master ブランチに戻す
 | 
				
			||||||
9. enjoy
 | 
					9. enjoy
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					11.11.0 (2019/05/05)
 | 
				
			||||||
 | 
					--------------------
 | 
				
			||||||
 | 
					### Improvements
 | 
				
			||||||
 | 
					* MisskeyPagesにリストから選択関数を追加
 | 
				
			||||||
 | 
					* MisskeyPagesに確率を指定できるテキストランダム選択関数を追加
 | 
				
			||||||
 | 
					* 外部サービス連携ログインリンクにアイコン追加
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Fixes
 | 
				
			||||||
 | 
					* MisskeyPagesでifを入れ子にできなくなっていた問題を修正
 | 
				
			||||||
 | 
					* MisskeyPagesで数値入力を作成するとテキスト入力になる問題を修正
 | 
				
			||||||
 | 
					* 外部サービス連携に関する問題を修正
 | 
				
			||||||
 | 
					
 | 
				
			||||||
11.10.1 (2019/05/04)
 | 
					11.10.1 (2019/05/04)
 | 
				
			||||||
--------------------
 | 
					--------------------
 | 
				
			||||||
### Fixes
 | 
					### Fixes
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1865,8 +1865,8 @@ pages:
 | 
				
			||||||
  font: "フォント"
 | 
					  font: "フォント"
 | 
				
			||||||
  fontSerif: "セリフ"
 | 
					  fontSerif: "セリフ"
 | 
				
			||||||
  fontSansSerif: "サンセリフ"
 | 
					  fontSansSerif: "サンセリフ"
 | 
				
			||||||
  set-eye-catchig-image: "アイキャッチ画像を設定"
 | 
					  set-eye-catching-image: "アイキャッチ画像を設定"
 | 
				
			||||||
  remove-eye-catchig-image: "アイキャッチ画像を削除"
 | 
					  remove-eye-catching-image: "アイキャッチ画像を削除"
 | 
				
			||||||
  choose-block: "ブロックを追加"
 | 
					  choose-block: "ブロックを追加"
 | 
				
			||||||
  select-type: "種類を選択"
 | 
					  select-type: "種類を選択"
 | 
				
			||||||
  enter-variable-name: "変数名を決めてください"
 | 
					  enter-variable-name: "変数名を決めてください"
 | 
				
			||||||
| 
						 | 
					@ -1941,6 +1941,7 @@ pages:
 | 
				
			||||||
      fn: "関数"
 | 
					      fn: "関数"
 | 
				
			||||||
      text: "テキスト操作"
 | 
					      text: "テキスト操作"
 | 
				
			||||||
      convert: "変換"
 | 
					      convert: "変換"
 | 
				
			||||||
 | 
					      list: "リスト"
 | 
				
			||||||
    blocks:
 | 
					    blocks:
 | 
				
			||||||
      text: "テキスト"
 | 
					      text: "テキスト"
 | 
				
			||||||
      multiLineText: "テキスト(複数行)"
 | 
					      multiLineText: "テキスト(複数行)"
 | 
				
			||||||
| 
						 | 
					@ -2059,6 +2060,13 @@ pages:
 | 
				
			||||||
      _seedRandomPick:
 | 
					      _seedRandomPick:
 | 
				
			||||||
        arg1: "シード"
 | 
					        arg1: "シード"
 | 
				
			||||||
        arg2: "リスト"
 | 
					        arg2: "リスト"
 | 
				
			||||||
 | 
					      DRPWPM: "確率付きリストからランダムに選択 (ユーザーごとに日替わり)"
 | 
				
			||||||
 | 
					      _DRPWPM:
 | 
				
			||||||
 | 
					        arg1: "テキストのリスト"
 | 
				
			||||||
 | 
					      pick: "リストから選択"
 | 
				
			||||||
 | 
					      _pick:
 | 
				
			||||||
 | 
					        arg1: "リスト"
 | 
				
			||||||
 | 
					        arg2: "位置"
 | 
				
			||||||
      number: "数値"
 | 
					      number: "数値"
 | 
				
			||||||
      stringToNumber: "テキストを数値に"
 | 
					      stringToNumber: "テキストを数値に"
 | 
				
			||||||
      _stringToNumber:
 | 
					      _stringToNumber:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	"name": "misskey",
 | 
						"name": "misskey",
 | 
				
			||||||
	"author": "syuilo <i@syuilo.com>",
 | 
						"author": "syuilo <i@syuilo.com>",
 | 
				
			||||||
	"version": "11.10.1",
 | 
						"version": "11.11.0",
 | 
				
			||||||
	"codename": "daybreak",
 | 
						"codename": "daybreak",
 | 
				
			||||||
	"repository": {
 | 
						"repository": {
 | 
				
			||||||
		"type": "git",
 | 
							"type": "git",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -34,7 +34,7 @@ body
 | 
				
			||||||
	.peg
 | 
						.peg
 | 
				
			||||||
		display block
 | 
							display block
 | 
				
			||||||
		position absolute
 | 
							position absolute
 | 
				
			||||||
		right 0px
 | 
							right 0
 | 
				
			||||||
		width 100px
 | 
							width 100px
 | 
				
			||||||
		height 100%
 | 
							height 100%
 | 
				
			||||||
		box-shadow 0 0 10px var(--primary), 0 0 5px var(--primary)
 | 
							box-shadow 0 0 10px var(--primary), 0 0 5px var(--primary)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -98,7 +98,7 @@ export default Vue.extend({
 | 
				
			||||||
		margin 0 auto
 | 
							margin 0 auto
 | 
				
			||||||
		text-align center
 | 
							text-align center
 | 
				
			||||||
		background #fff
 | 
							background #fff
 | 
				
			||||||
		box-shadow 0px 4px 16px rgba(#000, 0.2)
 | 
							box-shadow 0 4px 16px rgba(#000, 0.2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		> .fetching
 | 
							> .fetching
 | 
				
			||||||
			margin 0
 | 
								margin 0
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -52,7 +52,7 @@ function match(e: KeyboardEvent, patterns: action['patterns']): boolean {
 | 
				
			||||||
		pattern.ctrl == e.ctrlKey &&
 | 
							pattern.ctrl == e.ctrlKey &&
 | 
				
			||||||
		pattern.shift == e.shiftKey &&
 | 
							pattern.shift == e.shiftKey &&
 | 
				
			||||||
		pattern.alt == e.altKey &&
 | 
							pattern.alt == e.altKey &&
 | 
				
			||||||
		e.metaKey == false
 | 
							!e.metaKey
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,9 +11,9 @@ export default function(me, settings, note) {
 | 
				
			||||||
	return (
 | 
						return (
 | 
				
			||||||
		(!isMyNote && note.reply && includesMutedWords(note.reply.text)) ||
 | 
							(!isMyNote && note.reply && includesMutedWords(note.reply.text)) ||
 | 
				
			||||||
		(!isMyNote && note.renote && includesMutedWords(note.renote.text)) ||
 | 
							(!isMyNote && note.renote && includesMutedWords(note.renote.text)) ||
 | 
				
			||||||
		(settings.showMyRenotes === false && isMyNote && isPureRenote) ||
 | 
							(!settings.showMyRenotes && isMyNote && isPureRenote) ||
 | 
				
			||||||
		(settings.showRenotedMyNotes === false && isPureRenote && note.renote.userId == me.id) ||
 | 
							(!settings.showRenotedMyNotes && isPureRenote && note.renote.userId == me.id) ||
 | 
				
			||||||
		(settings.showLocalRenotes === false && isPureRenote && note.renote.user.host == null) ||
 | 
							(!settings.showLocalRenotes && isPureRenote && note.renote.user.host == null) ||
 | 
				
			||||||
		(!isMyNote && includesMutedWords(note.text))
 | 
							(!isMyNote && includesMutedWords(note.text))
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -80,7 +80,7 @@ export default Vue.extend({
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ms(): number {
 | 
							ms(): number {
 | 
				
			||||||
			return this.now.getMilliseconds() * this.smooth;
 | 
								return this.now.getMilliseconds() * this.smooth;
 | 
				
			||||||
		}
 | 
							},
 | 
				
			||||||
		s(): number {
 | 
							s(): number {
 | 
				
			||||||
			return this.now.getSeconds();
 | 
								return this.now.getSeconds();
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -202,7 +202,7 @@ export default Vue.extend({
 | 
				
			||||||
		left 0
 | 
							left 0
 | 
				
			||||||
		z-index 1
 | 
							z-index 1
 | 
				
			||||||
		width 100%
 | 
							width 100%
 | 
				
			||||||
		box-shadow 0 0px 2px rgba(#000, 0.2)
 | 
							box-shadow 0 0 2px rgba(#000, 0.2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		> .form
 | 
							> .form
 | 
				
			||||||
			background rgba(0, 0, 0, 0.02)
 | 
								background rgba(0, 0, 0, 0.02)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,7 +14,7 @@ import XImage from './els/page-editor.el.image.vue';
 | 
				
			||||||
import XButton from './els/page-editor.el.button.vue';
 | 
					import XButton from './els/page-editor.el.button.vue';
 | 
				
			||||||
import XTextInput from './els/page-editor.el.text-input.vue';
 | 
					import XTextInput from './els/page-editor.el.text-input.vue';
 | 
				
			||||||
import XTextareaInput from './els/page-editor.el.textarea-input.vue';
 | 
					import XTextareaInput from './els/page-editor.el.textarea-input.vue';
 | 
				
			||||||
import XNumberInput from './els/page-editor.el.text-input.vue';
 | 
					import XNumberInput from './els/page-editor.el.number-input.vue';
 | 
				
			||||||
import XSwitch from './els/page-editor.el.switch.vue';
 | 
					import XSwitch from './els/page-editor.el.switch.vue';
 | 
				
			||||||
import XIf from './els/page-editor.el.if.vue';
 | 
					import XIf from './els/page-editor.el.if.vue';
 | 
				
			||||||
import XPost from './els/page-editor.el.post.vue';
 | 
					import XPost from './els/page-editor.el.post.vue';
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -36,10 +36,10 @@
 | 
				
			||||||
				</ui-select>
 | 
									</ui-select>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				<div class="eyeCatch">
 | 
									<div class="eyeCatch">
 | 
				
			||||||
					<ui-button v-if="eyeCatchingImageId == null && !readonly" @click="setEyeCatchingImage()"><fa :icon="faPlus"/> {{ $t('set-eye-catchig-image') }}</ui-button>
 | 
										<ui-button v-if="eyeCatchingImageId == null && !readonly" @click="setEyeCatchingImage()"><fa :icon="faPlus"/> {{ $t('set-eye-catching-image') }}</ui-button>
 | 
				
			||||||
					<div v-else-if="eyeCatchingImage">
 | 
										<div v-else-if="eyeCatchingImage">
 | 
				
			||||||
						<img :src="eyeCatchingImage.url" :alt="eyeCatchingImage.name"/>
 | 
											<img :src="eyeCatchingImage.url" :alt="eyeCatchingImage.name"/>
 | 
				
			||||||
						<ui-button @click="removeEyeCatchingImage()" v-if="!readonly"><fa :icon="faTrashAlt"/> {{ $t('remove-eye-catchig-image') }}</ui-button>
 | 
											<ui-button @click="removeEyeCatchingImage()" v-if="!readonly"><fa :icon="faTrashAlt"/> {{ $t('remove-eye-catching-image') }}</ui-button>
 | 
				
			||||||
					</div>
 | 
										</div>
 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
			</template>
 | 
								</template>
 | 
				
			||||||
| 
						 | 
					@ -337,7 +337,7 @@ export default Vue.extend({
 | 
				
			||||||
		getScriptBlockList(type: string = null) {
 | 
							getScriptBlockList(type: string = null) {
 | 
				
			||||||
			const list = [];
 | 
								const list = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			const blocks = blockDefs.filter(block => type === null || block.out === null || block.out === type);
 | 
								const blocks = blockDefs.filter(block => type === null || block.out === null || block.out === type || typeof block.out === 'number');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			for (const block of blocks) {
 | 
								for (const block of blocks) {
 | 
				
			||||||
				const category = list.find(x => x.category === block.category);
 | 
									const category = list.find(x => x.category === block.category);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -54,7 +54,11 @@ export default Vue.extend({
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mounted() {
 | 
						mounted() {
 | 
				
			||||||
		document.cookie = `i=${this.$store.state.i.token}`;
 | 
							if (!document.cookie.match(/i=(\w+)/)) {
 | 
				
			||||||
 | 
								document.cookie = `i=${this.$store.state.i.token}; path=/;` +
 | 
				
			||||||
 | 
								` domain=${document.location.hostname}; max-age=31536000;` +
 | 
				
			||||||
 | 
								(document.location.protocol.startsWith('https') ? ' secure' : '');
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		this.$watch('$store.state.i', () => {
 | 
							this.$watch('$store.state.i', () => {
 | 
				
			||||||
			if (this.$store.state.i.twitter) {
 | 
								if (this.$store.state.i.twitter) {
 | 
				
			||||||
				if (this.twitterForm) this.twitterForm.close();
 | 
									if (this.twitterForm) this.twitterForm.close();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -273,7 +273,7 @@ export default Vue.extend({
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		import_() {
 | 
							import_() {
 | 
				
			||||||
			(this.$refs.file as any).click();
 | 
								(this.$refs.file as any).click();
 | 
				
			||||||
		}
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		export_() {
 | 
							export_() {
 | 
				
			||||||
			const blob = new Blob([this.selectedThemeCode], {
 | 
								const blob = new Blob([this.selectedThemeCode], {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,9 +15,9 @@
 | 
				
			||||||
		<template #prefix><fa icon="gavel"/></template>
 | 
							<template #prefix><fa icon="gavel"/></template>
 | 
				
			||||||
	</ui-input>
 | 
						</ui-input>
 | 
				
			||||||
	<ui-button type="submit" :disabled="signing">{{ signing ? $t('signing-in') : $t('@.signin') }}</ui-button>
 | 
						<ui-button type="submit" :disabled="signing">{{ signing ? $t('signing-in') : $t('@.signin') }}</ui-button>
 | 
				
			||||||
	<p v-if="meta && meta.enableTwitterIntegration" style="margin: 8px 0;"><a :href="`${apiUrl}/signin/twitter`">{{ $t('signin-with-twitter') }}</a></p>
 | 
						<p v-if="meta && meta.enableTwitterIntegration" style="margin: 8px 0;"><a :href="`${apiUrl}/signin/twitter`"><fa :icon="['fab', 'twitter']"/> {{ $t('signin-with-twitter') }}</a></p>
 | 
				
			||||||
	<p v-if="meta && meta.enableGithubIntegration"  style="margin: 8px 0;"><a :href="`${apiUrl}/signin/github`">{{ $t('signin-with-github') }}</a></p>
 | 
						<p v-if="meta && meta.enableGithubIntegration"  style="margin: 8px 0;"><a :href="`${apiUrl}/signin/github`"><fa :icon="['fab', 'github']"/> {{ $t('signin-with-github') }}</a></p>
 | 
				
			||||||
	<p v-if="meta && meta.enableDiscordIntegration" style="margin: 8px 0;"><a :href="`${apiUrl}/signin/discord`">{{ $t('signin-with-discord') /* TODO: Make these layouts better */ }}</a></p>
 | 
						<p v-if="meta && meta.enableDiscordIntegration" style="margin: 8px 0;"><a :href="`${apiUrl}/signin/discord`"><fa :icon="['fab', 'discord']"/> {{ $t('signin-with-discord') /* TODO: Make these layouts better */ }}</a></p>
 | 
				
			||||||
</form>
 | 
					</form>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -322,7 +322,7 @@ root(fill)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			> .value
 | 
								> .value
 | 
				
			||||||
				display block
 | 
									display block
 | 
				
			||||||
				width 0%
 | 
									width 0
 | 
				
			||||||
				height 100%
 | 
									height 100%
 | 
				
			||||||
				background transparent
 | 
									background transparent
 | 
				
			||||||
				border-radius 6px
 | 
									border-radius 6px
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -166,7 +166,7 @@ export default Vue.extend({
 | 
				
			||||||
			> .follow-button
 | 
								> .follow-button
 | 
				
			||||||
				position absolute
 | 
									position absolute
 | 
				
			||||||
				top 8px
 | 
									top 8px
 | 
				
			||||||
				right 0px
 | 
									right 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	> .more
 | 
						> .more
 | 
				
			||||||
		display block
 | 
							display block
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -160,7 +160,7 @@ export default Vue.extend({
 | 
				
			||||||
				this.$emit('top');
 | 
									this.$emit('top');
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (this.$store.state.settings.fetchOnScroll !== false) {
 | 
								if (this.$store.state.settings.fetchOnScroll) {
 | 
				
			||||||
				const current = this.$refs.body.scrollTop + this.$refs.body.clientHeight;
 | 
									const current = this.$refs.body.scrollTop + this.$refs.body.clientHeight;
 | 
				
			||||||
				if (current > this.$refs.body.scrollHeight - 1) this.$emit('bottom');
 | 
									if (current > this.$refs.body.scrollHeight - 1) this.$emit('bottom');
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -205,7 +205,7 @@ export default Vue.extend({
 | 
				
			||||||
			top -32px
 | 
								top -32px
 | 
				
			||||||
			left 0
 | 
								left 0
 | 
				
			||||||
			right 0
 | 
								right 0
 | 
				
			||||||
			width 0px
 | 
								width 0
 | 
				
			||||||
			margin 0 auto
 | 
								margin 0 auto
 | 
				
			||||||
			border-top solid 16px transparent
 | 
								border-top solid 16px transparent
 | 
				
			||||||
			border-left solid 16px transparent
 | 
								border-left solid 16px transparent
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -102,7 +102,7 @@ class Autocomplete {
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (isHashtag && opened == false) {
 | 
							if (isHashtag && !opened) {
 | 
				
			||||||
			const hashtag = text.substr(hashtagIndex + 1);
 | 
								const hashtag = text.substr(hashtagIndex + 1);
 | 
				
			||||||
			if (!hashtag.includes(' ')) {
 | 
								if (!hashtag.includes(' ')) {
 | 
				
			||||||
				this.open('hashtag', hashtag);
 | 
									this.open('hashtag', hashtag);
 | 
				
			||||||
| 
						 | 
					@ -110,7 +110,7 @@ class Autocomplete {
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (isEmoji && opened == false) {
 | 
							if (isEmoji && !opened) {
 | 
				
			||||||
			const emoji = text.substr(emojiIndex + 1);
 | 
								const emoji = text.substr(emojiIndex + 1);
 | 
				
			||||||
			if (!emoji.includes(' ')) {
 | 
								if (!emoji.includes(' ')) {
 | 
				
			||||||
				this.open('emoji', emoji);
 | 
									this.open('emoji', emoji);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -47,7 +47,7 @@ class Script {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public interpolate(str: string) {
 | 
						public interpolate(str: string) {
 | 
				
			||||||
		if (str == null) return null;
 | 
							if (str == null) return null;
 | 
				
			||||||
		return str.replace(/\{(.+?)\}/g, match => {
 | 
							return str.replace(/{(.+?)}/g, match => {
 | 
				
			||||||
			const v = this.vars[match.slice(1, -1).trim()];
 | 
								const v = this.vars[match.slice(1, -1).trim()];
 | 
				
			||||||
			return v == null ? 'NULL' : v.toString();
 | 
								return v == null ? 'NULL' : v.toString();
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -750,12 +750,17 @@ export default Vue.extend({
 | 
				
			||||||
				bottom 0
 | 
									bottom 0
 | 
				
			||||||
				animation-delay -1.0s
 | 
									animation-delay -1.0s
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			@keyframes sk-rotate { 100% { transform: rotate(360deg); }}
 | 
								@keyframes sk-rotate {
 | 
				
			||||||
 | 
									100% {
 | 
				
			||||||
 | 
										transform: rotate(360deg);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			@keyframes sk-bounce {
 | 
								@keyframes sk-bounce {
 | 
				
			||||||
				0%, 100% {
 | 
									0%, 100% {
 | 
				
			||||||
					transform: scale(0.0);
 | 
										transform: scale(0.0);
 | 
				
			||||||
				} 50% {
 | 
									}
 | 
				
			||||||
 | 
									50% {
 | 
				
			||||||
					transform: scale(1.0);
 | 
										transform: scale(1.0);
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -181,7 +181,7 @@ export default Vue.extend({
 | 
				
			||||||
				this.releaseQueue();
 | 
									this.releaseQueue();
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (this.$store.state.settings.fetchOnScroll !== false) {
 | 
								if (this.$store.state.settings.fetchOnScroll) {
 | 
				
			||||||
				const current = window.scrollY + window.innerHeight;
 | 
									const current = window.scrollY + window.innerHeight;
 | 
				
			||||||
				if (current > document.body.offsetHeight - 8) this.fetchMore();
 | 
									if (current > document.body.offsetHeight - 8) this.fetchMore();
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -377,7 +377,7 @@ export default Vue.extend({
 | 
				
			||||||
			}, err => {
 | 
								}, err => {
 | 
				
			||||||
				this.$root.dialog({
 | 
									this.$root.dialog({
 | 
				
			||||||
					type: 'error',
 | 
										type: 'error',
 | 
				
			||||||
					title: this.$t('error')
 | 
										title: this.$t('error'),
 | 
				
			||||||
					text: err.message
 | 
										text: err.message
 | 
				
			||||||
				});
 | 
									});
 | 
				
			||||||
			}, {
 | 
								}, {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -480,7 +480,7 @@ export default Vue.extend({
 | 
				
			||||||
		&:focus
 | 
							&:focus
 | 
				
			||||||
			&:not([data-is-modal])
 | 
								&:not([data-is-modal])
 | 
				
			||||||
				> .body
 | 
									> .body
 | 
				
			||||||
						box-shadow 0 0 0px 1px var(--primaryAlpha05), 0 2px 12px 0 var(--desktopWindowShadow)
 | 
											box-shadow 0 0 0 1px var(--primaryAlpha05), 0 2px 12px 0 var(--desktopWindowShadow)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		> .handle
 | 
							> .handle
 | 
				
			||||||
			$size = 8px
 | 
								$size = 8px
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -352,7 +352,7 @@ export default Vue.extend({
 | 
				
			||||||
				padding 0 16px
 | 
									padding 0 16px
 | 
				
			||||||
				line-height 48px
 | 
									line-height 48px
 | 
				
			||||||
				background var(--faceHeader)
 | 
									background var(--faceHeader)
 | 
				
			||||||
				box-shadow 0 1px 0px rgba(0, 0, 0, 0.1)
 | 
									box-shadow 0 1px 0 rgba(0, 0, 0, 0.1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				& + div
 | 
									& + div
 | 
				
			||||||
					max-height calc(100% - 48px)
 | 
										max-height calc(100% - 48px)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -505,7 +505,7 @@ class WindowSystem extends EventEmitter {
 | 
				
			||||||
function urlBase64ToUint8Array(base64String: string): Uint8Array {
 | 
					function urlBase64ToUint8Array(base64String: string): Uint8Array {
 | 
				
			||||||
	const padding = '='.repeat((4 - base64String.length % 4) % 4);
 | 
						const padding = '='.repeat((4 - base64String.length % 4) % 4);
 | 
				
			||||||
	const base64 = (base64String + padding)
 | 
						const base64 = (base64String + padding)
 | 
				
			||||||
		.replace(/\-/g, '+')
 | 
							.replace(/-/g, '+')
 | 
				
			||||||
		.replace(/_/g, '/');
 | 
							.replace(/_/g, '/');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const rawData = window.atob(base64);
 | 
						const rawData = window.atob(base64);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -83,7 +83,7 @@ export default Vue.extend({
 | 
				
			||||||
			hierarchyFolders: [],
 | 
								hierarchyFolders: [],
 | 
				
			||||||
			selectedFiles: [],
 | 
								selectedFiles: [],
 | 
				
			||||||
			info: null,
 | 
								info: null,
 | 
				
			||||||
			connection: null
 | 
								connection: null,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			fetching: true,
 | 
								fetching: true,
 | 
				
			||||||
			fetchingMoreFiles: false,
 | 
								fetchingMoreFiles: false,
 | 
				
			||||||
| 
						 | 
					@ -385,7 +385,7 @@ export default Vue.extend({
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		createFolder() {
 | 
							createFolder() {
 | 
				
			||||||
			this.$root.dialog({
 | 
								this.$root.dialog({
 | 
				
			||||||
				title: this.$t('folder-name')
 | 
									title: this.$t('folder-name'),
 | 
				
			||||||
				input: {
 | 
									input: {
 | 
				
			||||||
					default: this.folder.name
 | 
										default: this.folder.name
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
| 
						 | 
					@ -415,7 +415,7 @@ export default Vue.extend({
 | 
				
			||||||
				return;
 | 
									return;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			this.$root.dialog({
 | 
								this.$root.dialog({
 | 
				
			||||||
				title: this.$t('folder-name')
 | 
									title: this.$t('folder-name'),
 | 
				
			||||||
				input: {
 | 
									input: {
 | 
				
			||||||
					default: this.folder.name
 | 
										default: this.folder.name
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
| 
						 | 
					@ -597,12 +597,17 @@ export default Vue.extend({
 | 
				
			||||||
			bottom 0
 | 
								bottom 0
 | 
				
			||||||
			animation-delay -1.0s
 | 
								animation-delay -1.0s
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		@keyframes sk-rotate { 100% { transform: rotate(360deg); }}
 | 
							@keyframes sk-rotate {
 | 
				
			||||||
 | 
								100% {
 | 
				
			||||||
 | 
									transform: rotate(360deg);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		@keyframes sk-bounce {
 | 
							@keyframes sk-bounce {
 | 
				
			||||||
			0%, 100% {
 | 
								0%, 100% {
 | 
				
			||||||
				transform: scale(0.0);
 | 
									transform: scale(0.0);
 | 
				
			||||||
			} 50% {
 | 
								}
 | 
				
			||||||
 | 
								50% {
 | 
				
			||||||
				transform: scale(1.0);
 | 
									transform: scale(1.0);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -175,7 +175,7 @@ export default Vue.extend({
 | 
				
			||||||
				this.releaseQueue();
 | 
									this.releaseQueue();
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (this.$store.state.settings.fetchOnScroll !== false) {
 | 
								if (this.$store.state.settings.fetchOnScroll) {
 | 
				
			||||||
				// 親要素が display none だったら弾く
 | 
									// 親要素が display none だったら弾く
 | 
				
			||||||
				// https://github.com/syuilo/misskey/issues/1569
 | 
									// https://github.com/syuilo/misskey/issues/1569
 | 
				
			||||||
				// http://d.hatena.ne.jp/favril/20091105/1257403319
 | 
									// http://d.hatena.ne.jp/favril/20091105/1257403319
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -115,7 +115,7 @@ export default Vue.extend({
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		onScroll() {
 | 
							onScroll() {
 | 
				
			||||||
			if (this.$store.state.settings.fetchOnScroll !== false) {
 | 
								if (this.$store.state.settings.fetchOnScroll) {
 | 
				
			||||||
				// 親要素が display none だったら弾く
 | 
									// 親要素が display none だったら弾く
 | 
				
			||||||
				// https://github.com/syuilo/misskey/issues/1569
 | 
									// https://github.com/syuilo/misskey/issues/1569
 | 
				
			||||||
				// http://d.hatena.ne.jp/favril/20091105/1257403319
 | 
									// http://d.hatena.ne.jp/favril/20091105/1257403319
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -295,7 +295,7 @@ export default Vue.extend({
 | 
				
			||||||
			}, err => {
 | 
								}, err => {
 | 
				
			||||||
				this.$root.dialog({
 | 
									this.$root.dialog({
 | 
				
			||||||
					type: 'error',
 | 
										type: 'error',
 | 
				
			||||||
					title: this.$t('error')
 | 
										title: this.$t('error'),
 | 
				
			||||||
					text: err.message
 | 
										text: err.message
 | 
				
			||||||
				});
 | 
									});
 | 
				
			||||||
			}, {
 | 
								}, {
 | 
				
			||||||
| 
						 | 
					@ -341,7 +341,7 @@ export default Vue.extend({
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		post() {
 | 
							post() {
 | 
				
			||||||
			this.posting = true;
 | 
								this.posting = true;
 | 
				
			||||||
			const viaMobile = this.$store.state.settings.disableViaMobile !== true;
 | 
								const viaMobile = !this.$store.state.settings.disableViaMobile;
 | 
				
			||||||
			this.$root.api('notes/create', {
 | 
								this.$root.api('notes/create', {
 | 
				
			||||||
				text: this.text == '' ? undefined : this.text,
 | 
									text: this.text == '' ? undefined : this.text,
 | 
				
			||||||
				fileIds: this.files.length > 0 ? this.files.map(f => f.id) : undefined,
 | 
									fileIds: this.files.length > 0 ? this.files.map(f => f.id) : undefined,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -49,7 +49,7 @@ export default Vue.extend({
 | 
				
			||||||
	padding 0 8px
 | 
						padding 0 8px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	&.shadow
 | 
						&.shadow
 | 
				
			||||||
		box-shadow 0 0px 8px rgba(0, 0, 0, 0.25)
 | 
							box-shadow 0 0 8px rgba(0, 0, 0, 0.25)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	&, *
 | 
						&, *
 | 
				
			||||||
		user-select none
 | 
							user-select none
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
input
 | 
					input
 | 
				
			||||||
	min-width 0px
 | 
						min-width 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
input:not([type])
 | 
					input:not([type])
 | 
				
			||||||
input[type='text']
 | 
					input[type='text']
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -126,7 +126,7 @@ export default (os: MiOS) => new Vuex.Store({
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		logout(ctx) {
 | 
							logout(ctx) {
 | 
				
			||||||
			ctx.commit('updateI', null);
 | 
								ctx.commit('updateI', null);
 | 
				
			||||||
			document.cookie = 'i=;';
 | 
								document.cookie = `i=; max-age=0; domain=${document.location.hostname}`;
 | 
				
			||||||
			localStorage.removeItem('i');
 | 
								localStorage.removeItem('i');
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -129,7 +129,7 @@ export const mfmLanguage = P.createLanguage({
 | 
				
			||||||
	mention: () => {
 | 
						mention: () => {
 | 
				
			||||||
		return P((input, i) => {
 | 
							return P((input, i) => {
 | 
				
			||||||
			const text = input.substr(i);
 | 
								const text = input.substr(i);
 | 
				
			||||||
			const match = text.match(/^@\w([\w-]*\w)?(?:@[\w\.\-]+\w)?/);
 | 
								const match = text.match(/^@\w([\w-]*\w)?(?:@[\w.\-]+\w)?/);
 | 
				
			||||||
			if (!match) return P.makeFailure(i, 'not a mention');
 | 
								if (!match) return P.makeFailure(i, 'not a mention');
 | 
				
			||||||
			if (input[i - 1] != null && input[i - 1].match(/[a-z0-9]/i)) return P.makeFailure(i, 'not a mention');
 | 
								if (input[i - 1] != null && input[i - 1].match(/[a-z0-9]/i)) return P.makeFailure(i, 'not a mention');
 | 
				
			||||||
			return P.makeSuccess(i + match[0].length, match[0]);
 | 
								return P.makeSuccess(i + match[0].length, match[0]);
 | 
				
			||||||
| 
						 | 
					@ -141,7 +141,7 @@ export const mfmLanguage = P.createLanguage({
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	hashtag: () => P((input, i) => {
 | 
						hashtag: () => P((input, i) => {
 | 
				
			||||||
		const text = input.substr(i);
 | 
							const text = input.substr(i);
 | 
				
			||||||
		const match = text.match(/^#([^\s\.,!\?'"#:\/\[\]【】]+)/i);
 | 
							const match = text.match(/^#([^\s.,!?'"#:\/\[\]【】]+)/i);
 | 
				
			||||||
		if (!match) return P.makeFailure(i, 'not a hashtag');
 | 
							if (!match) return P.makeFailure(i, 'not a hashtag');
 | 
				
			||||||
		let hashtag = match[1];
 | 
							let hashtag = match[1];
 | 
				
			||||||
		hashtag = removeOrphanedBrackets(hashtag);
 | 
							hashtag = removeOrphanedBrackets(hashtag);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -36,4 +36,4 @@ export function createTree(type: string, children: MfmForest, props: any): MfmTr
 | 
				
			||||||
	return T.createTree({ type, props }, children);
 | 
						return T.createTree({ type, props }, children);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const urlRegex = /^https?:\/\/[\w\/:%#@\$&\?!\(\)\[\]~\.,=\+\-]+/;
 | 
					export const urlRegex = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+/;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -64,7 +64,7 @@ export class ASEvaluator {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@autobind
 | 
						@autobind
 | 
				
			||||||
	private interpolate(str: string, scope: Scope) {
 | 
						private interpolate(str: string, scope: Scope) {
 | 
				
			||||||
		return str.replace(/\{(.+?)\}/g, match => {
 | 
							return str.replace(/{(.+?)}/g, match => {
 | 
				
			||||||
			const v = scope.getState(match.slice(1, -1).trim());
 | 
								const v = scope.getState(match.slice(1, -1).trim());
 | 
				
			||||||
			return v == null ? 'NULL' : v.toString();
 | 
								return v == null ? 'NULL' : v.toString();
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
| 
						 | 
					@ -169,6 +169,7 @@ export class ASEvaluator {
 | 
				
			||||||
			stringToNumber: (a: string) => parseInt(a),
 | 
								stringToNumber: (a: string) => parseInt(a),
 | 
				
			||||||
			numberToString: (a: number) => a.toString(),
 | 
								numberToString: (a: number) => a.toString(),
 | 
				
			||||||
			splitStrByLine: (a: string) => a.split('\n'),
 | 
								splitStrByLine: (a: string) => a.split('\n'),
 | 
				
			||||||
 | 
								pick: (list: any[], i: number) => list[i - 1],
 | 
				
			||||||
			random: (probability: number) => Math.floor(seedrandom(`${this.opts.randomSeed}:${block.id}`)() * 100) < probability,
 | 
								random: (probability: number) => Math.floor(seedrandom(`${this.opts.randomSeed}:${block.id}`)() * 100) < probability,
 | 
				
			||||||
			rannum: (min: number, max: number) => min + Math.floor(seedrandom(`${this.opts.randomSeed}:${block.id}`)() * (max - min + 1)),
 | 
								rannum: (min: number, max: number) => min + Math.floor(seedrandom(`${this.opts.randomSeed}:${block.id}`)() * (max - min + 1)),
 | 
				
			||||||
			randomPick: (list: any[]) => list[Math.floor(seedrandom(`${this.opts.randomSeed}:${block.id}`)() * list.length)],
 | 
								randomPick: (list: any[]) => list[Math.floor(seedrandom(`${this.opts.randomSeed}:${block.id}`)() * list.length)],
 | 
				
			||||||
| 
						 | 
					@ -178,6 +179,27 @@ export class ASEvaluator {
 | 
				
			||||||
			seedRandom: (seed: any, probability: number) => Math.floor(seedrandom(seed)() * 100) < probability,
 | 
								seedRandom: (seed: any, probability: number) => Math.floor(seedrandom(seed)() * 100) < probability,
 | 
				
			||||||
			seedRannum: (seed: any, min: number, max: number) => min + Math.floor(seedrandom(seed)() * (max - min + 1)),
 | 
								seedRannum: (seed: any, min: number, max: number) => min + Math.floor(seedrandom(seed)() * (max - min + 1)),
 | 
				
			||||||
			seedRandomPick: (seed: any, list: any[]) => list[Math.floor(seedrandom(seed)() * list.length)],
 | 
								seedRandomPick: (seed: any, list: any[]) => list[Math.floor(seedrandom(seed)() * list.length)],
 | 
				
			||||||
 | 
								DRPWPM: (list: string[]) => {
 | 
				
			||||||
 | 
									const xs = [];
 | 
				
			||||||
 | 
									let totalFactor = 0;
 | 
				
			||||||
 | 
									for (const x of list) {
 | 
				
			||||||
 | 
										const parts = x.split(' ');
 | 
				
			||||||
 | 
										const factor = parseInt(parts.pop()!, 10);
 | 
				
			||||||
 | 
										const text = parts.join(' ');
 | 
				
			||||||
 | 
										totalFactor += factor;
 | 
				
			||||||
 | 
										xs.push({ factor, text });
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									const r = seedrandom(`${day}:${block.id}`)() * totalFactor;
 | 
				
			||||||
 | 
									let stackedFactor = 0;
 | 
				
			||||||
 | 
									for (const x of xs) {
 | 
				
			||||||
 | 
										if (r >= stackedFactor && r <= x.factor) {
 | 
				
			||||||
 | 
											return x.text;
 | 
				
			||||||
 | 
										} else {
 | 
				
			||||||
 | 
											stackedFactor += x.factor;
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									return xs[0].text;
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const fnName = block.type;
 | 
							const fnName = block.type;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -23,6 +23,7 @@ import {
 | 
				
			||||||
	faSortNumericUp,
 | 
						faSortNumericUp,
 | 
				
			||||||
	faExchangeAlt,
 | 
						faExchangeAlt,
 | 
				
			||||||
	faRecycle,
 | 
						faRecycle,
 | 
				
			||||||
 | 
						faIndent,
 | 
				
			||||||
} from '@fortawesome/free-solid-svg-icons';
 | 
					} from '@fortawesome/free-solid-svg-icons';
 | 
				
			||||||
import { faFlag } from '@fortawesome/free-regular-svg-icons';
 | 
					import { faFlag } from '@fortawesome/free-regular-svg-icons';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -72,6 +73,7 @@ export const funcDefs: Record<string, { in: any[]; out: any; category: string; i
 | 
				
			||||||
	stringToNumber:  { in: ['string'],                     out: 'number',      category: 'convert',    icon: faExchangeAlt, },
 | 
						stringToNumber:  { in: ['string'],                     out: 'number',      category: 'convert',    icon: faExchangeAlt, },
 | 
				
			||||||
	numberToString:  { in: ['number'],                     out: 'string',      category: 'convert',    icon: faExchangeAlt, },
 | 
						numberToString:  { in: ['number'],                     out: 'string',      category: 'convert',    icon: faExchangeAlt, },
 | 
				
			||||||
	splitStrByLine:  { in: ['string'],                     out: 'stringArray', category: 'convert',    icon: faExchangeAlt, },
 | 
						splitStrByLine:  { in: ['string'],                     out: 'stringArray', category: 'convert',    icon: faExchangeAlt, },
 | 
				
			||||||
 | 
						pick:            { in: [null],                         out: null,          category: 'list',       icon: faIndent, },
 | 
				
			||||||
	rannum:          { in: ['number', 'number'],           out: 'number',      category: 'random',     icon: faDice, },
 | 
						rannum:          { in: ['number', 'number'],           out: 'number',      category: 'random',     icon: faDice, },
 | 
				
			||||||
	dailyRannum:     { in: ['number', 'number'],           out: 'number',      category: 'random',     icon: faDice, },
 | 
						dailyRannum:     { in: ['number', 'number'],           out: 'number',      category: 'random',     icon: faDice, },
 | 
				
			||||||
	seedRannum:      { in: [null, 'number', 'number'],     out: 'number',      category: 'random',     icon: faDice, },
 | 
						seedRannum:      { in: [null, 'number', 'number'],     out: 'number',      category: 'random',     icon: faDice, },
 | 
				
			||||||
| 
						 | 
					@ -81,6 +83,7 @@ export const funcDefs: Record<string, { in: any[]; out: any; category: string; i
 | 
				
			||||||
	randomPick:      { in: [0],                            out: 0,             category: 'random',     icon: faDice, },
 | 
						randomPick:      { in: [0],                            out: 0,             category: 'random',     icon: faDice, },
 | 
				
			||||||
	dailyRandomPick: { in: [0],                            out: 0,             category: 'random',     icon: faDice, },
 | 
						dailyRandomPick: { in: [0],                            out: 0,             category: 'random',     icon: faDice, },
 | 
				
			||||||
	seedRandomPick:  { in: [null, 0],                      out: 0,             category: 'random',     icon: faDice, },
 | 
						seedRandomPick:  { in: [null, 0],                      out: 0,             category: 'random',     icon: faDice, },
 | 
				
			||||||
 | 
						DRPWPM:      { in: ['stringArray'],                out: 'string',      category: 'random',     icon: faDice, }, // dailyRandomPickWithProbabilityMapping
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const literalDefs: Record<string, { out: any; category: string; icon: any; }> = {
 | 
					export const literalDefs: Record<string, { out: any; category: string; icon: any; }> = {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,7 @@
 | 
				
			||||||
import { ulid } from 'ulid';
 | 
					import { ulid } from 'ulid';
 | 
				
			||||||
import { genAid } from './id/aid';
 | 
					import { genAid } from './id/aid';
 | 
				
			||||||
import { genMeid } from './id/meid';
 | 
					import { genMeid } from './id/meid';
 | 
				
			||||||
 | 
					import { genMeidg } from './id/meidg';
 | 
				
			||||||
import { genObjectId } from './id/object-id';
 | 
					import { genObjectId } from './id/object-id';
 | 
				
			||||||
import config from '../config';
 | 
					import config from '../config';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,6 +13,7 @@ export function genId(date?: Date): string {
 | 
				
			||||||
	switch (metohd) {
 | 
						switch (metohd) {
 | 
				
			||||||
		case 'aid': return genAid(date);
 | 
							case 'aid': return genAid(date);
 | 
				
			||||||
		case 'meid': return genMeid(date);
 | 
							case 'meid': return genMeid(date);
 | 
				
			||||||
 | 
							case 'meidg': return genMeidg(date);
 | 
				
			||||||
		case 'ulid': return ulid(date.getTime());
 | 
							case 'ulid': return ulid(date.getTime());
 | 
				
			||||||
		case 'objectid': return genObjectId(date);
 | 
							case 'objectid': return genObjectId(date);
 | 
				
			||||||
		default: throw new Error('unknown id generation method');
 | 
							default: throw new Error('unknown id generation method');
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										28
									
								
								src/misc/id/meidg.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/misc/id/meidg.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,28 @@
 | 
				
			||||||
 | 
					const CHARS = '0123456789abcdef';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//  4bit Fixed hex value 'g'
 | 
				
			||||||
 | 
					// 44bit UNIX Time ms in Hex
 | 
				
			||||||
 | 
					// 48bit Random value in Hex
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function getTime(time: number) {
 | 
				
			||||||
 | 
						if (time < 0) time = 0;
 | 
				
			||||||
 | 
						if (time === 0) {
 | 
				
			||||||
 | 
							return CHARS[0];
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return time.toString(16).padStart(11, CHARS[0]);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function getRandom() {
 | 
				
			||||||
 | 
						let str = '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (let i = 0; i < 12; i++) {
 | 
				
			||||||
 | 
							str += CHARS[Math.floor(Math.random() * CHARS.length)];
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return str;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function genMeidg(date: Date): string {
 | 
				
			||||||
 | 
						return 'g' + getTime(date.getTime()) + getRandom();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -26,7 +26,7 @@ export class UserProfile {
 | 
				
			||||||
	public birthday: string | null;
 | 
						public birthday: string | null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Column('varchar', {
 | 
						@Column('varchar', {
 | 
				
			||||||
		length: 1024, nullable: true,
 | 
							length: 2048, nullable: true,
 | 
				
			||||||
		comment: 'The description (bio) of the User.'
 | 
							comment: 'The description (bio) of the User.'
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
	public description: string | null;
 | 
						public description: string | null;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -128,6 +128,19 @@ export class UserRepository extends Repository<User> {
 | 
				
			||||||
					detail: true
 | 
										detail: true
 | 
				
			||||||
				}),
 | 
									}),
 | 
				
			||||||
				twoFactorEnabled: profile!.twoFactorEnabled,
 | 
									twoFactorEnabled: profile!.twoFactorEnabled,
 | 
				
			||||||
 | 
									twitter: profile!.twitter ? {
 | 
				
			||||||
 | 
										id: profile!.twitterUserId,
 | 
				
			||||||
 | 
										screenName: profile!.twitterScreenName
 | 
				
			||||||
 | 
									} : null,
 | 
				
			||||||
 | 
									github: profile!.github ? {
 | 
				
			||||||
 | 
										id: profile!.githubId,
 | 
				
			||||||
 | 
										login: profile!.githubLogin
 | 
				
			||||||
 | 
									} : null,
 | 
				
			||||||
 | 
									discord: profile!.discord ? {
 | 
				
			||||||
 | 
										id: profile!.discordId,
 | 
				
			||||||
 | 
										username: profile!.discordUsername,
 | 
				
			||||||
 | 
										discriminator: profile!.discordDiscriminator
 | 
				
			||||||
 | 
									} : null,
 | 
				
			||||||
			} : {}),
 | 
								} : {}),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			...(opts.detail && meId === user.id ? {
 | 
								...(opts.detail && meId === user.id ? {
 | 
				
			||||||
| 
						 | 
					@ -217,7 +230,7 @@ export class UserRepository extends Repository<User> {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public isValidBirthday(birthday: string): boolean {
 | 
						public isValidBirthday(birthday: string): boolean {
 | 
				
			||||||
		return typeof birthday == 'string' && /^([0-9]{4})\-([0-9]{2})-([0-9]{2})$/.test(birthday);
 | 
							return typeof birthday == 'string' && /^([0-9]{4})-([0-9]{2})-([0-9]{2})$/.test(birthday);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	//#endregion
 | 
						//#endregion
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -37,7 +37,7 @@ export const meta = {
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		type: {
 | 
							type: {
 | 
				
			||||||
			validator: $.optional.str.match(/^[a-zA-Z\/\-\*]+$/)
 | 
								validator: $.optional.str.match(/^[a-zA-Z\/\-*]+$/)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -27,7 +27,7 @@ export const meta = {
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		type: {
 | 
							type: {
 | 
				
			||||||
			validator: $.optional.str.match(/^[a-zA-Z\/\-\*]+$/)
 | 
								validator: $.optional.str.match(/^[a-zA-Z\/\-*]+$/)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -52,7 +52,7 @@ for (const endpoint of endpoints) {
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		if (endpoint.name.includes('-')) {
 | 
							if (endpoint.name.includes('-')) {
 | 
				
			||||||
			// 後方互換性のため
 | 
								// 後方互換性のため
 | 
				
			||||||
			router.post(`/${endpoint.name.replace(/\-/g, '_')}`, handler.bind(null, endpoint));
 | 
								router.post(`/${endpoint.name.replace(/-/g, '_')}`, handler.bind(null, endpoint));
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		router.post(`/${endpoint.name}`, handler.bind(null, endpoint));
 | 
							router.post(`/${endpoint.name}`, handler.bind(null, endpoint));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,7 +18,7 @@ export function convertSchemaToOpenApiSchema(schema: Schema) {
 | 
				
			||||||
	const res: any = schema;
 | 
						const res: any = schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (schema.type === 'object' && schema.properties) {
 | 
						if (schema.type === 'object' && schema.properties) {
 | 
				
			||||||
		res.required = Object.entries(schema.properties).filter(([k, v]) => v.optional !== true).map(([k]) => k);
 | 
							res.required = Object.entries(schema.properties).filter(([k, v]) => !v.optional).map(([k]) => k);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for (const k of Object.keys(schema.properties)) {
 | 
							for (const k of Object.keys(schema.properties)) {
 | 
				
			||||||
			res.properties[k] = convertSchemaToOpenApiSchema(schema.properties[k]);
 | 
								res.properties[k] = convertSchemaToOpenApiSchema(schema.properties[k]);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -34,7 +34,7 @@
 | 
				
			||||||
		"radix": false,
 | 
							"radix": false,
 | 
				
			||||||
		"ban-types": [
 | 
							"ban-types": [
 | 
				
			||||||
			true,
 | 
								true,
 | 
				
			||||||
			"Object"
 | 
								["Object", "Use {} instead."]
 | 
				
			||||||
		],
 | 
							],
 | 
				
			||||||
		"ban": [
 | 
							"ban": [
 | 
				
			||||||
			true,
 | 
								true,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -95,7 +95,7 @@ module.exports = {
 | 
				
			||||||
				loader: 'css-loader'
 | 
									loader: 'css-loader'
 | 
				
			||||||
			}, postcss]
 | 
								}, postcss]
 | 
				
			||||||
		}, {
 | 
							}, {
 | 
				
			||||||
			test: /\.(eot|woff|woff2|svg|ttf)([\?]?.*)$/,
 | 
								test: /\.(eot|woff|woff2|svg|ttf)([?]?.*)$/,
 | 
				
			||||||
			loader: 'url-loader'
 | 
								loader: 'url-loader'
 | 
				
			||||||
		}, {
 | 
							}, {
 | 
				
			||||||
			test: /\.json5$/,
 | 
								test: /\.json5$/,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue