♻️ Transformed API keys
This commit is contained in:
@@ -10,7 +10,7 @@ import type { SnAttachment } from '~/types/api'
|
||||
|
||||
const props = defineProps<{ item: SnAttachment }>()
|
||||
|
||||
const itemType = computed(() => props.item.mime_type.split('/')[0] ?? 'unknown')
|
||||
const itemType = computed(() => props.item.mimeType.split('/')[0] ?? 'unknown')
|
||||
|
||||
const apiBase = useSolarNetworkUrl();
|
||||
const remoteSource = computed(() => `${apiBase}/drive/files/${props.item.id}?original=true`)
|
||||
|
@@ -7,9 +7,9 @@
|
||||
<span class="text-xs">@{{ props.item.publisher.name }}</span>
|
||||
</p>
|
||||
<p class="text-xs flex gap-1">
|
||||
<span>{{ DateTime.fromISO(props.item.created_at).toRelative() }}</span>
|
||||
<span>{{ DateTime.fromISO(props.item.createdAt).toRelative() }}</span>
|
||||
<span class="font-bold">·</span>
|
||||
<span>{{ DateTime.fromISO(props.item.created_at).toLocaleString() }}</span>
|
||||
<span>{{ DateTime.fromISO(props.item.createdAt).toLocaleString() }}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -18,8 +18,9 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue'
|
||||
import { DateTime } from 'luxon'
|
||||
import type { SnPost } from '~/types/api';
|
||||
|
||||
const props = defineProps<{ item: any }>()
|
||||
const props = defineProps<{ item: SnPost }>()
|
||||
|
||||
const apiBase = useSolarNetworkUrl();
|
||||
const publisherAvatar = computed(() =>
|
||||
|
@@ -1,10 +1,28 @@
|
||||
// Solar Network aka the api client
|
||||
import { keysToCamel, keysToSnake } from '~/utils/transformKeys'
|
||||
|
||||
export const useSolarNetwork = () => {
|
||||
const apiBase = useSolarNetworkUrl();
|
||||
return $fetch.create({ baseURL: apiBase, credentials: 'include' })
|
||||
|
||||
return $fetch.create({
|
||||
baseURL: apiBase,
|
||||
credentials: 'include',
|
||||
// Transform response keys from snake_case to camelCase
|
||||
onResponse: ({ response }) => {
|
||||
if (response._data) {
|
||||
response._data = keysToCamel(response._data)
|
||||
}
|
||||
},
|
||||
// Transform request data from camelCase to snake_case
|
||||
onRequest: ({ options }) => {
|
||||
if (options.body && typeof options.body === 'object') {
|
||||
options.body = keysToSnake(options.body)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const useSolarNetworkUrl = () => {
|
||||
const config = useRuntimeConfig()
|
||||
return config.public.apiBase
|
||||
}
|
||||
}
|
||||
|
@@ -73,7 +73,7 @@ async function fetchActivites() {
|
||||
const resp = await api(
|
||||
activitesLast.value == null
|
||||
? '/sphere/activities'
|
||||
: `/sphere/activities?cursor=${new Date(activitesLast.value.created_at).toISOString()}`,
|
||||
: `/sphere/activities?cursor=${new Date(activitesLast.value.createdAt).toISOString()}`,
|
||||
)
|
||||
const data = resp as SnActivity[]
|
||||
activites.value = [...activites.value, ...data]
|
||||
|
@@ -4,11 +4,11 @@ import type { SnPost } from './post'
|
||||
export interface SnActivity {
|
||||
id: string;
|
||||
type: string;
|
||||
resource_identifier: string;
|
||||
resourceIdentifier: string;
|
||||
meta: Record<string, unknown>;
|
||||
data: SnPost;
|
||||
visibility: number;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
deleted_at: string | null;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
deletedAt: string | null;
|
||||
}
|
||||
|
@@ -16,26 +16,26 @@ export interface SnFileMeta {
|
||||
yoffset?: number;
|
||||
filename?: string | null;
|
||||
orientation?: number;
|
||||
'vips-loader'?: string;
|
||||
vipsLoader?: string;
|
||||
interpretation?: number;
|
||||
'bits-per-sample'?: number;
|
||||
'resolution-unit'?: string;
|
||||
bitsPerSample?: number;
|
||||
resolutionUnit?: string;
|
||||
}
|
||||
|
||||
// Attachment interface
|
||||
export interface SnAttachment {
|
||||
id: string;
|
||||
name: string;
|
||||
file_meta: SnFileMeta;
|
||||
user_meta: Record<string, unknown> | null;
|
||||
sensitive_marks: string[];
|
||||
mime_type: string;
|
||||
fileMeta: SnFileMeta;
|
||||
userMeta: Record<string, unknown> | null;
|
||||
sensitiveMarks: string[];
|
||||
mimeType: string;
|
||||
hash: string;
|
||||
size: number;
|
||||
has_compression: boolean;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
deleted_at: string | null;
|
||||
hasCompression: boolean;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
deletedAt: string | null;
|
||||
}
|
||||
|
||||
// Post interface
|
||||
@@ -44,40 +44,40 @@ export interface SnPost {
|
||||
title: string;
|
||||
description: string;
|
||||
slug: string | null;
|
||||
edited_at: string | null;
|
||||
published_at: string;
|
||||
editedAt: string | null;
|
||||
publishedAt: string;
|
||||
visibility: number;
|
||||
content: string;
|
||||
type: number;
|
||||
pin_mode: unknown | null;
|
||||
pinMode: unknown | null;
|
||||
meta: unknown | null;
|
||||
sensitive_marks: string[];
|
||||
embed_view: unknown | null;
|
||||
views_unique: number;
|
||||
views_total: number;
|
||||
sensitiveMarks: string[];
|
||||
embedView: unknown | null;
|
||||
viewsUnique: number;
|
||||
viewsTotal: number;
|
||||
upvotes: number;
|
||||
downvotes: number;
|
||||
awarded_score: number;
|
||||
reactions_count: Record<string, number>;
|
||||
replies_count: number;
|
||||
reactions_made: Record<string, unknown>;
|
||||
replied_gone: boolean;
|
||||
forwarded_gone: boolean;
|
||||
replied_post_id: string | null;
|
||||
replied_post: SnPost | null;
|
||||
forwarded_post_id: string | null;
|
||||
forwarded_post: SnPost | null;
|
||||
realm_id: string | null;
|
||||
awardedScore: number;
|
||||
reactionsCount: Record<string, number>;
|
||||
repliesCount: number;
|
||||
reactionsMade: Record<string, unknown>;
|
||||
repliedGone: boolean;
|
||||
forwardedGone: boolean;
|
||||
repliedPostId: string | null;
|
||||
repliedPost: SnPost | null;
|
||||
forwardedPostId: string | null;
|
||||
forwardedPost: SnPost | null;
|
||||
realmId: string | null;
|
||||
realm: unknown | null;
|
||||
attachments: SnAttachment[];
|
||||
publisher_id: string;
|
||||
publisherId: string;
|
||||
publisher: SnPublisher;
|
||||
awards: unknown | null;
|
||||
tags: string[];
|
||||
categories: string[];
|
||||
is_truncated: boolean;
|
||||
resource_identifier: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
deleted_at: string | null;
|
||||
isTruncated: boolean;
|
||||
resourceIdentifier: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
deletedAt: string | null;
|
||||
}
|
||||
|
@@ -5,7 +5,7 @@ export interface SnVerification {
|
||||
type: number;
|
||||
title: string;
|
||||
description: string;
|
||||
verified_by: string;
|
||||
verifiedBy: string;
|
||||
}
|
||||
|
||||
// Publisher interface
|
||||
@@ -15,16 +15,16 @@ export interface SnPublisher {
|
||||
name: string;
|
||||
nick: string;
|
||||
bio: string;
|
||||
picture_id: string;
|
||||
background_id: string;
|
||||
pictureId: string;
|
||||
backgroundId: string;
|
||||
picture: SnAttachment | null;
|
||||
background: SnAttachment | null;
|
||||
verification: SnVerification | null;
|
||||
account_id: string;
|
||||
realm_id: string | null;
|
||||
accountId: string;
|
||||
realmId: string | null;
|
||||
account: unknown | null;
|
||||
resource_identifier: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
deleted_at: string | null;
|
||||
resourceIdentifier: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
deletedAt: string | null;
|
||||
}
|
||||
|
@@ -14,62 +14,62 @@ export interface SnAccountBadge {
|
||||
label: string | null;
|
||||
caption: string | null;
|
||||
meta: Record<string, unknown>;
|
||||
activated_at: string;
|
||||
expired_at: string | null;
|
||||
account_id: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
deleted_at: string | null;
|
||||
activatedAt: string;
|
||||
expiredAt: string | null;
|
||||
accountId: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
deletedAt: string | null;
|
||||
}
|
||||
|
||||
// Account perk subscription interface
|
||||
export interface SnAccountPerkSubscription {
|
||||
id: string;
|
||||
identifier: string;
|
||||
begun_at: string;
|
||||
ended_at: string;
|
||||
is_active: boolean;
|
||||
is_available: boolean;
|
||||
is_free_trial: boolean;
|
||||
begunAt: string;
|
||||
endedAt: string;
|
||||
isActive: boolean;
|
||||
isAvailable: boolean;
|
||||
isFreeTrial: boolean;
|
||||
status: number;
|
||||
base_price: number;
|
||||
final_price: number;
|
||||
renewal_at: string;
|
||||
account_id: string;
|
||||
display_name: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
deleted_at: string | null;
|
||||
basePrice: number;
|
||||
finalPrice: number;
|
||||
renewalAt: string;
|
||||
accountId: string;
|
||||
displayName: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
deletedAt: string | null;
|
||||
}
|
||||
|
||||
// Account profile interface
|
||||
export interface SnAccountProfile {
|
||||
id: string;
|
||||
first_name: string;
|
||||
middle_name: string;
|
||||
last_name: string;
|
||||
firstName: string;
|
||||
middleName: string;
|
||||
lastName: string;
|
||||
bio: string;
|
||||
gender: string;
|
||||
pronouns: string;
|
||||
time_zone: string;
|
||||
timeZone: string;
|
||||
location: string;
|
||||
links: SnAccountLink[];
|
||||
birthday: string;
|
||||
last_seen_at: string;
|
||||
lastSeenAt: string;
|
||||
verification: SnVerification | null;
|
||||
active_badge: unknown | null;
|
||||
activeBadge: unknown | null;
|
||||
experience: number;
|
||||
level: number;
|
||||
social_credits: number;
|
||||
social_credits_level: number;
|
||||
leveling_progress: number;
|
||||
socialCredits: number;
|
||||
socialCreditsLevel: number;
|
||||
levelingProgress: number;
|
||||
picture: SnAttachment | null;
|
||||
background: SnAttachment | null;
|
||||
account_id: string;
|
||||
resource_identifier: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
deleted_at: string | null;
|
||||
accountId: string;
|
||||
resourceIdentifier: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
deletedAt: string | null;
|
||||
}
|
||||
|
||||
// Account interface
|
||||
@@ -79,14 +79,14 @@ export interface SnAccount {
|
||||
nick: string;
|
||||
language: string;
|
||||
region: string;
|
||||
activated_at: string;
|
||||
is_superuser: boolean;
|
||||
automated_id: string | null;
|
||||
activatedAt: string;
|
||||
isSuperuser: boolean;
|
||||
automatedId: string | null;
|
||||
profile: SnAccountProfile;
|
||||
contacts: unknown[];
|
||||
badges: SnAccountBadge[];
|
||||
perk_subscription: SnAccountPerkSubscription | null;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
deleted_at: string | null;
|
||||
perkSubscription: SnAccountPerkSubscription | null;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
deletedAt: string | null;
|
||||
}
|
||||
|
57
app/utils/transformKeys.ts
Normal file
57
app/utils/transformKeys.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Transform object keys between snake_case and camelCase
|
||||
*/
|
||||
|
||||
type TransformFunction = (key: string) => string
|
||||
|
||||
const snakeCase: TransformFunction = (key: string): string => {
|
||||
return key.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`)
|
||||
}
|
||||
|
||||
const camelCase: TransformFunction = (key: string): string => {
|
||||
return key.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase())
|
||||
}
|
||||
|
||||
function transformKeys(obj: unknown, transformFn: TransformFunction): unknown {
|
||||
if (obj === null || obj === undefined) {
|
||||
return obj
|
||||
}
|
||||
|
||||
if (Array.isArray(obj)) {
|
||||
return obj.map(item => transformKeys(item, transformFn))
|
||||
}
|
||||
|
||||
if (typeof obj === 'object' && obj.constructor === Object) {
|
||||
const transformed: Record<string, unknown> = {}
|
||||
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
const transformedKey = transformFn(key)
|
||||
transformed[transformedKey] = transformKeys(value, transformFn)
|
||||
}
|
||||
|
||||
return transformed
|
||||
}
|
||||
|
||||
return obj
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert snake_case keys to camelCase
|
||||
*/
|
||||
export function keysToCamel<T = unknown>(obj: unknown): T {
|
||||
return transformKeys(obj, camelCase) as T
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert camelCase keys to snake_case
|
||||
*/
|
||||
export function keysToSnake<T = unknown>(obj: unknown): T {
|
||||
return transformKeys(obj, snakeCase) as T
|
||||
}
|
||||
|
||||
/**
|
||||
* Deep clone and transform keys
|
||||
*/
|
||||
export function deepTransformKeys<T = unknown>(obj: unknown, transformFn: TransformFunction): T {
|
||||
return JSON.parse(JSON.stringify(transformKeys(obj, transformFn))) as T
|
||||
}
|
Reference in New Issue
Block a user