{"version":3,"file":"ProfileView-BORmFpQY.js","sources":["../../../ui/src/components/UserProfile.vue","../../../ui/src/views/ProfileView.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed } from 'vue';\nimport IconTwitter from '~icons/bxl/twitter';\nimport IconGithub from '~icons/bxl/github';\nimport IconLinkedin from '~icons/bxl/linkedin';\nimport IconGlobe from '~icons/mdi/globe';\nimport type { GetProfileResponse } from '~/utilities/pyscript-api-models';\n\ninterface Props {\n  usernameOrId: string;\n  user: GetProfileResponse | null;\n  isOwner: boolean;\n}\nconst props = defineProps<Props>();\n\ndefineEmits<{\n  (e: 'update:show', show: boolean): void;\n}>();\n\nconst hasSocials = computed(() => {\n  if (!props.user) return false;\n  return Object.values(props.user.socials).some((value) => !!value);\n});\n\nfunction generateSocial(name: string, username: string) {\n  if (!username) return '';\n  switch (name) {\n    case 'twitter':\n      return `twitter.com/${username}`;\n    case 'github':\n      return `github.com/${username}`;\n    case 'linkedin':\n      return `linkedin.com/in/${username}`;\n    case 'website':\n      return `${username.replace(/^https?:\\/\\//i, '')}`;\n  }\n}\n</script>\n\n<template>\n  <section class=\"flex flex-col items-stretch\">\n    <div v-if=\"user?.bio || hasSocials\" class=\"flex h-full flex-col gap-6\">\n      <div v-if=\"user?.bio\" class=\"flex flex-col flex-nowrap items-start\">\n        <p class=\"leading-[1.4]\">{{ user.bio }}</p>\n      </div>\n\n      <div v-if=\"user && hasSocials\">\n        <ul class=\"flex flex-col gap-1\">\n          <template v-for=\"(value, key) in user.socials\" :key=\"key\">\n            <li v-if=\"value\" class=\"flex items-center gap-2 text-sm\">\n              <span>\n                <IconTwitter v-if=\"key === 'twitter' && value\" aria-hidden=\"true\" />\n                <IconGithub v-else-if=\"key === 'github' && value\" aria-hidden=\"true\" />\n                <IconLinkedin v-else-if=\"key === 'linkedin' && value\" aria-hidden=\"true\" />\n                <IconGlobe v-else-if=\"key === 'website' && value\" aria-hidden=\"true\" />\n              </span>\n\n              <a\n                :href=\"`https://${generateSocial(key, value)}`\"\n                target=\"_blank\"\n                rel=\"noopener noreferrer\"\n                class=\"align-center gap-2 text-sm hover:text-slate-blue-500 hover:underline\"\n              >\n                {{ generateSocial(key, value) }}\n              </a>\n            </li>\n          </template>\n        </ul>\n      </div>\n    </div>\n  </section>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, reactive, ref, watch } from 'vue';\nimport { useHead } from '@unhead/vue';\nimport { Tab, TabGroup, TabList, TabPanel, TabPanels } from '@headlessui/vue';\nimport { onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router';\nimport { useI18n } from 'vue-i18n';\nimport { refDebounced } from '@vueuse/core';\nimport type {\n  Collection,\n  GetProfileResponse,\n  GetUserStatsResponse,\n  UserBadge,\n} from '~/utilities/pyscript-api-models';\nimport { show404Page } from '~/utilities/show-404-page';\nimport pyscriptApi from '~/utilities/pyscript-api';\nimport UserProfilePicture from '~/components/UserProfilePicture.vue';\nimport AppHeader from '~/components/AppHeader.vue';\nimport SearchProjects from '~/components/SearchProjects.vue';\nimport SearchProjectsCollapsible from '~/components/SearchProjectsCollapsible.vue';\nimport UserProfile from '~/components/UserProfile.vue';\nimport UserBadges from '~/components/UserBadges.vue';\nimport UserStats from '~/components/UserStats.vue';\nimport Spinner from '~/components/Spinner.vue';\nimport ProjectCatalog from '~/components/project-catalog/ProjectCatalog.vue';\nimport ProjectSettingsModal from '~/components/project-settings/ProjectSettingsModal.vue';\nimport EditUserProfile from '~/components/account-settings/EditAccountProfile.vue';\nimport SortDropdown from '~/components/SortDropdown.vue';\nimport ListGridToggle from '~/components/ListGridToggle.vue';\nimport ItemsPerPageDropdown from '~/components/ItemsPerPageDropdown.vue';\nimport CollectionCatalog from '~/components/collections/CollectionCatalog.vue';\nimport EmptySearchResults from '~/components/EmptySearchResults.vue';\nimport EmptyNoneCreated from '~/components/EmptyNoneCreated.vue';\nimport NewProjectButton from '~/components/buttons/NewProjectButton.vue';\nimport NewCollectionButton from '~/components/buttons/NewCollectionButton.vue';\nimport { useProjectSettingsModalStore } from '~/stores/project-settings-modal-store';\nimport { useUserStore } from '~/stores/user-store';\nimport { useCollectionsStore } from '~/stores/collections-store';\nimport { usePaginatedProjectList } from '~/composables/paginated-project-list';\nimport { useSortPreference } from '~/composables/sort-preference';\n\n// Populated by the Vue Router\nconst props = defineProps<{\n  usernameOrUserId: string;\n}>();\n\nconst { t } = useI18n();\nconst router = useRouter();\nconst route = useRoute();\nconst userStore = useUserStore();\nconst collectionsStore = useCollectionsStore();\nconst isEditingProfile = ref(false);\nconst projectSettingsModalStore = useProjectSettingsModalStore();\nconst { savedSort } = useSortPreference();\n\nconst tabTitleList = [\n  { title: t('profile.projects_title'), query: 'projects' },\n  { title: t('profile.collections_title'), query: 'collections' },\n];\n\nconst state = reactive({\n  loading: true,\n  profile: null as GetProfileResponse | null,\n  badges: [] as UserBadge[],\n  stats: {} as GetUserStatsResponse,\n  /** Only used if not the profile owner. If they are the owner the collectionsStore is used. */\n  nonOwnerCollections: [] as Collection[],\n  isSearchOpen: false,\n  tabTitles: tabTitleList.map((tab) => tab.title),\n  activeTab: 0,\n});\n\nconst isOwner = computed(() => {\n  if (!userStore.isLoggedIn) return false;\n  return userStore.user?.id === state.profile?.id;\n});\n\nconst searchQuery = ref('');\nconst collectionsSearchQueryDebounced = refDebounced(searchQuery, 300);\nconst collectionsSearchResults = ref<Collection[]>([]);\n\n/**\n * Determines which list of collections is visible. If the user is searching,\n */\nconst collectionsList = computed(() => {\n  if (collectionsSearchQueryDebounced.value) {\n    return collectionsSearchResults.value;\n  }\n\n  return isOwner.value ? collectionsStore.collections : state.nonOwnerCollections;\n});\n\nif (router.currentRoute.value.query.q) {\n  searchQuery.value = router.currentRoute.value.query.q as string;\n  onSearch();\n}\n\n// Fetch collections search results when the debounced search value changes.\nwatch(collectionsSearchQueryDebounced, async (query) => {\n  if (query) {\n    if (isOwner.value) {\n      collectionsSearchResults.value = await collectionsStore.fetchSearchResults(\n        collectionsSearchQueryDebounced.value,\n      );\n    } else {\n      collectionsSearchResults.value = await pyscriptApi.listCollections({\n        q: collectionsSearchQueryDebounced.value,\n        user: state.profile!.id,\n        sortBy: savedSort.value.sortBy,\n        sortOrder: savedSort.value.sortOrder,\n      });\n    }\n  } else {\n    refreshCollections();\n  }\n});\n\n// Refetch collections when the sort changes.\nwatch(savedSort, () => {\n  refreshCollections();\n});\n\nconst searchPlaceholder = computed(() => {\n  return state.activeTab === 1\n    ? t('general.search_collections_placeholder')\n    : t('general.search_placeholder');\n});\n\nconst projectPagination = reactive(\n  usePaginatedProjectList({\n    scope: 'other',\n    user: computed(() => props.usernameOrUserId),\n    q: searchQuery,\n  }),\n);\n\nasync function pageTitle() {\n  if (!state.profile) return '';\n  const { username, name, id } = state.profile;\n  return username && name ? `@${username} (${name})` : username || name || id;\n}\n\nuseHead({ title: pageTitle() });\n\nconst avatar = computed(() => state.profile?.avatar || '');\n\ninitData(props.usernameOrUserId);\n\n/**\n * When the route changes, fetch the new data.\n *\n * For example, if I was viewing someone else's profile and then navigated to\n * my own profile, this is considered a route update.\n */\nonBeforeRouteUpdate(async (to, from) => {\n  /**\n   * The paths would be the same in the case where we add the q query param\n   * when filtering projects using the search field.\n   */\n  if (to.path !== from.path) {\n    await initData(to.params.usernameOrUserId as string);\n  }\n});\n\n/*\n * * * * * * * * * * * *\n *       METHODS       *\n * * * * * * * * * * * *\n */\n\nasync function initData(usernameOrUserId: string) {\n  state.loading = true;\n\n  // Update the active tab based on the query params.\n  if (route.query.tab) {\n    const tabIndex = tabTitleList.findIndex((tab) => tab.query === route.query.tab);\n\n    if (tabIndex !== -1) {\n      state.activeTab = tabIndex;\n    }\n  }\n\n  const [profileResult, badgesResult, statsResult] = await Promise.allSettled([\n    pyscriptApi.getProfile(usernameOrUserId),\n    pyscriptApi.listUserBadges(usernameOrUserId),\n    pyscriptApi.userStats(usernameOrUserId),\n  ]);\n\n  if (profileResult.status === 'fulfilled') {\n    state.profile = profileResult.value;\n\n    if (!isOwner.value) {\n      state.nonOwnerCollections = isOwner.value\n        ? collectionsStore.collections\n        : await pyscriptApi.listCollections({ user: state.profile!.id });\n    }\n  } else {\n    show404Page();\n    return;\n  }\n\n  if (statsResult.status === 'fulfilled') {\n    state.stats = statsResult.value;\n  }\n\n  if (badgesResult.status === 'fulfilled') {\n    state.badges = badgesResult.value;\n  }\n\n  state.loading = false;\n}\n\nfunction changeTab(index: number) {\n  router.replace({ query: { tab: index === 0 ? undefined : tabTitleList[index].query } });\n  state.activeTab = index;\n}\n\nasync function refreshProjectList() {\n  await projectPagination.refetch();\n}\n\nasync function refreshCollections() {\n  if (isOwner.value) {\n    return await collectionsStore.fetchCollections();\n  }\n\n  state.nonOwnerCollections = await pyscriptApi.listCollections({\n    user: state.profile!.id,\n    sortBy: savedSort.value.sortBy,\n    sortOrder: savedSort.value.sortOrder,\n  });\n}\n\nfunction onSearch() {\n  const query = searchQuery.value ? { q: searchQuery.value } : {};\n  router.replace({ query });\n}\n\nfunction onTagClick(tag: string) {\n  state.isSearchOpen = true;\n  searchQuery.value = searchQuery.value === tag ? '' : tag;\n  onSearch();\n}\n\nasync function refetchUserProfile(id: string) {\n  isEditingProfile.value = false;\n  const profile = await pyscriptApi.getProfile(id);\n\n  if (profile) {\n    state.profile = profile;\n  }\n}\n</script>\n\n<template>\n  <ProjectSettingsModal\n    v-if=\"projectSettingsModalStore.project\"\n    @project-deleted=\"\n      () => {\n        projectSettingsModalStore.close();\n        refreshProjectList();\n      }\n    \"\n    @project-updated=\"refreshProjectList\"\n  />\n\n  <div\n    class=\"container-spacing border-b border-new-gray-100 bg-white dark:border-new-gray-950/80 dark:bg-new-slate-700\"\n  >\n    <AppHeader class=\"flex items-center !gap-0\">\n      <template #leftColumn>\n        <div class=\"relative flex w-full items-center\">\n          <h1 class=\"text-lg font-semibold\">{{ $t('profile.heading') }}</h1>\n\n          <!-- Visible on mobile only. -->\n          <div class=\"ml-auto flex h-8 items-center md:hidden\">\n            <SearchProjectsCollapsible\n              v-model=\"searchQuery\"\n              v-model:is-open=\"state.isSearchOpen\"\n              :search-fn=\"onSearch\"\n              :placeholder=\"searchPlaceholder\"\n            />\n          </div>\n        </div>\n      </template>\n\n      <template #centerColumn>\n        <!-- Visible on desktop only. -->\n        <SearchProjects\n          v-model=\"searchQuery\"\n          class=\"hidden md:block md:w-80\"\n          :search-fn=\"onSearch\"\n          :placeholder=\"searchPlaceholder\"\n        />\n      </template>\n    </AppHeader>\n  </div>\n\n  <div v-if=\"state.loading\" class=\"flex h-full items-center justify-center text-gray-600\">\n    <Spinner class=\"h-8 w-8\" />\n  </div>\n\n  <main v-else class=\"container-spacing overflow-auto pt-3 md:flex md:flex-auto md:gap-x-12\">\n    <aside\n      class=\"custom-scrollbar w-full shrink-0 overflow-auto pb-4 md:sticky md:top-0 md:max-w-xs\"\n    >\n      <EditUserProfile\n        v-if=\"isOwner && state.profile && isEditingProfile\"\n        :profile=\"state.profile\"\n        :cancel-btn-handler=\"() => (isEditingProfile = false)\"\n        @profile-updated=\"refetchUserProfile(usernameOrUserId)\"\n      />\n\n      <div v-show=\"!isEditingProfile\" class=\"flex flex-col gap-5\">\n        <section v-if=\"state.profile\" class=\"flex flex-row flex-wrap items-center gap-3\">\n          <div class=\"relative mt-2 h-20 w-20 flex-shrink-0 rounded-full\">\n            <UserProfilePicture :avatar=\"avatar\" />\n          </div>\n\n          <div>\n            <h2 class=\"text-blue-700 text-xl font-semibold leading-none outline-none\">\n              {{ state.profile.name }}\n            </h2>\n\n            <p>{{ `@${state.profile.username || usernameOrUserId}` }}</p>\n          </div>\n        </section>\n\n        <button\n          v-if=\"isOwner\"\n          class=\"btn --space-sm --primary max-w-[105px] px-4 text-sm font-medium\"\n          @click=\"isEditingProfile = true\"\n        >\n          Edit Profile\n        </button>\n\n        <UserProfile :username-or-id=\"usernameOrUserId\" :user=\"state.profile\" :is-owner=\"isOwner\" />\n\n        <hr class=\"border-t border-gray-200 dark:border-new-gray-950\" />\n\n        <UserStats :stats=\"state.stats\" class=\"text-sm\" />\n\n        <UserBadges v-if=\"state.badges.length\" :user-badges=\"state.badges\" class=\"max-w-md\" />\n      </div>\n    </aside>\n\n    <section v-if=\"projectPagination.projectList !== null\" class=\"h-min w-full min-w-0 pb-5\">\n      <TabGroup :selected-index=\"state.activeTab\" @change=\"changeTab\">\n        <div class=\"mb-2 flex items-center\">\n          <TabList class=\"flex flex-nowrap gap-6\">\n            <Tab v-for=\"title of state.tabTitles\" :key=\"title\" v-slot=\"{ selected }\" as=\"template\">\n              <button\n                class=\"py-2 text-base font-semibold leading-none outline-none\"\n                :class=\"[\n                  selected\n                    ? 'border-b-2 border-b-new-green-500'\n                    : 'border-b-2 border-b-transparent text-gray-500 hover:text-gray-600 dark:text-gray-300',\n                ]\"\n              >\n                {{ title }}\n              </button>\n            </Tab>\n          </TabList>\n\n          <div class=\"ml-auto flex gap-1\">\n            <ListGridToggle class=\"ml-1\" />\n            <SortDropdown class=\"ml-1\" />\n            <ItemsPerPageDropdown class=\"ml-1.5\" />\n          </div>\n        </div>\n\n        <TabPanels>\n          <!-- Projects -->\n          <TabPanel :unmount=\"false\">\n            <!-- No Projects -->\n            <template v-if=\"projectPagination.hasNoProjects\">\n              <EmptyNoneCreated v-if=\"isOwner\" class=\"mt-10\" i18n-key=\"states.noun.projects\">\n                <template #button>\n                  <NewProjectButton />\n                </template>\n              </EmptyNoneCreated>\n\n              <EmptyNoneCreated\n                v-else\n                class=\"mt-10\"\n                i18n-key=\"states.noun.projects\"\n                :description=\"$t('profile.no_projects.non_owner_text')\"\n              />\n            </template>\n\n            <!-- No search results -->\n            <EmptySearchResults\n              v-else-if=\"searchQuery && projectPagination.projectList?.length === 0\"\n              i18n-key=\"states.noun.projects\"\n            />\n\n            <!-- Catalog -->\n            <ProjectCatalog\n              v-else-if=\"projectPagination.projectList && projectPagination.pagination\"\n              :project-list=\"projectPagination.projectList\"\n              :pagination=\"projectPagination.pagination\"\n              :set-page=\"projectPagination.setPage\"\n              :tag-click-handler=\"onTagClick\"\n              :refetch=\"refreshProjectList\"\n            />\n          </TabPanel>\n\n          <!-- Collections -->\n          <TabPanel>\n            <template v-if=\"collectionsList.length === 0\">\n              <!-- No search results -->\n              <EmptySearchResults\n                v-if=\"collectionsSearchQueryDebounced\"\n                i18n-key=\"states.noun.collections\"\n              />\n\n              <!-- No Collections -->\n              <EmptyNoneCreated\n                v-else\n                class=\"mt-10\"\n                i18n-key=\"states.noun.collections\"\n                :description=\"isOwner ? undefined : $t('profile.no_collections.non_owner_text')\"\n              >\n                <template v-if=\"isOwner\" #button>\n                  <NewCollectionButton />\n                </template>\n              </EmptyNoneCreated>\n            </template>\n\n            <CollectionCatalog v-else :collections=\"collectionsList\" />\n          </TabPanel>\n        </TabPanels>\n      </TabGroup>\n    </section>\n  </main>\n</template>\n\n<style scoped lang=\"postcss\">\n.slide-fade-enter-active,\n.slide-fade-leave-active {\n  transition: all 0.3s;\n}\n.slide-fade-enter,\n.slide-fade-leave-to {\n  opacity: 0;\n  transform: scale(0.5);\n}\n.slide-fade-leave-active {\n  position: absolute;\n}\n</style>\n"],"names":["props","__props","hasSocials","computed","value","generateSocial","name","username","t","useI18n","router","useRouter","route","useRoute","userStore","useUserStore","collectionsStore","useCollectionsStore","isEditingProfile","ref","projectSettingsModalStore","useProjectSettingsModalStore","savedSort","useSortPreference","tabTitleList","state","reactive","tab","isOwner","_a","_b","searchQuery","collectionsSearchQueryDebounced","refDebounced","collectionsSearchResults","collectionsList","onSearch","watch","query","pyscriptApi","refreshCollections","searchPlaceholder","projectPagination","usePaginatedProjectList","pageTitle","id","useHead","avatar","initData","onBeforeRouteUpdate","to","from","usernameOrUserId","tabIndex","profileResult","badgesResult","statsResult","show404Page","changeTab","index","refreshProjectList","onTagClick","tag","refetchUserProfile","profile"],"mappings":"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"}