Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

is it possible to have a super-sugar API? #120

Closed
Tracked by #142
jchris opened this issue Nov 4, 2022 · 16 comments
Closed
Tracked by #142

is it possible to have a super-sugar API? #120

jchris opened this issue Nov 4, 2022 · 16 comments
Assignees
Labels
question Further information is requested
Milestone

Comments

@jchris
Copy link
Contributor

jchris commented Nov 4, 2022

A couple of places in the app code that smell like they could be tucked in:

AuthProvider UploaderProvider UploadsListProvider IdentityLoader

<AuthProvider>
      <UploaderProvider>
        <UploadsListProvider>
          <IdentityLoader>

Also here:

Screen Shot 2022-11-04 at 1 57 46 PM

import { useUploader } from '@w3ui/react-uploader'
import { useUploadsList } from '@w3ui/react-uploads-list'
import { withIdentity } from './components/Authenticator'

I think the fix would be to have a monolithic upload component that includes login automatically, and has an easy way to enable lists with custom markup. Also we need to include the Authenticator so no one has to write it.

I like the withIdentity style of wrapping ContentPage, maybe we can make a monolithic withUploader?

@jchris
Copy link
Contributor Author

jchris commented Nov 5, 2022

I'm thinking from the perspective of someone dropping us into an app. Say I have an app where users are already logged into something, and I just want to add a drop target for uploads.

For instance, this seems to be about the minimal amount of code to add right now. I am thinking something 1/2 between here and "upload.js"

@alanshaw
Copy link
Member

alanshaw commented Nov 23, 2022

The code for the Authenticator component seen in the examples will not be needed when the w3access keyring site is built - it will essentially move there.

I separated the uploader and upload listing components so you didn't have to include all the code to build a UnixFS DAG when all you wanted to do was list items (we have this arrangement currently in the NFT.Storage site). They also both have reactive state that I wanted to keep separated. It's also concievable that you might want multiple listings of items, for example from different spaces.

The auth provider is likely to be used by the w3access keyring on it's own without the upload related providers so needs to be separate. It also needs to be separate to allow it to provide context to multiple other providers and components in the tree without introducing dependencies between them.

Should be trivial to do a monolith though:

import React, { useContext } from 'react'
import { AuthContext } from '@w3ui/react-keyring'
import { UploaderContext } from '@w3ui/react-uploader'
import { UploadsListContext } from '@w3ui/react-uploads-list'

export function MonolithicProvider ({ children }) {
  return (
    <AuthProvider>
      <UploaderProvider>
        <UploadsListProvider>
          {children}
        </UploadsListProvider>
      </UploaderProvider>
    </AuthProvider>
  )
}

export function useMonolithic () {
  const authCtx = useContext(AuthContext)
  const uploaderCtx = useContext(UploaderContext)
  const listCtx = useContext(UploadsListContext)
  return [{ ...authCtx[0], ...uploaderCtx[0], ...listCtx[0] }, { ...authCtx[1], ...uploaderCtx[1], ...listCtx[1] }]
}

@travis
Copy link
Member

travis commented Nov 28, 2022

Yep agree with all this! I think it should also be pretty trivial to expose standalone/convenience versions of these components that don't require a separate Provider - something like:

import { UploadsListProvider, UploadsList } from '@w3ui/react-uploads-list'

export function StandaloneUploadsList () {
  return (
    <AuthProvider>
      <UploadsListProvider>
        <UploadsList/>
      </UploadsListProvider>
    </AuthProvider>
  )
}

and/or

import { UploadsListProvider, UploadsList } from '@w3ui/react-uploads-list'

export function SpaceAgentUploadsList ({ space, agent }) {
  return (
    <UploadsListProvider>
      <UploadsList space={space} agent={agent} />
    </UploadsListProvider>
  )
}

I think the specific shape of these examples isn't quite right, but they demonstrate a) hiding the provider inside a standalone component for the case where a user just wants to drop a single upload list (or an uploader) into an app and b) parameterizing an upload list with a space and an agent manually to display a specific uploads list.

The second example suggests another question - what kind of user/developer experience do we want to provide for people who just want to display a particular uploads list on their website? I suspect that rather than parameterizing a component with space and agent we'd just want to have them pass the whole UCAN, something like:

import { UploadsListContext } from '@w3ui/react-uploads-list'

export function UCANUploadsList ({ ucan }) {
  return (
    <UploadsListProvider>
      <UploadsList ucan={ucan} />
    </UploadsListProvider>
  )
}

Which would let the end user/developer do something like:

import { UCANUploadsList } from '@w3ui/react-uploads-list'

function MyApp () {
  return (
    <div>
      <h1>check out my kewl filez</h1>
      <UCANUploadsList ucan="paste-a-ucan-here" />
    </div>
  )
}

We could display a UCAN appropriate to paste into the ucan parameter value somewhere in the w3console UI, and potentially even give people single-click copy-paste examples of React/Vue/Svelte code with the appropriate UCAN filled in - I think that's the simplest possible first-time user experience I can imagine for embedding an uploads list:

  1. log in to w3console
  2. navigate to the space you'd like to embed
  3. click a button to copy a React UCANUploadsList example pre-populated with the appropriate UCAN
  4. paste into your app (should just be a single import and a single component)

