-
Notifications
You must be signed in to change notification settings - Fork 103
Proposals on variables #10
Comments
ask()
action to ask
global variable
I've also been thinking this out in my head. For the naming of the global variables, remember that the contribution guide suggests the display name from the shortcuts app itself (in English). My approach is quite similar to yours, but with a couple differences as described below: // Global Variables
const askWhenRun: WFSerialization = {
Value: {
Type: 'Ask',
},
WFSerializationType: 'WFTextTokenAttachment',
};
const clipboard: WFSerialization = {
Value: {
Type: 'Variable',
},
WFSerializationType: 'WFTextTokenAttachment',
};
const currentDate: WFSerialization = {
Value: {
Type: 'CurrentDate',
},
WFSerializationType: 'WFTextTokenAttachment',
};
const shortcutInput: WFSerialization = {
Value: {
Type: 'ExtensionInput',
},
WFSerializationType: 'WFTextTokenAttachment',
};
// Util methods
const /** or makeMagicVariable */ actionOutput = (name?: string): WFSerialization => ({
Value: {
...(name && { OutputName: name }),
OutputUUID: uuidv4(),
Type: 'ActionOutput',
},
WFSerializationType: 'WFTextTokenAttachment',
});
const formatSerialization = (variable: WFSerialization | string): WFSerialization => {
// Named Variable
if (typeof variable === 'string') {
return {
Value: {
Type: 'Variable',
VariableName: name,
},
WFSerializationType: 'WFTextTokenAttachment',
};
}
// Already Serialized
return variable;
};
const withVariables = (
strings: TemplateStringsArray,
...vars: string[]
): WFSerialization => ({
WFSerializationType: 'WFTextTokenString',
Value: {
string: strings.join('\ufffc'), // Join strings with object replacement character
attachmentsByRange: strings.reduce(
(a, _, i, s) => {
// ...
return {
...a,
[`{${lengthSoFar}, 1}`]: formatSerialization(vars[i]).Value,
};
},
{},
),
},
}); Here I pulled the What this allows us is that any variable can be passed into Later on we can improve I'm curious what you think of my approach and any questions you have just ask. |
I see, boolean values in dictionaries currently support WFTextTokenAttachments.
If const result = magicVariable()
const myResult = magicVariable('My Result')
const list = variable('List') |
In that scenario where we have @joshfarrant What do you think? |
That's exactly what I did. It will remove every check/test and it will make it easier to write the shortcut2js converter (#4) later on. Using WFSerialization as base, the const getVariable = (
{
variable,
}: GetVariableOptions,
): WFWorkflowAction => ({
WFWorkflowActionIdentifier: 'is.workflow.actions.getvariable',
WFWorkflowActionParameters: {
WFVariable: variable,
},
}); |
@Archez I'd tend to agree with you in leaning towards the latter. Having withVariables`Testing ${'Some Variable'}`; Could very easily be assumed to be equal to withVariables`Testing Some Variable`; Which is clearly is not. Initialising the named variables beforehand, while it will add a bit more boilerplate, will make the code easier to understand, and the user's intentions clearer. I really like the direction this is going in, definitely a big improvement over my initial implementation! |
That is exactly my thought. The boiler plating aligns more with a 'code' structure rather than a 'iOS Shortcut' structure and also paves the way for a more concrete |
Fantastic! Me too @xAlien95! 🧞♂️ |
I pushed it to my fork, you can see and try it there. @joshfarrant, do I have to split @Archez, should I use Note: I really don't know why my real name appeared twice on the commit.. It's the first time I'm using Atom to push commits. Any suggestion on how to prevent this behavior is highly appreciated! |
If we do not use the For your note: I just use VSCode (its great just like Atom), and haven't had strange issues like that using it's built in git tools. |
@Archez, I'm not familiar with TypeScript, but if we use the I split import SerializationValue from '../interfaces/WF/SerializationValue';
import WFSerialization from '../interfaces/WF/WFSerialization';
import WFWorkflowAction from '../interfaces/WF/WFWorkflowAction';
export const withActionOutput = <OptionsType>(
actionBuilder: (options: OptionsType) => WFWorkflowAction,
) => (
(
options: OptionsType,
output?: WFSerialization,
): WFWorkflowAction => {
const action = actionBuilder(options);
if (output) {
const value = <SerializationValue>output.Value;
action.WFWorkflowActionParameters.UUID = value.OutputUUID;
if (value.OutputName) {
action.WFWorkflowActionParameters.CustomOutputName = value.OutputName;
}
}
return action;
}
); Is there a better way to achieve it? |
That's strange, it shouldn't prefer one type over any other, as far as I know. As I'm sure you've seen, the WFSerialization interface is defined here, and it's Value should expect a SerializationValue. This is the first TypeScript project I've built from the ground up, so there may well be a better way of defining the EDIT: I should also mention that I agree with @Archez in leaning towards |
I'd say have I'm wondering whether it might make sense to have another directory, That way, users would have something like: import {
buildShortcut,
withVariables,
variable,
actionOutput,
} from '@joshfarrant/shortcuts-js';
import {
askWhenRun,
clipboard,
} from '@joshfarrant/shortcuts-js/variables'; Thoughts? |
Later on, it could be useful to have a const result = variable('Result');
const actions = [
number({
number: 1112223333,
}),
setVariable({
variable: result,
}),
showResult({
text: withVariables`Number is ${result}, phone number is ${result.with({
type: 'PhoneNumber',
})}!`,
}),
];
I agree with having both @joshfarrant, this issue is going off-topic from the original title. Should I rename it to something like "Proposals on variables", or close this one and open a new one? |
@xAlien95 I agree about renaming the issue, please go ahead 🙂 |
@joshfarrant yes (although a phone number cast does nothing at all, it just returns the number as a string since the number type doesn't have properties). If you set a Text action with "Today at 9:00 am" and then you insert its output magic variable to be considered as a Date, you'll get the same time string you get typing "Today at 9:00 am" in the Date action. |
@joshfarrant, step 2 of the variable implementation is live in my fork's variable-class branch.
To do:
Contributors that have to implement actions that accept variables as parameters have to just add getDeviceDetails({
detail: askWhenRun,
}) Regarding const myVar = variable('My Variable');
myVar.with({
type: 'Dictionary',
getValueForKey: 'My Key',
});
myVar.with({
type: 'Date',
dateFormat: 'Custom',
customFormat: 'dd MMM at HH:mm',
});
myVar.with({
type: 'Image',
get: 'Album',
}); You can take a look at all the changes here: xAlien95@024d0ba. In step 3 I'll implement all the Aggrandizements, I already listed them all in this gist. |
@xAlien95 Looks fantastic, great work. Can't wait to see the PR, feel free to submit whenever you're ready.
This is very exciting. Everything else is coming along well, so I might start taking a look into ways to approach that soon. |
I'm in the process of presenting a fork in which variables (named variables, magic variables and global variables (Ask, Clipboard, CurrentDate and ExtensionInput)) are objects instead of strings. In my local repository, I wrote:
Using lowerCamelCase, an
ask
global variable would conflict with theask()
action function, so I ended up distinguishing them using CapitalCamelCase for variable objects. It could make sense since in my forkActionOutput
andVariable
acts like JS types/classes:Unfortunately the current lint script accepts variable names only in lowerCamelCase or UPPER_CASE.
How should I name the Ask global variable?
The text was updated successfully, but these errors were encountered: