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

Add asset support to :play #19

Merged
merged 16 commits into from
May 24, 2024
Merged

Add asset support to :play #19

merged 16 commits into from
May 24, 2024

Conversation

ObsidianSnoo
Copy link
Collaborator

💸 TL;DR

Adds support for assets, both virtual and from the local filesystem.

📜 Details

  • VirtualFS:
    • Leverages IndexedDB to persist assets
  • Local filesystem access:
  • Files are converted to blob: URLs and served offline

🧪 Testing Steps / Validation

✅ Checks

  • CI tests (if present) are passing
  • Adheres to code style for repo
  • Contributor License Agreement (CLA) completed if not a Reddit employee

@ObsidianSnoo ObsidianSnoo requested a review from niedzielski May 14, 2024 04:09
@ObsidianSnoo ObsidianSnoo force-pushed the assets branch 2 times, most recently from 7233022 to ecf59cb Compare May 14, 2024 16:00
Copy link
Member

@niedzielski niedzielski left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 👍 looking good! I left some feedback on the singleton and subclass approaches that I'm hoping we can find an alternative for. I like a lot of the other stuff I saw! thank you for the patch!

src/elements/play-icon/icons/archive-outline.svg Outdated Show resolved Hide resolved
src/elements/play-icon/play-icon.ts Show resolved Hide resolved
src/utils/file-access-api.test.ts Outdated Show resolved Hide resolved
src/storage/settings-save.ts Show resolved Hide resolved
src/utils/file-access-api.ts Outdated Show resolved Hide resolved
src/assets/asset-manager.ts Outdated Show resolved Hide resolved
src/assets/asset-manager.ts Outdated Show resolved Hide resolved
src/elements/play-assets/file-upload-dropper.ts Outdated Show resolved Hide resolved
src/elements/play-assets/file-upload-dropper.ts Outdated Show resolved Hide resolved
src/elements/play-assets/play-asset-manager-listener.ts Outdated Show resolved Hide resolved
src/storage/settings-save.ts Outdated Show resolved Hide resolved
src/elements/play-export-dialog.ts Outdated Show resolved Hide resolved
src/elements/play-pen/play-pen.ts Outdated Show resolved Hide resolved
src/elements/play-pen/play-pen.ts Outdated Show resolved Hide resolved
src/elements/play-pen/play-pen.ts Outdated Show resolved Hide resolved
src/elements/play-assets/play-assets.ts Outdated Show resolved Hide resolved
src/elements/play-assets/play-assets.ts Outdated Show resolved Hide resolved
src/elements/play-assets/play-assets.ts Outdated Show resolved Hide resolved
src/elements/play-assets/play-assets.ts Outdated Show resolved Hide resolved
src/elements/play-assets/play-assets-virtual-fs.ts Outdated Show resolved Hide resolved
src/elements/play-pen/play-pen.ts Outdated Show resolved Hide resolved
src/elements/play-assets/play-assets.ts Outdated Show resolved Hide resolved
src/elements/play-pen/play-pen.ts Outdated Show resolved Hide resolved
src/elements/play-assets-dialog.ts Outdated Show resolved Hide resolved
src/elements/play-pen/play-pen.ts Outdated Show resolved Hide resolved
src/assets/asset-manager.ts Outdated Show resolved Hide resolved
src/elements/play-pen/play-pen.ts Outdated Show resolved Hide resolved
src/elements/play-pen/play-pen.ts Outdated Show resolved Hide resolved
src/elements/play-assets-dialog.ts Outdated Show resolved Hide resolved
src/elements/play-assets/file-upload-dropper.ts Outdated Show resolved Hide resolved
src/bundler/linker.ts Outdated Show resolved Hide resolved
src/elements/play-pen/play-pen.ts Outdated Show resolved Hide resolved
src/elements/play-icon/icons/archive-outline.svg Outdated Show resolved Hide resolved
src/assets/asset-manager.ts Outdated Show resolved Hide resolved
src/elements/play-pen/play-pen.ts Outdated Show resolved Hide resolved
src/assets/asset-manager.ts Outdated Show resolved Hide resolved
src/storage/settings-save.ts Outdated Show resolved Hide resolved
}