Does this all make sense? Am I missing anything?

@jchris
Copy link
Contributor Author

jchris commented Nov 30, 2022

One of the things I like about the withIdentity style is that our complete thing can be done in one file. Discussed with Travis that this not an either/or, but rather a few ways to use the composability. So I'm imagining two main super-sugar options:

  • something like <MonolithicUpload/> that uses withMonolithic inside itself and can be embedded with no options to get the default flow including login/registration, upload and list.
  • something like <Upload/><UploadList/> that can be dropped into custom positions in your component, and then your component calls withMonolithic on it's export. (Note in this case there's still no need for a useUploadList hook call yet.)
  • further desugaring could introduce the innards of the <Upload/><UploadList/> components. Eg: you are expected to call the hooks yourself at that level of desugar.

@travis
Copy link
Member

travis commented Dec 1, 2022

ok I threw together an initial prototype of an Uploader component based on the pattern used by headlessui:

https://github.com/web3-storage/w3ui/blob/5136b7be9e9410945be65bfd2225fe4b8cf8602a/packages/react-ui/src/Uploader.jsx#L102

there are definitely some design issues to be worked through here - making a component this complex that gives the developer maximum control over presentation is tricky - but I think it's promising. will add a bit more tomorrow in line with your comments above @jchris - I should be able to throw together a draft version of the functions and components you're talking about pretty quickly now

@jchris
Copy link
Contributor Author

jchris commented Dec 1, 2022

The question I didn’t address in my comment is whether the login flow is embedded in those upload and upload list components, or if it’s required for you to somehow bridge login and those components state yourself. I think it is worth having a distinct <Login/> component that uses withMonolithic to bridge everything if possible.

@travis
Copy link
Member

travis commented Dec 1, 2022

Yep agreed. Here's a rough sketch of what I'm thinking:

  1. We will provide components at 4 levels of abstraction:
    1. "No UI" - this is what we have today - react-keyring just gives us a Provider and a useKeyring hook that lets developers create custom components that use the w3up identity and data storage APIs
    2. "Headless UI" - these are the kinds of components that HeadlessUI provides (eg, https://headlessui.com/react/combobox) - they encapsulate UI functionality without making concrete markup or styling choices. This lets developers use their own markup without reimplementing UX logic. This is what I've implemented in a very basic way in https://github.com/web3-storage/w3ui/blob/5136b7be9e9410945be65bfd2225fe4b8cf8602a/packages/react-ui/src/Uploader.jsx
    3. "Customizable UI" - batteries mostly included components - we'll make some markup/styling choices for end users, but ideally leave them with some ability to customize (perhaps via careful choice and documentation of class names? or maybe "styled components"? Plate uses styled components to "provide components with overridable styles and markup" (https://plate.udecode.io/docs/styling#plate-ui) and while that sounds nice in theory, in practice I've found them kind of clunky to use and have never entirely trusted the extra dependency.) Definitely interested in input from other folks how how we could create components with sensible but overridable markup and styling. In this option we'll still provide the various components in an "a-la-carte" style and let developers plug them together.
    4. "Drop-in UI" - grab-n-go components that you can drop into a page - these will automatically handle authentication, and potentially even mash together an uploader and an uploads list in a way that makes sense. As this library grows we'll have combinatorially many options for how this will actually work, and will need to make decisions about which components to provide based on the needs of our users. Unlike other options we'll definitely integrate the providers into the components so users don't need to worry about adding them to the stack.
  2. Developers will have three options for ensuring the proper "Providers" are in place for their components:
    1. Wrapping their components in the Providers they need. This could happen in the file in which the component is used, or further up the stack (ie, in _app.jsx in a Next.js app). We'll let users use just the providers they need OR use a convenient W3UIProvider that bundles all the other providers together.
    2. Using a Higher Order Component (HOC) like withUploaderProvider or withW3UIProvider to wrap components that use our UI components. It's worth noting here that HOCs have fallen out of favor in the React community at least (see, eg, https://www.reddit.com/r/reactjs/comments/rzmowa/hoc_vs_custom_hooks/ and note that the new React docs have dropped any reference to HOCs https://beta.reactjs.org/). I'm not a fan of HOCs and very rarely use them in my own code, but it's easy enough to provide them for developers who do like them and cases where they are still simpler/easier to use than a hook or markup-style wrapping provider.
    3. Using the "Drop-in UI" components discussed above. These will include providers under the hood so that developers using them don't need to think about this.

This is certainly a lot of options and will require a bit of complexity and careful design to do well, but if we pull it off it provides a clean path for developers at any level of experience to start using our components and elegantly evolve their usage over time. A developer who starts with "Drop-in" components will be able to look at the source for them and easily transition to Customizable or Headless components as their needs evolve.

This is becoming a wall of text so I'll stop now - perhaps a good idea to start a design doc in this repo and maybe some issues to start tracking each of these separately? Particularly interested in your thoughts @jchris and @alanshaw as I think you two have thought about this the most so far!

@travis
Copy link
Member

