GithubHelp home page GithubHelp logo

maxchehab / phelia Goto Github PK

View Code? Open in Web Editor NEW
1.9K 15.0 45.0 26.52 MB

⚡ A reactive Slack application framework.

Home Page: https://npmjs.com/phelia

License: MIT License

TypeScript 99.86% JavaScript 0.14%

phelia's Introduction

⚡ Phelia

React for Slack Apps

Build interactive Slack apps without webhooks or JSON headache. If you know React, you know how to make a Slack app.

Quick start

  1. Create your message with React:

    import randomImage from "../utils";
    
    export function RandomImage({ useState }: PheliaMessageProps) {
      const [imageUrl, setImageUrl] = useState("imageUrl", randomImage());
    
      return (
        <Message text="Choose a dog">
          <ImageBlock
            title="an adorable :dog:"
            alt="a very adorable doggy dog"
            imageUrl={imageUrl}
            emoji
          />
          <Divider />
          <Actions>
            <Button
              style="primary"
              action="randomImage"
              onClick={() => setImageUrl(randomImage())}
            >
              New doggy
            </Button>
          </Actions>
        </Message>
      );
    }
  2. Register your component

    const client = new Phelia(process.env.SLACK_TOKEN);
    
    app.post(
      "/interactions",
      client.messageHandler(process.env.SLACK_SIGNING_SECRET, [RandomImage])
    );
    
    client.postMessage(RandomImage, "@max");
  3. Interact with your message:

See: docs for more info or join our community Slack.

How this works

Phelia transforms React components into Slack messages by use of a custom React reconciler. Components (with their internal state and props) are serialized into a custom storage. When a user interacts with a posted message Phelia retrieves the component, re-hydrates it's state and props, and performs any actions which may result in a new state.

Components

Each component is a mapping of a specific object type for a slack block. There are 3 categories of components, each with special rules for how that component can be used with other components.

  1. Surface Components (Message, Home, Modal) - Root components that contains Block Components
  2. Block Components (Actions, Context, Divider, Image, Input, Section) - Direct descendants of a Surface Component. Contains Block Components
  3. Block Element Components (Text, CheckBoxes, TextField, etc) - Direct descendants of a Block Components.

Feature Support

To request a feature submit a new issue.

Component Example
Actions Counter
Button Counter
Channel Select Menus Channel Select Menu
Checkboxes Modal Example
Confirmation dialog Random Image
Context
Conversation Select Menus Conversation Select Menu
Date Picker Birthday Picker
Divider Random Image
External Select Menus External Select Menu
Home Tab Home App Example
Image Block Random Image
Image Random Image
Input Modal Example
Messages Server
Modals Modal Example
Multi channels select Menu Multi Channels Select Menu
Multi conversations select Menu Multi Conversations Select Menu
Multi external select Menu Multi External Select Menu
Multi static select Menu Multi Static Select Menu
Multi users select Menu Multi Users Select Menu
Option group Static Select Menu
Option
Overflow Menu Overflow Menu
Plain-text input Modal Example
Radio button group Radio Buttons
Section Counter
Static Select Menus Static Select Menu
Text Counter
Text Random Image
User Select Menus User Select Menu

phelia's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

phelia's Issues

MultiSelectMenu initialOptions aren't rendered

Hey, great project!

I've been having issues trying to initialize MultiSelectMenu with initialOptions.

The docs/example state you should write something like:

  <MultiSelectMenu
    type="external"
    action="authors"
    placeholder="Select an option"
    onSearchOptions={() => { ... }}
    initialOptions={[
      <Option key={'key'} value={'value'}>
        label
      </Option>,
    ]}
    minQueryLength={1}
  />

But when trying that out, the initial value is left out.

I tried playing around with the source, and saw that here, initialOptions is read from MultiSelectMenu's children. But when I tried providing it as children it didn't work either 🤷‍♂️.

Step by Step "Getting Started" guide

Right now the docs show you a lot of cool things you can do. What they don't do is show you the path of least resistance getting to a working app.

I was able to get a repo partially working: https://github.com/hipstersmoothie/phelia-test

But I keep running into this error:

Screen Shot 2020-04-16 at 9 22 19 AM

I think I've given it all the permissions it needs, but it was really unclear from the docs what scopes I should give the bot.

Screen Shot 2020-04-16 at 9 23 23 AM

What do I want?

A step-by-step guide taking me from no project to working slack integration. This should include where to go in the slack settings ui to activate/enable all necessary features.

Asynchronous data fetching / other effects

Hey, first of all thank you for releasing this - looks great so far! 🙂

I have gotten a basic app working now, with some placeholder interactions. Now I would like to show some "live data" in the Home Tab, and was wondering if there was an example for this or if you had any hints. My attempts either resulted in stale state, that was only refreshed after another interaction, or lots of requests triggered that didn't do anything at all 🙈

Something like "send a GET request on load, fetch data, show result to user". In a normal React app I'd use useEffect(() => fetch(requestTarget).then(data => setState(data)), [requestTarget]) or something like that and I'd expect that to cause an update of the view. However that didn't seem to work for me 🤔

message external callbacks

First of all, thank you for awesome project!

However I can't see way to trigger external methods. We have server with many features, and what to add slack integration to trigger some methods of our services. Phelia runs over our nestjs app.

For example inside our service we have method setNotificationViewed(itemId:string, viewed:boolean) and we send user message with button. So we want somehow hide button after use and trigger external method. It would be nice to have something like this:

const key = this.phelia.postMessage(TestMsg, '@user', {
      itemId,
      buttonPressed: ()=>this.setNotificationViewed(itemId, true),
    })

and in TestMsg:

const handleBtnClick = () => {
props.buttonPressed()
setButtonHidden(true)
}

but for now props seems to be stringified and doesn't accept functions. We can't modify this behaviour because JSON.stringify is not part of storage but part of Phelia, while it is used in each Storage.set.

Handling Slack Commands

I'd love to display a Phelia component when a particular slack command is invoked.

I'd join and ask in the slack, but as you can see by #28 - there's no slack.

[feature request] Support for multi workspace

Phelia currently does not support distributed app.

If your app is installed in more than one workspace, Phelia has no way of handling this as it is bound to only one access token.

Move JSON.stringify(state) to storage side.

Currenly we serializing state data with JSON.stringify(data) before saving it to storage. It's completely ok, if we have default Map<string, string>() storage. But if we want to use some DB we may want to store custom data types in it's own format.

In my case I want to store Dates in MongoDB. It's driver is able to save dates in Date format, but I need to pass JS Date object there as input. Currently i made rought hack when i convert objects with special field $$type = 'date' to Date objects right before saving to DB. But better solution here is to change phelia's code.

I propose to move JSON.stringify/JSON.parse to storage side. Default storage then became more complicated, but we will be able to use custom storage.

Thanks!

Markdown not rendering

I am trying to use markdown rendering for the Text type, but it seems to do nothing.

<Text type="mrkdwn">:star: **Recent Open Source Projects** :star:</Text>

I've tried a bunch of different ways/props with no luck. The stars render but the text never bolded

React.createElement invalid type when clicking Action Button inside of modal

I've been running into an issue with handling onClick inside of a modal. Reproducible simple example-- https://gist.github.com/tejasmanohar/a558884cb2dbca648c872c53c241e56f

