mirror of
https://github.com/CompassConnections/Compass.git
synced 2026-02-23 18:36:02 -05:00
Add profile field: place they grew up
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@compass/api",
|
||||
"description": "Backend API endpoints",
|
||||
"version": "1.10.1",
|
||||
"version": "1.11.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"watch:serve": "tsx watch src/serve.ts",
|
||||
|
||||
@@ -52,6 +52,9 @@ export type profileQueryType = {
|
||||
lat?: number | undefined
|
||||
lon?: number | undefined
|
||||
radius?: number | undefined
|
||||
raised_in_lat?: number | undefined
|
||||
raised_in_lon?: number | undefined
|
||||
raised_in_radius?: number | undefined
|
||||
compatibleWithUserId?: string | undefined
|
||||
skipId?: string | undefined
|
||||
orderBy?: string | undefined
|
||||
@@ -107,6 +110,9 @@ export const loadProfiles = async (props: profileQueryType) => {
|
||||
lat,
|
||||
lon,
|
||||
radius,
|
||||
raised_in_lat,
|
||||
raised_in_lon,
|
||||
raised_in_radius,
|
||||
compatibleWithUserId,
|
||||
orderBy: orderByParam = 'created_time',
|
||||
lastModificationWithin,
|
||||
@@ -115,6 +121,7 @@ export const loadProfiles = async (props: profileQueryType) => {
|
||||
} = props
|
||||
|
||||
const filterLocation = lat && lon && radius
|
||||
const filterRaisedInLocation = raised_in_lat && raised_in_lon && raised_in_radius
|
||||
|
||||
const keywords = name
|
||||
? name
|
||||
@@ -372,6 +379,21 @@ export const loadProfiles = async (props: profileQueryType) => {
|
||||
{target_lat: lat, target_lon: lon, radius},
|
||||
),
|
||||
|
||||
filterRaisedInLocation &&
|
||||
where(
|
||||
`
|
||||
raised_in_lat BETWEEN $(target_lat) - ($(radius) / 69.0)
|
||||
AND $(target_lat) + ($(radius) / 69.0)
|
||||
AND raised_in_lon BETWEEN $(target_lon) - ($(radius) / (69.0 * COS(RADIANS($(target_lat)))))
|
||||
AND $(target_lon) + ($(radius) / (69.0 * COS(RADIANS($(target_lat)))))
|
||||
AND SQRT(
|
||||
POWER(raised_in_lat - $(target_lat), 2)
|
||||
+ POWER((raised_in_lon - $(target_lon)) * COS(RADIANS($(target_lat))), 2)
|
||||
) <= $(radius) / 69.0
|
||||
`,
|
||||
{target_lat: raised_in_lat, target_lon: raised_in_lon, radius: raised_in_radius},
|
||||
),
|
||||
|
||||
skipId && where(`profiles.user_id != $(skipId)`, {skipId}),
|
||||
|
||||
!shortBio &&
|
||||
|
||||
@@ -3,6 +3,13 @@ import type {User} from 'common/user'
|
||||
|
||||
// for email template testing
|
||||
|
||||
// A subset of Profile with only essential fields for testing
|
||||
export type PartialProfile = Pick<ProfileRow,
|
||||
'id' | 'user_id' | 'created_time' | 'last_modification_time' |
|
||||
'disabled' | 'looking_for_matches' | 'messaging_status' |
|
||||
'comments_enabled' | 'visibility'
|
||||
> & Partial<ProfileRow>
|
||||
|
||||
export const mockUser: User = {
|
||||
createdTime: 0,
|
||||
bio: 'the futa in futarchy',
|
||||
@@ -26,98 +33,22 @@ export const mockUser: User = {
|
||||
},
|
||||
}
|
||||
|
||||
export const sinclairProfile: ProfileRow = {
|
||||
export const sinclairProfile: PartialProfile = {
|
||||
// Required fields
|
||||
id: 55,
|
||||
user_id: '0k1suGSJKVUnHbCPEhHNpgZPkUP2',
|
||||
created_time: '2023-10-27T00:41:59.851776+00:00',
|
||||
last_modification_time: '2024-05-17T02:11:48.83+00:00',
|
||||
city: 'San Francisco',
|
||||
gender: 'trans-female',
|
||||
pref_gender: ['female', 'trans-female'],
|
||||
pref_age_min: 18,
|
||||
pref_age_max: 21,
|
||||
religion: [],
|
||||
image_descriptions: {},
|
||||
languages: ['english'],
|
||||
pref_relation_styles: ['friendship'],
|
||||
pref_romantic_styles: ['poly', 'open', 'mono'],
|
||||
disabled: false,
|
||||
wants_kids_strength: 3,
|
||||
looking_for_matches: true,
|
||||
visibility: 'public',
|
||||
mbti: 'intj',
|
||||
messaging_status: 'open',
|
||||
comments_enabled: true,
|
||||
has_kids: 0,
|
||||
is_smoker: false,
|
||||
drinks_per_month: 0,
|
||||
diet: null,
|
||||
political_beliefs: ['e/acc', 'libertarian'],
|
||||
relationship_status: ['married'],
|
||||
religious_belief_strength: null,
|
||||
religious_beliefs: null,
|
||||
political_details: '',
|
||||
photo_urls: [
|
||||
'https://firebasestorage.googleapis.com/v0/b/mantic-markets.appspot.com/o/user-images%2FSinclair%2Flove-images%2FnJz22lr3Bl.jpg?alt=media&token=f1e99ba3-39cc-4637-8702-16a3a8dd49db',
|
||||
'https://firebasestorage.googleapis.com/v0/b/mantic-markets.appspot.com/o/user-images%2FSinclair%2Flove-images%2FygM0mGgP_j.HEIC?alt=media&token=573b23d9-693c-4d6e-919b-097309f370e1',
|
||||
'https://firebasestorage.googleapis.com/v0/b/mantic-markets.appspot.com/o/user-images%2FSinclair%2Flove-images%2FWPZNKxjHGV.HEIC?alt=media&token=190625e1-2cf0-49a6-824b-09b6f4002f2a',
|
||||
'https://firebasestorage.googleapis.com/v0/b/polylove.firebasestorage.app/o/user-images%2FSinclair%2Flove-images%2FlVFKKoLHyV.jpg?alt=media&token=ecb3a003-3672-4382-9ba0-ca894247bb3f',
|
||||
'https://firebasestorage.googleapis.com/v0/b/polylove.firebasestorage.app/o/user-images%2FSinclair%2Flove-images%2Fh659K0bmd4.jpg?alt=media&token=6561ed05-0e2d-4f31-95ee-c7c1c0b33ea6',
|
||||
'https://firebasestorage.googleapis.com/v0/b/polylove.firebasestorage.app/o/user-images%2FSinclair%2Flove-images%2F5OMTo5rhB-.jpg?alt=media&token=4aba4e5a-5115-4d2e-9d57-1e6162e15708',
|
||||
'https://firebasestorage.googleapis.com/v0/b/polylove.firebasestorage.app/o/user-images%2FSinclair%2Flove-images%2FwCT-Y-bgpc.jpg?alt=media&token=91994528-e436-4055-af69-421fa9e29e5c',
|
||||
],
|
||||
pinned_url:
|
||||
'https://firebasestorage.googleapis.com/v0/b/mantic-markets.appspot.com/o/user-images%2FSinclair%2Flove-images%2FYXD19m12D7.jpg?alt=media&token=6cb095b4-dfc8-4bc9-ae67-6f12f29be0a5',
|
||||
ethnicity: ['asian'],
|
||||
born_in_location: null,
|
||||
height_in_inches: 70,
|
||||
education_level: 'bachelors',
|
||||
university: 'Santa Clara University',
|
||||
occupation: null,
|
||||
occupation_title: 'Founding Engineer',
|
||||
company: 'Manifold Markets',
|
||||
website: 'sincl.ai',
|
||||
twitter: 'x.com/singularitttt',
|
||||
region_code: 'CA',
|
||||
country: 'United States of America',
|
||||
city_latitude: 37.7775,
|
||||
city_longitude: -122.416389,
|
||||
geodb_city_id: '126964',
|
||||
referred_by_username: null,
|
||||
bio_length: 1000,
|
||||
bio: {
|
||||
type: 'doc',
|
||||
content: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
marks: [
|
||||
{
|
||||
type: 'link',
|
||||
attrs: {
|
||||
href: 'https://sinclaaair.notion.site/Date-Me-487ef432c1f54938bf5e7a45ef05d57b',
|
||||
target: '_blank',
|
||||
},
|
||||
},
|
||||
],
|
||||
text: 'https://sinclaaair.notion.site/Date-Me-487ef432c1f54938bf5e7a45ef05d57b',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
bio_text: 'the futa in futarchy',
|
||||
bio_tsv: 'the futa in futarchy',
|
||||
search_text: 'the futa in futarchy',
|
||||
search_tsv: 'the futa in futarchy',
|
||||
visibility: 'public',
|
||||
|
||||
// Optional commonly used fields for testing
|
||||
city: 'San Francisco',
|
||||
gender: 'trans-female',
|
||||
age: 25,
|
||||
big5_agreeableness: 10,
|
||||
big5_openness: 10,
|
||||
big5_conscientiousness: 10,
|
||||
big5_extraversion: 10,
|
||||
big5_neuroticism: 10,
|
||||
}
|
||||
|
||||
export const jamesUser: User = {
|
||||
@@ -141,99 +72,20 @@ export const jamesUser: User = {
|
||||
},
|
||||
}
|
||||
|
||||
export const jamesProfile: ProfileRow = {
|
||||
export const jamesProfile: PartialProfile = {
|
||||
// Required fields
|
||||
id: 2,
|
||||
user_id: '5LZ4LgYuySdL1huCWe7bti02ghx2',
|
||||
created_time: '2023-10-21T21:18:26.691211+00:00',
|
||||
last_modification_time: '2024-05-17T02:11:48.83+00:00',
|
||||
city: 'San Francisco',
|
||||
gender: 'male',
|
||||
pref_gender: ['female'],
|
||||
disabled: false,
|
||||
pref_age_min: 22,
|
||||
pref_age_max: 32,
|
||||
religion: [],
|
||||
image_descriptions: {},
|
||||
languages: ['english'],
|
||||
pref_relation_styles: ['friendship'],
|
||||
pref_romantic_styles: ['poly', 'open', 'mono'],
|
||||
wants_kids_strength: 4,
|
||||
looking_for_matches: true,
|
||||
visibility: 'public',
|
||||
messaging_status: 'open',
|
||||
comments_enabled: true,
|
||||
has_kids: 0,
|
||||
is_smoker: false,
|
||||
drinks_per_month: 5,
|
||||
diet: null,
|
||||
political_beliefs: ['libertarian'],
|
||||
relationship_status: ['single'],
|
||||
religious_belief_strength: null,
|
||||
religious_beliefs: '',
|
||||
mbti: 'intj',
|
||||
political_details: '',
|
||||
photo_urls: [
|
||||
'https://firebasestorage.googleapis.com/v0/b/mantic-markets.appspot.com/o/user-images%2FJamesGrugett%2Flove-images%2FKl0WtbZsZW.jpg?alt=media&token=c928604f-e5ff-4406-a229-152864a4aa48',
|
||||
'https://firebasestorage.googleapis.com/v0/b/mantic-markets.appspot.com/o/user-images%2FJamesGrugett%2Flove-images%2Fsii17zOItz.jpg?alt=media&token=474034b9-0d23-4005-97ad-5864abfd85fe',
|
||||
'https://firebasestorage.googleapis.com/v0/b/mantic-markets.appspot.com/o/user-images%2FJamesGrugett%2Flove-images%2F3ICeb-0mwB.jpg?alt=media&token=975dbdb9-5547-4553-b504-e6545eb82ec0',
|
||||
'https://firebasestorage.googleapis.com/v0/b/mantic-markets.appspot.com/o/user-images%2FJamesGrugett%2Flove-images%2FdtuSGk_13Q.jpg?alt=media&token=98191d86-9d10-4571-879c-d00ab9cab09e',
|
||||
],
|
||||
pinned_url:
|
||||
'https://firebasestorage.googleapis.com/v0/b/mantic-markets.appspot.com/o/user-images%2FJamesGrugett%2Flove-images%2FXkLhuxZoOX.jpg?alt=media&token=7f2304dd-bace-4806-8e3c-78c35e57287c',
|
||||
ethnicity: ['caucasian'],
|
||||
born_in_location: 'Melbourne, FL',
|
||||
height_in_inches: 70,
|
||||
education_level: 'bachelors',
|
||||
university: 'Carnegie Mellon',
|
||||
occupation: 'Entrepreneur',
|
||||
occupation_title: 'CEO',
|
||||
company: 'Codebuff',
|
||||
website: 'https://jamesgrugett.com/',
|
||||
twitter: 'https://twitter.com/jahooma',
|
||||
region_code: 'CA',
|
||||
country: 'United States of America',
|
||||
city_latitude: 37.7775,
|
||||
city_longitude: -122.416389,
|
||||
geodb_city_id: '126964',
|
||||
referred_by_username: null,
|
||||
bio_length: 1000,
|
||||
bio: {
|
||||
type: 'doc',
|
||||
content: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: "Optimist that's working to improve the world!",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'paragraph',
|
||||
},
|
||||
{
|
||||
type: 'paragraph',
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: 'I like outdoor activities, hanging out with my housemates, strategy board games, libertarian and utilitarian ideas, getting boba, and riding my electric unicycle. I also enjoy working hard on bold new initiatives with huge potential for value creation!',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'paragraph',
|
||||
},
|
||||
],
|
||||
},
|
||||
bio_text: 'the futa in futarchy',
|
||||
bio_tsv: 'the futa in futarchy',
|
||||
search_text: 'the futa in futarchy',
|
||||
search_tsv: 'the futa in futarchy',
|
||||
visibility: 'public',
|
||||
|
||||
// Optional commonly used fields for testing
|
||||
city: 'San Francisco',
|
||||
gender: 'male',
|
||||
age: 32,
|
||||
big5_agreeableness: 10,
|
||||
big5_openness: 10,
|
||||
big5_conscientiousness: 10,
|
||||
big5_extraversion: 10,
|
||||
big5_neuroticism: 10,
|
||||
}
|
||||
|
||||
@@ -47,4 +47,5 @@ BEGIN;
|
||||
\i backend/supabase/migrations/20251112_add_mbti_to_profiles.sql
|
||||
\i backend/supabase/migrations/20260213_add_big_5_to_profiles.sql
|
||||
\i backend/supabase/migrations/20260218_add_events.sql
|
||||
\i backend/supabase/migrations/20260218_add_notification_templates.sql
|
||||
COMMIT;
|
||||
|
||||
@@ -43,6 +43,13 @@ CREATE TABLE IF NOT EXISTS profiles (
|
||||
pref_gender TEXT[],
|
||||
pref_relation_styles TEXT[],
|
||||
pref_romantic_styles TEXT[],
|
||||
raised_in_city TEXT,
|
||||
raised_in_country TEXT,
|
||||
raised_in_geodb_city_id TEXT,
|
||||
raised_in_lat NUMERIC(9, 6),
|
||||
raised_in_lon NUMERIC(9, 6),
|
||||
raised_in_radius INTEGER,
|
||||
raised_in_region_code TEXT,
|
||||
referred_by_username TEXT,
|
||||
region_code TEXT,
|
||||
relationship_status TEXT[],
|
||||
@@ -92,9 +99,11 @@ CREATE INDEX IF NOT EXISTS idx_profiles_bio_length
|
||||
|
||||
-- Fastest general-purpose index
|
||||
CREATE INDEX IF NOT EXISTS profiles_lat_lon_idx ON profiles (city_latitude, city_longitude);
|
||||
CREATE INDEX IF NOT EXISTS profiles_lat_lon_idx ON profiles (raised_in_lat, raised_in_lon);
|
||||
|
||||
-- Optional additional index for large tables / clustered inserts
|
||||
CREATE INDEX IF NOT EXISTS profiles_lat_lon_brin_idx ON profiles USING BRIN (city_latitude, city_longitude) WITH (pages_per_range = 32);
|
||||
CREATE INDEX IF NOT EXISTS profiles_lat_lon_brin_idx ON profiles USING BRIN (raised_in_lat, raised_in_lon) WITH (pages_per_range = 32);
|
||||
|
||||
CREATE INDEX profiles_pref_gender_gin ON profiles USING GIN (pref_gender);
|
||||
CREATE INDEX profiles_pref_relation_styles_gin ON profiles USING GIN (pref_relation_styles);
|
||||
@@ -118,6 +127,7 @@ CREATE INDEX profiles_smoker_idx ON profiles (is_smoker);
|
||||
CREATE INDEX profiles_education_level_idx ON profiles (education_level);
|
||||
CREATE INDEX profiles_gender_idx ON profiles (gender);
|
||||
CREATE INDEX profiles_geodb_city_idx ON profiles (geodb_city_id);
|
||||
CREATE INDEX profiles_raised_in_geodb_city_idx ON profiles (raised_in_geodb_city_id);
|
||||
|
||||
CREATE INDEX profiles_recent_active_idx
|
||||
ON profiles (last_modification_time DESC)
|
||||
|
||||
@@ -548,6 +548,9 @@ export const API = (_apiTypeCheck = {
|
||||
lat: z.coerce.number().optional(),
|
||||
lon: z.coerce.number().optional(),
|
||||
radius: z.coerce.number().optional(),
|
||||
raised_in_lat: z.coerce.number().optional(),
|
||||
raised_in_lon: z.coerce.number().optional(),
|
||||
raised_in_radius: z.coerce.number().optional(),
|
||||
compatibleWithUserId: z.string().optional(),
|
||||
skipId: z.string().optional(),
|
||||
locale: z.string().optional(),
|
||||
|
||||
@@ -97,6 +97,13 @@ const optionalProfilesSchema = z.object({
|
||||
political_beliefs: z.array(z.string()).optional().nullable(),
|
||||
political_details: z.string().optional().nullable(),
|
||||
pref_romantic_styles: z.array(z.string()).nullable(),
|
||||
raised_in_city: z.string().optional().nullable(),
|
||||
raised_in_country: z.string().optional().nullable(),
|
||||
raised_in_geodb_city_id: z.string().optional().nullable(),
|
||||
raised_in_lat: z.number().optional().nullable(),
|
||||
raised_in_lon: z.number().optional().nullable(),
|
||||
raised_in_radius: z.number().optional().nullable(),
|
||||
raised_in_region_code: z.string().optional().nullable(),
|
||||
relationship_status: z.array(z.string()).optional().nullable(),
|
||||
religion: z.array(z.string()).optional().nullable(),
|
||||
religious_belief_strength: z.number().optional().nullable(),
|
||||
|
||||
@@ -15,6 +15,9 @@ export type FilterFields = {
|
||||
lat: number | null
|
||||
lon: number | null
|
||||
radius: number | null
|
||||
raised_in_lat: number | null
|
||||
raised_in_lon: number | null
|
||||
raised_in_radius: number | null
|
||||
genders: string[]
|
||||
education_levels: string[]
|
||||
mbti: string[]
|
||||
@@ -73,6 +76,9 @@ export const initialFilters: Partial<FilterFields> = {
|
||||
lat: undefined,
|
||||
lon: undefined,
|
||||
radius: undefined,
|
||||
raised_in_lat: undefined,
|
||||
raised_in_lon: undefined,
|
||||
raised_in_radius: undefined,
|
||||
name: undefined,
|
||||
genders: undefined,
|
||||
education_levels: undefined,
|
||||
|
||||
@@ -504,6 +504,42 @@ export type Database = {
|
||||
},
|
||||
]
|
||||
}
|
||||
notification_templates: {
|
||||
Row: {
|
||||
created_time: number
|
||||
data: Json | null
|
||||
id: string
|
||||
source_slug: string | null
|
||||
source_text: string
|
||||
source_type: string
|
||||
source_update_type: string | null
|
||||
source_user_avatar_url: string | null
|
||||
title: string | null
|
||||
}
|
||||
Insert: {
|
||||
created_time: number
|
||||
data?: Json | null
|
||||
id: string
|
||||
source_slug?: string | null
|
||||
source_text: string
|
||||
source_type: string
|
||||
source_update_type?: string | null
|
||||
source_user_avatar_url?: string | null
|
||||
title?: string | null
|
||||
}
|
||||
Update: {
|
||||
created_time?: number
|
||||
data?: Json | null
|
||||
id?: string
|
||||
source_slug?: string | null
|
||||
source_text?: string
|
||||
source_type?: string
|
||||
source_update_type?: string | null
|
||||
source_user_avatar_url?: string | null
|
||||
title?: string | null
|
||||
}
|
||||
Relationships: []
|
||||
}
|
||||
private_user_message_channel_members: {
|
||||
Row: {
|
||||
channel_id: number
|
||||
@@ -990,6 +1026,13 @@ export type Database = {
|
||||
pref_gender: string[] | null
|
||||
pref_relation_styles: string[] | null
|
||||
pref_romantic_styles: string[] | null
|
||||
raised_in_city: string | null
|
||||
raised_in_country: string | null
|
||||
raised_in_geodb_city_id: string | null
|
||||
raised_in_lat: number | null
|
||||
raised_in_lon: number | null
|
||||
raised_in_radius: number | null
|
||||
raised_in_region_code: string | null
|
||||
referred_by_username: string | null
|
||||
region_code: string | null
|
||||
relationship_status: string[] | null
|
||||
@@ -1052,6 +1095,13 @@ export type Database = {
|
||||
pref_gender?: string[] | null
|
||||
pref_relation_styles?: string[] | null
|
||||
pref_romantic_styles?: string[] | null
|
||||
raised_in_city?: string | null
|
||||
raised_in_country?: string | null
|
||||
raised_in_geodb_city_id?: string | null
|
||||
raised_in_lat?: number | null
|
||||
raised_in_lon?: number | null
|
||||
raised_in_radius?: number | null
|
||||
raised_in_region_code?: string | null
|
||||
referred_by_username?: string | null
|
||||
region_code?: string | null
|
||||
relationship_status?: string[] | null
|
||||
@@ -1114,6 +1164,13 @@ export type Database = {
|
||||
pref_gender?: string[] | null
|
||||
pref_relation_styles?: string[] | null
|
||||
pref_romantic_styles?: string[] | null
|
||||
raised_in_city?: string | null
|
||||
raised_in_country?: string | null
|
||||
raised_in_geodb_city_id?: string | null
|
||||
raised_in_lat?: number | null
|
||||
raised_in_lon?: number | null
|
||||
raised_in_radius?: number | null
|
||||
raised_in_region_code?: string | null
|
||||
referred_by_username?: string | null
|
||||
region_code?: string | null
|
||||
relationship_status?: string[] | null
|
||||
@@ -1322,19 +1379,29 @@ export type Database = {
|
||||
Row: {
|
||||
data: Json
|
||||
notification_id: string
|
||||
template_id: string | null
|
||||
user_id: string
|
||||
}
|
||||
Insert: {
|
||||
data: Json
|
||||
notification_id: string
|
||||
template_id?: string | null
|
||||
user_id: string
|
||||
}
|
||||
Update: {
|
||||
data?: Json
|
||||
notification_id?: string
|
||||
template_id?: string | null
|
||||
user_id?: string
|
||||
}
|
||||
Relationships: [
|
||||
{
|
||||
foreignKeyName: 'user_notifications_template_id_fkey'
|
||||
columns: ['template_id']
|
||||
isOneToOne: false
|
||||
referencedRelation: 'notification_templates'
|
||||
referencedColumns: ['id']
|
||||
},
|
||||
{
|
||||
foreignKeyName: 'user_notifications_user_id_fkey'
|
||||
columns: ['user_id']
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
RACE_CHOICES,
|
||||
RELATIONSHIP_CHOICES,
|
||||
RELIGION_CHOICES,
|
||||
} from 'common/lib/choices'
|
||||
} from 'common/choices'
|
||||
|
||||
class UserAccountInformation {
|
||||
name = faker.person.fullName()
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
{"path": "./backend/shared"},
|
||||
{"path": "./backend/shared/tsconfig.test.json"},
|
||||
{"path": "./common"},
|
||||
{"path": "./common/tsconfig.test.json"},
|
||||
{"path": "./web"},
|
||||
{"path": "./web/tsconfig.test.json"}
|
||||
{"path": "./common/tsconfig.test.json"}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@ export function DesktopFilters(props: {
|
||||
setYourFilters: (checked: boolean) => void
|
||||
isYourFilters: boolean
|
||||
locationFilterProps: LocationFilterProps
|
||||
raisedInLocationFilterProps?: LocationFilterProps
|
||||
includeRelationshipFilters: boolean | undefined
|
||||
choices: Record<OptionTableKey, Record<string, string>>
|
||||
}) {
|
||||
@@ -64,6 +65,7 @@ export function DesktopFilters(props: {
|
||||
setYourFilters,
|
||||
isYourFilters,
|
||||
locationFilterProps,
|
||||
raisedInLocationFilterProps,
|
||||
includeRelationshipFilters,
|
||||
choices,
|
||||
} = props
|
||||
@@ -152,6 +154,34 @@ export function DesktopFilters(props: {
|
||||
menuWidth="w-80"
|
||||
/>
|
||||
|
||||
{/* RAISED IN LOCATION */}
|
||||
{raisedInLocationFilterProps && (
|
||||
<CustomizeableDropdown
|
||||
buttonContent={(open: boolean) => (
|
||||
<DropdownButton
|
||||
content={
|
||||
<LocationFilterText
|
||||
youProfile={youProfile}
|
||||
location={raisedInLocationFilterProps.location}
|
||||
radius={raisedInLocationFilterProps.radius ?? 100}
|
||||
highlightedClass={open ? 'text-primary-500' : ''}
|
||||
labelPrefix={t('filter.raised_in', 'Grew up')}
|
||||
/>
|
||||
}
|
||||
open={open}
|
||||
/>
|
||||
)}
|
||||
dropdownMenuContent={
|
||||
<LocationFilter
|
||||
youProfile={youProfile}
|
||||
locationFilterProps={raisedInLocationFilterProps}
|
||||
/>
|
||||
}
|
||||
popoverClassName="bg-canvas-50"
|
||||
menuWidth="w-80"
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* AGE RANGE */}
|
||||
<CustomizeableDropdown
|
||||
buttonContent={(open: boolean) => (
|
||||
|
||||
@@ -21,18 +21,22 @@ export function LocationFilterText(props: {
|
||||
youProfile: Profile | undefined | null
|
||||
radius: number
|
||||
highlightedClass?: string
|
||||
labelPrefix?: string
|
||||
}) {
|
||||
const {location, radius, highlightedClass} = props
|
||||
const {location, radius, highlightedClass, labelPrefix} = props
|
||||
const {measurementSystem} = useMeasurementSystem()
|
||||
|
||||
const t = useT()
|
||||
const locationLabel = labelPrefix
|
||||
? t('filter.raised_in', labelPrefix)
|
||||
: t('filter.location', 'Living')
|
||||
if (!location) {
|
||||
return (
|
||||
<span>
|
||||
<span className="">{locationLabel} </span>
|
||||
<span className={clsx('text-semibold', highlightedClass)}>
|
||||
{t('filter.location.any', 'Any')}
|
||||
{t('filter.location.any', 'anywhere')}
|
||||
</span>
|
||||
<span className=""> {t('filter.location', 'location')}</span>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
@@ -41,6 +45,7 @@ export function LocationFilterText(props: {
|
||||
|
||||
return (
|
||||
<span className="font-semibold">
|
||||
<span className="">{locationLabel} </span>
|
||||
<span className="">
|
||||
<span className={clsx(highlightedClass)}>{formattedDistance}</span>
|
||||
</span>{' '}
|
||||
|
||||
@@ -55,6 +55,7 @@ function MobileFilters(props: {
|
||||
setYourFilters: (checked: boolean) => void
|
||||
isYourFilters: boolean
|
||||
locationFilterProps: LocationFilterProps
|
||||
raisedInLocationFilterProps: LocationFilterProps
|
||||
includeRelationshipFilters: boolean | undefined
|
||||
choices: Record<OptionTableKey, Record<string, string>>
|
||||
}) {
|
||||
@@ -67,6 +68,7 @@ function MobileFilters(props: {
|
||||
setYourFilters,
|
||||
isYourFilters,
|
||||
locationFilterProps,
|
||||
raisedInLocationFilterProps,
|
||||
includeRelationshipFilters,
|
||||
choices,
|
||||
} = props
|
||||
@@ -141,7 +143,7 @@ function MobileFilters(props: {
|
||||
|
||||
{/* LOCATION */}
|
||||
<MobileFilterSection
|
||||
title={t('profile.optional.location', 'Location')}
|
||||
title={t('profile.optional.location', 'Living')}
|
||||
openFilter={openFilter}
|
||||
setOpenFilter={setOpenFilter}
|
||||
isActive={!!locationFilterProps.location}
|
||||
@@ -157,6 +159,27 @@ function MobileFilters(props: {
|
||||
<LocationFilter youProfile={youProfile} locationFilterProps={locationFilterProps} />
|
||||
</MobileFilterSection>
|
||||
|
||||
{/* RAISED IN LOCATION */}
|
||||
<MobileFilterSection
|
||||
title={t('profile.optional.raised_in', 'Grew up')}
|
||||
openFilter={openFilter}
|
||||
setOpenFilter={setOpenFilter}
|
||||
isActive={!!raisedInLocationFilterProps.location}
|
||||
selection={
|
||||
<LocationFilterText
|
||||
location={raisedInLocationFilterProps.location}
|
||||
radius={raisedInLocationFilterProps.radius}
|
||||
labelPrefix={t('filter.raised_in', 'Grew up')}
|
||||
youProfile={youProfile}
|
||||
highlightedClass={
|
||||
!raisedInLocationFilterProps.location ? 'text-ink-900' : 'text-primary-600'
|
||||
}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<LocationFilter youProfile={youProfile} locationFilterProps={raisedInLocationFilterProps} />
|
||||
</MobileFilterSection>
|
||||
|
||||
{/* AGE RANGE */}
|
||||
<MobileFilterSection
|
||||
title={t('profile.optional.age', 'Age')}
|
||||
|
||||
@@ -113,6 +113,7 @@ export const Search = forwardRef<
|
||||
setYourFilters: (checked: boolean) => void
|
||||
isYourFilters: boolean
|
||||
locationFilterProps: LocationFilterProps
|
||||
raisedInLocationFilterProps: LocationFilterProps
|
||||
bookmarkedSearches: BookmarkedSearchesType[]
|
||||
refreshBookmarkedSearches: () => void
|
||||
profileCount: number | undefined
|
||||
@@ -141,6 +142,7 @@ export const Search = forwardRef<
|
||||
setOpenFiltersModal: parentSetOpenFiltersModal,
|
||||
highlightFilters,
|
||||
highlightSort,
|
||||
raisedInLocationFilterProps,
|
||||
} = props
|
||||
|
||||
const [internalOpenFiltersModal, setInternalOpenFiltersModal] = useState(false)
|
||||
@@ -280,6 +282,7 @@ export const Search = forwardRef<
|
||||
setYourFilters={setYourFilters}
|
||||
isYourFilters={isYourFilters}
|
||||
locationFilterProps={locationFilterProps}
|
||||
raisedInLocationFilterProps={raisedInLocationFilterProps}
|
||||
includeRelationshipFilters={youSeekingRelationship}
|
||||
choices={choices}
|
||||
/>
|
||||
@@ -297,6 +300,7 @@ export const Search = forwardRef<
|
||||
setYourFilters={setYourFilters}
|
||||
isYourFilters={isYourFilters}
|
||||
locationFilterProps={locationFilterProps}
|
||||
raisedInLocationFilterProps={raisedInLocationFilterProps}
|
||||
includeRelationshipFilters={youSeekingRelationship}
|
||||
choices={choices}
|
||||
/>
|
||||
|
||||
@@ -30,6 +30,7 @@ export const useFilters = (you: Profile | undefined) => {
|
||||
const clearFilters = () => {
|
||||
setFilters(isLooking ? initialFilters : {...initialFilters, orderBy: 'created_time'})
|
||||
setLocation(undefined)
|
||||
setRaisedInLocation(undefined)
|
||||
}
|
||||
|
||||
const [radius, setRadius] = usePersistentLocalState<number>(100, 'search-radius')
|
||||
@@ -41,6 +42,19 @@ export const useFilters = (you: Profile | undefined) => {
|
||||
'nearby-origin-location',
|
||||
)
|
||||
|
||||
const [raisedInRadius, setRaisedInRadius] = usePersistentLocalState<number>(
|
||||
100,
|
||||
'raised-in-radius',
|
||||
)
|
||||
|
||||
const debouncedSetRaisedInRadius = useCallback(debounce(setRaisedInRadius, 200), [
|
||||
setRaisedInRadius,
|
||||
])
|
||||
|
||||
const [raisedInLocation, setRaisedInLocation] = usePersistentLocalState<
|
||||
OriginLocation | undefined | null
|
||||
>(undefined, 'raised-in-location')
|
||||
|
||||
// const nearbyCities = useNearbyCities(location?.id, radius)
|
||||
//
|
||||
// useEffectCheckEquality(() => {
|
||||
@@ -55,6 +69,22 @@ export const useFilters = (you: Profile | undefined) => {
|
||||
}
|
||||
}, [location?.id, radius])
|
||||
|
||||
useEffect(() => {
|
||||
if (raisedInLocation?.lat && raisedInLocation?.lon) {
|
||||
updateFilter({
|
||||
raised_in_lat: raisedInLocation.lat,
|
||||
raised_in_lon: raisedInLocation.lon,
|
||||
raised_in_radius: raisedInRadius,
|
||||
})
|
||||
} else {
|
||||
updateFilter({
|
||||
raised_in_lat: undefined,
|
||||
raised_in_lon: undefined,
|
||||
raised_in_radius: undefined,
|
||||
})
|
||||
}
|
||||
}, [raisedInLocation?.id, raisedInRadius])
|
||||
|
||||
const locationFilterProps = {
|
||||
location,
|
||||
setLocation,
|
||||
@@ -62,6 +92,13 @@ export const useFilters = (you: Profile | undefined) => {
|
||||
setRadius: debouncedSetRadius,
|
||||
}
|
||||
|
||||
const raisedInLocationFilterProps = {
|
||||
location: raisedInLocation,
|
||||
setLocation: setRaisedInLocation,
|
||||
radius: raisedInRadius,
|
||||
setRadius: debouncedSetRaisedInRadius,
|
||||
}
|
||||
|
||||
const yourFilters: Partial<FilterFields> = {
|
||||
pref_gender: you?.gender?.length ? [you.gender] : undefined,
|
||||
genders: you?.pref_gender?.length ? you.pref_gender : undefined,
|
||||
@@ -146,6 +183,7 @@ export const useFilters = (you: Profile | undefined) => {
|
||||
setYourFilters,
|
||||
isYourFilters,
|
||||
locationFilterProps,
|
||||
raisedInLocationFilterProps,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
12
web/components/icons.tsx
Normal file
12
web/components/icons.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
import {ReactNode} from 'react'
|
||||
import {Row} from 'web/components/layout/row'
|
||||
|
||||
export function IconWithInfo(props: {text: string; icon: ReactNode}) {
|
||||
const {text, icon} = props
|
||||
return (
|
||||
<Row className="items-center gap-0.5">
|
||||
<div className="text-ink-500">{icon}</div>
|
||||
{text}
|
||||
</Row>
|
||||
)
|
||||
}
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
ROMANTIC_CHOICES,
|
||||
} from 'common/choices'
|
||||
import {MultipleChoiceOptions} from 'common/profiles/multiple-choice'
|
||||
import {getProfileRow, ProfileWithoutUser} from 'common/profiles/profile'
|
||||
import {getProfileRow, Profile, ProfileWithoutUser} from 'common/profiles/profile'
|
||||
import {PLATFORM_LABELS, type Site, SITE_ORDER} from 'common/socials'
|
||||
import {User} from 'common/user'
|
||||
import {removeUndefinedProps} from 'common/util/object'
|
||||
@@ -243,6 +243,40 @@ export const OptionalProfileUserForm = (props: {
|
||||
}
|
||||
}
|
||||
|
||||
function profileToRaisedInCity(profile: Profile): City | undefined {
|
||||
if (profile.raised_in_geodb_city_id && profile.raised_in_lat && profile.raised_in_lon) {
|
||||
return {
|
||||
geodb_city_id: profile.raised_in_geodb_city_id,
|
||||
city: profile.raised_in_city ?? null,
|
||||
region_code: profile.raised_in_region_code ?? '',
|
||||
country: profile.raised_in_country ?? '',
|
||||
country_code: '',
|
||||
latitude: profile.raised_in_lat,
|
||||
longitude: profile.raised_in_lon,
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
function setProfileRaisedInCity(inputCity: City | undefined) {
|
||||
if (!inputCity) {
|
||||
setProfile('raised_in_geodb_city_id', null)
|
||||
setProfile('raised_in_city', null)
|
||||
setProfile('raised_in_region_code', null)
|
||||
setProfile('raised_in_country', null)
|
||||
setProfile('raised_in_lat', null)
|
||||
setProfile('raised_in_lon', null)
|
||||
} else {
|
||||
const {geodb_city_id, city, region_code, country, latitude, longitude} = inputCity
|
||||
setProfile('raised_in_geodb_city_id', geodb_city_id)
|
||||
setProfile('raised_in_city', city)
|
||||
setProfile('raised_in_region_code', region_code)
|
||||
setProfile('raised_in_country', country)
|
||||
setProfile('raised_in_lat', latitude)
|
||||
setProfile('raised_in_lon', longitude)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{/*<Row className={'justify-end'}>*/}
|
||||
@@ -431,6 +465,41 @@ export const OptionalProfileUserForm = (props: {
|
||||
/>
|
||||
</Col>
|
||||
|
||||
<Col className={clsx(colClassName)}>
|
||||
<label className={clsx(labelClassName)}>
|
||||
{t('profile.optional.raised_in', 'Place you grew up')}
|
||||
</label>
|
||||
<label className={clsx('guidance')}>
|
||||
{t(
|
||||
'profile.optional.raised_in_hint',
|
||||
'Especially useful if you grew up in a different country than where you live now—and if it reflects your cultural references, values, and life experiences.',
|
||||
)}
|
||||
</label>
|
||||
{profile.raised_in_geodb_city_id ? (
|
||||
<Row className="border-primary-500 w-full justify-between rounded border px-4 py-2">
|
||||
<CityRow
|
||||
city={profileToRaisedInCity(profile as Profile)!}
|
||||
onSelect={() => {}}
|
||||
className="pointer-events-none"
|
||||
/>
|
||||
<button
|
||||
className="text-ink-700 hover:text-primary-700 text-sm underline"
|
||||
onClick={() => {
|
||||
setProfileRaisedInCity(undefined)
|
||||
}}
|
||||
>
|
||||
{t('common.change', 'Change')}
|
||||
</button>
|
||||
</Row>
|
||||
) : (
|
||||
<CitySearchBox
|
||||
onCitySelected={(city: City | undefined) => {
|
||||
setProfileRaisedInCity(city)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Col>
|
||||
|
||||
<Category title={t('profile.optional.category.interested_in', "Who I'm looking for")} />
|
||||
|
||||
<Col className={clsx(colClassName)}>
|
||||
|
||||
@@ -14,6 +14,7 @@ import {convertGenderPlural, Gender} from 'common/gender'
|
||||
import {formatHeight, MeasurementSystem} from 'common/measurement-utils'
|
||||
import {Profile} from 'common/profiles/profile'
|
||||
import {UserActivity} from 'common/user'
|
||||
import {Home} from 'lucide-react'
|
||||
import React, {ReactNode} from 'react'
|
||||
import {BiSolidDrink} from 'react-icons/bi'
|
||||
import {BsPersonHeart, BsPersonVcard} from 'react-icons/bs'
|
||||
@@ -29,6 +30,7 @@ import {RiScales3Line} from 'react-icons/ri'
|
||||
import {TbBulb, TbCheck, TbMoodSad, TbUsers} from 'react-icons/tb'
|
||||
import {Col} from 'web/components/layout/col'
|
||||
import {Row} from 'web/components/layout/row'
|
||||
import {getLocationText} from 'web/components/profile/profile-location'
|
||||
import {UserHandles} from 'web/components/user/user-handles'
|
||||
import {useChoices} from 'web/hooks/use-choices'
|
||||
import {useLocale, useT} from 'web/lib/locale'
|
||||
@@ -151,6 +153,7 @@ export default function ProfileAbout(props: {
|
||||
?.map((r: any) => t(`profile.race.${r}`, convertRace(r)))}
|
||||
testId="profile-about-ethnicity"
|
||||
/>
|
||||
<RaisedIn profile={profile} />
|
||||
<Smoker profile={profile} />
|
||||
<Drinks profile={profile} />
|
||||
<AboutRow
|
||||
@@ -505,6 +508,20 @@ function HasKids(props: {profile: Profile}) {
|
||||
return <AboutRow icon={icon} text={hasKidsText} testId={'profile-about-has-kids'} />
|
||||
}
|
||||
|
||||
function RaisedIn(props: {profile: Profile}) {
|
||||
const t = useT()
|
||||
const locationText = getLocationText(props.profile, 'raised_in_')
|
||||
if (!locationText) {
|
||||
return null
|
||||
}
|
||||
return (
|
||||
<AboutRow
|
||||
icon={<Home className="h-5 w-5" />}
|
||||
text={t('profile.about.raised_in', `Raised in ${locationText}`, {location: locationText})}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export const formatProfileValue = (
|
||||
key: string,
|
||||
value: any,
|
||||
|
||||
@@ -92,7 +92,7 @@ export default function ProfileHeader(props: {
|
||||
) : (
|
||||
<span className="font-semibold">{user.name}</span>
|
||||
)}
|
||||
{profile.age ? `, ${profile.age}` : ''}
|
||||
{profile.age && `, ${t('profile.header.age', '{age}', {age: profile.age})}`}
|
||||
</span>
|
||||
</Row>
|
||||
<ProfilePrimaryInfo profile={profile} />
|
||||
|
||||
29
web/components/profile/profile-location.tsx
Normal file
29
web/components/profile/profile-location.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import {Profile} from 'common/profiles/profile'
|
||||
import {IoLocationOutline} from 'react-icons/io5'
|
||||
import {IconWithInfo} from 'web/components/icons'
|
||||
|
||||
export function getLocationText(profile: Profile, prefix?: string) {
|
||||
const city = profile[`${prefix}city` as keyof Profile]
|
||||
const country = profile[`${prefix}country` as keyof Profile]
|
||||
const regionCode = profile[`${prefix}region_code` as keyof Profile]
|
||||
|
||||
const stateOrCountry = country === 'United States of America' ? regionCode : country
|
||||
|
||||
if (!city) {
|
||||
return null
|
||||
}
|
||||
|
||||
return `${city}${stateOrCountry && ', '}${stateOrCountry}`
|
||||
}
|
||||
|
||||
export function ProfileLocation(props: {profile: Profile; prefix?: string}) {
|
||||
const {profile, prefix = ''} = props
|
||||
|
||||
const text = getLocationText(profile, prefix)
|
||||
|
||||
if (!text) {
|
||||
return null
|
||||
}
|
||||
|
||||
return <IconWithInfo text={text} icon={<IoLocationOutline className="h-4 w-4" />} />
|
||||
}
|
||||
@@ -1,30 +1,23 @@
|
||||
import {convertGender, Gender} from 'common/gender'
|
||||
import {Profile} from 'common/profiles/profile'
|
||||
import {capitalize} from 'lodash'
|
||||
import {ReactNode} from 'react'
|
||||
import {IoLocationOutline} from 'react-icons/io5'
|
||||
import {MdHeight} from 'react-icons/md'
|
||||
import {IconWithInfo} from 'web/components/icons'
|
||||
import {Row} from 'web/components/layout/row'
|
||||
import {useMeasurementSystem} from 'web/hooks/use-measurement-system'
|
||||
import {useT} from 'web/lib/locale'
|
||||
|
||||
import GenderIcon from '../gender-icon'
|
||||
import {formatProfileValue} from '../profile-about'
|
||||
import {ProfileLocation} from './profile-location'
|
||||
|
||||
export default function ProfilePrimaryInfo(props: {profile: Profile}) {
|
||||
const {profile} = props
|
||||
const t = useT()
|
||||
const {measurementSystem} = useMeasurementSystem()
|
||||
const stateOrCountry =
|
||||
profile.country === 'United States of America' ? profile.region_code : profile.country
|
||||
return (
|
||||
<Row className="text-ink-700 gap-4 text-sm" data-testid="profile-gender-location-height-inches">
|
||||
{profile.city && (
|
||||
<IconWithInfo
|
||||
text={`${profile.city ?? ''}, ${stateOrCountry ?? ''}`}
|
||||
icon={<IoLocationOutline className="h-4 w-4" />}
|
||||
/>
|
||||
)}
|
||||
<ProfileLocation profile={profile} />
|
||||
{profile.gender && (
|
||||
<IconWithInfo
|
||||
text={capitalize(
|
||||
@@ -42,13 +35,3 @@ export default function ProfilePrimaryInfo(props: {profile: Profile}) {
|
||||
</Row>
|
||||
)
|
||||
}
|
||||
|
||||
function IconWithInfo(props: {text: string; icon: ReactNode}) {
|
||||
const {text, icon} = props
|
||||
return (
|
||||
<Row className="items-center gap-0.5">
|
||||
<div className="text-ink-500">{icon}</div>
|
||||
{text}
|
||||
</Row>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -28,8 +28,15 @@ export function ProfilesHome() {
|
||||
const user = useUser()
|
||||
const you = useProfile()
|
||||
|
||||
const {filters, updateFilter, clearFilters, setYourFilters, isYourFilters, locationFilterProps} =
|
||||
useFilters(you ?? undefined)
|
||||
const {
|
||||
filters,
|
||||
updateFilter,
|
||||
clearFilters,
|
||||
setYourFilters,
|
||||
isYourFilters,
|
||||
locationFilterProps,
|
||||
raisedInLocationFilterProps,
|
||||
} = useFilters(you ?? undefined)
|
||||
|
||||
const [profiles, setProfiles] = usePersistentInMemoryState<Profile[] | undefined>(
|
||||
undefined,
|
||||
@@ -260,6 +267,7 @@ export function ProfilesHome() {
|
||||
setYourFilters={setYourFilters}
|
||||
isYourFilters={isYourFilters}
|
||||
locationFilterProps={locationFilterProps}
|
||||
raisedInLocationFilterProps={raisedInLocationFilterProps}
|
||||
bookmarkedSearches={bookmarkedSearches}
|
||||
refreshBookmarkedSearches={refreshBookmarkedSearches}
|
||||
profileCount={profileCount}
|
||||
|
||||
@@ -221,12 +221,6 @@
|
||||
"filter.gender.gender": "Geschlecht",
|
||||
"filter.gender.genders": "Geschlechter",
|
||||
"filter.gender.they_seek": "Geschlecht, das sie suchen",
|
||||
"filter.location": "Standort",
|
||||
"filter.near": "in der Nähe von",
|
||||
"filter.location.any": "Jeder",
|
||||
"filter.location.distance_miles": "Entfernung (Meilen)",
|
||||
"filter.location.search_city": "Stadt suchen...",
|
||||
"filter.location.set_any_city": "Jede Stadt",
|
||||
"filter.mine_toggle": "Meine Filter",
|
||||
"filter.multiple": "Mehrere",
|
||||
"filter.relationship.any_connection": "Jede Beziehung",
|
||||
@@ -724,6 +718,16 @@
|
||||
"profile.optional.username_or_url": "Benutzername oder URL",
|
||||
"profile.optional.want_kids": "Ich möchte Kinder haben",
|
||||
"profile.optional.work": "Arbeit",
|
||||
"profile.optional.raised_in": "Ort, an dem ich aufgewachsen bin",
|
||||
"profile.optional.raised_in_hint": "Besonders nützlich, wenn Sie in einem anderen Land aufgewachsen sind als dem, in dem Sie jetzt leben – und wenn dies Ihre kulturellen Referenzen, Werte und Lebenserfahrungen widerspiegelt.",
|
||||
"profile.about.raised_in": "Aufgewachsen in {location}",
|
||||
"profile.header.age": "{age}",
|
||||
"filter.raised_in": "Aufgewachsen",
|
||||
"filter.location": "Wohnhaft",
|
||||
"filter.location.any": "überall",
|
||||
"filter.near": "in der Nähe von",
|
||||
"filter.location.search_city": "Stadt suchen...",
|
||||
"filter.location.set_any_city": "Auf jede Stadt einstellen",
|
||||
"profile.big5_openness": "Offenheit",
|
||||
"profile.big5_conscientiousness": "Gewissenhaftigkeit",
|
||||
"profile.big5_extraversion": "Extraversion",
|
||||
|
||||
@@ -218,12 +218,11 @@
|
||||
"filter.gender.gender": "genre",
|
||||
"filter.gender.genders": "genres",
|
||||
"filter.gender.they_seek": "Genre qu'ils recherchent",
|
||||
"filter.location": "localisation",
|
||||
"filter.near": "près de",
|
||||
"filter.location.any": "Toute",
|
||||
"filter.location.distance_miles": "Distance (miles)",
|
||||
"filter.location.search_city": "Rechercher une ville...",
|
||||
"filter.location.set_any_city": "N'importe quelle ville",
|
||||
"filter.location": "Vit",
|
||||
"filter.location.any": "n'importe où",
|
||||
"filter.near": "près de",
|
||||
"filter.location.search_city": "Rechercher une ville...",
|
||||
"filter.mine_toggle": "Mes filtres",
|
||||
"filter.multiple": "Multiple",
|
||||
"filter.relationship.any_connection": "Toute relation",
|
||||
@@ -721,6 +720,11 @@
|
||||
"profile.optional.username_or_url": "Nom d'utilisateur ou URL",
|
||||
"profile.optional.want_kids": "Je souhaite avoir des enfants",
|
||||
"profile.optional.work": "Domaine de travail",
|
||||
"profile.optional.raised_in": "Lieu où j'ai grandi",
|
||||
"profile.optional.raised_in_hint": "Particulièrement utile si vous avez grandi dans un pays différent de celui où vous vivez maintenant – et si cela reflète vos références culturelles, vos valeurs et vos expériences de vie.",
|
||||
"profile.about.raised_in": "A grandi à {location}",
|
||||
"profile.header.age": "{age} ans",
|
||||
"filter.raised_in": "A grandi",
|
||||
"profile.big5_openness": "Ouverture",
|
||||
"profile.big5_conscientiousness": "Conscienciosité",
|
||||
"profile.big5_extraversion": "Extraversion",
|
||||
|
||||
Reference in New Issue
Block a user