Radish alpha
r
rad:z4D5UCArafTzTQpDZNQRuqswh3ury
Radicle desktop app
Radicle
Git
Restyle and move issue/patch state buttons
Rūdolfs Ošiņš committed 1 year ago
commit 82560dbb0b6ef44e46038314fb304d89657aa0b6
parent 5845bf3
6 files changed +154 -187
deleted src/components/IssueStateBadge.svelte
@@ -1,22 +0,0 @@
-
<script lang="ts">
-
  import type { Issue } from "@bindings/cob/issue/Issue";
-

-
  import capitalize from "lodash/capitalize";
-
  import { issueStatusColor } from "@app/lib/utils";
-

-
  interface Props {
-
    state: Issue["state"];
-
  }
-

-
  const { state }: Props = $props();
-
</script>
-

-
<div
-
  class="global-counter txt-small"
-
  style:width="fit-content"
-
  style:color="var(--color-foreground-match-background)"
-
  style:background-color={issueStatusColor[state.status]}>
-
  {capitalize(state.status)}{state.status === "closed"
-
    ? ` as ${state.reason}`
-
    : ""}
-
</div>
modified src/components/IssueStateButton.svelte
@@ -1,88 +1,91 @@
<script lang="ts">
  import type { State } from "@bindings/cob/issue/State";

+
  import capitalize from "lodash/capitalize";
  import isEqual from "lodash/isEqual";

  import { closeFocused } from "@app/components/Popover.svelte";
+
  import { issueStatusBackgroundColor, issueStatusColor } from "@app/lib/utils";

  import Border from "@app/components/Border.svelte";
-
  import Button from "@app/components/Button.svelte";
  import DropdownList from "@app/components/DropdownList.svelte";
  import DropdownListItem from "@app/components/DropdownListItem.svelte";
  import Icon from "@app/components/Icon.svelte";
  import Popover from "@app/components/Popover.svelte";

-
  const {
-
    save,
-
    issueState,
-
  }: {
-
    save: (state: State) => Promise<void>;
-
    issueState: State;
-
  } = $props();
-

-
  const actions: { caption: string; state: State }[] = [
-
    { caption: "Reopen", state: { status: "open" } },
-
    {
-
      caption: "Close as solved",
-
      state: { status: "closed", reason: "solved" },
-
    },
-
    { caption: "Close as other", state: { status: "closed", reason: "other" } },
-
  ];
-

-
  let selectedAction = $state(
-
    issueState.status === "open" ? actions[1] : actions[0],
-
  );
+
  interface Props {
+
    selectedState: State;
+
    onSelect: (selectedStatus: State) => void;
+
  }

-
  // React to state changes that come from outside of this button.
-
  $effect(() => {
-
    selectedAction = issueState.status === "open" ? actions[1] : actions[0];
-
  });
+
  const { selectedState, onSelect }: Props = $props();
</script>

<style>
-
  .main {
+
  button {
+
    cursor: pointer;
+
    border: 0;
+
    background: none;
+
    margin: 0;
+
    padding: 0;
    display: flex;
-
    flex-direction: row;
+
    align-items: center;
    justify-content: center;
+
    font-size: var(--font-size-small);
+
  }
+
  .badge {
+
    gap: 6px;
+
    padding-right: 10px;
  }
</style>

-
<div class="main">
-
  <Button
-
    styleHeight="2.5rem"
-
    variant="secondary"
-
    flatRight
-
    onclick={() => void save($state.snapshot(selectedAction["state"]))}>
-
    {selectedAction["caption"]}
-
  </Button>
-

-
  <Popover
-
    popoverPadding="0"
-
    popoverPositionTop="3rem"
-
    popoverPositionRight="0">
-
    {#snippet toggle(onclick)}
-
      <Button styleHeight="2.5rem" flatLeft {onclick} variant="secondary">
+
<Popover popoverPadding="0" popoverPositionTop="2rem" popoverPositionLeft="0">
+
  {#snippet toggle(onclick)}
+
    <button {onclick}>
+
      <span
+
        class="global-counter badge"
+
        style:color={issueStatusColor[selectedState.status]}
+
        style:background-color={issueStatusBackgroundColor[
+
          selectedState.status
+
        ]}>
+
        <Icon
+
          name={selectedState.status === "open"
+
            ? "issue"
+
            : `issue-${selectedState.status}`} />
+
        {capitalize(selectedState.status)}
+
        {selectedState.status === "closed" ? `as ${selectedState.reason}` : ""}
        <Icon name="chevron-down" />
-
      </Button>
-
    {/snippet}
-
    {#snippet popover()}
-
      <Border variant="ghost">
-
        <DropdownList
-
          items={actions.filter(a => !isEqual(a.state, issueState))}>
-
          {#snippet item(action)}
-
            <DropdownListItem
-
              styleGap="0.5rem"
-
              styleMinHeight="2.5rem"
-
              selected={isEqual(selectedAction, action)}
-
              onclick={() => {
-
                selectedAction = action;
-
                closeFocused();
-
              }}>
-
              {action.caption}
-
            </DropdownListItem>
-
          {/snippet}
-
        </DropdownList>
-
      </Border>
-
    {/snippet}
-
  </Popover>
-
</div>
+
      </span>
+
    </button>
+
  {/snippet}
+
  {#snippet popover()}
+
    <Border variant="ghost">
+
      <DropdownList
+
        items={[
+
          { status: "open" },
+
          { status: "closed", reason: "solved" },
+
          { status: "closed", reason: "other" },
+
        ] as State[]}>
+
        {#snippet item(state)}
+
          <DropdownListItem
+
            selected={isEqual(selectedState, state)}
+
            onclick={() => {
+
              onSelect(state);
+
              closeFocused();
+
            }}>
+
            <span
+
              class="global-flex"
+
              style:color={issueStatusColor[state.status]}>
+
              <Icon
+
                name={state.status === "open"
+
                  ? "issue"
+
                  : `issue-${state.status}`} />
+
              {capitalize(state.status)}
+
              {state.status === "closed" ? `as ${state.reason}` : ""}
+
            </span>
+
          </DropdownListItem>
+
        {/snippet}
+
      </DropdownList>
+
    </Border>
+
  {/snippet}
+
</Popover>
deleted src/components/PatchStateBadge.svelte
@@ -1,20 +0,0 @@
-
<script lang="ts">
-
  import type { Patch } from "@bindings/cob/patch/Patch";
-

-
  import capitalize from "lodash/capitalize";
-
  import { patchStatusColor } from "@app/lib/utils";
-

-
  interface Props {
-
    state: Patch["state"];
-
  }
-

-
  const { state }: Props = $props();
-
</script>
-

-
<div
-
  class="global-counter txt-small"
-
  style:width="fit-content"
-
  style:color="var(--color-foreground-match-background)"
-
  style:background-color={patchStatusColor[state.status]}>
-
  {capitalize(state.status)}
-
</div>
modified src/components/PatchStateButton.svelte
@@ -1,89 +1,88 @@
<script lang="ts">
  import type { State } from "@bindings/cob/patch/State";

-
  import isEqual from "lodash/isEqual";
+
  import capitalize from "lodash/capitalize";

  import { closeFocused } from "@app/components/Popover.svelte";
+
  import { patchStatusBackgroundColor, patchStatusColor } from "@app/lib/utils";

  import Border from "@app/components/Border.svelte";
-
  import Button from "@app/components/Button.svelte";
  import DropdownList from "@app/components/DropdownList.svelte";
  import DropdownListItem from "@app/components/DropdownListItem.svelte";
  import Icon from "@app/components/Icon.svelte";
  import Popover from "@app/components/Popover.svelte";

-
  const {
-
    save,
-
    patchState,
-
  }: {
-
    save: (state: State) => Promise<void>;
-
    patchState: State;
-
  } = $props();
-

-
  const actions: { caption: string; state: State }[] = [
-
    {
-
      caption: "Reopen",
-
      state: { status: "open" },
-
    },
-
    { caption: "Convert to draft", state: { status: "draft" } },
-
    { caption: "Archive", state: { status: "archived" } },
-
  ];
-

-
  let selectedAction = $state(
-
    patchState.status === "open" ? actions[1] : actions[0],
-
  );
+
  interface Props {
+
    selectedState: State;
+
    onSelect: (newState: State) => void;
+
  }

-
  // React to state changes that come from outside of this button.
-
  $effect(() => {
-
    selectedAction = patchState.status === "open" ? actions[1] : actions[0];
-
  });
+
  const { selectedState, onSelect }: Props = $props();
</script>

<style>
-
  .main {
+
  button {
+
    cursor: pointer;
+
    border: 0;
+
    background: none;
+
    margin: 0;
+
    padding: 0;
    display: flex;
-
    flex-direction: row;
+
    align-items: center;
    justify-content: center;
+
    font-size: var(--font-size-small);
+
  }
+
  .badge {
+
    gap: 6px;
+
    padding-right: 10px;
  }
</style>

-
<div class="main">
-
  <Button
-
    styleHeight="2.5rem"
-
    variant="secondary"
-
    flatRight
-
    onclick={() =>
-
      void save($state.snapshot(selectedAction["state"]) as State)}>
-
    {selectedAction["caption"]}
-
  </Button>
-

-
  <Popover
-
    popoverPadding="0"
-
    popoverPositionTop="3rem"
-
    popoverPositionRight="0">
-
    {#snippet toggle(onclick)}
-
      <Button styleHeight="2.5rem" flatLeft {onclick} variant="secondary">
+
<Popover popoverPadding="0" popoverPositionTop="2rem" popoverPositionLeft="0">
+
  {#snippet toggle(onclick)}
+
    <button {onclick}>
+
      <span
+
        class="global-counter badge"
+
        style:color={patchStatusColor[selectedState.status]}
+
        style:background-color={patchStatusBackgroundColor[
+
          selectedState.status
+
        ]}>
+
        <Icon
+
          name={selectedState.status === "open"
+
            ? "patch"
+
            : `patch-${selectedState.status}`} />
+
        {capitalize(selectedState.status)}
        <Icon name="chevron-down" />
-
      </Button>
-
    {/snippet}
-
    {#snippet popover()}
-
      <Border variant="ghost">
-
        <DropdownList
-
          items={actions.filter(a => !isEqual(a.state, patchState))}>
-
          {#snippet item(action)}
-
            <DropdownListItem
-
              styleGap="0.5rem"
-
              styleMinHeight="2.5rem"
-
              selected={isEqual(selectedAction, action)}
-
              onclick={() => {
-
                selectedAction = action;
-
                closeFocused();
-
              }}>
-
              {action.caption}
-
            </DropdownListItem>
-
          {/snippet}
-
        </DropdownList>
-
      </Border>
-
    {/snippet}
-
  </Popover>
-
</div>
+
      </span>
+
    </button>
+
  {/snippet}
+
  {#snippet popover()}
+
    <Border variant="ghost">
+
      <DropdownList
+
        items={[
+
          { status: "open" },
+
          { status: "draft" },
+
          { status: "archived" },
+
        ] as State[]}>
+
        {#snippet item(state)}
+
          <DropdownListItem
+
            selected={selectedState.status === state.status}
+
            onclick={() => {
+
              onSelect(state);
+
              closeFocused();
+
            }}>
+
            <span
+
              class="global-flex"
+
              style:color={patchStatusColor[state.status]}>
+
              <Icon
+
                name={state.status === "open"
+
                  ? "patch"
+
                  : `patch-${state.status}`} />
+
              {capitalize(state.status)}
+
            </span>
+
          </DropdownListItem>
+
        {/snippet}
+
      </DropdownList>
+
    </Border>
+
  {/snippet}
+
</Popover>
modified src/views/repo/Issue.svelte
@@ -30,7 +30,6 @@
  import Icon from "@app/components/Icon.svelte";
  import InlineTitle from "@app/components/InlineTitle.svelte";
  import IssueSecondColumn from "@app/components/IssueSecondColumn.svelte";
-
  import IssueStateBadge from "@app/components/IssueStateBadge.svelte";
  import IssueStateButton from "@app/components/IssueStateButton.svelte";
  import IssueTimeline from "@app/components/IssueTimeline.svelte";
  import LabelInput from "@app/components/LabelInput.svelte";
@@ -410,7 +409,6 @@
                updatedTitle = issue.title;
                editingTitle = !editingTitle;
              }} />
-
            <IssueStateButton issueState={issue.state} save={saveState} />
          </div>
        </div>
      {:else}
@@ -433,7 +431,6 @@
          {#if roles.isDelegateOrAuthor( config.publicKey, repo.delegates.map(delegate => delegate.did), issue.body.author.did, )}
            <div class="title-icons">
              <Icon name="pen" onclick={() => (editingTitle = !editingTitle)} />
-
              <IssueStateButton issueState={issue.state} save={saveState} />
            </div>
          {/if}
        </div>
@@ -443,7 +440,14 @@
    <Border variant="ghost" styleGap="0">
      <div class="metadata-section" style:min-width="8rem">
        <div class="metadata-section-title">Status</div>
-
        <IssueStateBadge state={issue.state} />
+
        <IssueStateButton
+
          selectedState={issue.state}
+
          onSelect={newState => {
+
            void saveState(newState);
+
            if (status !== "all" && newState.status !== status) {
+
              void loadIssues("all");
+
            }
+
          }} />
      </div>

      <div class="metadata-divider"></div>
modified src/views/repo/Patch.svelte
@@ -38,7 +38,6 @@
  import Link from "@app/components/Link.svelte";
  import NakedButton from "@app/components/NakedButton.svelte";
  import OutlineButton from "@app/components/OutlineButton.svelte";
-
  import PatchStateBadge from "@app/components/PatchStateBadge.svelte";
  import PatchStateButton from "@app/components/PatchStateButton.svelte";
  import PatchTeaser from "@app/components/PatchTeaser.svelte";
  import PatchTimeline from "@app/components/PatchTimeline.svelte";
@@ -174,20 +173,17 @@
    }
  }

-
  async function saveState(state: Patch["state"]) {
+
  async function saveState(newState: Patch["state"]) {
    try {
      await invoke("edit_patch", {
        rid: repo.rid,
        cobId: patch.id,
        action: {
          type: "lifecycle",
-
          state,
+
          state: newState,
        },
        opts: { announce: $nodeRunning && $announce },
      });
-
      if (initialStatus !== undefined) {
-
        status = state["status"];
-
      }
    } catch (error) {
      console.error("Changing state failed", error);
    } finally {
@@ -343,6 +339,7 @@
    flex-direction: column;
    align-items: flex-start;
    height: 100%;
+
    z-index: 20;
  }
  .metadata-section-title {
    margin-bottom: 0.5rem;
@@ -594,7 +591,6 @@
                  updatedTitle = patch.title;
                  editingTitle = !editingTitle;
                }} />
-
              <PatchStateButton patchState={patch.state} save={saveState} />
            </div>
          </div>
        {:else}
@@ -618,7 +614,6 @@
                <Icon
                  name="pen"
                  onclick={() => (editingTitle = !editingTitle)} />
-
                <PatchStateButton patchState={patch.state} save={saveState} />
              </div>
            {/if}
          </div>
@@ -627,7 +622,15 @@
      <Border variant="ghost" styleGap="0">
        <div class="metadata-section" style:min-width="8rem">
          <div class="metadata-section-title">Status</div>
-
          <PatchStateBadge state={patch.state} />
+
          <PatchStateButton
+
            selectedState={patch.state}
+
            onSelect={newState => {
+
              void saveState(newState);
+
              if (status !== undefined && newState.status !== status) {
+
                status = undefined;
+
                void loadPatches(status);
+
              }
+
            }} />
        </div>

        <div class="metadata-divider"></div>