Whenever I click it, I see a call to my "interactive" Slack endpoint and following error in my logs

Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
    at createFiberFromTypeAndProps (/Users/tejasmanohar/dev/src/github.com/carrytravel/hightouch-api/node_modules/react-reconciler/cjs/react-reconciler.development.js:15378:21)
    at createFiberFromElement (/Users/tejasmanohar/dev/src/github.com/carrytravel/hightouch-api/node_modules/react-reconciler/cjs/react-reconciler.development.js:15401:15)
    at reconcileSingleElement (/Users/tejasmanohar/dev/src/github.com/carrytravel/hightouch-api/node_modules/react-reconciler/cjs/react-reconciler.development.js:5003:23)
    at reconcileChildFibers (/Users/tejasmanohar/dev/src/github.com/carrytravel/hightouch-api/node_modules/react-reconciler/cjs/react-reconciler.development.js:5063:35)
    at reconcileChildren (/Users/tejasmanohar/dev/src/github.com/carrytravel/hightouch-api/node_modules/react-reconciler/cjs/react-reconciler.development.js:7573:28)
    at updateHostRoot (/Users/tejasmanohar/dev/src/github.com/carrytravel/hightouch-api/node_modules/react-reconciler/cjs/react-reconciler.development.js:8062:5)
    at beginWork (/Users/tejasmanohar/dev/src/github.com/carrytravel/hightouch-api/node_modules/react-reconciler/cjs/react-reconciler.development.js:9428:14)
    at Object.invokeGuardedCallbackImpl (/Users/tejasmanohar/dev/src/github.com/carrytravel/hightouch-api/node_modules/react-reconciler/cjs/react-reconciler.development.js:10557:10)
    at invokeGuardedCallback (/Users/tejasmanohar/dev/src/github.com/carrytravel/hightouch-api/node_modules/react-reconciler/cjs/react-reconciler.development.js:10733:31)
    at beginWork$1 (/Users/tejasmanohar/dev/src/github.com/carrytravel/hightouch-api/node_modules/react-reconciler/cjs/react-reconciler.development.js:14616:7)
    at performUnitOfWork (/Users/tejasmanohar/dev/src/github.com/carrytravel/hightouch-api/node_modules/react-reconciler/cjs/react-reconciler.development.js:13570:12)
    at workLoopSync (/Users/tejasmanohar/dev/src/github.com/carrytravel/hightouch-api/node_modules/react-reconciler/cjs/react-reconciler.development.js:13543:22)
    at performSyncWorkOnRoot (/Users/tejasmanohar/dev/src/github.com/carrytravel/hightouch-api/node_modules/react-reconciler/cjs/react-reconciler.development.js:13141:9)
    at /Users/tejasmanohar/dev/src/github.com/carrytravel/hightouch-api/node_modules/react-reconciler/cjs/react-reconciler.development.js:1802:24
    at unstable_runWithPriority (/Users/tejasmanohar/dev/src/github.com/carrytravel/hightouch-api/node_modules/scheduler/cjs/scheduler.development.js:653:12)
    at runWithPriority (/Users/tejasmanohar/dev/src/github.com/carrytravel/hightouch-api/node_modules/react-reconciler/cjs/react-reconciler.development.js:1752:10)
    at flushSyncCallbackQueueImpl (/Users/tejasmanohar/dev/src/github.com/carrytravel/hightouch-api/node_modules/react-reconciler/cjs/react-reconciler.development.js:1797:7)
    at flushSyncCallbackQueue (/Users/tejasmanohar/dev/src/github.com/carrytravel/hightouch-api/node_modules/react-reconciler/cjs/react-reconciler.development.js:1785:3)
    at scheduleUpdateOnFiber (/Users/tejasmanohar/dev/src/github.com/carrytravel/hightouch-api/node_modules/react-reconciler/cjs/react-reconciler.development.js:12584:9)
    at Object.updateContainer (/Users/tejasmanohar/dev/src/github.com/carrytravel/hightouch-api/node_modules/react-reconciler/cjs/react-reconciler.development.js:15851:3)
    at reconcile (/Users/tejasmanohar/dev/src/github.com/carrytravel/hightouch-api/node_modules/phelia/dist/core/reconciler.js:234:24)
    at Object.<anonymous> (/Users/tejasmanohar/dev/src/github.com/carrytravel/hightouch-api/node_modules/phelia/dist/core/reconciler.js:240:36)
    at Generator.next (<anonymous>)
    at /Users/tejasmanohar/dev/src/github.com/carrytravel/hightouch-api/node_modules/phelia/dist/core/reconciler.js:8:71
    at new Promise (<anonymous>)
    at __awaiter (/Users/tejasmanohar/dev/src/github.com/carrytravel/hightouch-api/node_modules/phelia/dist/core/reconciler.js:4:12)
    at Object.render (/Users/tejasmanohar/dev/src/github.com/carrytravel/hightouch-api/node_modules/phelia/dist/core/reconciler.js:239:12)
    at Phelia.<anonymous> (/Users/tejasmanohar/dev/src/github.com/carrytravel/hightouch-api/node_modules/phelia/dist/core/phelia.js:316:36)
    at Generator.next (<anonymous>)
    at fulfilled (/Users/tejasmanohar/dev/src/github.com/carrytravel/hightouch-api/node_modules/phelia/dist/core/phelia.js:5:58)

TypeError: option.isSelected is not a function

          <MultiSelectMenu action="selection" placeholder="A placeholder">
            <OptionGroup label="an option group">
              <Option value="option-a" selected>
                option a
              </Option>
              <Option value="option-b" selected>
                option b
              </Option>
              {/* <Option value="option-b">option b</Option>
              <Option value="option-c">option c</Option> */}
            </OptionGroup>
          </MultiSelectMenu>
TypeError: option.isSelected is not a function

Context: App Home

How are event_id/block IDs determined?

How does Phelia come up with event_id/block_ids to match components/events from Slack? Are they generated on new Phelia? Are they consistent / always the same if you don't change the React components?

One thing I thought about when pushing updates to other in-Slack Block kit UIs in the past (not App Home's but guessing it applies here, too) is that my backend needs to knows how to handle events from the last version of the code (using their action IDs / block IDs) in case someone is currently interacting with the app.

Is there a story around that here? Doesn't matter much for my current use case but would matter if you cared about not having downtime / functionality break during your deploys

(Please forgive me if this is a silly question, might be missing something obvious)

jsDoc Annotations

I think it would be useful to combine some of your current docs/slacks with the source code for a slightly better developer experience. I am fine with doing of this, here is a sample

Screen Shot 2020-04-17 at 8 36 13 PM

I would also add an eslint rule to ensure that these annotations are always present.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.