NearSocial / viewer

A browser for social data and web4
The Unlicense
57 stars 55 forks source link

Feature: Component draft version #137

Closed milenakrawczyk closed 1 year ago

milenakrawczyk commented 1 year ago

Following up on the discussion: https://github.com/NearSocial/viewer/discussions/133

Overview

Component draft version is a feature that enables a user to save a draft version of a component to blockchain without publishing it. A draft version may be reviewed by peers.

UI Implementation

Instead of the Save Widget button, there are two buttons:

Flow

  1. User forks a widget.
  2. User works on it.
  3. User saves a draft: the code is saved to a default draft branch, the state is set as DRAFT, the empty bracket code is missing.
  4. User decides to publish: state is set to PUBLISHED, the code is published under empty brackets (as is in the current implementation)

The state=DRAFT indicates that there is no published version yet. Saving a draft of a published widget does not change the state in any way. The state field may be used for filtering the published widgets from unpublished.

Schema for Saving a Draft

The initial implementation may encompass saving a draft version of just one component and may look like this:

{
  "post":{
    "commit": {
      "text": "Initial commit.",
      "type": "md",
      "keys": ["user.near/widget/Component1/branch/draft", "user.near/widget/Component2/branch/draft"]
    }
  },
  "widget": {
    "Component1": {
      "state": "DRAFT",
      "branch": {
        "draft": "return \"Hello from draft branch Component1\";", 
      }
    },
  }
}

Schema for Publishing a Widget

The code publishing involves setting the state to PUBLISHED.

{
  "widget": {
    "Component1": {
      "state": "PUBLISHED",
      "": "return \"Hello from published version\";",
    },
  }
}

Full Schema for Saving Draft

Eventually saving a draft may involve choosing from all the files in the editor (saved in the local storage) that should be included in the draft commit. The schema may look as follows:

{
  "post":{
    "commit": {
      "text": "Initial commit.",
      "type": "md",
      "keys": ["user.near/widget/Component1/branch/draft", "user.near/widget/Component2/branch/draft"]
    }
  },
  "widget": {
    "Component1": {
      "state": "DRAFT",
      "branch": {
        "draft": "return \"Hello from draft branch Component1\";", 
      }
    },
    "Component2": {
      "state": "DRAFT",
      "branch": {
        "draft": "return \"Hello from draft branch Component2\";",
      }
    }
  }
}

Possible Extensions

This initial implementation may extend to encompass a system where a user may save a code to different branches, load and view those branches.

charleslavon commented 1 year ago

Does state serve any purpose other than making explicit the existing expressions of the presence of an empty key string which indicates PUBLISHED: "": {...} and the absence of an empty key string indicating DRAFT?

marcinbodnar commented 1 year ago

Agree, we don't need state. We also don't need the branch. It's complicating schema unnecessarily.

The solution I proposed here is simpler: https://github.com/NearSocial/viewer/discussions/133#discussioncomment-4923256

With drafts, we would use only one branch and call it draft, the schema will look like this:

{
  "widget": {
    "Genie": {
      "": {
        "": "..."
      },
      "draft": {
        "": "..."
      },
    },
    "GenieQuestionList": {
      "": {
        "": "..."
      },
      "draft": {
        "": "..."
      }
    },
  },
}

It's simpler and can be extended easily if we decide to use branches or something similar.

"" is main/published version - draft is keeping the draft version. If there is no draft, then we know there is no draft version. If "" is empty, then there is no published version yet.

cc @mpeterdev

milenakrawczyk commented 1 year ago

@charleslavon No, and as such the state is redundant. I was thinking rather of an ease of filtering and a bit more explicit labelling of what this object is. It could also possibly be extended in the future to encompass other state type (deprecated?). However, after giving it some thought I think that it should be omitted at this point.

@marcinbodnar In my opinion the branch is necessary. The most important argument here is the fact that if there is no branch and branches are stored directly in the component root, there might be a naming conflict between the branch name and other objects stored in the widget. Another argument is grouping the branches together under one object branch for the ease of listing them (then the explicit list of all branches is not needed). Finally, since other objects are/might be stored under a component root, not having branch might result in a lot of chaos.

marcinbodnar commented 1 year ago

@milenakrawczyk

The most important argument here is the fact that if there is no branch and branches are stored directly in the component root, there might be a naming conflict between the branch name and other objects stored in the widget.

No, every key of the object "widget" is a separate component. Every, key of the component object (for example "Genie") is a branch (or published if ""). And then every branch has a full component object.

Another argument is grouping the branches together under one object branch for the ease of listing them (then the explicit list of all branches is not needed).

In my proposition, if you would like to list all branches you just list all widget keys - nothing more is necessary - from my example, you just list the keys for "Genie".

Finally, since other objects are/might be stored under a component root, not having branch might result in a lot of chaos.

No, the whole widget object is stored separately for every branch ("draft") and published version (""):

"Genie": {
      "": {
        "": "...",
        metadata: {}
      },
      "draft": {
        "": "...",
        metadata: {}        
      },
    },
milenakrawczyk commented 1 year ago

No, every key of the object "widget" is a separate component. Every, key of the component object (for example "Genie") is a branch (or published if ""). And then every branch has a full component object.

By widget here I didn't mean the widget key but a component (aka widget) e.g. Genie. What I meant is that we may store other keys in the component, not only branches and this could lead to a naming conflict. Unless you assume that metadata will contain ALL possible/future keys related to the component.

In my proposition, if you would like to list all branches you just list all widget keys - nothing more is necessary - from my example, you just list the keys for "Genie".

As far as I understand there would be also a metadata key which is not a branch. Is this correct?

No, the whole widget object is stored separately for every branch ("draft") and published version (""):

What I meant here is that we may store some other keys under the component e.g. Genie.

marcinbodnar commented 1 year ago

No :) In my proposition, the whole component data is inside "" or "draft" - so for example "draft" has "" which is component code, and "metadata" - you can work on this component whatever you like, you can add new values etc.

Direct values of "Genie" are branches, and every branch is storing the full widget like it is now - nothing changes here regarding the current implementation.

With your proposition, we will mix the component code, metadata, and branches in one object - which is not a good idea.

mpeterdev commented 1 year ago

closed in alpha.near.org viewer by above mentioned PR. If this is desired on Social we can have a more generic effort to port editor improvements