travis commented Dec 1, 2022

ideally leave them with some ability to customize (perhaps via careful choice and documentation of class names? or maybe "styled components"?

oo - we should definitely throw "custom css properties" in the ring for this one - see this comment and demo from @cmunns - I think this is probably the right way to implement the "customizable ui" components from 1.iii above:

#134 (comment)

@cmunns
Copy link

cmunns commented Dec 2, 2022

@travis @jchris Here are two examples of theme/look using only css custom properties.

Screenshot 2022-12-02 at 12 33 14 PM

right now that list is namespaced to the uploader here, but I imagine some would be global and some would be component specific

:root {
  --w3ui-uploader-container-padding: 0;
  --w3ui-uploader-container-background: transparent;
  --w3ui-uploader-padding: 2rem;
  --w3ui-uploader-background: rgba(255, 255, 255, 0.05);
  --w3ui-uploader-background-hover: rgba(255, 255, 255, 0.08);
  --w3ui-uploader-height: 200px;
  --w3ui-uploader-border-color: rgba(255, 255, 255, 0.25);
  --w3ui-uploader-border-radius: 8px;
  --w3ui-uploader-primary: salmon;
  --w3ui-uploader-primary-hover: rgb(250, 136, 124);
  --w3ui-overlay: rgba(29, 32, 39, 0.8);
  --w3ui-gradient-stop-1: orange; 
  --w3ui-gradient-stop-2: salmon;
  --w3ui-button-color: #000;
}

@jchris
Copy link
Contributor Author

jchris commented Dec 5, 2022

The all-in-one component, for super simple login and upload, is probably ready for us to think about how it looks on the w3ui website. I think it is not just a 4th component, but rather an umbrella. So maybe it goes over the top (excuse my sketch skills).

image

We should open a new ticket to track the website homepage style update. Wanted to get everyone thinking about it in the context first.

@travis
Copy link
Member

travis commented Dec 5, 2022

I think it is not just a 4th component, but rather an umbrella

Yep agreed - I've been imagining we'd have a series of components like this, probably each in their own package, that use combinations of the lower level components to do common things - this serves the dual purpose of providing concrete examples of how to use the lower level components AND giving developers a super easy "drop a single component into your page" way to get started with w3ui.

In this context, I wonder how we'd present this - maybe each of the 4 "levels" gets its own section on the website - one just highlighting the hooks/no-UI components for each framework, one for the Headless components, one for the "customizable" components and another that shows off all of the different example/drop-in components we introduce over time... would this be too much content for the homepage of the w3ui site?

@jchris
Copy link
Contributor Author

jchris commented Dec 5, 2022

At some point the detail fades into supporting evidence, not headline news. The headline is probably:

  • One line of code allows anyone to add uploads to any HTML app (we might not be there yet, maybe for now "any React app")
  • Designed and built to support Real Coders™ doing Real Work

These are in tension because headless / framework specific are part of bullet two, but the first bullet is for people who don't even want to think about that stuff.

As far as how we roll these out, are there any circumstances where someone uses our uploader but not our keyring? I'm kinda guessing not. So then we want the easiest version of each of our components to include keyring.

@jchris
Copy link
Contributor Author

jchris commented Dec 5, 2022

We should start to think about how to carve out the heavy lift part of the all-in-one component, because maybe we should have that for after w3console ships. Heavy lift is stuff that is beyond writing the code for the all-in-one (we should probably keep doing that.) Stuff like:

  • w3ui website design for all-in-one component
  • deciding if we also have vue etc ones, or if we have to make a vanilla js one?
  • hardening the build for an uploads.js that can be shipped without npm like https://stripe.com/docs/js/including

This is distinct from the w3console effort, so we should make sure to cross-pollinate as w3console is a huge w3ui user, and should reflect the same w3ui brand look-and-feel as the default theme of the w3ui all-in-one components.

@travis
Copy link
Member

travis commented Dec 5, 2022

As far as how we roll these out, are there any circumstances where someone uses our uploader but not our keyring? I'm kinda guessing not. So then we want the easiest version of each of our components to include keyring.

At the "headless" level there will definitely be some folks who won't want to use markup or styling we provide, however customizable it is - at this level of abstraction I still think it's a good idea to just provide the building blocks and let people put them together however they want.

At the "customizable" and "drop-in" component levels I think you're right - we should probably just fold the keyring and uploader together so people don't need to worry about how to fit them together. I could see people using the "uploads list" without a keyring - @mikeal and @Gozala riffed a bit at the end of my demo on Friday about this and I think the idea was that we could pass the component the CID of a UCAN invocation that would return the list of uploads. Would definitely need to dig a bit deeper (and, tbh, understand UCAN invocations a bit better) to fully execute on this but it sounds like it's a use case worth considering.

@jchris
Copy link
Contributor Author

jchris commented Dec 7, 2022

I drew a flow for the all-in-one drop in component.

Screen Shot 2022-12-06 at 4 52 42 PM

@alanshaw
Copy link
Member

🙏 Can we close this in favor of the other issues for creating HeadlessUI and CustomUI?

@jchris jchris closed this as completed Jan 25, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

6 participants