Show user fields (#3590)
This commit is contained in:
		
							parent
							
								
									1d1a373ca8
								
							
						
					
					
						commit
						638d81b66e
					
				
					 7 changed files with 132 additions and 1 deletions
				
			
		| 
						 | 
				
			
			@ -26,6 +26,14 @@
 | 
			
		|||
			<div class="description">
 | 
			
		||||
				<misskey-flavored-markdown v-if="user.description" :text="user.description" :author="user" :i="$store.state.i" :custom-emojis="user.emojis"/>
 | 
			
		||||
			</div>
 | 
			
		||||
			<div class="fields" v-if="user.fields">
 | 
			
		||||
				<dl class="field" v-for="(field, i) in user.fields" :key="i">
 | 
			
		||||
					<dt class="name">{{ field.name }}</dt>
 | 
			
		||||
					<dd class="value">
 | 
			
		||||
						<misskey-flavored-markdown :text="field.value" :author="user" :i="$store.state.i" :custom-emojis="user.emojis"/>
 | 
			
		||||
					</dd>
 | 
			
		||||
				</dl>
 | 
			
		||||
			</div>
 | 
			
		||||
			<div class="counts">
 | 
			
		||||
				<div>
 | 
			
		||||
					<b>{{ user.notesCount | number }}</b>
 | 
			
		||||
| 
						 | 
				
			
			@ -416,6 +424,31 @@ export default Vue.extend({
 | 
			
		|||
			border-right solid 16px transparent
 | 
			
		||||
			border-bottom solid 16px var(--face)
 | 
			
		||||
 | 
			
		||||
		> .fields
 | 
			
		||||
			margin-top 8px
 | 
			
		||||
 | 
			
		||||
			> .field
 | 
			
		||||
				display flex
 | 
			
		||||
				padding 0
 | 
			
		||||
				margin 0
 | 
			
		||||
 | 
			
		||||
				> .name
 | 
			
		||||
					padding 4px
 | 
			
		||||
					margin 4px
 | 
			
		||||
					width 30%
 | 
			
		||||
					overflow hidden
 | 
			
		||||
					white-space nowrap
 | 
			
		||||
					text-overflow ellipsis
 | 
			
		||||
					font-weight bold
 | 
			
		||||
 | 
			
		||||
				> .value
 | 
			
		||||
					padding 4px
 | 
			
		||||
					margin 4px
 | 
			
		||||
					width 70%
 | 
			
		||||
					overflow hidden
 | 
			
		||||
					white-space nowrap
 | 
			
		||||
					text-overflow ellipsis
 | 
			
		||||
 | 
			
		||||
		> .counts
 | 
			
		||||
			display grid
 | 
			
		||||
			grid-template-columns 1fr 1fr 1fr
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,6 +18,14 @@
 | 
			
		|||
		<div class="description">
 | 
			
		||||
			<misskey-flavored-markdown v-if="user.description" :text="user.description" :author="user" :i="$store.state.i" :custom-emojis="user.emojis"/>
 | 
			
		||||
		</div>
 | 
			
		||||
		<div class="fields" v-if="user.fields">
 | 
			
		||||
			<dl class="field" v-for="(field, i) in user.fields" :key="i">
 | 
			
		||||
				<dt class="name">{{ field.name }}</dt>
 | 
			
		||||
				<dd class="value">
 | 
			
		||||
					<misskey-flavored-markdown :text="field.value" :author="user" :i="$store.state.i" :custom-emojis="user.emojis"/>
 | 
			
		||||
				</dd>
 | 
			
		||||
			</dl>
 | 
			
		||||
		</div>
 | 
			
		||||
		<div class="info">
 | 
			
		||||
			<span class="location" v-if="user.host === null && user.profile.location"><fa icon="map-marker"/> {{ user.profile.location }}</span>
 | 
			
		||||
			<span class="birthday" v-if="user.host === null && user.profile.birthday"><fa icon="birthday-cake"/> {{ user.profile.birthday.replace('-', $t('year')).replace('-', $t('month')) + $t('day') }} ({{ $t('years-old', { age }) }})</span>
 | 
			
		||||
| 
						 | 
				
			
			@ -174,6 +182,33 @@ export default Vue.extend({
 | 
			
		|||
		padding 16px 16px 16px 154px
 | 
			
		||||
		color var(--text)
 | 
			
		||||
 | 
			
		||||
		> .fields
 | 
			
		||||
			margin-top 16px
 | 
			
		||||
 | 
			
		||||
			> .field
 | 
			
		||||
				display flex
 | 
			
		||||
				padding 0
 | 
			
		||||
				margin 0
 | 
			
		||||
 | 
			
		||||
				> .name
 | 
			
		||||
					border-right solid 1px var(--faceDivider)
 | 
			
		||||
					padding 4px
 | 
			
		||||
					margin 4px
 | 
			
		||||
					width 30%
 | 
			
		||||
					overflow hidden
 | 
			
		||||
					white-space nowrap
 | 
			
		||||
					text-overflow ellipsis
 | 
			
		||||
					font-weight bold
 | 
			
		||||
					text-align center
 | 
			
		||||
 | 
			
		||||
				> .value
 | 
			
		||||
					padding 4px
 | 
			
		||||
					margin 4px
 | 
			
		||||
					width 70%
 | 
			
		||||
					overflow hidden
 | 
			
		||||
					white-space nowrap
 | 
			
		||||
					text-overflow ellipsis
 | 
			
		||||
 | 
			
		||||
		> .info
 | 
			
		||||
			margin-top 16px
 | 
			
		||||
			padding-top 16px
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,6 +24,14 @@
 | 
			
		|||
				<div class="description">
 | 
			
		||||
					<misskey-flavored-markdown v-if="user.description" :text="user.description" :author="user" :i="$store.state.i" :custom-emojis="user.emojis"/>
 | 
			
		||||
				</div>
 | 
			
		||||
				<div class="fields" v-if="user.fields">
 | 
			
		||||
					<dl class="field" v-for="(field, i) in user.fields" :key="i">
 | 
			
		||||
						<dt class="name">{{ field.name }}</dt>
 | 
			
		||||
						<dd class="value">
 | 
			
		||||
							<misskey-flavored-markdown :text="field.value" :author="user" :i="$store.state.i" :custom-emojis="user.emojis"/>
 | 
			
		||||
						</dd>
 | 
			
		||||
					</dl>
 | 
			
		||||
				</div>
 | 
			
		||||
				<div class="info">
 | 
			
		||||
					<p class="location" v-if="user.host === null && user.profile.location">
 | 
			
		||||
						<fa icon="map-marker"/>{{ user.profile.location }}
 | 
			
		||||
| 
						 | 
				
			
			@ -301,6 +309,33 @@ main
 | 
			
		|||
				margin 8px 0
 | 
			
		||||
				color var(--mobileUserPageDescription)
 | 
			
		||||
 | 
			
		||||
			> .fields
 | 
			
		||||
				margin 8px 0
 | 
			
		||||
 | 
			
		||||
				> .field
 | 
			
		||||
					display flex
 | 
			
		||||
					padding 0
 | 
			
		||||
					margin 0
 | 
			
		||||
 | 
			
		||||
					> .name
 | 
			
		||||
						padding 4px
 | 
			
		||||
						margin 4px
 | 
			
		||||
						width 30%
 | 
			
		||||
						overflow hidden
 | 
			
		||||
						white-space nowrap
 | 
			
		||||
						text-overflow ellipsis
 | 
			
		||||
						font-weight bold
 | 
			
		||||
						color var(--mobileUserPageStatusHighlight)
 | 
			
		||||
 | 
			
		||||
					> .value
 | 
			
		||||
						padding 4px
 | 
			
		||||
						margin 4px
 | 
			
		||||
						width 70%
 | 
			
		||||
						overflow hidden
 | 
			
		||||
						white-space nowrap
 | 
			
		||||
						text-overflow ellipsis
 | 
			
		||||
						color var(--mobileUserPageStatusHighlight)
 | 
			
		||||
 | 
			
		||||
			> .info
 | 
			
		||||
				margin 8px 0
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -41,7 +41,7 @@ export default function(html: string): string {
 | 
			
		|||
				if ((rel && rel.value.match('tag') !== null) || !href || href.value == txt) {
 | 
			
		||||
					text += txt;
 | 
			
		||||
				// メンション
 | 
			
		||||
				} else if (txt.startsWith('@')) {
 | 
			
		||||
				} else if (txt.startsWith('@') && !rel || !rel.value.match(/^me /)) {
 | 
			
		||||
					const part = txt.split('@');
 | 
			
		||||
 | 
			
		||||
					if (part.length == 2) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -109,6 +109,10 @@ export interface ILocalUser extends IUserBase {
 | 
			
		|||
		birthday: string; // 'YYYY-MM-DD'
 | 
			
		||||
		tags: string[];
 | 
			
		||||
	};
 | 
			
		||||
	fields?: {
 | 
			
		||||
		name: string;
 | 
			
		||||
		value: string;
 | 
			
		||||
	}[];
 | 
			
		||||
	isCat: boolean;
 | 
			
		||||
	isAdmin?: boolean;
 | 
			
		||||
	isModerator?: boolean;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,6 +17,7 @@ import registerInstance from '../../../services/register-instance';
 | 
			
		|||
import Instance from '../../../models/instance';
 | 
			
		||||
import getDriveFileUrl from '../../../misc/get-drive-file-url';
 | 
			
		||||
import { IEmoji } from '../../../models/emoji';
 | 
			
		||||
import { ITag } from './tag';
 | 
			
		||||
 | 
			
		||||
const log = debug('misskey:activitypub');
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -135,6 +136,10 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<IU
 | 
			
		|||
 | 
			
		||||
	const host = toUnicode(new URL(object.id).hostname.toLowerCase());
 | 
			
		||||
 | 
			
		||||
	const fields = await extractFields(person.attachment).catch(e => {
 | 
			
		||||
		console.log(`cat not extract fields: ${e}`);
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	const isBot = object.type == 'Service';
 | 
			
		||||
 | 
			
		||||
	// Create user
 | 
			
		||||
| 
						 | 
				
			
			@ -164,6 +169,7 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<IU
 | 
			
		|||
			endpoints: person.endpoints,
 | 
			
		||||
			uri: person.id,
 | 
			
		||||
			url: person.url,
 | 
			
		||||
			fields,
 | 
			
		||||
			isBot: isBot,
 | 
			
		||||
			isCat: (person as any).isCat === true
 | 
			
		||||
		}) as IRemoteUser;
 | 
			
		||||
| 
						 | 
				
			
			@ -325,6 +331,10 @@ export async function updatePerson(uri: string, resolver?: Resolver, hint?: obje
 | 
			
		|||
 | 
			
		||||
	const emojiNames = emojis.map(emoji => emoji.name);
 | 
			
		||||
 | 
			
		||||
	const fields = await extractFields(person.attachment).catch(e => {
 | 
			
		||||
		console.log(`cat not extract fields: ${e}`);
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	// Update user
 | 
			
		||||
	await User.update({ _id: exist._id }, {
 | 
			
		||||
		$set: {
 | 
			
		||||
| 
						 | 
				
			
			@ -346,6 +356,7 @@ export async function updatePerson(uri: string, resolver?: Resolver, hint?: obje
 | 
			
		|||
			name: person.name,
 | 
			
		||||
			url: person.url,
 | 
			
		||||
			endpoints: person.endpoints,
 | 
			
		||||
			fields,
 | 
			
		||||
			isBot: object.type == 'Service',
 | 
			
		||||
			isCat: (person as any).isCat === true,
 | 
			
		||||
			isLocked: person.manuallyApprovesFollowers,
 | 
			
		||||
| 
						 | 
				
			
			@ -382,6 +393,18 @@ export async function resolvePerson(uri: string, verifier?: string, resolver?: R
 | 
			
		|||
	return await createPerson(uri, resolver);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function extractFields(attachments: ITag[]) {
 | 
			
		||||
	if (!attachments) return [];
 | 
			
		||||
 | 
			
		||||
	return attachments.filter(a => a.type === 'PropertyValue' && a.name && a.value)
 | 
			
		||||
		.map(a => {
 | 
			
		||||
			return {
 | 
			
		||||
				name: a.name,
 | 
			
		||||
				value: htmlToMFM(a.value)
 | 
			
		||||
			};
 | 
			
		||||
		});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function updateFeatured(userId: mongo.ObjectID) {
 | 
			
		||||
	const user = await User.findOne({ _id: userId });
 | 
			
		||||
	if (!isRemoteUser(user)) return;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,6 +7,7 @@ export type ITag = {
 | 
			
		|||
	id: string;
 | 
			
		||||
	type: string;
 | 
			
		||||
	name?: string;
 | 
			
		||||
	value?: string;
 | 
			
		||||
	updated?: Date;
 | 
			
		||||
	icon?: IIcon;
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue