Skip to content

Commit

Permalink
Add placeholder directives for creating/deleting pins and launching p…
Browse files Browse the repository at this point in the history
…ins/groups
  • Loading branch information
SKaplanOfficial committed May 7, 2024
1 parent 0cbe717 commit 9d8f8b8
Show file tree
Hide file tree
Showing 7 changed files with 199 additions and 5 deletions.
10 changes: 9 additions & 1 deletion DEVLOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
# Pins DevLog - A more detailed changelog

## 1.7.0 Release, TBA
## 1.8.0 Release, TBA

### 2024-04-23

- Added `{{launchPin:pinName}}` and `{{launchGroup:groupName}}` placeholder directives.
- Added action to open all pins in a group in the "View Groups" command.
- Fixed bug where placeholders using values from LocalData would not update on time.

## 1.7.0 Release, 2024-01-14

### 2024-01-12

Expand Down
56 changes: 56 additions & 0 deletions src/lib/placeholders/custom-placeholders/createPin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { Placeholder, PlaceholderCategory, PlaceholderType } from "placeholders-toolkit";
import { createNewPin } from "../../Pins";
import { Group, createNewGroup } from "../../Groups";
import { StorageKey } from "../../constants";
import { getStorage } from "../../storage";

/**
* Placeholder directive for creating a new pin.
*/
const CreatePinDirective: Placeholder = {
name: "createPin",
regex:
/{{createPin:(([^{]|{(?!{)|{{[\s\S]*?}})*?)(:(([^{]|{(?!{)|{{[\s\S]*?}})*?))?(:(([^{]|{(?!{)|{{[\s\S]*?}})*?))?}}/,
rules: [],
apply: async (str) => {
const matches = str.match(
/{{createPin:(([^{]|{(?!{)|{{[\s\S]*?}})*?)(:(([^{]|{(?!{)|{{[\s\S]*?}})*?))?(:(([^{]|{(?!{)|{{[\s\S]*?}})*?))?}}/,
);
const name = matches?.[1] || "";
const target = matches?.[4] || name;
const group = matches?.[7] || "None";
if (!name) return { result: "" };

const allGroups: Group[] = await getStorage(StorageKey.LOCAL_GROUPS);
if (group != "None" && !allGroups.some((g) => g.name == group)) {
await createNewGroup(group, "None");
}

await createNewPin(
name,
target,
"None",
group,
"None",
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
);
return { result: "" };
},
constant: false,
fn: async (name, target, group) =>
(await CreatePinDirective.apply(`{{createPin:${name}:${target}:${group}}}`)).result,
example: "{{createPin:myPinName:myPinTarget:myPinGroup}}",
description: "Creates a new pin.",
hintRepresentation: "{{createPin:...:...:...}}",
fullRepresentation: "Create Pin",
type: PlaceholderType.InteractiveDirective,
categories: [PlaceholderCategory.Meta],
};

export default CreatePinDirective;
35 changes: 35 additions & 0 deletions src/lib/placeholders/custom-placeholders/deletePin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Placeholder, PlaceholderCategory, PlaceholderType } from "placeholders-toolkit";
import { Pin, deletePin } from "../../Pins";
import { StorageKey } from "../../constants";
import { getStorage } from "../../storage";

/**
* Placeholder directive for deleting a pin.
*/
const DeletePinDirective: Placeholder = {
name: "deletePin",
regex: /{{deletePin:(([^{]|{(?!{)|{{[\s\S]*?}})*?)}}/,
rules: [],
apply: async (str) => {
const matches = str.match(/{{deletePin:(([^{]|{(?!{)|{{[\s\S]*?}})*?)}}/);
const pinRef = matches?.[1] || "";
if (!pinRef) return { result: "" };

const allPins: Pin[] = await getStorage(StorageKey.LOCAL_PINS);
const pin = allPins.find((p) => p.name == pinRef || p.id.toString() == pinRef);
if (!pin) return { result: "" };

await deletePin(pin, () => {}, true, true);
return { result: "" };
},
constant: false,
fn: async (pinRef) => (await DeletePinDirective.apply(`{{deletePin:${pinRef}}}`)).result,
example: "{{deletePin:pinName}}",
description: "Deletes a pin.",
hintRepresentation: "{{deletePin:...}}",
fullRepresentation: "Delete Pin",
type: PlaceholderType.InteractiveDirective,
categories: [PlaceholderCategory.Meta],
};

export default DeletePinDirective;
39 changes: 39 additions & 0 deletions src/lib/placeholders/custom-placeholders/launchGroup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Placeholder, PlaceholderCategory, PlaceholderType } from "placeholders-toolkit";
import { Pin, openPin } from "../../Pins";
import { getStorage } from "../../storage";
import { StorageKey } from "../../constants";
import { getPreferenceValues } from "@raycast/api";
import { ExtensionPreferences } from "../../preferences";
import { Group } from "../../Groups";

/**
* Placeholder directive for opening/launching all pins in a target group.
*/
const LaunchGroupDirective: Placeholder = {
name: "launchGroup",
regex: /{{(launchGroup|openGroup):(([^{]|{(?!{)|{{[\s\S]*?}})*?)}}/,
rules: [],
apply: async (str) => {
const matches = str.match(/{{(launchGroup|openGroup):(([^{]|{(?!{)|{{[\s\S]*?}})*?)}}/);
const targetRep = matches?.[2] || "";
if (!targetRep) return { result: "" };
const pins: Pin[] = (await getStorage(StorageKey.LOCAL_PINS)) || [];
const groups: Group[] = (await getStorage(StorageKey.LOCAL_GROUPS)) || [];
const target = groups.find((g) => g.name == targetRep || g.id.toString() == targetRep);
if (!target) return { result: "" };
const groupPins = pins.filter((p) => p.group == target.name);
const preferences = getPreferenceValues<ExtensionPreferences>();
await Promise.all(groupPins.map((p) => openPin(p, preferences)));
return { result: "" };
},
constant: false,
fn: async (target: string) => (await LaunchGroupDirective.apply(`{{launchPin:${target}}}`)).result,
example: "{{launchPin:myPinName}}",
description: "Opens the target pin.",
hintRepresentation: "{{launchPin:...}}",
fullRepresentation: "Launch Group",
type: PlaceholderType.StaticDirective,
categories: [PlaceholderCategory.Meta],
};

export default LaunchGroupDirective;
36 changes: 36 additions & 0 deletions src/lib/placeholders/custom-placeholders/launchPin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Placeholder, PlaceholderCategory, PlaceholderType } from "placeholders-toolkit";
import { Pin, openPin } from "../../Pins";
import { getStorage } from "../../storage";
import { StorageKey } from "../../constants";
import { getPreferenceValues } from "@raycast/api";
import { ExtensionPreferences } from "../../preferences";

/**
* Placeholder directive for opening/launching a target pin.
*/
const LaunchPinDirective: Placeholder = {
name: "launchPin",
regex: /{{(launchPin|openPin|runPin):(([^{]|{(?!{)|{{[\s\S]*?}})*?)}}/,
rules: [],
apply: async (str) => {
const matches = str.match(/{{(launchPin|openPin|runPin):(([^{]|{(?!{)|{{[\s\S]*?}})*?)}}/);
const targetRep = matches?.[2] || "";
if (!targetRep) return { result: "" };
const pins: Pin[] = (await getStorage(StorageKey.LOCAL_PINS)) || [];
const target = pins.find((p) => p.name == targetRep || p.id.toString() == targetRep);
if (!target) return { result: "" };
const preferences = getPreferenceValues<ExtensionPreferences>();
openPin(target, preferences);
return { result: "" };
},
constant: false,
fn: async (target: string) => (await LaunchPinDirective.apply(`{{launchPin:${target}}}`)).result,
example: "{{launchPin:myPinName}}",
description: "Opens the target pin.",
hintRepresentation: "{{launchPin:...}}",
fullRepresentation: "Launch Pin",
type: PlaceholderType.StaticDirective,
categories: [PlaceholderCategory.Meta],
};

export default LaunchPinDirective;
8 changes: 8 additions & 0 deletions src/lib/placeholders/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ import PinNamesPlaceholder from "./custom-placeholders/pinNames";
import { JavaScriptPlaceholder } from "placeholders-toolkit/dist/lib/defaultPlaceholders";
import vm from "vm";
import PinStatisticsPlaceholder from "./custom-placeholders/pinStatistics";
import LaunchPinDirective from "./custom-placeholders/launchPin";
import LaunchGroupDirective from "./custom-placeholders/launchGroup";
import CreatePinDirective from "./custom-placeholders/createPin";
import DeletePinDirective from "./custom-placeholders/deletePin";

const filteredPlaceholders = Object.values(DefaultPlaceholders).filter((p) => !["location", "js"].includes(p.name));

Expand Down Expand Up @@ -44,6 +48,10 @@ const PinsPlaceholders = [
GroupsPlaceholder,
AskAIDirective,
InputDirective,
LaunchPinDirective,
LaunchGroupDirective,
CreatePinDirective,
DeletePinDirective,
...filteredPlaceholders.filter((p) => p.type == PlaceholderType.InteractiveDirective),
...filteredPlaceholders.filter((p) => p.type == PlaceholderType.Custom),
...filteredPlaceholders.filter((p) => p.type == PlaceholderType.Script),
Expand Down
20 changes: 16 additions & 4 deletions src/view-groups.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@ import {
import { setStorage, getStorage } from "./lib/storage";
import { Direction, StorageKey } from "./lib/constants";
import { Group, deleteGroup, useGroups } from "./lib/Groups";
import { Pin, usePins } from "./lib/Pins";
import { Pin, openPin, usePins } from "./lib/Pins";
import { addIDAccessory, addParentGroupAccessory, addSortingStrategyAccessory } from "./lib/accessories";
import { getGroupIcon } from "./lib/icons";
import GroupForm from "./components/GroupForm";
import { InstallExamplesAction } from "./components/actions/InstallExamplesAction";
import { useEffect, useState } from "react";
import CopyGroupActionsSubmenu from "./components/actions/CopyGroupActionsSubmenu";
import { ViewGroupsPreferences } from "./lib/preferences";
import { ExtensionPreferences, ViewGroupsPreferences } from "./lib/preferences";
import { pluralize } from "./lib/utils";

/**
* Action to create a new group. Opens a form view with blank/default fields.
Expand Down Expand Up @@ -62,7 +63,7 @@ export default function ViewGroupsCommand() {
const { groups, setGroups, revalidateGroups } = useGroups();
const { pins } = usePins();
const [examplesInstalled, setExamplesInstalled] = useState<LocalStorage.Value | undefined>(true);
const preferences = getPreferenceValues<ViewGroupsPreferences>();
const preferences = getPreferenceValues<ExtensionPreferences & ViewGroupsPreferences>();

useEffect(() => {
Promise.resolve(LocalStorage.getItem(StorageKey.EXAMPLE_GROUPS_INSTALLED)).then((examplesInstalled) => {
Expand Down Expand Up @@ -99,13 +100,24 @@ export default function ViewGroupsCommand() {
return (
<List.Item
title={group.name}
subtitle={`${groupPins.length} pin${groupPins.length == 1 ? "" : "s"}`}
subtitle={`${groupPins.length} ${pluralize("Pin", groupPins.length)}`}
accessories={accessories}
key={group.id}
icon={getGroupIcon(group)}
actions={
<ActionPanel>
<ActionPanel.Section title="Group Actions">
<Action
title={`Open ${groupPins.length} ${pluralize("Pin", groupPins.length)}`}
icon={Icon.ChevronRight}
onAction={async () => {
await Promise.all(
groupPins.map(async (pin) => {
await openPin(pin, preferences)
})
);
}}
/>
<Action.Push
title="Edit"
icon={Icon.Pencil}
Expand Down

0 comments on commit 9d8f8b8

Please sign in to comment.