Skip to content

RFC: Apps folder structure and hooks #1923

@PeerRich

Description

@PeerRich

RFC summary of the recent discussion around apps (previously integrations) and what the best architecture is to build third-party apps

Goals

  • super easy to launch your own app
  • unified abstractions around installation, removal, authentication (i.e. OAuth)
  • minimize merge conflicts
  • touch as little lines of core code as possible (with smart abstractions)

Status quo

  • the new app store branch is introducing a new folder apps where each new app can live in (i.e. apps/zoom) with metadata, images, descriptions, etc., however, it is not clear yet how the zoom app will be hooked into the core app.
  • right now, apps like "web3" are hardcoded into event-types and booking pages

Proposal

What if we find a good abstraction (like WordPress, i know–different set of technologies) that allows adding apps to the core views without touching them.

Example 1:

in Shell.tsx

const navigation = [

we have

const navigation = [
    {
      name: t("event_types_page_title"),
      href: "/event-types",
      icon: LinkIcon,
      current: router.asPath.startsWith("/event-types"),
    },
    {
      name: t("bookings"),
      href: "/bookings/upcoming",
      icon: CalendarIcon,
      current: router.asPath.startsWith("/bookings"),
    },
    {
      name: t("availability"),
      href: "/availability",
      icon: ClockIcon,
      current: router.asPath.startsWith("/availability"),
    },
    {
      name: t("integrations"),
      href: "/integrations",
      icon: PuzzleIcon,
      current: router.asPath.startsWith("/integrations"),
    },
    {
      name: t("settings"),
      href: "/settings/profile",
      icon: CogIcon,
      current: router.asPath.startsWith("/settings"),
    },
  ];

what if we extend this

<nav className="mt-2 flex-1 space-y-1 bg-white px-2 lg:mt-5">
                  {navigation.map((item) => (
                    <Link key={item.name} href={item.href}>
                      <a
                        className={classNames(
                          item.current
                            ? "bg-neutral-100 text-neutral-900"
                            : "text-neutral-500 hover:bg-gray-50 hover:text-neutral-900",
                          "group flex items-center rounded-sm px-2 py-2 text-sm font-medium"
                        )}>
                        <item.icon
                          className={classNames(
                            item.current
                              ? "text-neutral-500"
                              : "text-neutral-400 group-hover:text-neutral-500",
                            "h-5 w-5 flex-shrink-0 ltr:mr-3 rtl:ml-3"
                          )}
                          aria-hidden="true"
                        />
                        <span className="hidden lg:inline">{item.name}</span>
                      </a>
                    </Link>
                  ))}
</nav>

with appNavigation:

<nav className="mt-2 flex-1 space-y-1 bg-white px-2 lg:mt-5">
                  {navigation.map((item) => (
                    <Link key={item.name} href={item.href}>
                      <a
                        className={classNames(
                          item.current
                            ? "bg-neutral-100 text-neutral-900"
                            : "text-neutral-500 hover:bg-gray-50 hover:text-neutral-900",
                          "group flex items-center rounded-sm px-2 py-2 text-sm font-medium"
                        )}>
                        <item.icon
                          className={classNames(
                            item.current
                              ? "text-neutral-500"
                              : "text-neutral-400 group-hover:text-neutral-500",
                            "h-5 w-5 flex-shrink-0 ltr:mr-3 rtl:ml-3"
                          )}
                          aria-hidden="true"
                        />
                        <span className="hidden lg:inline">{item.name}</span>
                      </a>
                    </Link>
                  ))}

                 <hr/>

                  {appNavigation.map((item) => (
                    <Link key={item.name} href={item.href}>
                      <a
                        className={classNames(
                          item.current
                            ? "bg-neutral-100 text-neutral-900"
                            : "text-neutral-500 hover:bg-gray-50 hover:text-neutral-900",
                          "group flex items-center rounded-sm px-2 py-2 text-sm font-medium"
                        )}>
                        <item.icon
                          className={classNames(
                            item.current
                              ? "text-neutral-500"
                              : "text-neutral-400 group-hover:text-neutral-500",
                            "h-5 w-5 flex-shrink-0 ltr:mr-3 rtl:ml-3"
                          )}
                          aria-hidden="true"
                        />
                        <span className="hidden lg:inline">{item.name}</span>
                      </a>
                    </Link>
                  ))}
</nav>

and appNavigation is dynamically built based on either file-system or other references in apps/appRegistry.ts

Goal: don't touch navigation when adding a new app (note: not every apps needs a navigation item)

Example 2:

event-types/[type].tsx

{location.type === LocationType.GoogleMeet && (

should abstract all video apps and not hardcode them into the page

Goal: don't touch this file when adding a new video app (less merge conflicts, less redundant code)

Example 3:

DRAFT: Calendar Integrations

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions