+1
-1
frontend-v2/schema.graphql
+1
-1
frontend-v2/schema.graphql
+8
-7
frontend-v2/server/profile-init.ts
+8
-7
frontend-v2/server/profile-init.ts
···
18
18
export async function initializeUserProfile(
19
19
userDid: string,
20
20
userHandle: string,
21
-
tokens: TokenInfo
21
+
tokens: TokenInfo,
22
22
): Promise<void> {
23
23
if (!API_URL || !SLICE_URI) {
24
24
console.error("Missing API_URL or VITE_SLICE_URI environment variables");
···
26
26
}
27
27
28
28
try {
29
-
const graphqlUrl = `${API_URL}/graphql?slice=${encodeURIComponent(SLICE_URI)}`;
29
+
const graphqlUrl = `${API_URL}/graphql?slice=${
30
+
encodeURIComponent(SLICE_URI)
31
+
}`;
30
32
const authHeader = `${tokens.tokenType} ${tokens.accessToken}`;
31
33
32
34
// 1. Check if profile already exists
···
132
134
});
133
135
134
136
if (!bskyResponse.ok) {
135
-
throw new Error(`Fetch Bluesky profile failed: ${bskyResponse.statusText}`);
137
+
throw new Error(
138
+
`Fetch Bluesky profile failed: ${bskyResponse.statusText}`,
139
+
);
136
140
}
137
141
138
142
const bskyData = await bskyResponse.json();
···
160
164
) {
161
165
// Reconstruct blob format for AT Protocol
162
166
profileInput.avatar = {
163
-
$type: "blob",
164
-
ref: {
165
-
$link: bskyProfile.avatar.ref,
166
-
},
167
+
ref: bskyProfile.avatar.ref,
167
168
mimeType: bskyProfile.avatar.mimeType,
168
169
size: bskyProfile.avatar.size,
169
170
};
+35
-6
frontend-v2/src/__generated__/ProfileSettingsUploadBlobMutation.graphql.ts
+35
-6
frontend-v2/src/__generated__/ProfileSettingsUploadBlobMutation.graphql.ts
···
1
1
/**
2
-
* @generated SignedSource<<a2334c7e93bb6d5b4748df1211a418ae>>
2
+
* @generated SignedSource<<728b9a3525f975b6c58a5cdcd323f89e>>
3
3
* @lightSyntaxTransform
4
4
* @nogrep
5
5
*/
···
15
15
};
16
16
export type ProfileSettingsUploadBlobMutation$data = {
17
17
readonly uploadBlob: {
18
-
readonly blob: any;
18
+
readonly blob: {
19
+
readonly mimeType: string;
20
+
readonly ref: string;
21
+
readonly size: number;
22
+
};
19
23
};
20
24
};
21
25
export type ProfileSettingsUploadBlobMutation = {
···
59
63
{
60
64
"alias": null,
61
65
"args": null,
62
-
"kind": "ScalarField",
66
+
"concreteType": "Blob",
67
+
"kind": "LinkedField",
63
68
"name": "blob",
69
+
"plural": false,
70
+
"selections": [
71
+
{
72
+
"alias": null,
73
+
"args": null,
74
+
"kind": "ScalarField",
75
+
"name": "ref",
76
+
"storageKey": null
77
+
},
78
+
{
79
+
"alias": null,
80
+
"args": null,
81
+
"kind": "ScalarField",
82
+
"name": "mimeType",
83
+
"storageKey": null
84
+
},
85
+
{
86
+
"alias": null,
87
+
"args": null,
88
+
"kind": "ScalarField",
89
+
"name": "size",
90
+
"storageKey": null
91
+
}
92
+
],
64
93
"storageKey": null
65
94
}
66
95
],
···
85
114
"selections": (v1/*: any*/)
86
115
},
87
116
"params": {
88
-
"cacheID": "3a4a6b19d2898f14635b098941614cab",
117
+
"cacheID": "afd8db2ee7590308e81afc0b0e5c86dd",
89
118
"id": null,
90
119
"metadata": {},
91
120
"name": "ProfileSettingsUploadBlobMutation",
92
121
"operationKind": "mutation",
93
-
"text": "mutation ProfileSettingsUploadBlobMutation(\n $data: String!\n $mimeType: String!\n) {\n uploadBlob(data: $data, mimeType: $mimeType) {\n blob\n }\n}\n"
122
+
"text": "mutation ProfileSettingsUploadBlobMutation(\n $data: String!\n $mimeType: String!\n) {\n uploadBlob(data: $data, mimeType: $mimeType) {\n blob {\n ref\n mimeType\n size\n }\n }\n}\n"
94
123
}
95
124
};
96
125
})();
97
126
98
-
(node as any).hash = "76da65b07a282ed7f2dee12b4cac82d6";
127
+
(node as any).hash = "74a3a8bf43181cd62d2e81c45be384e5";
99
128
100
129
export default node;
+18
-13
frontend-v2/src/pages/ProfileSettings.tsx
+18
-13
frontend-v2/src/pages/ProfileSettings.tsx
···
1
-
import { useParams, Link } from "react-router-dom";
1
+
import { Link, useParams } from "react-router-dom";
2
2
import { useState } from "react";
3
3
import { graphql, useLazyLoadQuery, useMutation } from "react-relay";
4
4
import type { ProfileSettingsQuery } from "../__generated__/ProfileSettingsQuery.graphql.ts";
···
44
44
where: {
45
45
actorHandle: { eq: handle },
46
46
},
47
-
}
47
+
},
48
48
);
49
49
50
50
const profile = data.networkSlicesActorProfiles.edges[0]?.node;
···
59
59
graphql`
60
60
mutation ProfileSettingsUploadBlobMutation($data: String!, $mimeType: String!) {
61
61
uploadBlob(data: $data, mimeType: $mimeType) {
62
-
blob
62
+
blob {
63
+
ref
64
+
mimeType
65
+
size
66
+
}
63
67
}
64
68
}
65
-
`
69
+
`,
66
70
);
67
71
68
72
const [commitUpdateProfile, isUpdatingProfile] = useMutation(
···
80
84
}
81
85
}
82
86
}
83
-
`
87
+
`,
84
88
);
85
89
86
90
const [commitCreateProfile, isCreatingProfile] = useMutation(
···
98
102
}
99
103
}
100
104
}
101
-
`
105
+
`,
102
106
);
103
107
104
108
// Helper to convert File to base64
···
108
112
reader.onload = () => {
109
113
const arrayBuffer = reader.result as ArrayBuffer;
110
114
const bytes = new Uint8Array(arrayBuffer);
111
-
const binary = Array.from(bytes).map(b => String.fromCharCode(b)).join('');
115
+
const binary = Array.from(bytes).map((b) => String.fromCharCode(b))
116
+
.join("");
112
117
resolve(btoa(binary));
113
118
};
114
119
reader.onerror = reject;
···
129
134
// Upload new avatar
130
135
const base64Data = await fileToBase64(avatarFile);
131
136
132
-
const uploadResult = await new Promise<{ uploadBlob: { blob: unknown } }>((resolve, reject) => {
137
+
const uploadResult = await new Promise<
138
+
{ uploadBlob: { blob: unknown } }
139
+
>((resolve, reject) => {
133
140
commitUploadBlob({
134
141
variables: {
135
142
data: base64Data,
136
143
mimeType: avatarFile.type,
137
144
},
138
-
onCompleted: (data) => resolve(data as { uploadBlob: { blob: unknown } }),
145
+
onCompleted: (data) =>
146
+
resolve(data as { uploadBlob: { blob: unknown } }),
139
147
onError: (error) => reject(error),
140
148
});
141
149
});
···
144
152
} else if (profile?.avatar) {
145
153
// Keep existing avatar - reconstruct blob with $type field for AT Protocol
146
154
avatarBlob = {
147
-
$type: "blob",
148
-
ref: {
149
-
$link: profile.avatar.ref,
150
-
},
155
+
ref: profile.avatar.ref,
151
156
mimeType: profile.avatar.mimeType,
152
157
size: profile.avatar.size,
153
158
};