Merge branch 'develop' into pr/ThatOneCalculator/8764
This commit is contained in:
		
						commit
						61568ad780
					
				
					 19 changed files with 130 additions and 77 deletions
				
			
		|  | @ -20,6 +20,7 @@ You should also include the user name that made the change. | ||||||
| ### Improvements | ### Improvements | ||||||
| 
 | 
 | ||||||
| ### Bugfixes | ### Bugfixes | ||||||
|  | - Server: 引用内の文章がnyaizeされてしまう問題を修正 @kabo2468 | ||||||
| - Server: Bug fix for Pinned Users lookup on instance @squidicuzz | - Server: Bug fix for Pinned Users lookup on instance @squidicuzz | ||||||
| - Client: インスタンスティッカーのfaviconを読み込む際に偽サイト警告が出ることがあるのを修正 @syuilo | - Client: インスタンスティッカーのfaviconを読み込む際に偽サイト警告が出ることがあるのを修正 @syuilo | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										12
									
								
								package.json
									
										
									
									
									
								
							
							
						
						
									
										12
									
								
								package.json
									
										
									
									
									
								
							|  | @ -15,8 +15,8 @@ | ||||||
| 	"private": true, | 	"private": true, | ||||||
| 	"scripts": { | 	"scripts": { | ||||||
| 		"build": "yarn workspaces foreach run build && yarn run gulp", | 		"build": "yarn workspaces foreach run build && yarn run gulp", | ||||||
| 		"start": "cd packages/backend && node --experimental-json-modules ./built/boot/index.js", | 		"start": "cd packages/backend && node ./built/boot/index.js", | ||||||
| 		"start:test": "cd packages/backend && cross-env NODE_ENV=test node --experimental-json-modules ./built/boot/index.js", | 		"start:test": "cd packages/backend && cross-env NODE_ENV=test node ./built/boot/index.js", | ||||||
| 		"init": "yarn migrate", | 		"init": "yarn migrate", | ||||||
| 		"migrate": "cd packages/backend && npx typeorm migration:run -d ormconfig.js", | 		"migrate": "cd packages/backend && npx typeorm migration:run -d ormconfig.js", | ||||||
| 		"migrateandstart": "yarn migrate && yarn start", | 		"migrateandstart": "yarn migrate && yarn start", | ||||||
|  | @ -51,13 +51,13 @@ | ||||||
| 		"js-yaml": "4.1.0" | 		"js-yaml": "4.1.0" | ||||||
| 	}, | 	}, | ||||||
| 	"devDependencies": { | 	"devDependencies": { | ||||||
| 		"@types/gulp": "4.0.9", | 		"@types/gulp": "4.0.10", | ||||||
| 		"@types/gulp-rename": "2.0.1", | 		"@types/gulp-rename": "2.0.1", | ||||||
| 		"@typescript-eslint/eslint-plugin": "latest", | 		"@typescript-eslint/eslint-plugin": "latest", | ||||||
| 		"@typescript-eslint/parser": "5.42.1", | 		"@typescript-eslint/parser": "5.43.0", | ||||||
| 		"cross-env": "7.0.3", | 		"cross-env": "7.0.3", | ||||||
| 		"cypress": "11.0.1", | 		"cypress": "11.1.0", | ||||||
| 		"start-server-and-test": "1.14.0", | 		"start-server-and-test": "1.14.0", | ||||||
| 		"typescript": "4.8.4" | 		"typescript": "4.9.3" | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -35,13 +35,13 @@ | ||||||
| 		"@peertube/http-signature": "1.7.0", | 		"@peertube/http-signature": "1.7.0", | ||||||
| 		"@sinonjs/fake-timers": "10.0.0", | 		"@sinonjs/fake-timers": "10.0.0", | ||||||
| 		"@syuilo/aiscript": "0.11.1", | 		"@syuilo/aiscript": "0.11.1", | ||||||
| 		"ajv": "8.11.0", | 		"ajv": "8.11.2", | ||||||
| 		"archiver": "5.3.1", | 		"archiver": "5.3.1", | ||||||
| 		"autobind-decorator": "2.4.0", | 		"autobind-decorator": "2.4.0", | ||||||
| 		"autwh": "0.1.0", | 		"autwh": "0.1.0", | ||||||
| 		"aws-sdk": "2.1253.0", | 		"aws-sdk": "2.1258.0", | ||||||
| 		"bcryptjs": "2.4.3", | 		"bcryptjs": "2.4.3", | ||||||
| 		"blurhash": "1.1.5", | 		"blurhash": "2.0.4", | ||||||
| 		"bull": "4.10.1", | 		"bull": "4.10.1", | ||||||
| 		"cacheable-lookup": "6.1.0", | 		"cacheable-lookup": "6.1.0", | ||||||
| 		"cbor": "8.1.0", | 		"cbor": "8.1.0", | ||||||
|  | @ -58,10 +58,10 @@ | ||||||
| 		"file-type": "18.0.0", | 		"file-type": "18.0.0", | ||||||
| 		"fluent-ffmpeg": "2.1.2", | 		"fluent-ffmpeg": "2.1.2", | ||||||
| 		"form-data": "^4.0.0", | 		"form-data": "^4.0.0", | ||||||
| 		"got": "12.5.2", | 		"got": "12.5.3", | ||||||
| 		"hpagent": "1.2.0", | 		"hpagent": "1.2.0", | ||||||
| 		"ioredis": "4.28.5", | 		"ioredis": "4.28.5", | ||||||
| 		"ip-cidr": "3.0.10", | 		"ip-cidr": "3.0.11", | ||||||
| 		"is-svg": "4.3.2", | 		"is-svg": "4.3.2", | ||||||
| 		"jest-mock": "^29.0.3", | 		"jest-mock": "^29.0.3", | ||||||
| 		"js-yaml": "4.1.0", | 		"js-yaml": "4.1.0", | ||||||
|  | @ -92,7 +92,7 @@ | ||||||
| 		"os-utils": "0.0.14", | 		"os-utils": "0.0.14", | ||||||
| 		"parse5": "7.1.1", | 		"parse5": "7.1.1", | ||||||
| 		"pg": "8.8.0", | 		"pg": "8.8.0", | ||||||
| 		"private-ip": "2.3.4", | 		"private-ip": "3.0.0", | ||||||
| 		"probe-image-size": "7.2.3", | 		"probe-image-size": "7.2.3", | ||||||
| 		"promise-limit": "2.7.0", | 		"promise-limit": "2.7.0", | ||||||
| 		"pug": "3.0.2", | 		"pug": "3.0.2", | ||||||
|  | @ -118,7 +118,7 @@ | ||||||
| 		"stringz": "2.1.0", | 		"stringz": "2.1.0", | ||||||
| 		"summaly": "2.7.0", | 		"summaly": "2.7.0", | ||||||
| 		"syslog-pro": "1.0.0", | 		"syslog-pro": "1.0.0", | ||||||
| 		"systeminformation": "5.12.14", | 		"systeminformation": "5.13.5", | ||||||
| 		"tinycolor2": "1.4.2", | 		"tinycolor2": "1.4.2", | ||||||
| 		"tmp": "0.2.1", | 		"tmp": "0.2.1", | ||||||
| 		"ts-loader": "9.4.1", | 		"ts-loader": "9.4.1", | ||||||
|  | @ -136,8 +136,8 @@ | ||||||
| 		"xev": "3.0.2" | 		"xev": "3.0.2" | ||||||
| 	}, | 	}, | ||||||
| 	"devDependencies": { | 	"devDependencies": { | ||||||
| 		"@redocly/openapi-core": "1.0.0-beta.112", | 		"@redocly/openapi-core": "1.0.0-beta.114", | ||||||
| 		"@swc/core": "1.3.15", | 		"@swc/core": "1.3.18", | ||||||
| 		"@swc/jest": "0.2.23", | 		"@swc/jest": "0.2.23", | ||||||
| 		"@types/archiver": "5.3.1", | 		"@types/archiver": "5.3.1", | ||||||
| 		"@types/bcryptjs": "2.4.2", | 		"@types/bcryptjs": "2.4.2", | ||||||
|  | @ -145,7 +145,7 @@ | ||||||
| 		"@types/cbor": "6.0.0", | 		"@types/cbor": "6.0.0", | ||||||
| 		"@types/escape-regexp": "0.0.1", | 		"@types/escape-regexp": "0.0.1", | ||||||
| 		"@types/fluent-ffmpeg": "2.1.20", | 		"@types/fluent-ffmpeg": "2.1.20", | ||||||
| 		"@types/jest": "29.2.2", | 		"@types/jest": "29.2.3", | ||||||
| 		"@types/js-yaml": "4.0.5", | 		"@types/js-yaml": "4.0.5", | ||||||
| 		"@types/jsdom": "20.0.1", | 		"@types/jsdom": "20.0.1", | ||||||
| 		"@types/jsonld": "1.5.7", | 		"@types/jsonld": "1.5.7", | ||||||
|  | @ -186,13 +186,13 @@ | ||||||
| 		"@types/web-push": "3.3.2", | 		"@types/web-push": "3.3.2", | ||||||
| 		"@types/websocket": "1.0.5", | 		"@types/websocket": "1.0.5", | ||||||
| 		"@types/ws": "8.5.3", | 		"@types/ws": "8.5.3", | ||||||
| 		"@typescript-eslint/eslint-plugin": "5.42.1", | 		"@typescript-eslint/eslint-plugin": "5.43.0", | ||||||
| 		"@typescript-eslint/parser": "5.42.1", | 		"@typescript-eslint/parser": "5.43.0", | ||||||
| 		"cross-env": "7.0.3", | 		"cross-env": "7.0.3", | ||||||
| 		"eslint": "8.27.0", | 		"eslint": "8.28.0", | ||||||
| 		"eslint-plugin-import": "2.26.0", | 		"eslint-plugin-import": "2.26.0", | ||||||
| 		"execa": "6.1.0", | 		"execa": "6.1.0", | ||||||
| 		"jest": "29.3.1", | 		"jest": "29.3.1", | ||||||
| 		"typescript": "4.8.4" | 		"typescript": "4.9.3" | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -9,6 +9,7 @@ import { QueueService } from '@/core/QueueService.js'; | ||||||
| import { CreateSystemUserService } from '@/core/CreateSystemUserService.js'; | import { CreateSystemUserService } from '@/core/CreateSystemUserService.js'; | ||||||
| import { ApRendererService } from '@/core/remote/activitypub/ApRendererService.js'; | import { ApRendererService } from '@/core/remote/activitypub/ApRendererService.js'; | ||||||
| import { DI } from '@/di-symbols.js'; | import { DI } from '@/di-symbols.js'; | ||||||
|  | import { deepClone } from '@/misc/clone.js'; | ||||||
| 
 | 
 | ||||||
| const ACTOR_USERNAME = 'relay.actor' as const; | const ACTOR_USERNAME = 'relay.actor' as const; | ||||||
| 
 | 
 | ||||||
|  | @ -105,7 +106,7 @@ export class RelayService { | ||||||
| 		})); | 		})); | ||||||
| 		if (relays.length === 0) return; | 		if (relays.length === 0) return; | ||||||
| 	 | 	 | ||||||
| 		const copy = structuredClone(activity); | 		const copy = deepClone(activity); | ||||||
| 		if (!copy.to) copy.to = ['https://www.w3.org/ns/activitystreams#Public']; | 		if (!copy.to) copy.to = ['https://www.w3.org/ns/activitystreams#Public']; | ||||||
| 	 | 	 | ||||||
| 		const signed = await this.apRendererService.attachLdSignature(copy, user); | 		const signed = await this.apRendererService.attachLdSignature(copy, user); | ||||||
|  |  | ||||||
|  | @ -9,6 +9,7 @@ import { awaitAll } from '@/misc/prelude/await-all.js'; | ||||||
| import type { User } from '@/models/entities/User.js'; | import type { User } from '@/models/entities/User.js'; | ||||||
| import type { DriveFile } from '@/models/entities/DriveFile.js'; | import type { DriveFile } from '@/models/entities/DriveFile.js'; | ||||||
| import { appendQuery, query } from '@/misc/prelude/url.js'; | import { appendQuery, query } from '@/misc/prelude/url.js'; | ||||||
|  | import { deepClone } from '@/misc/clone.js'; | ||||||
| import { UtilityService } from '../UtilityService.js'; | import { UtilityService } from '../UtilityService.js'; | ||||||
| import { UserEntityService } from './UserEntityService.js'; | import { UserEntityService } from './UserEntityService.js'; | ||||||
| import { DriveFolderEntityService } from './DriveFolderEntityService.js'; | import { DriveFolderEntityService } from './DriveFolderEntityService.js'; | ||||||
|  | @ -55,7 +56,7 @@ export class DriveFileEntityService { | ||||||
| 
 | 
 | ||||||
| 	public getPublicProperties(file: DriveFile): DriveFile['properties'] { | 	public getPublicProperties(file: DriveFile): DriveFile['properties'] { | ||||||
| 		if (file.properties.orientation != null) { | 		if (file.properties.orientation != null) { | ||||||
| 			const properties = structuredClone(file.properties); | 			const properties = deepClone(file.properties); | ||||||
| 			if (file.properties.orientation >= 5) { | 			if (file.properties.orientation >= 5) { | ||||||
| 				[properties.width, properties.height] = [properties.height, properties.width]; | 				[properties.width, properties.height] = [properties.height, properties.width]; | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
|  | @ -329,12 +329,20 @@ export class NoteEntityService implements OnModuleInit { | ||||||
| 
 | 
 | ||||||
| 		if (packed.user.isCat && packed.text) { | 		if (packed.user.isCat && packed.text) { | ||||||
| 			const tokens = packed.text ? mfm.parse(packed.text) : []; | 			const tokens = packed.text ? mfm.parse(packed.text) : []; | ||||||
| 			mfm.inspect(tokens, node => { | 			function nyaizeNode(node: mfm.MfmNode) { | ||||||
|  | 				if (node.type === 'quote') return; | ||||||
| 				if (node.type === 'text') { | 				if (node.type === 'text') { | ||||||
| 					// TODO: quoteなtextはskip
 |  | ||||||
| 					node.props.text = nyaize(node.props.text); | 					node.props.text = nyaize(node.props.text); | ||||||
| 				} | 				} | ||||||
| 			}); | 				if (node.children) { | ||||||
|  | 					for (const child of node.children) { | ||||||
|  | 						nyaizeNode(child); | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			for (const node of tokens) { | ||||||
|  | 				nyaizeNode(node); | ||||||
|  | 			} | ||||||
| 			packed.text = mfm.toString(tokens); | 			packed.text = mfm.toString(tokens); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										18
									
								
								packages/backend/src/misc/clone.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								packages/backend/src/misc/clone.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | ||||||
|  | // structredCloneが遅いため
 | ||||||
|  | // SEE: http://var.blog.jp/archives/86038606.html
 | ||||||
|  | 
 | ||||||
|  | type Cloneable = string | number | boolean | null | { [key: string]: Cloneable } | Cloneable[]; | ||||||
|  | 
 | ||||||
|  | export function deepClone<T extends Cloneable>(x: T): T { | ||||||
|  | 	if (typeof x === 'object') { | ||||||
|  | 		if (x === null) return x; | ||||||
|  | 		if (Array.isArray(x)) return x.map(deepClone) as T; | ||||||
|  | 		const obj = {} as Record<string, Cloneable>; | ||||||
|  | 		for (const [k, v] of Object.entries(x)) { | ||||||
|  | 			obj[k] = deepClone(v); | ||||||
|  | 		} | ||||||
|  | 		return obj as T; | ||||||
|  | 	} else { | ||||||
|  | 		return x; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -26,6 +26,7 @@ import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityServi | ||||||
| import { ClipEntityService } from '@/core/entities/ClipEntityService.js'; | import { ClipEntityService } from '@/core/entities/ClipEntityService.js'; | ||||||
| import { ChannelEntityService } from '@/core/entities/ChannelEntityService.js'; | import { ChannelEntityService } from '@/core/entities/ChannelEntityService.js'; | ||||||
| import type { ChannelsRepository, ClipsRepository, GalleryPostsRepository, NotesRepository, PagesRepository, UserProfilesRepository, UsersRepository } from '@/models/index.js'; | import type { ChannelsRepository, ClipsRepository, GalleryPostsRepository, NotesRepository, PagesRepository, UserProfilesRepository, UsersRepository } from '@/models/index.js'; | ||||||
|  | import { deepClone } from '@/misc/clone.js'; | ||||||
| import manifest from './manifest.json' assert { type: 'json' }; | import manifest from './manifest.json' assert { type: 'json' }; | ||||||
| import { FeedService } from './FeedService.js'; | import { FeedService } from './FeedService.js'; | ||||||
| import { UrlPreviewService } from './UrlPreviewService.js'; | import { UrlPreviewService } from './UrlPreviewService.js'; | ||||||
|  | @ -86,7 +87,7 @@ export class ClientServerService { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	private async manifestHandler(ctx: Koa.Context) { | 	private async manifestHandler(ctx: Koa.Context) { | ||||||
| 		const res = structuredClone(manifest); | 		const res = deepClone(manifest); | ||||||
| 
 | 
 | ||||||
| 		const instance = await this.metaService.fetch(true); | 		const instance = await this.metaService.fetch(true); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -17,11 +17,11 @@ | ||||||
| 		"@vue/compiler-sfc": "3.2.45", | 		"@vue/compiler-sfc": "3.2.45", | ||||||
| 		"autobind-decorator": "2.4.0", | 		"autobind-decorator": "2.4.0", | ||||||
| 		"autosize": "5.0.1", | 		"autosize": "5.0.1", | ||||||
| 		"blurhash": "1.1.5", | 		"blurhash": "2.0.4", | ||||||
| 		"broadcast-channel": "4.18.1", | 		"broadcast-channel": "4.18.1", | ||||||
| 		"browser-image-resizer": "git+https://github.com/misskey-dev/browser-image-resizer#v2.2.1-misskey.3", | 		"browser-image-resizer": "git+https://github.com/misskey-dev/browser-image-resizer#v2.2.1-misskey.3", | ||||||
| 		"chart.js": "3.9.1", | 		"chart.js": "4.0.1", | ||||||
| 		"chartjs-adapter-date-fns": "2.0.0", | 		"chartjs-adapter-date-fns": "2.0.1", | ||||||
| 		"chartjs-plugin-gradient": "0.5.1", | 		"chartjs-plugin-gradient": "0.5.1", | ||||||
| 		"chartjs-plugin-zoom": "1.2.1", | 		"chartjs-plugin-zoom": "1.2.1", | ||||||
| 		"compare-versions": "5.0.1", | 		"compare-versions": "5.0.1", | ||||||
|  | @ -54,10 +54,10 @@ | ||||||
| 		"tsc-alias": "1.7.1", | 		"tsc-alias": "1.7.1", | ||||||
| 		"tsconfig-paths": "4.1.0", | 		"tsconfig-paths": "4.1.0", | ||||||
| 		"twemoji-parser": "14.0.0", | 		"twemoji-parser": "14.0.0", | ||||||
| 		"typescript": "4.8.4", | 		"typescript": "4.9.3", | ||||||
| 		"uuid": "9.0.0", | 		"uuid": "9.0.0", | ||||||
| 		"vanilla-tilt": "1.7.3", | 		"vanilla-tilt": "1.7.3", | ||||||
| 		"vite": "3.2.3", | 		"vite": "3.2.4", | ||||||
| 		"vue": "3.2.45", | 		"vue": "3.2.45", | ||||||
| 		"vue-prism-editor": "2.0.0-alpha.2", | 		"vue-prism-editor": "2.0.0-alpha.2", | ||||||
| 		"vuedraggable": "4.0.1" | 		"vuedraggable": "4.0.1" | ||||||
|  | @ -65,7 +65,7 @@ | ||||||
| 	"devDependencies": { | 	"devDependencies": { | ||||||
| 		"@types/escape-regexp": "0.0.1", | 		"@types/escape-regexp": "0.0.1", | ||||||
| 		"@types/glob": "8.0.0", | 		"@types/glob": "8.0.0", | ||||||
| 		"@types/gulp": "4.0.9", | 		"@types/gulp": "4.0.10", | ||||||
| 		"@types/gulp-rename": "2.0.1", | 		"@types/gulp-rename": "2.0.1", | ||||||
| 		"@types/katex": "0.14.0", | 		"@types/katex": "0.14.0", | ||||||
| 		"@types/matter-js": "0.18.2", | 		"@types/matter-js": "0.18.2", | ||||||
|  | @ -76,11 +76,11 @@ | ||||||
| 		"@types/uuid": "8.3.4", | 		"@types/uuid": "8.3.4", | ||||||
| 		"@types/websocket": "1.0.5", | 		"@types/websocket": "1.0.5", | ||||||
| 		"@types/ws": "8.5.3", | 		"@types/ws": "8.5.3", | ||||||
| 		"@typescript-eslint/eslint-plugin": "5.42.1", | 		"@typescript-eslint/eslint-plugin": "5.43.0", | ||||||
| 		"@typescript-eslint/parser": "5.42.1", | 		"@typescript-eslint/parser": "5.43.0", | ||||||
| 		"cross-env": "7.0.3", | 		"cross-env": "7.0.3", | ||||||
| 		"cypress": "11.0.1", | 		"cypress": "11.1.0", | ||||||
| 		"eslint": "8.27.0", | 		"eslint": "8.28.0", | ||||||
| 		"eslint-plugin-import": "2.26.0", | 		"eslint-plugin-import": "2.26.0", | ||||||
| 		"eslint-plugin-vue": "9.7.0", | 		"eslint-plugin-vue": "9.7.0", | ||||||
| 		"rollup": "3.3.0", | 		"rollup": "3.3.0", | ||||||
|  |  | ||||||
|  | @ -129,6 +129,7 @@ import { $i } from '@/account'; | ||||||
| import { i18n } from '@/i18n'; | import { i18n } from '@/i18n'; | ||||||
| import { getNoteMenu } from '@/scripts/get-note-menu'; | import { getNoteMenu } from '@/scripts/get-note-menu'; | ||||||
| import { useNoteCapture } from '@/scripts/use-note-capture'; | import { useNoteCapture } from '@/scripts/use-note-capture'; | ||||||
|  | import { deepClone } from '@/scripts/clone'; | ||||||
| 
 | 
 | ||||||
| const props = defineProps<{ | const props = defineProps<{ | ||||||
| 	note: misskey.entities.Note; | 	note: misskey.entities.Note; | ||||||
|  | @ -137,12 +138,12 @@ const props = defineProps<{ | ||||||
| 
 | 
 | ||||||
| const inChannel = inject('inChannel', null); | const inChannel = inject('inChannel', null); | ||||||
| 
 | 
 | ||||||
| let note = $ref(JSON.parse(JSON.stringify(props.note))); | let note = $ref(deepClone(props.note)); | ||||||
| 
 | 
 | ||||||
| // plugin | // plugin | ||||||
| if (noteViewInterruptors.length > 0) { | if (noteViewInterruptors.length > 0) { | ||||||
| 	onMounted(async () => { | 	onMounted(async () => { | ||||||
| 		let result = JSON.parse(JSON.stringify(note)); | 		let result = deepClone(note); | ||||||
| 		for (const interruptor of noteViewInterruptors) { | 		for (const interruptor of noteViewInterruptors) { | ||||||
| 			result = await interruptor.handler(result); | 			result = await interruptor.handler(result); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | @ -139,6 +139,7 @@ import { $i } from '@/account'; | ||||||
| import { i18n } from '@/i18n'; | import { i18n } from '@/i18n'; | ||||||
| import { getNoteMenu } from '@/scripts/get-note-menu'; | import { getNoteMenu } from '@/scripts/get-note-menu'; | ||||||
| import { useNoteCapture } from '@/scripts/use-note-capture'; | import { useNoteCapture } from '@/scripts/use-note-capture'; | ||||||
|  | import { deepClone } from '@/scripts/clone'; | ||||||
| 
 | 
 | ||||||
| const props = defineProps<{ | const props = defineProps<{ | ||||||
| 	note: misskey.entities.Note; | 	note: misskey.entities.Note; | ||||||
|  | @ -147,12 +148,12 @@ const props = defineProps<{ | ||||||
| 
 | 
 | ||||||
| const inChannel = inject('inChannel', null); | const inChannel = inject('inChannel', null); | ||||||
| 
 | 
 | ||||||
| let note = $ref(JSON.parse(JSON.stringify(props.note))); | let note = $ref(deepClone(props.note)); | ||||||
| 
 | 
 | ||||||
| // plugin | // plugin | ||||||
| if (noteViewInterruptors.length > 0) { | if (noteViewInterruptors.length > 0) { | ||||||
| 	onMounted(async () => { | 	onMounted(async () => { | ||||||
| 		let result = JSON.parse(JSON.stringify(note)); | 		let result = deepClone(note); | ||||||
| 		for (const interruptor of noteViewInterruptors) { | 		for (const interruptor of noteViewInterruptors) { | ||||||
| 			result = await interruptor.handler(result); | 			result = await interruptor.handler(result); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | @ -89,6 +89,7 @@ import { i18n } from '@/i18n'; | ||||||
| import { instance } from '@/instance'; | import { instance } from '@/instance'; | ||||||
| import { $i, getAccounts, openAccountMenu as openAccountMenu_ } from '@/account'; | import { $i, getAccounts, openAccountMenu as openAccountMenu_ } from '@/account'; | ||||||
| import { uploadFile } from '@/scripts/upload'; | import { uploadFile } from '@/scripts/upload'; | ||||||
|  | import { deepClone } from '@/scripts/clone'; | ||||||
| 
 | 
 | ||||||
| const modal = inject('modal'); | const modal = inject('modal'); | ||||||
| 
 | 
 | ||||||
|  | @ -575,7 +576,7 @@ async function post() { | ||||||
| 	// plugin | 	// plugin | ||||||
| 	if (notePostInterruptors.length > 0) { | 	if (notePostInterruptors.length > 0) { | ||||||
| 		for (const interruptor of notePostInterruptors) { | 		for (const interruptor of notePostInterruptors) { | ||||||
| 			postData = await interruptor.handler(JSON.parse(JSON.stringify(postData))); | 			postData = await interruptor.handler(deepClone(postData)); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -66,8 +66,9 @@ import * as os from '@/os'; | ||||||
| import { defaultStore } from '@/store'; | import { defaultStore } from '@/store'; | ||||||
| import { i18n } from '@/i18n'; | import { i18n } from '@/i18n'; | ||||||
| import { definePageMetadata } from '@/scripts/page-metadata'; | import { definePageMetadata } from '@/scripts/page-metadata'; | ||||||
|  | import { deepClone } from '@/scripts/clone'; | ||||||
| 
 | 
 | ||||||
| let reactions = $ref(JSON.parse(JSON.stringify(defaultStore.state.reactions))); | let reactions = $ref(deepClone(defaultStore.state.reactions)); | ||||||
| 
 | 
 | ||||||
| const reactionPickerSize = $computed(defaultStore.makeGetterSetter('reactionPickerSize')); | const reactionPickerSize = $computed(defaultStore.makeGetterSetter('reactionPickerSize')); | ||||||
| const reactionPickerWidth = $computed(defaultStore.makeGetterSetter('reactionPickerWidth')); | const reactionPickerWidth = $computed(defaultStore.makeGetterSetter('reactionPickerWidth')); | ||||||
|  | @ -101,7 +102,7 @@ async function setDefault() { | ||||||
| 	}); | 	}); | ||||||
| 	if (canceled) return; | 	if (canceled) return; | ||||||
| 
 | 
 | ||||||
| 	reactions = JSON.parse(JSON.stringify(defaultStore.def.reactions.default)); | 	reactions = deepClone(defaultStore.def.reactions.default); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function chooseEmoji(ev: MouseEvent) { | function chooseEmoji(ev: MouseEvent) { | ||||||
|  |  | ||||||
|  | @ -91,13 +91,14 @@ import FormRange from '@/components/form/range.vue'; | ||||||
| import * as os from '@/os'; | import * as os from '@/os'; | ||||||
| import { defaultStore } from '@/store'; | import { defaultStore } from '@/store'; | ||||||
| import { i18n } from '@/i18n'; | import { i18n } from '@/i18n'; | ||||||
|  | import { deepClone } from '@/scripts/clone'; | ||||||
| 
 | 
 | ||||||
| const props = defineProps<{ | const props = defineProps<{ | ||||||
| 	_id: string; | 	_id: string; | ||||||
| 	userLists: any[] | null; | 	userLists: any[] | null; | ||||||
| }>(); | }>(); | ||||||
| 
 | 
 | ||||||
| const statusbar = reactive(JSON.parse(JSON.stringify(defaultStore.state.statusbars.find(x => x.id === props._id)))); | const statusbar = reactive(deepClone(defaultStore.state.statusbars.find(x => x.id === props._id))); | ||||||
| 
 | 
 | ||||||
| watch(() => statusbar.type, () => { | watch(() => statusbar.type, () => { | ||||||
| 	if (statusbar.type === 'rss') { | 	if (statusbar.type === 'rss') { | ||||||
|  | @ -128,8 +129,8 @@ watch(statusbar, save); | ||||||
| 
 | 
 | ||||||
| async function save() { | async function save() { | ||||||
| 	const i = defaultStore.state.statusbars.findIndex(x => x.id === props._id); | 	const i = defaultStore.state.statusbars.findIndex(x => x.id === props._id); | ||||||
| 	const statusbars = JSON.parse(JSON.stringify(defaultStore.state.statusbars)); | 	const statusbars = deepClone(defaultStore.state.statusbars); | ||||||
| 	statusbars[i] = JSON.parse(JSON.stringify(statusbar)); | 	statusbars[i] = deepClone(statusbar); | ||||||
| 	defaultStore.set('statusbars', statusbars); | 	defaultStore.set('statusbars', statusbars); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										18
									
								
								packages/client/src/scripts/clone.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								packages/client/src/scripts/clone.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | ||||||
|  | // structredCloneが遅いため
 | ||||||
|  | // SEE: http://var.blog.jp/archives/86038606.html
 | ||||||
|  | 
 | ||||||
|  | type Cloneable = string | number | boolean | null | { [key: string]: Cloneable } | Cloneable[]; | ||||||
|  | 
 | ||||||
|  | export function deepClone<T extends Cloneable>(x: T): T { | ||||||
|  | 	if (typeof x === 'object') { | ||||||
|  | 		if (x === null) return x; | ||||||
|  | 		if (Array.isArray(x)) return x.map(deepClone) as T; | ||||||
|  | 		const obj = {} as Record<string, Cloneable>; | ||||||
|  | 		for (const [k, v] of Object.entries(x)) { | ||||||
|  | 			obj[k] = deepClone(v); | ||||||
|  | 		} | ||||||
|  | 		return obj as T; | ||||||
|  | 	} else { | ||||||
|  | 		return x; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -13,6 +13,7 @@ export type Theme = { | ||||||
| 
 | 
 | ||||||
| import lightTheme from '@/themes/_light.json5'; | import lightTheme from '@/themes/_light.json5'; | ||||||
| import darkTheme from '@/themes/_dark.json5'; | import darkTheme from '@/themes/_dark.json5'; | ||||||
|  | import { deepClone } from './clone'; | ||||||
| 
 | 
 | ||||||
| export const themeProps = Object.keys(lightTheme.props).filter(key => !key.startsWith('X')); | export const themeProps = Object.keys(lightTheme.props).filter(key => !key.startsWith('X')); | ||||||
| 
 | 
 | ||||||
|  | @ -60,7 +61,7 @@ export function applyTheme(theme: Theme, persist = true) { | ||||||
| 	const colorSchema = theme.base === 'dark' ? 'dark' : 'light'; | 	const colorSchema = theme.base === 'dark' ? 'dark' : 'light'; | ||||||
| 
 | 
 | ||||||
| 	// Deep copy
 | 	// Deep copy
 | ||||||
| 	const _theme = JSON.parse(JSON.stringify(theme)); | 	const _theme = deepClone(theme); | ||||||
| 
 | 
 | ||||||
| 	if (_theme.base) { | 	if (_theme.base) { | ||||||
| 		const base = [lightTheme, darkTheme].find(x => x.id === _theme.base); | 		const base = [lightTheme, darkTheme].find(x => x.id === _theme.base); | ||||||
|  |  | ||||||
|  | @ -4,6 +4,7 @@ import { notificationTypes } from 'misskey-js'; | ||||||
| import { Storage } from '../../pizzax'; | import { Storage } from '../../pizzax'; | ||||||
| import { i18n } from '@/i18n'; | import { i18n } from '@/i18n'; | ||||||
| import { api } from '@/os'; | import { api } from '@/os'; | ||||||
|  | import { deepClone } from '@/scripts/clone'; | ||||||
| 
 | 
 | ||||||
| type ColumnWidget = { | type ColumnWidget = { | ||||||
| 	name: string; | 	name: string; | ||||||
|  | @ -25,10 +26,6 @@ export type Column = { | ||||||
| 	tl?: 'home' | 'local' | 'social' | 'global'; | 	tl?: 'home' | 'local' | 'social' | 'global'; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| function copy<T>(x: T): T { |  | ||||||
| 	return JSON.parse(JSON.stringify(x)); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export const deckStore = markRaw(new Storage('deck', { | export const deckStore = markRaw(new Storage('deck', { | ||||||
| 	profile: { | 	profile: { | ||||||
| 		where: 'deviceAccount', | 		where: 'deviceAccount', | ||||||
|  | @ -128,7 +125,7 @@ export function swapColumn(a: Column['id'], b: Column['id']) { | ||||||
| 	const aY = deckStore.state.layout[aX].findIndex(id => id === a); | 	const aY = deckStore.state.layout[aX].findIndex(id => id === a); | ||||||
| 	const bX = deckStore.state.layout.findIndex(ids => ids.indexOf(b) !== -1); | 	const bX = deckStore.state.layout.findIndex(ids => ids.indexOf(b) !== -1); | ||||||
| 	const bY = deckStore.state.layout[bX].findIndex(id => id === b); | 	const bY = deckStore.state.layout[bX].findIndex(id => id === b); | ||||||
| 	const layout = copy(deckStore.state.layout); | 	const layout = deepClone(deckStore.state.layout); | ||||||
| 	layout[aX][aY] = b; | 	layout[aX][aY] = b; | ||||||
| 	layout[bX][bY] = a; | 	layout[bX][bY] = a; | ||||||
| 	deckStore.set('layout', layout); | 	deckStore.set('layout', layout); | ||||||
|  | @ -136,7 +133,7 @@ export function swapColumn(a: Column['id'], b: Column['id']) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function swapLeftColumn(id: Column['id']) { | export function swapLeftColumn(id: Column['id']) { | ||||||
| 	const layout = copy(deckStore.state.layout); | 	const layout = deepClone(deckStore.state.layout); | ||||||
| 	deckStore.state.layout.some((ids, i) => { | 	deckStore.state.layout.some((ids, i) => { | ||||||
| 		if (ids.includes(id)) { | 		if (ids.includes(id)) { | ||||||
| 			const left = deckStore.state.layout[i - 1]; | 			const left = deckStore.state.layout[i - 1]; | ||||||
|  | @ -152,7 +149,7 @@ export function swapLeftColumn(id: Column['id']) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function swapRightColumn(id: Column['id']) { | export function swapRightColumn(id: Column['id']) { | ||||||
| 	const layout = copy(deckStore.state.layout); | 	const layout = deepClone(deckStore.state.layout); | ||||||
| 	deckStore.state.layout.some((ids, i) => { | 	deckStore.state.layout.some((ids, i) => { | ||||||
| 		if (ids.includes(id)) { | 		if (ids.includes(id)) { | ||||||
| 			const right = deckStore.state.layout[i + 1]; | 			const right = deckStore.state.layout[i + 1]; | ||||||
|  | @ -168,9 +165,9 @@ export function swapRightColumn(id: Column['id']) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function swapUpColumn(id: Column['id']) { | export function swapUpColumn(id: Column['id']) { | ||||||
| 	const layout = copy(deckStore.state.layout); | 	const layout = deepClone(deckStore.state.layout); | ||||||
| 	const idsIndex = deckStore.state.layout.findIndex(ids => ids.includes(id)); | 	const idsIndex = deckStore.state.layout.findIndex(ids => ids.includes(id)); | ||||||
| 	const ids = copy(deckStore.state.layout[idsIndex]); | 	const ids = deepClone(deckStore.state.layout[idsIndex]); | ||||||
| 	ids.some((x, i) => { | 	ids.some((x, i) => { | ||||||
| 		if (x === id) { | 		if (x === id) { | ||||||
| 			const up = ids[i - 1]; | 			const up = ids[i - 1]; | ||||||
|  | @ -188,9 +185,9 @@ export function swapUpColumn(id: Column['id']) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function swapDownColumn(id: Column['id']) { | export function swapDownColumn(id: Column['id']) { | ||||||
| 	const layout = copy(deckStore.state.layout); | 	const layout = deepClone(deckStore.state.layout); | ||||||
| 	const idsIndex = deckStore.state.layout.findIndex(ids => ids.includes(id)); | 	const idsIndex = deckStore.state.layout.findIndex(ids => ids.includes(id)); | ||||||
| 	const ids = copy(deckStore.state.layout[idsIndex]); | 	const ids = deepClone(deckStore.state.layout[idsIndex]); | ||||||
| 	ids.some((x, i) => { | 	ids.some((x, i) => { | ||||||
| 		if (x === id) { | 		if (x === id) { | ||||||
| 			const down = ids[i + 1]; | 			const down = ids[i + 1]; | ||||||
|  | @ -208,7 +205,7 @@ export function swapDownColumn(id: Column['id']) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function stackLeftColumn(id: Column['id']) { | export function stackLeftColumn(id: Column['id']) { | ||||||
| 	let layout = copy(deckStore.state.layout); | 	let layout = deepClone(deckStore.state.layout); | ||||||
| 	const i = deckStore.state.layout.findIndex(ids => ids.includes(id)); | 	const i = deckStore.state.layout.findIndex(ids => ids.includes(id)); | ||||||
| 	layout = layout.map(ids => ids.filter(_id => _id !== id)); | 	layout = layout.map(ids => ids.filter(_id => _id !== id)); | ||||||
| 	layout[i - 1].push(id); | 	layout[i - 1].push(id); | ||||||
|  | @ -218,7 +215,7 @@ export function stackLeftColumn(id: Column['id']) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function popRightColumn(id: Column['id']) { | export function popRightColumn(id: Column['id']) { | ||||||
| 	let layout = copy(deckStore.state.layout); | 	let layout = deepClone(deckStore.state.layout); | ||||||
| 	const i = deckStore.state.layout.findIndex(ids => ids.includes(id)); | 	const i = deckStore.state.layout.findIndex(ids => ids.includes(id)); | ||||||
| 	const affected = layout[i]; | 	const affected = layout[i]; | ||||||
| 	layout = layout.map(ids => ids.filter(_id => _id !== id)); | 	layout = layout.map(ids => ids.filter(_id => _id !== id)); | ||||||
|  | @ -226,7 +223,7 @@ export function popRightColumn(id: Column['id']) { | ||||||
| 	layout = layout.filter(ids => ids.length > 0); | 	layout = layout.filter(ids => ids.length > 0); | ||||||
| 	deckStore.set('layout', layout); | 	deckStore.set('layout', layout); | ||||||
| 
 | 
 | ||||||
| 	const columns = copy(deckStore.state.columns); | 	const columns = deepClone(deckStore.state.columns); | ||||||
| 	for (const column of columns) { | 	for (const column of columns) { | ||||||
| 		if (affected.includes(column.id)) { | 		if (affected.includes(column.id)) { | ||||||
| 			column.active = true; | 			column.active = true; | ||||||
|  | @ -238,9 +235,9 @@ export function popRightColumn(id: Column['id']) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function addColumnWidget(id: Column['id'], widget: ColumnWidget) { | export function addColumnWidget(id: Column['id'], widget: ColumnWidget) { | ||||||
| 	const columns = copy(deckStore.state.columns); | 	const columns = deepClone(deckStore.state.columns); | ||||||
| 	const columnIndex = deckStore.state.columns.findIndex(c => c.id === id); | 	const columnIndex = deckStore.state.columns.findIndex(c => c.id === id); | ||||||
| 	const column = copy(deckStore.state.columns[columnIndex]); | 	const column = deepClone(deckStore.state.columns[columnIndex]); | ||||||
| 	if (column == null) return; | 	if (column == null) return; | ||||||
| 	if (column.widgets == null) column.widgets = []; | 	if (column.widgets == null) column.widgets = []; | ||||||
| 	column.widgets.unshift(widget); | 	column.widgets.unshift(widget); | ||||||
|  | @ -250,9 +247,9 @@ export function addColumnWidget(id: Column['id'], widget: ColumnWidget) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function removeColumnWidget(id: Column['id'], widget: ColumnWidget) { | export function removeColumnWidget(id: Column['id'], widget: ColumnWidget) { | ||||||
| 	const columns = copy(deckStore.state.columns); | 	const columns = deepClone(deckStore.state.columns); | ||||||
| 	const columnIndex = deckStore.state.columns.findIndex(c => c.id === id); | 	const columnIndex = deckStore.state.columns.findIndex(c => c.id === id); | ||||||
| 	const column = copy(deckStore.state.columns[columnIndex]); | 	const column = deepClone(deckStore.state.columns[columnIndex]); | ||||||
| 	if (column == null) return; | 	if (column == null) return; | ||||||
| 	column.widgets = column.widgets.filter(w => w.id !== widget.id); | 	column.widgets = column.widgets.filter(w => w.id !== widget.id); | ||||||
| 	columns[columnIndex] = column; | 	columns[columnIndex] = column; | ||||||
|  | @ -261,9 +258,9 @@ export function removeColumnWidget(id: Column['id'], widget: ColumnWidget) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function setColumnWidgets(id: Column['id'], widgets: ColumnWidget[]) { | export function setColumnWidgets(id: Column['id'], widgets: ColumnWidget[]) { | ||||||
| 	const columns = copy(deckStore.state.columns); | 	const columns = deepClone(deckStore.state.columns); | ||||||
| 	const columnIndex = deckStore.state.columns.findIndex(c => c.id === id); | 	const columnIndex = deckStore.state.columns.findIndex(c => c.id === id); | ||||||
| 	const column = copy(deckStore.state.columns[columnIndex]); | 	const column = deepClone(deckStore.state.columns[columnIndex]); | ||||||
| 	if (column == null) return; | 	if (column == null) return; | ||||||
| 	column.widgets = widgets; | 	column.widgets = widgets; | ||||||
| 	columns[columnIndex] = column; | 	columns[columnIndex] = column; | ||||||
|  | @ -272,9 +269,9 @@ export function setColumnWidgets(id: Column['id'], widgets: ColumnWidget[]) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function updateColumnWidget(id: Column['id'], widgetId: string, widgetData: any) { | export function updateColumnWidget(id: Column['id'], widgetId: string, widgetData: any) { | ||||||
| 	const columns = copy(deckStore.state.columns); | 	const columns = deepClone(deckStore.state.columns); | ||||||
| 	const columnIndex = deckStore.state.columns.findIndex(c => c.id === id); | 	const columnIndex = deckStore.state.columns.findIndex(c => c.id === id); | ||||||
| 	const column = copy(deckStore.state.columns[columnIndex]); | 	const column = deepClone(deckStore.state.columns[columnIndex]); | ||||||
| 	if (column == null) return; | 	if (column == null) return; | ||||||
| 	column.widgets = column.widgets.map(w => w.id === widgetId ? { | 	column.widgets = column.widgets.map(w => w.id === widgetId ? { | ||||||
| 		...w, | 		...w, | ||||||
|  | @ -286,9 +283,9 @@ export function updateColumnWidget(id: Column['id'], widgetId: string, widgetDat | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function updateColumn(id: Column['id'], column: Partial<Column>) { | export function updateColumn(id: Column['id'], column: Partial<Column>) { | ||||||
| 	const columns = copy(deckStore.state.columns); | 	const columns = deepClone(deckStore.state.columns); | ||||||
| 	const columnIndex = deckStore.state.columns.findIndex(c => c.id === id); | 	const columnIndex = deckStore.state.columns.findIndex(c => c.id === id); | ||||||
| 	const currentColumn = copy(deckStore.state.columns[columnIndex]); | 	const currentColumn = deepClone(deckStore.state.columns[columnIndex]); | ||||||
| 	if (currentColumn == null) return; | 	if (currentColumn == null) return; | ||||||
| 	for (const [k, v] of Object.entries(column)) { | 	for (const [k, v] of Object.entries(column)) { | ||||||
| 		currentColumn[k] = v; | 		currentColumn[k] = v; | ||||||
|  |  | ||||||
|  | @ -47,12 +47,13 @@ | ||||||
| 
 | 
 | ||||||
| <script lang="ts" setup> | <script lang="ts" setup> | ||||||
| import { onMounted, onUnmounted, reactive, ref } from 'vue'; | import { onMounted, onUnmounted, reactive, ref } from 'vue'; | ||||||
| import { GetFormResultType } from '@/scripts/form'; |  | ||||||
| import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; | import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; | ||||||
|  | import { GetFormResultType } from '@/scripts/form'; | ||||||
| import { stream } from '@/stream'; | import { stream } from '@/stream'; | ||||||
| import number from '@/filters/number'; | import number from '@/filters/number'; | ||||||
| import * as sound from '@/scripts/sound'; | import * as sound from '@/scripts/sound'; | ||||||
| import * as os from '@/os'; | import * as os from '@/os'; | ||||||
|  | import { deepClone } from '@/scripts/clone'; | ||||||
| 
 | 
 | ||||||
| const name = 'jobQueue'; | const name = 'jobQueue'; | ||||||
| 
 | 
 | ||||||
|  | @ -100,12 +101,12 @@ const prev = reactive({} as typeof current); | ||||||
| const jammedSound = sound.setVolume(sound.getAudio('syuilo/queue-jammed'), 1); | const jammedSound = sound.setVolume(sound.getAudio('syuilo/queue-jammed'), 1); | ||||||
| 
 | 
 | ||||||
| for (const domain of ['inbox', 'deliver']) { | for (const domain of ['inbox', 'deliver']) { | ||||||
| 	prev[domain] = JSON.parse(JSON.stringify(current[domain])); | 	prev[domain] = deepClone(current[domain]); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const onStats = (stats) => { | const onStats = (stats) => { | ||||||
| 	for (const domain of ['inbox', 'deliver']) { | 	for (const domain of ['inbox', 'deliver']) { | ||||||
| 		prev[domain] = JSON.parse(JSON.stringify(current[domain])); | 		prev[domain] = deepClone(current[domain]); | ||||||
| 		current[domain].activeSincePrevTick = stats[domain].activeSincePrevTick; | 		current[domain].activeSincePrevTick = stats[domain].activeSincePrevTick; | ||||||
| 		current[domain].active = stats[domain].active; | 		current[domain].active = stats[domain].active; | ||||||
| 		current[domain].waiting = stats[domain].waiting; | 		current[domain].waiting = stats[domain].waiting; | ||||||
|  |  | ||||||
|  | @ -2,6 +2,7 @@ import { reactive, watch } from 'vue'; | ||||||
| import { throttle } from 'throttle-debounce'; | import { throttle } from 'throttle-debounce'; | ||||||
| import { Form, GetFormResultType } from '@/scripts/form'; | import { Form, GetFormResultType } from '@/scripts/form'; | ||||||
| import * as os from '@/os'; | import * as os from '@/os'; | ||||||
|  | import { deepClone } from '@/scripts/clone'; | ||||||
| 
 | 
 | ||||||
| export type Widget<P extends Record<string, unknown>> = { | export type Widget<P extends Record<string, unknown>> = { | ||||||
| 	id: string; | 	id: string; | ||||||
|  | @ -32,7 +33,7 @@ export const useWidgetPropsManager = <F extends Form & Record<string, { default: | ||||||
| 	save: () => void; | 	save: () => void; | ||||||
| 	configure: () => void; | 	configure: () => void; | ||||||
| } => { | } => { | ||||||
| 	const widgetProps = reactive(props.widget ? JSON.parse(JSON.stringify(props.widget.data)) : {}); | 	const widgetProps = reactive(props.widget ? deepClone(props.widget.data) : {}); | ||||||
| 
 | 
 | ||||||
| 	const mergeProps = () => { | 	const mergeProps = () => { | ||||||
| 		for (const prop of Object.keys(propsDef)) { | 		for (const prop of Object.keys(propsDef)) { | ||||||
|  | @ -43,14 +44,14 @@ export const useWidgetPropsManager = <F extends Form & Record<string, { default: | ||||||
| 	}; | 	}; | ||||||
| 	watch(widgetProps, () => { | 	watch(widgetProps, () => { | ||||||
| 		mergeProps(); | 		mergeProps(); | ||||||
| 	}, { deep: true, immediate: true, }); | 	}, { deep: true, immediate: true }); | ||||||
| 
 | 
 | ||||||
| 	const save = throttle(3000, () => { | 	const save = throttle(3000, () => { | ||||||
| 		emit('updateProps', widgetProps); | 		emit('updateProps', widgetProps); | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
| 	const configure = async () => { | 	const configure = async () => { | ||||||
| 		const form = JSON.parse(JSON.stringify(propsDef)); | 		const form = deepClone(propsDef); | ||||||
| 		for (const item of Object.keys(form)) { | 		for (const item of Object.keys(form)) { | ||||||
| 			form[item].default = widgetProps[item]; | 			form[item].default = widgetProps[item]; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue