Compare commits
	
		
			5 commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | e90d8e3b05 | ||
|  | 82eb927dd4 | ||
|  | 446ab176ac | ||
|  | 7177354219 | ||
|  | 4f652dab02 | 
					 12 changed files with 39 additions and 1 deletions
				
			
		|  | @ -26,6 +26,7 @@ You should also include the user name that made the change. | ||||||
|   Your own theme color may be unset if it was in an invalid format. |   Your own theme color may be unset if it was in an invalid format. | ||||||
|   Admins should check their instance settings if in doubt. |   Admins should check their instance settings if in doubt. | ||||||
| - Perform port diagnosis at startup only when Listen fails @mei23 | - Perform port diagnosis at startup only when Listen fails @mei23 | ||||||
|  | - Add pronouns to vCard and display when replying @ThatOneCalculator | ||||||
| 
 | 
 | ||||||
| ### Bugfixes | ### Bugfixes | ||||||
| - Client: fix settings page @tamaina | - Client: fix settings page @tamaina | ||||||
|  |  | ||||||
|  | @ -258,6 +258,7 @@ remoteUserCaution: "As this user is from a remote instance, the shown informatio | ||||||
| activity: "Activity" | activity: "Activity" | ||||||
| images: "Images" | images: "Images" | ||||||
| birthday: "Birthday" | birthday: "Birthday" | ||||||
|  | pronouns: "Pronouns" | ||||||
| yearsOld: "{age} years old" | yearsOld: "{age} years old" | ||||||
| registeredDate: "Joined on" | registeredDate: "Joined on" | ||||||
| location: "Location" | location: "Location" | ||||||
|  |  | ||||||
|  | @ -258,6 +258,7 @@ remoteUserCaution: "リモートユーザーのため、情報が不完全です | ||||||
| activity: "アクティビティ" | activity: "アクティビティ" | ||||||
| images: "画像" | images: "画像" | ||||||
| birthday: "誕生日" | birthday: "誕生日" | ||||||
|  | pronouns: "代名詞" | ||||||
| yearsOld: "{age}歳" | yearsOld: "{age}歳" | ||||||
| registeredDate: "登録日" | registeredDate: "登録日" | ||||||
| location: "場所" | location: "場所" | ||||||
|  |  | ||||||
|  | @ -17,6 +17,12 @@ export class UserProfile { | ||||||
| 	@JoinColumn() | 	@JoinColumn() | ||||||
| 	public user: User | null; | 	public user: User | null; | ||||||
| 
 | 
 | ||||||
|  | 	@Column('varchar', { | ||||||
|  | 		length: 32, nullable: true, | ||||||
|  | 		comment: 'The pronouns of the User.', | ||||||
|  | 	}) | ||||||
|  | 	public pronouns: string | null; | ||||||
|  | 
 | ||||||
| 	@Column('varchar', { | 	@Column('varchar', { | ||||||
| 		length: 128, nullable: true, | 		length: 128, nullable: true, | ||||||
| 		comment: 'The location of the User.', | 		comment: 'The location of the User.', | ||||||
|  |  | ||||||
|  | @ -16,7 +16,7 @@ const userInstanceCache = new Cache<Instance | null>(1000 * 60 * 60 * 3); | ||||||
| 
 | 
 | ||||||
| type IsUserDetailed<Detailed extends boolean> = Detailed extends true ? Packed<'UserDetailed'> : Packed<'UserLite'>; | type IsUserDetailed<Detailed extends boolean> = Detailed extends true ? Packed<'UserDetailed'> : Packed<'UserLite'>; | ||||||
| type IsMeAndIsUserDetailed<ExpectsMe extends boolean | null, Detailed extends boolean> = | type IsMeAndIsUserDetailed<ExpectsMe extends boolean | null, Detailed extends boolean> = | ||||||
| 	Detailed extends true ?  | 	Detailed extends true ? | ||||||
| 		ExpectsMe extends true ? Packed<'MeDetailed'> : | 		ExpectsMe extends true ? Packed<'MeDetailed'> : | ||||||
| 		ExpectsMe extends false ? Packed<'UserDetailedNotMe'> : | 		ExpectsMe extends false ? Packed<'UserDetailedNotMe'> : | ||||||
| 		Packed<'UserDetailed'> : | 		Packed<'UserDetailed'> : | ||||||
|  | @ -30,6 +30,7 @@ const nameSchema = { type: 'string', minLength: 1, maxLength: 50 } as const; | ||||||
| const descriptionSchema = { type: 'string', minLength: 1, maxLength: 500 } as const; | const descriptionSchema = { type: 'string', minLength: 1, maxLength: 500 } as const; | ||||||
| const locationSchema = { type: 'string', minLength: 1, maxLength: 50 } as const; | const locationSchema = { type: 'string', minLength: 1, maxLength: 50 } as const; | ||||||
| const birthdaySchema = { type: 'string', pattern: /^([0-9]{4})-([0-9]{2})-([0-9]{2})$/.toString().slice(1, -1) } as const; | const birthdaySchema = { type: 'string', pattern: /^([0-9]{4})-([0-9]{2})-([0-9]{2})$/.toString().slice(1, -1) } as const; | ||||||
|  | const pronounsSchema = { type: 'string', minLength: 1, maxLength: 20 } as const; | ||||||
| 
 | 
 | ||||||
| function isLocalUser(user: User): user is ILocalUser; | function isLocalUser(user: User): user is ILocalUser; | ||||||
| function isLocalUser<T extends { host: User['host'] }>(user: T): user is T & { host: null; }; | function isLocalUser<T extends { host: User['host'] }>(user: T): user is T & { host: null; }; | ||||||
|  | @ -50,6 +51,7 @@ export const UserRepository = db.getRepository(User).extend({ | ||||||
| 	descriptionSchema, | 	descriptionSchema, | ||||||
| 	locationSchema, | 	locationSchema, | ||||||
| 	birthdaySchema, | 	birthdaySchema, | ||||||
|  | 	pronounsSchema, | ||||||
| 
 | 
 | ||||||
| 	//#region Validators
 | 	//#region Validators
 | ||||||
| 	validateLocalUsername: ajv.compile(localUsernameSchema), | 	validateLocalUsername: ajv.compile(localUsernameSchema), | ||||||
|  | @ -58,6 +60,7 @@ export const UserRepository = db.getRepository(User).extend({ | ||||||
| 	validateDescription: ajv.compile(descriptionSchema), | 	validateDescription: ajv.compile(descriptionSchema), | ||||||
| 	validateLocation: ajv.compile(locationSchema), | 	validateLocation: ajv.compile(locationSchema), | ||||||
| 	validateBirthday: ajv.compile(birthdaySchema), | 	validateBirthday: ajv.compile(birthdaySchema), | ||||||
|  | 	validatePronouns: ajv.compile(pronounsSchema), | ||||||
| 	//#endregion
 | 	//#endregion
 | ||||||
| 
 | 
 | ||||||
| 	async getRelation(me: User['id'], target: User['id']) { | 	async getRelation(me: User['id'], target: User['id']) { | ||||||
|  | @ -318,6 +321,7 @@ export const UserRepository = db.getRepository(User).extend({ | ||||||
| 				isSilenced: user.isSilenced || falsy, | 				isSilenced: user.isSilenced || falsy, | ||||||
| 				isSuspended: user.isSuspended || falsy, | 				isSuspended: user.isSuspended || falsy, | ||||||
| 				description: profile!.description, | 				description: profile!.description, | ||||||
|  | 				pronouns: profile!.pronouns, | ||||||
| 				location: profile!.location, | 				location: profile!.location, | ||||||
| 				birthday: profile!.birthday, | 				birthday: profile!.birthday, | ||||||
| 				lang: profile!.lang, | 				lang: profile!.lang, | ||||||
|  |  | ||||||
|  | @ -143,6 +143,11 @@ export const packedUserDetailedNotMeOnlySchema = { | ||||||
| 			nullable: true, optional: false, | 			nullable: true, optional: false, | ||||||
| 			example: 'Hi masters, I am Ai!', | 			example: 'Hi masters, I am Ai!', | ||||||
| 		}, | 		}, | ||||||
|  | 		pronouns: { | ||||||
|  | 			type: 'string', | ||||||
|  | 			nullable: true, optional: false, | ||||||
|  | 			example: 'They/Them', | ||||||
|  | 		}, | ||||||
| 		location: { | 		location: { | ||||||
| 			type: 'string', | 			type: 'string', | ||||||
| 			nullable: true, optional: false, | 			nullable: true, optional: false, | ||||||
|  |  | ||||||
|  | @ -192,6 +192,7 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<Us | ||||||
| 				description: person.summary ? htmlToMfm(truncate(person.summary, summaryLength), person.tag) : null, | 				description: person.summary ? htmlToMfm(truncate(person.summary, summaryLength), person.tag) : null, | ||||||
| 				url: getOneApHrefNullable(person.url), | 				url: getOneApHrefNullable(person.url), | ||||||
| 				fields, | 				fields, | ||||||
|  | 				pronouns: person['vcard:Nickname'] || null, | ||||||
| 				birthday: bday ? bday[0] : null, | 				birthday: bday ? bday[0] : null, | ||||||
| 				location: person['vcard:Address'] || null, | 				location: person['vcard:Address'] || null, | ||||||
| 				userHost: host, | 				userHost: host, | ||||||
|  | @ -368,6 +369,7 @@ export async function updatePerson(uri: string, resolver?: Resolver | null, hint | ||||||
| 		url: getOneApHrefNullable(person.url), | 		url: getOneApHrefNullable(person.url), | ||||||
| 		fields, | 		fields, | ||||||
| 		description: person.summary ? htmlToMfm(truncate(person.summary, summaryLength), person.tag) : null, | 		description: person.summary ? htmlToMfm(truncate(person.summary, summaryLength), person.tag) : null, | ||||||
|  | 		pronouns: person['vcard:Nickname'] || null, | ||||||
| 		birthday: bday ? bday[0] : null, | 		birthday: bday ? bday[0] : null, | ||||||
| 		location: person['vcard:Address'] || null, | 		location: person['vcard:Address'] || null, | ||||||
| 	}); | 	}); | ||||||
|  |  | ||||||
|  | @ -77,6 +77,10 @@ export async function renderPerson(user: ILocalUser) { | ||||||
| 		attachment: attachment.length ? attachment : undefined, | 		attachment: attachment.length ? attachment : undefined, | ||||||
| 	} as any; | 	} as any; | ||||||
| 
 | 
 | ||||||
|  | 	if (profile?.pronouns) { | ||||||
|  | 		person['vcard:Nickname'] = profile.pronouns; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	if (profile?.birthday) { | 	if (profile?.birthday) { | ||||||
| 		person['vcard:bday'] = profile.birthday; | 		person['vcard:bday'] = profile.birthday; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -164,6 +164,7 @@ export interface IActor extends IObject { | ||||||
| 	endpoints?: { | 	endpoints?: { | ||||||
| 		sharedInbox?: string; | 		sharedInbox?: string; | ||||||
| 	}; | 	}; | ||||||
|  | 	'vcard:Nickname'?: string; | ||||||
| 	'vcard:bday'?: string; | 	'vcard:bday'?: string; | ||||||
| 	'vcard:Address'?: string; | 	'vcard:Address'?: string; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -72,6 +72,7 @@ export const paramDef = { | ||||||
| 	properties: { | 	properties: { | ||||||
| 		name: { ...Users.nameSchema, nullable: true }, | 		name: { ...Users.nameSchema, nullable: true }, | ||||||
| 		description: { ...Users.descriptionSchema, nullable: true }, | 		description: { ...Users.descriptionSchema, nullable: true }, | ||||||
|  | 		pronouns: { ...Users.pronounsSchema, nullable: true }, | ||||||
| 		location: { ...Users.locationSchema, nullable: true }, | 		location: { ...Users.locationSchema, nullable: true }, | ||||||
| 		birthday: { ...Users.birthdaySchema, nullable: true }, | 		birthday: { ...Users.birthdaySchema, nullable: true }, | ||||||
| 		lang: { type: 'string', enum: [null, ...Object.keys(langmap)], nullable: true }, | 		lang: { type: 'string', enum: [null, ...Object.keys(langmap)], nullable: true }, | ||||||
|  | @ -132,6 +133,7 @@ export default define(meta, paramDef, async (ps, _user, token) => { | ||||||
| 	if (ps.name !== undefined) updates.name = ps.name; | 	if (ps.name !== undefined) updates.name = ps.name; | ||||||
| 	if (ps.description !== undefined) profileUpdates.description = ps.description; | 	if (ps.description !== undefined) profileUpdates.description = ps.description; | ||||||
| 	if (ps.lang !== undefined) profileUpdates.lang = ps.lang; | 	if (ps.lang !== undefined) profileUpdates.lang = ps.lang; | ||||||
|  | 	if (ps.pronouns !== undefined) profileUpdates.pronouns = ps.pronouns; | ||||||
| 	if (ps.location !== undefined) profileUpdates.location = ps.location; | 	if (ps.location !== undefined) profileUpdates.location = ps.location; | ||||||
| 	if (ps.birthday !== undefined) profileUpdates.birthday = ps.birthday; | 	if (ps.birthday !== undefined) profileUpdates.birthday = ps.birthday; | ||||||
| 	if (ps.ffVisibility !== undefined) profileUpdates.ffVisibility = ps.ffVisibility; | 	if (ps.ffVisibility !== undefined) profileUpdates.ffVisibility = ps.ffVisibility; | ||||||
|  |  | ||||||
|  | @ -17,6 +17,11 @@ | ||||||
| 		<template #caption>{{ i18n.ts._profile.youCanIncludeHashtags }}</template> | 		<template #caption>{{ i18n.ts._profile.youCanIncludeHashtags }}</template> | ||||||
| 	</FormTextarea> | 	</FormTextarea> | ||||||
| 
 | 
 | ||||||
|  | 	<FormInput v-model="profile.pronouns" manual-save class="_formBlock"> | ||||||
|  | 		<template #label>{{ i18n.ts.pronouns }}</template> | ||||||
|  | 		<template #prefix><i class="fas fa-heart"></i></template> | ||||||
|  | 	</FormInput> | ||||||
|  | 
 | ||||||
| 	<FormInput v-model="profile.location" manual-save class="_formBlock"> | 	<FormInput v-model="profile.location" manual-save class="_formBlock"> | ||||||
| 		<template #label>{{ i18n.ts.location }}</template> | 		<template #label>{{ i18n.ts.location }}</template> | ||||||
| 		<template #prefix><i class="fas fa-map-marker-alt"></i></template> | 		<template #prefix><i class="fas fa-map-marker-alt"></i></template> | ||||||
|  | @ -82,6 +87,7 @@ import { langmap } from '@/scripts/langmap'; | ||||||
| const profile = reactive({ | const profile = reactive({ | ||||||
| 	name: $i.name, | 	name: $i.name, | ||||||
| 	description: $i.description, | 	description: $i.description, | ||||||
|  | 	pronouns: $i.pronouns, | ||||||
| 	location: $i.location, | 	location: $i.location, | ||||||
| 	birthday: $i.birthday, | 	birthday: $i.birthday, | ||||||
| 	lang: $i.lang, | 	lang: $i.lang, | ||||||
|  | @ -120,6 +126,7 @@ function save() { | ||||||
| 	os.apiWithDialog('i/update', { | 	os.apiWithDialog('i/update', { | ||||||
| 		name: profile.name || null, | 		name: profile.name || null, | ||||||
| 		description: profile.description || null, | 		description: profile.description || null, | ||||||
|  | 		pronouns: profile.pronouns || null, | ||||||
| 		location: profile.location || null, | 		location: profile.location || null, | ||||||
| 		birthday: profile.birthday || null, | 		birthday: profile.birthday || null, | ||||||
| 		lang: profile.lang || null, | 		lang: profile.lang || null, | ||||||
|  |  | ||||||
|  | @ -47,6 +47,10 @@ | ||||||
| 								<p v-else class="empty">{{ $ts.noAccountDescription }}</p> | 								<p v-else class="empty">{{ $ts.noAccountDescription }}</p> | ||||||
| 							</div> | 							</div> | ||||||
| 							<div class="fields system"> | 							<div class="fields system"> | ||||||
|  | 								<dl v-if="user.pronouns" class="field"> | ||||||
|  | 									<dt class="name"><i class="fas fa-heart fa-fw"></i> {{ $ts.pronouns }}</dt> | ||||||
|  | 									<dd class="value">{{ user.pronouns }}</dd> | ||||||
|  | 								</dl> | ||||||
| 								<dl v-if="user.location" class="field"> | 								<dl v-if="user.location" class="field"> | ||||||
| 									<dt class="name"><i class="fas fa-map-marker fa-fw"></i> {{ $ts.location }}</dt> | 									<dt class="name"><i class="fas fa-map-marker fa-fw"></i> {{ $ts.location }}</dt> | ||||||
| 									<dd class="value">{{ user.location }}</dd> | 									<dd class="value">{{ user.location }}</dd> | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue