134134</template >
135135
136136<script setup>
137- import { ref , onMounted , onUnmounted , watch , nextTick } from " vue" ;
137+ import { ref , onMounted , onUnmounted , watch , nextTick , computed } from " vue" ;
138138import { useRoute , useRouter } from " vue-router" ;
139139import { storeToRefs } from " pinia" ;
140140import MainLayout from " ~/layouts/MainLayout.vue" ;
@@ -143,6 +143,7 @@ import { useProfileStore } from "~/stores/profile";
143143import { useAuthStore } from " ~/stores/auth" ;
144144import { useUtils } from " @/composables/useUtils" ;
145145import { useI18n } from " vue-i18n" ;
146+ import { useHead } from " @unhead/vue" ;
146147
147148const { formatCount } = useUtils ();
148149const authStore = useAuthStore ();
@@ -164,6 +165,90 @@ const tabBarRef = ref(null);
164165
165166const { posts , allLikes } = storeToRefs (profileStore);
166167
168+ const metaTitle = computed (() => {
169+ if (! profileStore .name ) return " Loops" ;
170+ return ` ${ profileStore .name } (@${ profileStore .username } ) | Loops` ;
171+ });
172+
173+ const metaDescription = computed (() => {
174+ if (! profileStore .username ) return " Watch videos on Loops" ;
175+
176+ const parts = [];
177+
178+ if (profileStore .bio ) {
179+ parts .push (profileStore .bio );
180+ }
181+
182+ const stats = [
183+ ` ${ formatCount (profileStore .postCount )} videos` ,
184+ ` ${ formatCount (profileStore .followerCount )} followers` ,
185+ ` ${ formatCount (profileStore .allLikes )} likes` ,
186+ ];
187+
188+ parts .push (stats .join (" · " ));
189+
190+ return parts .join (" | " );
191+ });
192+
193+ const profileUrl = computed (() => {
194+ if (! profileStore .username ) return " " ;
195+ return ` ${ window .location .origin } /@${ profileStore .username } ` ;
196+ });
197+
198+ const profileAvatar = computed (() => {
199+ return profileStore .avatar || " /storage/avatars/default.jpg" ;
200+ });
201+
202+ useHead ({
203+ title: metaTitle,
204+ meta: [
205+ {
206+ name: " description" ,
207+ content: metaDescription,
208+ },
209+ {
210+ property: " og:title" ,
211+ content: metaTitle,
212+ },
213+ {
214+ property: " og:description" ,
215+ content: metaDescription,
216+ },
217+ {
218+ property: " og:image" ,
219+ content: profileAvatar,
220+ },
221+ {
222+ property: " og:url" ,
223+ content: profileUrl,
224+ },
225+ {
226+ property: " og:type" ,
227+ content: " profile" ,
228+ },
229+ {
230+ property: " profile:username" ,
231+ content : () => profileStore .username || " " ,
232+ },
233+ {
234+ name: " twitter:card" ,
235+ content: " summary" ,
236+ },
237+ {
238+ name: " twitter:title" ,
239+ content: metaTitle,
240+ },
241+ {
242+ name: " twitter:description" ,
243+ content: metaDescription,
244+ },
245+ {
246+ name: " twitter:image" ,
247+ content: profileAvatar,
248+ },
249+ ],
250+ });
251+
167252let scrollTimeout = null ;
168253
169254const handleTabChange = (tab ) => {
0 commit comments