fix: More instances of Avatar and booker links fixed (#10083)

This commit is contained in:
Hariom Balhara authored and GitHub committed 2023-07-13 19:22:21 +05:30
1 parent 213caa722e
commit 2918a34e1a
19 files changed
+98 -53

No files matched your search

@@ -10,6 +10,7 @@ import ViewRecordingsDialog from "@calcom/features/ee/video/ViewRecordingsDialog
import classNames from "@calcom/lib/classNames";
import { formatTime } from "@calcom/lib/date-fns";
import getPaymentAppData from "@calcom/lib/getPaymentAppData";
import { useBookerUrl } from "@calcom/lib/hooks/useBookerUrl";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { getEveryFreqFor } from "@calcom/lib/recurringStrings";
import { BookingStatus } from "@calcom/prisma/enums";
@@ -49,6 +50,8 @@ type BookingItemProps = BookingItem & {
function BookingListItem(booking: BookingItemProps) {
// Get user so we can determine 12/24 hour format preferences
const query = useMeQuery();
const bookerUrl = useBookerUrl();
const user = query.data;
const {
t,
@@ -170,7 +173,7 @@ function BookingListItem(booking: BookingItemProps) {
id: "reschedule",
icon: Clock,
label: t("reschedule_booking"),
href: `/reschedule/${booking.uid}${
href: `${bookerUrl}/reschedule/${booking.uid}${
booking.seatsReferences.length ? `?seatReferenceUid=${getSeatReferenceUid()}` : ""
}`,
},
+2 -2
View File
@@ -152,9 +152,9 @@ function TeamPage({ team, isUnpublished, markdownStrippedBio, isValidOrgDomain }
<div className="overflow-hidden rounded-sm border dark:border-gray-900">
<div className="text-muted dark:text-inverted p-8 text-center">
<h2 className="font-cal dark:text-inverted text-emphasis600 mb-2 text-3xl">
{" " + t("no_teams_yet")}
{" " + t("org_no_teams_yet")}
</h2>
<p className="mx-auto max-w-md">{t("no_teams_yet_description")}</p>
<p className="mx-auto max-w-md">{t("org_no_teams_yet_description")}</p>
</div>
</div>
</div>
@@ -42,7 +42,6 @@ import { deleteScheduledEmailReminder } from "@calcom/features/ee/workflows/lib/
import { scheduleWorkflowReminders } from "@calcom/features/ee/workflows/lib/reminders/reminderScheduler";
import { deleteScheduledSMSReminder } from "@calcom/features/ee/workflows/lib/reminders/smsReminderManager";
import { deleteScheduledWhatsappReminder } from "@calcom/features/ee/workflows/lib/reminders/whatsappReminderManager";
import type { GetSubscriberOptions } from "@calcom/features/webhooks/lib/getWebhooks";
import getWebhooks from "@calcom/features/webhooks/lib/getWebhooks";
import { isPrismaObjOrUndefined, parseRecurringEvent } from "@calcom/lib";
@@ -57,6 +56,7 @@ import isOutOfBounds, { BookingDateInPastError } from "@calcom/lib/isOutOfBounds
import logger from "@calcom/lib/logger";
import { handlePayment } from "@calcom/lib/payment/handlePayment";
import { checkBookingLimits, checkDurationLimits, getLuckyUser } from "@calcom/lib/server";
import { getBookerUrl } from "@calcom/lib/server/getBookerUrl";
import { getTranslation } from "@calcom/lib/server/i18n";
import { slugify } from "@calcom/lib/slugify";
import { updateWebUser as syncServicesUpdateWebUser } from "@calcom/lib/sync/SyncServiceManager";
@@ -1012,6 +1012,7 @@ async function handler(
"calEventUserFieldsResponses" in reqBody ? reqBody.calEventUserFieldsResponses : null;
let evt: CalendarEvent = {
bookerUrl: await getBookerUrl(organizerUser),
type: eventType.title,
title: getEventName(eventNameObject), //this needs to be either forced in english, or fetched for each attendee and organizer separately
description: eventType.description,
@@ -2,6 +2,7 @@ import type { PropsWithChildren } from "react";
import { useState } from "react";
import classNames from "@calcom/lib/classNames";
import { useBookerUrl } from "@calcom/lib/hooks/useBookerUrl";
import type { RouterOutputs } from "@calcom/trpc";
import { Avatar, TextField } from "@calcom/ui";
@@ -19,7 +20,6 @@ export default function TeamInviteFromOrg({
orgMembers,
}: TeamInviteFromOrgProps) {
const [searchQuery, setSearchQuery] = useState("");
const filteredMembers = orgMembers?.filter((member) => {
if (!searchQuery) {
return true; // return all members if searchQuery is empty
@@ -65,12 +65,13 @@ function UserToInviteItem({
isSelected: boolean;
onChange: () => void;
}) {
const bookerUrl = useBookerUrl();
return (
<div
key={member.userId}
onClick={() => onChange()} // We handle this on click on the div also - for a11y we handle it with label and checkbox below
className={classNames(
"flex cursor-pointer items-center rounded-md py-1 px-2",
"flex cursor-pointer items-center rounded-md px-2 py-1",
isSelected ? "bg-emphasis" : "hover:bg-subtle "
)}>
<div className="flex items-center space-x-2 rtl:space-x-reverse">
@@ -78,7 +79,7 @@ function UserToInviteItem({
size="sm"
alt="Users avatar"
asChild
imageSrc={`/api/user/${member.user.username}`}
imageSrc={`${bookerUrl}/${member.user.username}/avatar.png`}
gravatarFallbackMd5="hash"
/>
<label
@@ -7,6 +7,8 @@ export function useOrgBrandingValues() {
return trpc.viewer.organizations.getBrand.useQuery(undefined, {
// Only fetch if we have a session to avoid flooding logs with errors
enabled: session.status === "authenticated",
initialData: null,
// This is important because usage of useOrgBrandingValues is spread across the app and there isn't going to be anything new on fetching on every new mount of a component
// We can just refetch the query only when org slug or logo changes which would be not frequent
staleTime: Infinity,
}).data;
}
@@ -37,5 +37,6 @@ export function subdomainSuffix() {
}
export function getOrgFullDomain(slug: string, options: { protocol: boolean } = { protocol: true }) {
// TODO: It is a replacement for WEBAPP_URL and that doesn't have / in the end. Remove / after ensuring that it works reliably everywhere
return `${options.protocol ? `${new URL(WEBAPP_URL).protocol}//` : ""}${slug}.${subdomainSuffix()}/`;
}
@@ -7,6 +7,7 @@ import InviteLinkSettingsModal from "@calcom/features/ee/teams/components/Invite
import MemberInvitationModal from "@calcom/features/ee/teams/components/MemberInvitationModal";
import { classNames } from "@calcom/lib";
import { APP_NAME, WEBAPP_URL } from "@calcom/lib/constants";
import { useBookerUrl } from "@calcom/lib/hooks/useBookerUrl";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import type { RouterOutputs } from "@calcom/trpc/react";
import { trpc } from "@calcom/trpc/react";
@@ -193,7 +194,7 @@ const PendingMemberItem = (props: { member: TeamMember; index: number; teamId: n
const { member, index, teamId } = props;
const { t } = useLocale();
const utils = trpc.useContext();
const bookerUrl = useBookerUrl();
const removeMemberMutation = trpc.viewer.teams.removeMember.useMutation({
async onSuccess() {
await utils.viewer.teams.get.invalidate();
@@ -214,7 +215,7 @@ const PendingMemberItem = (props: { member: TeamMember; index: number; teamId: n
<Avatar
gravatarFallbackMd5="teamMember"
size="mdLg"
imageSrc={WEBAPP_URL + "/" + member.username + "/avatar.png"}
imageSrc={bookerUrl + "/" + member.username + "/avatar.png"}
alt="owner-avatar"
/>
<div>
@@ -2,7 +2,7 @@ import {
FilterCheckboxField,
FilterCheckboxFieldsContainer,
} from "@calcom/features/filters/components/TeamsFilter";
import { WEBAPP_URL } from "@calcom/lib/constants";
import { useBookerUrl } from "@calcom/lib/hooks/useBookerUrl";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import type { RouterOutputs } from "@calcom/trpc";
import { trpc } from "@calcom/trpc";
@@ -21,6 +21,7 @@ const mapUserToOption = (user: User): Option => ({
export const UserListInTeam = () => {
const { t } = useLocale();
const bookerUrl = useBookerUrl();
const { filter, setConfigFilters } = useFilterContext();
const { selectedFilter, selectedTeamId, selectedMemberUserId, isAll } = filter;
const { data, isSuccess } = trpc.viewer.insights.userList.useQuery({
@@ -67,7 +68,7 @@ export const UserListInTeam = () => {
icon={
<Avatar
alt={`${member?.value} avatar`}
imageSrc={member.username ? `${WEBAPP_URL}/${member.username}/avatar.png` : undefined}
imageSrc={member.username ? `${bookerUrl}/${member.username}/avatar.png` : undefined}
size="xs"
/>
}
@@ -2,6 +2,7 @@ import { useRouter } from "next/router";
import { Suspense } from "react";
import { APP_NAME, WEBAPP_URL } from "@calcom/lib/constants";
import { useBookerUrl } from "@calcom/lib/hooks/useBookerUrl";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc/react";
import type { WebhooksByViewer } from "@calcom/trpc/server/routers/viewer/webhook/getByViewer.handler";
@@ -54,6 +55,7 @@ const WebhooksList = ({ webhooksByViewer }: { webhooksByViewer: WebhooksByViewer
const router = useRouter();
const { profiles, webhookGroups } = webhooksByViewer;
const bookerUrl = useBookerUrl();
const hasTeams = profiles && profiles.length > 1;
@@ -69,7 +71,7 @@ const WebhooksList = ({ webhooksByViewer }: { webhooksByViewer: WebhooksByViewer
<div className="items-centers flex ">
<Avatar
alt={group.profile.image || ""}
imageSrc={group.profile.image || `${WEBAPP_URL}/${group.profile.name}/avatar.png`}
imageSrc={group.profile.image || `${bookerUrl}/${group.profile.name}/avatar.png`}
size="md"
className="inline-flex justify-center"
/>
+3 -3
View File
@@ -157,12 +157,12 @@ const getSeatReferenceId = (calEvent: CalendarEvent): string => {
export const getManageLink = (calEvent: CalendarEvent, t: TFunction) => {
return `
${t("need_to_reschedule_or_cancel")}
${WEBAPP_URL + "/booking/" + getUid(calEvent) + "?changes=true"}
${(calEvent.bookerUrl ?? WEBAPP_URL) + "/booking/" + getUid(calEvent) + "?changes=true"}
`;
};
export const getCancelLink = (calEvent: CalendarEvent): string => {
const cancelLink = new URL(WEBAPP_URL + `/booking/${getUid(calEvent)}`);
const cancelLink = new URL((calEvent.bookerUrl ?? WEBAPP_URL) + `/booking/${getUid(calEvent)}`);
cancelLink.searchParams.append("cancel", "true");
cancelLink.searchParams.append("allRemainingBookings", String(!!calEvent.recurringEvent));
const seatReferenceUid = getSeatReferenceId(calEvent);
@@ -174,7 +174,7 @@ export const getRescheduleLink = (calEvent: CalendarEvent): string => {
const Uid = getUid(calEvent);
const seatUid = getSeatReferenceId(calEvent);
return `${WEBAPP_URL}/reschedule/${seatUid ? seatUid : Uid}`;
return `${calEvent.bookerUrl ?? WEBAPP_URL}/reschedule/${seatUid ? seatUid : Uid}`;
};
export const getRichDescription = (calEvent: CalendarEvent, t_?: TFunction /*, attendee?: Person*/) => {
+7
View File
@@ -0,0 +1,7 @@
import { useOrgBrandingValues } from "@calcom/features/ee/organizations/hooks";
import { WEBAPP_URL } from "@calcom/lib/constants";
export const useBookerUrl = () => {
const orgBranding = useOrgBrandingValues();
return orgBranding?.fullDomain ?? WEBAPP_URL;
};
+7
View File
@@ -0,0 +1,7 @@
import { WEBAPP_URL } from "../constants";
import { getBrand } from "./getBrand";
export const getBookerUrl = async (user: { organizationId: number | null }) => {
const orgBrand = await getBrand(user.organizationId);
return orgBrand?.fullDomain ?? WEBAPP_URL;
};
+32
View File
@@ -0,0 +1,32 @@
import { subdomainSuffix, getOrgFullDomain } from "@calcom/features/ee/organizations/lib/orgDomains";
import { prisma } from "@calcom/prisma";
import { teamMetadataSchema } from "@calcom/prisma/zod-utils";
export const getBrand = async (orgId: number | null) => {
if (!orgId) {
return null;
}
const org = await prisma.team.findFirst({
where: {
id: orgId,
},
select: {
logo: true,
name: true,
slug: true,
metadata: true,
},
});
const metadata = teamMetadataSchema.parse(org?.metadata);
const slug = (org?.slug || metadata?.requestedSlug) as string;
const fullDomain = getOrgFullDomain(slug);
const domainSuffix = subdomainSuffix();
return {
...org,
metadata,
slug,
fullDomain,
domainSuffix,
};
};
@@ -1,7 +1,7 @@
import { type PrismaClient } from "@prisma/client";
import { CAL_URL } from "@calcom/lib/constants";
import { isOrganization, withRoleCanCreateEntity } from "@calcom/lib/entityPermissionUtils";
import { getBookerUrl } from "@calcom/lib/server/getBookerUrl";
import type { TrpcSessionUser } from "@calcom/trpc/server/trpc";
import { TRPCError } from "@trpc/server";
@@ -46,12 +46,15 @@ export const teamsAndUserProfilesQuery = async ({ ctx }: TeamsAndUserProfileOpti
},
},
},
organizationId: true,
},
});
if (!user) {
throw new TRPCError({ code: "INTERNAL_SERVER_ERROR" });
}
const image = user?.username ? `${CAL_URL}/${user.username}/avatar.png` : undefined;
const bookerUrl = await getBookerUrl(user);
const image = user?.username ? `${bookerUrl}/${user.username}/avatar.png` : undefined;
const nonOrgTeams = user.teams.filter((membership) => !isOrganization({ team: membership.team }));
return [
@@ -66,7 +69,7 @@ export const teamsAndUserProfilesQuery = async ({ ctx }: TeamsAndUserProfileOpti
teamId: membership.team.id,
name: membership.team.name,
slug: membership.team.slug ? "team/" + membership.team.slug : null,
image: `${CAL_URL}/team/${membership.team.slug}/avatar.png`,
image: `${bookerUrl}${membership.team.slug ? "/team" : ""}/${membership.team.slug}/avatar.png`,
role: membership.role,
readOnly: !withRoleCanCreateEntity(membership.role),
})),
@@ -4,6 +4,7 @@ import { orderBy } from "lodash";
import { hasFilter } from "@calcom/features/filters/lib/hasFilter";
import { CAL_URL } from "@calcom/lib/constants";
import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML";
import { getBookerUrl } from "@calcom/lib/server/getBookerUrl";
import { baseEventTypeSelect } from "@calcom/prisma";
import { MembershipRole, SchedulingType } from "@calcom/prisma/enums";
import { teamMetadataSchema } from "@calcom/prisma/zod-utils";
@@ -89,6 +90,7 @@ export const getByViewerHandler = async ({ ctx, input }: GetByViewerOptions) =>
endTime: true,
bufferTime: true,
avatar: true,
organizationId: true,
teams: {
where: {
accepted: true,
@@ -276,6 +278,7 @@ export const getByViewerHandler = async ({ ctx, input }: GetByViewerOptions) =>
})
);
const bookerUrl = await getBookerUrl(user);
return {
// don't display event teams without event types,
eventTypeGroups: eventTypeGroups.filter((groupBy) => !!groupBy.eventTypes?.length),
@@ -285,7 +288,7 @@ export const getByViewerHandler = async ({ ctx, input }: GetByViewerOptions) =>
...group.metadata,
teamId: group.teamId,
membershipRole: group.membershipRole,
image: `${CAL_URL}${group.teamId ? "/team" : ""}/${group.profile.slug}/avatar.png`,
image: `${bookerUrl}${group.teamId ? "/team" : ""}/${group.profile.slug}/avatar.png`,
})),
};
};
@@ -1,6 +1,4 @@
import { subdomainSuffix, getOrgFullDomain } from "@calcom/features/ee/organizations/lib/orgDomains";
import { prisma } from "@calcom/prisma";
import { teamMetadataSchema } from "@calcom/prisma/zod-utils";
import { getBrand } from "@calcom/lib/server/getBrand";
import type { TrpcSessionUser } from "@calcom/trpc/server/trpc";
type VerifyCodeOptions = {
@@ -14,28 +12,5 @@ export const getBrandHandler = async ({ ctx }: VerifyCodeOptions) => {
if (!user.organizationId) return null;
const team = await prisma.team.findFirst({
where: {
id: user.organizationId,
},
select: {
logo: true,
name: true,
slug: true,
metadata: true,
},
});
const metadata = teamMetadataSchema.parse(team?.metadata);
const slug = (team?.slug || metadata?.requestedSlug) as string;
const fullDomain = getOrgFullDomain(slug);
const domainSuffix = subdomainSuffix();
return {
...team,
metadata,
slug,
fullDomain,
domainSuffix,
};
return await getBrand(user.organizationId);
};
@@ -1,4 +1,4 @@
import { CAL_URL } from "@calcom/lib/constants";
import { getBookerUrl } from "@calcom/lib/server/getBookerUrl";
import { prisma } from "@calcom/prisma";
import type { Webhook } from "@calcom/prisma/client";
import { MembershipRole } from "@calcom/prisma/enums";
@@ -49,6 +49,7 @@ export const getByViewerHandler = async ({ ctx }: GetByViewerOptions) => {
avatar: true,
name: true,
webhooks: true,
organizationId: true,
teams: {
where: {
accepted: true,
@@ -81,8 +82,9 @@ export const getByViewerHandler = async ({ ctx }: GetByViewerOptions) => {
const userWebhooks = user.webhooks;
let webhookGroups: WebhookGroup[] = [];
const bookerUrl = await getBookerUrl(user);
const image = user?.username ? `${CAL_URL}/${user.username}/avatar.png` : undefined;
const image = user?.username ? `${bookerUrl}/${user.username}/avatar.png` : undefined;
webhookGroups.push({
teamId: null,
profile: {
@@ -119,7 +121,7 @@ export const getByViewerHandler = async ({ ctx }: GetByViewerOptions) => {
? `/team`
: "" + membership.team.slug
: null,
image: `${CAL_URL}/team/${membership.team.slug}/avatar.png`,
image: `${bookerUrl}/team/${membership.team.slug}/avatar.png`,
},
metadata: {
readOnly:
@@ -142,7 +144,6 @@ export const getByViewerHandler = async ({ ctx }: GetByViewerOptions) => {
teamId: group.teamId,
...group.profile,
...group.metadata,
image: `${CAL_URL}/${group.profile.slug}/avatar.png`,
})),
};
};
+3
View File
@@ -146,6 +146,9 @@ export type CalEventResponses = Record<
// If modifying this interface, probably should update builders/calendarEvent files
export interface CalendarEvent {
// Instead of sending this per event.
// TODO: Links sent in email should be validated and automatically redirected to org domain or regular app. It would be a much cleaner way. Maybe use existing /api/link endpoint
bookerUrl?: string;
type: string;
title: string;
startTime: string;
@@ -1,6 +1,6 @@
import { useRouter } from "next/router";
import { WEBAPP_URL } from "@calcom/lib/constants";
import { useBookerUrl } from "@calcom/lib/hooks/useBookerUrl";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import {
Avatar,
@@ -38,6 +38,8 @@ export type CreateBtnProps = {
export function CreateButton(props: CreateBtnProps) {
const { t } = useLocale();
const router = useRouter();
const bookerUrl = useBookerUrl();
const {
createDialog,
options,
@@ -113,7 +115,7 @@ export function CreateButton(props: CreateBtnProps) {
StartIcon={(props) => (
<Avatar
alt={option.label || ""}
imageSrc={option.image || `${WEBAPP_URL}/${option.label}/avatar.png`} // if no image, use default avatar
imageSrc={option.image || `${bookerUrl}/${option.label}/avatar.png`} // if no image, use default avatar
size="sm"
{...props}
/>