Skip to content

Commit

Permalink
Merge: Nexus.R (#19)
Browse files Browse the repository at this point in the history
Nexus.R (React)
  • Loading branch information
ShindouMihou committed Dec 2, 2023
2 parents ce64bc0 + b7002ac commit 4049185
Show file tree
Hide file tree
Showing 46 changed files with 2,839 additions and 64 deletions.
7 changes: 7 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[submodule "addons/nexus.entour"]
path = addons/nexus.entour
url = https://github.com/ShindouMihou/nexus.entour

[submodule "addons/nexus.coroutines"]
path = addons/nexus.coroutines
url = https://github.com/ShindouMihou/nexus.coroutines
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ object ReportUserContextMenu: NexusUserContextMenu() {
}
```

For Kotlin users who are looking to build highly reactive, or interactive bots, you may be interested in our new rendering mechanism
called [Nexus.R], read more at [GitHub Wiki](https://github.com/ShindouMihou/Nexus/wiki/Nexus.R).

## Getting Started

To get started with Nexus, we recommend reading the wiki in its chronological order:
Expand All @@ -53,6 +56,7 @@ Nexus is used in production by Discord bots, such as:
- [Beemo](https://beemo.gg): An anti-raid Discord bot that prevents raids on many large servers.
- [Amelia-chan](https://github.com/Amelia-chan/Amelia): A simple RSS Discord bot for the novel site, ScribbleHub.
- [Threadscore](https://threadscore.mihou.pw): Gamifying Q&A for Discord.
- [Flyght](https://flyght.mihou.pw): Adds Know-Your-Member to Discord. Powered by [Nexus.R](https://github.com/ShindouMihou/Nexus/wiki/Nexus.R/).

If you want to add your bot to the list, feel free to add it by creating a pull request!

Expand All @@ -61,6 +65,7 @@ Nexus was created from the ground up to power Discord bots with a simplistic yet
on performance, allowing developers to build their Discord bots fast and clean.
- [x] **Object-based commands**
- [x] **Object-based context menus**
- [x] **Supports an innovative, React+Svelte-like Rendering Mechanism**
- [x] **Middlewares, Afterwares**
- [x] **Supports auto-deferring of responses**
- [x] **Flexible command synchronization system**
Expand Down
15 changes: 15 additions & 0 deletions addons/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Addons

In order to keep the repository specific to the actual functionalities of the Nexus features,
other cool things that would be nice to have in Nexus, such as confirmation menus, etc. are stored into different
repositories and linked here through Git Submodule for others to see.

Here are the list of each different add-on that extends upon Nexus' features:
- [`entour`](nexus.entour) is a set of handy built-in components, hooks and tools for Nexus.R. This contains many
different things for develoeprs who uses Nexus.R such as Confirmation Menus.
- [`coroutines`](nexus.coroutines) adds coroutine support to the framework.

### Contributing

If you want to add your repository into this list, feel free to create a Issue stating the name, repository link and
the description for your repository.
1 change: 1 addition & 0 deletions addons/nexus.coroutines
Submodule nexus.coroutines added at 70dc51
1 change: 1 addition & 0 deletions addons/nexus.entour
Submodule nexus.entour added at 9b2d80
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ plugins {
}

group = 'pw.mihou'
version = '1.0.1'
version = '1.1.0'
description = 'Nexus is the next-generation Javacord framework that aims to create Discord bots with less code, dynamic, more simplicity and beauty.'

repositories {
Expand Down
43 changes: 43 additions & 0 deletions examples/nexus.r/ContextMenu.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import org.javacord.api.event.interaction.MessageContextMenuCommandEvent
import org.javacord.api.interaction.MessageContextMenuInteraction
import pw.mihou.nexus.features.command.facade.NexusCommandEvent
import pw.mihou.nexus.features.command.facade.NexusHandler
import pw.mihou.nexus.features.contexts.NexusContextMenuEvent
import pw.mihou.nexus.features.contexts.NexusMessageContextMenu
import pw.mihou.nexus.features.contexts.NexusUserContextMenu
import pw.mihou.nexus.features.messages.R
import pw.mihou.nexus.features.react.elements.Button
import pw.mihou.nexus.features.react.elements.Embed
import java.time.Instant

/**
* Nexus.R supports basically everything, you can copy-paste the code from one event to another and it'll work
* the same regardless without much if any modification. In this example, we demonstrate how we can use Nexus.R
* to build amazingly simple yet reactive component.
*
* Our example will render a simple message with a button that increments the click count and re-render the
* message whenever a click is registered.
*/
object ContextMenu: NexusMessageContextMenu() {
val name = "react"
override fun onEvent(event: NexusContextMenuEvent<MessageContextMenuCommandEvent, MessageContextMenuInteraction>) {
event.R {
var clicks by writable(0)
render {
Embed {
Title("Rendered with Nexus.R")
SpacedBody(
p("This message was rendered with Nexus.R."),
p("The button has been clicked ") + bold("$clicks times.")
)
Color(java.awt.Color.YELLOW)
Timestamp(Instant.now())
}
Button(label = "Click me!") {
it.buttonInteraction.acknowledge()
clicks += 1
}
}
}
}
}
58 changes: 58 additions & 0 deletions examples/nexus.r/Interaction.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import org.javacord.api.event.interaction.ButtonClickEvent
import org.javacord.api.event.interaction.MessageContextMenuCommandEvent
import org.javacord.api.event.interaction.SelectMenuChooseEvent
import org.javacord.api.interaction.Interaction
import org.javacord.api.interaction.MessageContextMenuInteraction
import org.javacord.api.listener.interaction.ButtonClickListener
import org.javacord.api.listener.interaction.SelectMenuChooseListener
import pw.mihou.nexus.features.command.facade.NexusCommandEvent
import pw.mihou.nexus.features.command.facade.NexusHandler
import pw.mihou.nexus.features.commons.R
import pw.mihou.nexus.features.contexts.NexusContextMenuEvent
import pw.mihou.nexus.features.contexts.NexusMessageContextMenu
import pw.mihou.nexus.features.contexts.NexusUserContextMenu
import pw.mihou.nexus.features.messages.R
import pw.mihou.nexus.features.react.elements.Button
import pw.mihou.nexus.features.react.elements.Embed
import java.time.Instant

/**
* Nexus.R supports basically everything, you can copy-paste the code from one event to another and it'll work
* the same regardless without much if any modification. In this example, we demonstrate how we can use Nexus.R
* to build amazingly simple yet reactive component.
*
* Our example will render a simple message with a button that increments the click count and re-render the
* message whenever a click is registered.
*/
object InteractionExample: ButtonClickListener, SelectMenuChooseListener {
private fun render(interaction: Interaction) {
interaction.R(ephemeral = false) {
var clicks by writable(0)
render {
Embed {
Title("Rendered with Nexus.R")
SpacedBody(
p("This message was rendered with Nexus.R."),
p("The button has been clicked ") + bold("$clicks times.")
)
Color(java.awt.Color.YELLOW)
Timestamp(Instant.now())
}
Button(label = "Click me!") {
it.buttonInteraction.acknowledge()
clicks += 1
}
}
}
}

override fun onButtonClick(event: ButtonClickEvent) {
render(event.interaction)
}

override fun onSelectMenuChoose(event: SelectMenuChooseEvent) {
render(event.interaction)
}


}
39 changes: 39 additions & 0 deletions examples/nexus.r/MessageEvent.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import org.javacord.api.event.message.MessageCreateEvent
import org.javacord.api.listener.message.MessageCreateListener
import pw.mihou.nexus.features.messages.R
import pw.mihou.nexus.features.react.elements.Button
import pw.mihou.nexus.features.react.elements.Embed
import java.time.Instant

/**
* Nexus.R supports basically everything, you can copy-paste the code from one event to another and it'll work
* the same regardless without much if any modification. In this example, we demonstrate how we can use Nexus.R
* to build amazingly simple yet reactive component.
*
* Our example will render a simple message with a button that increments the click count and re-render the
* message whenever a click is registered.
*/
object MessageEvent: MessageCreateListener {
override fun onMessageCreate(event: MessageCreateEvent) {
if (event.messageContent == "%nexus.r") {
event.R {
var clicks by writable(0)
render {
Embed {
Title("Rendered with Nexus.R")
SpacedBody(
p("This message was rendered with Nexus.R."),
p("The button has been clicked ") + bold("$clicks times.")
)
Color(java.awt.Color.YELLOW)
Timestamp(Instant.now())
}
Button(label = "Click me!") {
it.buttonInteraction.acknowledge()
clicks += 1
}
}
}
}
}
}
31 changes: 31 additions & 0 deletions examples/nexus.r/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# nexus.R

React Native + Svelte for Discord Bots. Nexus.R is an innovative way to create and send responses or messages
to any text channel or interaction from slash commands to messages commands and even Users or text channels
with reactivity, enabling quick and simple re-rendering of messages upon state changes.

### Features
- [x] Write-once, Use Everywhere
- Nexus.R allows you to easily reuse responses whether it was written for slash commands or
for messages with hardly any changes.
- [x] States in Discord Bot
- Nexus.R introduces the infamous states of JavaScript world into Discord bots, allowing you to create
messages that will re-render itself upon different state changes, just like a reactive website!
- [x] Webdev-like Feel
- In order to make Discord bot development more accessible to even web developers, we've made the
feature feel like writing web code (JSX) but simpler!

### Table of Contents

As Nexus.R is incredibly simple, you can read the examples and pretty much get a gist of how to use it. Here
are the examples we think that you should read first though:
1. [**Starting out with Nexus.R**](): It's all the same, except for how it's started. *Read which one you prefer to see.*
- [Slash Commands](SlashCommand.kt)
- [Context Menus](ContextMenu.kt)
- [Message Events i.e. Message Commands](MessageEvent.kt)
- [Interactions i.e. Buttons and Select Menus](Interaction.kt)
2. [**Passing State to Another Function**](%5B1%5D_passing_state): As states are not simply the same regular Kotlin properties, there needs a
little bit of a very tiny change when you want to pass state to another function outside of the `Nexus.R` scope.
3. [**Creating Components**](%5B2%5D_components): Reusing code is also important in coding, and components are just one way to reuse code
4. [**Data Fetching**](%5B3%5D_data_fetching/DataFetching.kt): Data fetching is also an important factor in many bots, this is how you can load data before rending the message!
5. [**Hooks**](%5B4%5D_hooks): Hooks are vital to be able to reuse a lot of code, unlike components, hooks are not affected by rerenders and is a great place to define writables and related!
39 changes: 39 additions & 0 deletions examples/nexus.r/SlashCommand.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import pw.mihou.nexus.features.command.facade.NexusCommandEvent
import pw.mihou.nexus.features.command.facade.NexusHandler
import pw.mihou.nexus.features.messages.R
import pw.mihou.nexus.features.react.elements.Button
import pw.mihou.nexus.features.react.elements.Embed
import java.time.Instant

/**
* Nexus.R supports basically everything, you can copy-paste the code from one event to another and it'll work
* the same regardless without much if any modification. In this example, we demonstrate how we can use Nexus.R
* to build amazingly simple yet reactive component.
*
* Our example will render a simple message with a button that increments the click count and re-render the
* message whenever a click is registered.
*/
object SlashCommand: NexusHandler {
val name = "react"
val description = "Shows a demonstration of Nexus.R."
override fun onEvent(event: NexusCommandEvent) {
event.R {
var clicks by writable(0)
render {
Embed {
Title("Rendered with Nexus.R")
SpacedBody(
p("This message was rendered with Nexus.R."),
p("The button has been clicked ") + bold("$clicks times.")
)
Color(java.awt.Color.YELLOW)
Timestamp(Instant.now())
}
Button(label = "Click me!") {
it.buttonInteraction.acknowledge()
clicks += 1
}
}
}
}
}
73 changes: 73 additions & 0 deletions examples/nexus.r/[1]_passing_state/PassingState.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import pw.mihou.nexus.features.command.facade.NexusCommandEvent
import pw.mihou.nexus.features.command.facade.NexusHandler
import pw.mihou.nexus.features.react.React
import pw.mihou.nexus.features.react.elements.Button
import pw.mihou.nexus.features.react.elements.Embed
import pw.mihou.nexus.features.react.writable.plusAssign
import java.time.Instant

/**
* This example demonstrates how you pass [React.Writable] from the render function to another function.
* Unlike JavaScript frameworks, this isn't easily done as Kotlin maintains an immutable-only function argument,
* which means that we have to circumvent this by also maintaining the original [React.Writable] instance itself and
* passing that instead.
*/
object PassingState: NexusHandler {
val name = "react"
val description = "Shows a demonstration of Nexus.R."
override fun onEvent(event: NexusCommandEvent) {
event.R {
// As we need to maintain the original Writable instance, we need to separate it from the
// delegated property, this property won't have the native setter and related.
val clicksDelegate = writable(0)
var clicks by clicksDelegate

render {
Embed {
Title("Rendered with Nexus.R")
SpacedBody(
p("This message was rendered with Nexus.R."),
p("The button has been clicked ") + bold("$clicks times.")
)
Color(java.awt.Color.YELLOW)
Timestamp(Instant.now())
}
Button(label = "Click me!") {
it.buttonInteraction.acknowledge()
increment(clicksDelegate)
}
Button(label = "Click this!") {
it.buttonInteraction.acknowledge()
clicks += 1
}
Button(label = "Click also!") {
it.buttonInteraction.acknowledge()
incrementDelegated(clicksDelegate)
}
}
}
}

/**
* This demonstrates using the [React.Writable] methods to manipulate the value of the [React.Writable], thereby
* causing a re-render. Generally, unless you perform a ton of manipulation, you can live with this.
*/
private fun increment(clicks: React.Writable<Int>) {
// Newer version after a recent commit adding operator overloads over primitive types
// and lists.
clicks += 1

// Older method and more optimal for cases that are not supported with the operator overloads
// such as types that are not supported.
// clicks.update { it + 1 }
}

/**
* This demonstrates how you can also still delegate inside the method as well to maintain
* a similar feel to how a regular property would.
*/
private fun incrementDelegated(clicksDelegate: React.Writable<Int>) {
var clicks by clicksDelegate
clicks += 1
}
}
8 changes: 8 additions & 0 deletions examples/nexus.r/[1]_passing_state/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Passing States (Writables)

As writables aren't simple properties, we can't just simply pass them to another function outside of the `Nexus.R` scope and expect it to work the same
especially since Kotlin doesn't support mutable function arguments. As such, in this example, we demonstrate how to properly pass writables from the
`Nexus.R` scope to another function by passing the Delegate class (`React.Writable`) itself.

In the example, we also demonstrate how the `React.Writable` can then be delegated again inside the other function and
will still re-render the message when needed.
Loading

0 comments on commit 4049185

Please sign in to comment.