@customElement('play-assets')
export class PlayAssets extends ReactiveElement {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand that this is a ReactiveElement to participate in @property and dispatch but I don't understand why. here's my thinking: 1) tying a model to UI is complex and we should avoid if practical 2) if I return a new object and assign it to my @state in play-pen, it magically percolates down to all components that take a .playAssets property. I see your patch already does 2 so I think that either the issue is that a child is mutating this object directly instead of dispatching event to request mutation or the reactivity is not needed.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't need to be a ReactiveElement in particular, but this app uses lit-html and is more convenient than using the underlying HTMLElement interfaces directly. This element still participates in emitting events and passing attributes/properties around, but doesn't render anything so it doesn't need to be a LitElement.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

an element can only have one parent so it breaks the data flow promise of children emitting events upward and parents pushing data down. an element can't subscribe to events without using PlayAssets as a side-channel. it's not as problematic as using globalThis but it does establish an event bus that the rest of the app doesn't use.

Copy link
Collaborator Author

@ObsidianSnoo ObsidianSnoo May 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you're still considering the original approach of elements nesting <play-assets> when they wanted access. This version is only used once in <play-pen> and contains all the filesystem logic. As of the latest commit the asset state is owned by <play-pen> and passed down into <play-assets> as well as down the tree into the various elements that read from it. All fields in AssetsState are readonly and changes made by <play-assets> are emitted as an assets-updated event with a new copy for <play-pen> to receive and then apply back down the tree.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand that most of the events now dispatch upwards from the dialogs and there's no mutation there 👍

I don't understand why assets is a reactive element or dispatches anything. I think tying up what is largely a model with UI was one of the things that made Studio hardest to reason about. testing UI is so much harder than testing models but what really sucks is reasoning about Lit lifecycles in a model. if it's an async issue, I'd return a new copy (or current copy if no change) of the model that play-pen awaits and assigns to drive a UI change.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd say I'm using it for encapsulation in this case. The code that actually interacts with the filesystem needs to go somewhere and making <play-assets> an element means that I can continue using the same pattern for registering for events as everything else instead of either creating a class that extends EventTarget or rolling my own simple callback/listener loop and managing registration in connectedCallback/disconnectedCallback. Essentially, it's a cosmetic choice 😅

protected override update(changedProperties: PropertyValues<this>) {
super.update(changedProperties)
if (changedProperties.has('acceptTypes')) {
this._accept = flattenAcceptTypes(this.acceptTypes)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this will drive another state change. should we use willUpdate()?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modifying state in update() does not trigger further updates/renders: https://lit.dev/docs/api/ReactiveElement/#ReactiveElement.update Though willUpdate() would be fine since it occurs before other callbacks, it's not necessary here since _accept isn't critical to rendering or other updates since it's for the file picker dialog.

src/elements/play-pen/play-pen.ts Outdated Show resolved Hide resolved
src/elements/play-assets/play-assets.ts Show resolved Hide resolved
}

@customElement('play-assets')
export class PlayAssets extends ReactiveElement {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand that most of the events now dispatch upwards from the dialogs and there's no mutation there 👍

I don't understand why assets is a reactive element or dispatches anything. I think tying up what is largely a model with UI was one of the things that made Studio hardest to reason about. testing UI is so much harder than testing models but what really sucks is reasoning about Lit lifecycles in a model. if it's an async issue, I'd return a new copy (or current copy if no change) of the model that play-pen awaits and assigns to drive a UI change.

@ObsidianSnoo ObsidianSnoo merged commit a26564f into reddit:main May 24, 2024
1 check passed
@ObsidianSnoo ObsidianSnoo deleted the assets branch May 24, 2024 18:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

2 participants