-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,8 @@ import rover.rdo.AtomicObjectState | |
import rover.rdo.client.RdObject | ||
import cats.implicits._ | ||
import com.monovore.decline._ | ||
import rover.Client.OAuth2Credentials | ||
import rover.{Client, Server, Session} | ||
|
||
import scala.concurrent.ExecutionContext.Implicits.global | ||
import scala.async.Async.{async, await} | ||
|
@@ -30,8 +32,9 @@ class ChatMessage(val body: String, | |
|
||
// FIXME: ensure messages can be read, but not modified or reassigned... | ||
// FIXME: after state & rd object impl change | ||
class Chat(_onStateModified: Chat#Updater) extends RdObject[List[ChatMessage]](AtomicObjectState.initial(List[ChatMessage]())) { | ||
type Updater = AtomicObjectState[List[ChatMessage]] => Promise[Unit] | ||
class Chat(_onStateModified: Chat#Updater, initialState: AtomicObjectState[List[ChatMessage]] = AtomicObjectState.initial(List[ChatMessage]())) extends RdObject[List[ChatMessage]](AtomicObjectState.initial(List[ChatMessage]())) { | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
steffansluis
Author
Owner
|
||
// type State = List[ChatMessage] | ||
type Updater = AtomicObjectState[List[ChatMessage]] => Future[Unit] | ||
|
||
def send(message: ChatMessage): Promise[Unit]= { | ||
val op: AtomicObjectState[List[ChatMessage]]#Op = s => s :+ message | ||
|
@@ -41,7 +44,7 @@ class Chat(_onStateModified: Chat#Updater) extends RdObject[List[ChatMessage]](A | |
} | ||
} | ||
|
||
override def onStateModified(oldState: AtomicObjectState[List[ChatMessage]]): Promise[Unit] = { | ||
override def onStateModified(oldState: AtomicObjectState[List[ChatMessage]]): Future[Unit] = { | ||
_onStateModified(state) | ||
} | ||
|
||
|
@@ -50,27 +53,50 @@ class Chat(_onStateModified: Chat#Updater) extends RdObject[List[ChatMessage]](A | |
} | ||
|
||
} | ||
|
||
object Chat { | ||
val SIZE = 10 | ||
def fromRDO(rdo: RdObject[List[ChatMessage]], _onStateModified: Chat#Updater): Chat = { | ||
new Chat(_onStateModified, rdo.state) | ||
} | ||
} | ||
|
||
def client(serverAddress: String, user: User): Unit = { | ||
val printer = (string: String) => { | ||
val cls = s"${string.split("\n").map(c => s"${REPL.UP}${REPL.ERASE_LINE_BEFORE}${REPL.ERASE_LINE_AFTER}").mkString("")}" | ||
// Prepend two spaces to match input indentation of "> " | ||
val text = string.split("\n").map(line => s" $line").mkString("\n") | ||
class ChatServer() extends Server[OAuth2Credentials](null) { | ||
|
||
s"${REPL.SAVE_CURSOR}$cls\r$text${REPL.RESTORE_CURSOR}" | ||
} | ||
} | ||
|
||
class ChatClient(serverAddress: String) extends Client.OAuth2Client(serverAddress, (credentials: OAuth2Credentials) => credentials.accessToken) { | ||
var session: Session[OAuth2Credentials] = null | ||
var user: User = null | ||
var chat: Chat = null; | ||
|
||
val printer = (string: String) => { | ||
val cls = s"${string.split("\n").map(c => s"${REPL.UP}${REPL.ERASE_LINE_BEFORE}${REPL.ERASE_LINE_AFTER}").mkString("")}" | ||
// Prepend two spaces to match input indentation of "> " | ||
val text = string.split("\n").map(line => s" $line").mkString("\n") | ||
|
||
s"${REPL.SAVE_CURSOR}$cls\r$text${REPL.RESTORE_CURSOR}" | ||
} | ||
|
||
// TODO: This is hacky, figure out a better way to do this | ||
val updater: Chat#Updater = state => Promise() completeWith async { | ||
val text = state.immutableState.takeRight(SIZE).map(m => m.toString()).mkString("\n") | ||
print(s"${printer(text)}") | ||
val updater: Chat#Updater = state => async { | ||
val text = state.immutableState.takeRight(ChatClient.SIZE).map(m => m.toString()).mkString("\n") | ||
print(s"${printer(text)}") | ||
} | ||
|
||
def login(user: User): Future[Unit] = { | ||
async { | ||
val credentials = new OAuth2Credentials("fake credentials", "fake credentials") | ||
this.user = user | ||
session = createSession(credentials) | ||
val rdo = await(session.importRDO[List[ChatMessage]]("chat")) | ||
chat = Chat.fromRDO(rdo, updater) | ||
} | ||
} | ||
|
||
val chat = new Chat(updater) | ||
def render(): Future[Unit] = { | ||
// val chat = new Chat(updater) | ||
println(" Welcome to Rover Chat!") | ||
print((1 to SIZE).map(i => "\n").mkString("")) | ||
print((1 to ChatClient.SIZE).map(i => "\n").mkString("")) | ||
|
||
// Simulate conversation | ||
async { | ||
|
@@ -93,23 +119,37 @@ object Chat { | |
s | ||
} | ||
val executor = (input: String) => { | ||
async { | ||
val p = chat.send(new ChatMessage(input, user)) | ||
await(p.future) | ||
// This clears the input line | ||
print(s"${REPL.UP}${REPL.ERASE_LINE_AFTER}") | ||
chat.immutableState.takeRight(SIZE).map(m => s"${m.toString()}").mkString("\n") | ||
} | ||
async { | ||
val p = chat.send(new ChatMessage(input, user)) | ||
await(p.future) | ||
// This clears the input line | ||
print(s"${REPL.UP}${REPL.ERASE_LINE_AFTER}") | ||
chat.state.immutableState.takeRight(ChatClient.SIZE).map(m => s"${m.toString()}").mkString("\n") | ||
} | ||
} | ||
val repl: REPL[String] = new REPL(reader, executor, printer) | ||
Await.result(repl.loop(), Duration.Inf) | ||
// Await.result(repl.loop(), Duration.Inf) | ||
repl.loop() | ||
} | ||
|
||
} | ||
|
||
object ChatClient { | ||
val SIZE = 10 | ||
|
||
def main(args: Array[String]): Unit = { | ||
client("bla", User.Steffan) | ||
val serverAddress = "bla" | ||
val client = new ChatClient(serverAddress) | ||
val f = async { | ||
await(client.login(User.Steffan)) | ||
await(client.render()) | ||
} | ||
|
||
Await.result(f, Duration.Inf) | ||
} | ||
} | ||
|
||
|
||
// TODO: Add client and server subcommands | ||
object ChatCLI extends CommandApp( | ||
name = "rover-chat", | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package rover | ||
|
||
class Client[C](serverAddress: String, identifier: Session[C]#Identifier) { | ||
val server = Server.fromAddress[C](serverAddress) | ||
|
||
def createSession(credentials: C) = { | ||
server.createSession(credentials) | ||
} | ||
} | ||
|
||
|
||
object Client { | ||
class OAuth2Credentials(val accessToken: String, val refreshToken: String) {} | ||
type OAuth2Client = Client[OAuth2Credentials] | ||
|
||
def oauth2(): OAuth2Client = { | ||
null | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package rover | ||
|
||
class Server[C](identifier: Session[C]#Identifier) { | ||
// TODO: Determine what to do with this | ||
def clientFromCredentials(credentials: C): Client[C] = { | ||
null | ||
} | ||
|
||
def createSession(credentials: C) = { | ||
new Session[C](credentials, this, clientFromCredentials(credentials)) | ||
} | ||
} | ||
|
||
object Server { | ||
def fromAddress[C](address: String): Server[C] = { | ||
new Server[C](null) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package rover | ||
|
||
import rover.rdo.AtomicObjectState | ||
import rover.rdo.client.RdObject | ||
|
||
import scala.concurrent.Future | ||
import scala.concurrent.ExecutionContext.Implicits.global | ||
import scala.async.Async.{async, await} | ||
|
||
class Session[C](credentials: C, server: Server[C], client: Client[C]) { | ||
type Id = String | ||
type Identifier = C => Id | ||
|
||
// TODO: Move this to the proper place | ||
type ObjectId = Id | ||
|
||
// val server: Server[C] = null | ||
// val client: Client[C] = null | ||
|
||
// TODO: Implement errors | ||
def importRDO[T](objectId: ObjectId): Future[RdObject[T]] = { | ||
// TODO: Hacks | ||
async { | ||
if (objectId == "chat") new RdObject[T]( AtomicObjectState.initial(List[Any]().asInstanceOf[T]) ) | ||
else null | ||
} | ||
} | ||
|
||
def exportRDO[T](rdo: RdObject[T]): ObjectId = { | ||
null | ||
} | ||
|
||
|
||
} | ||
|
||
object Session { | ||
// TODO: Probably better to make this mutable? It needs to be persistent in any case | ||
private var _CACHE = Map[Session[Any]#Id, Session[Any]]() | ||
|
||
def get[T](sessionId: Session[T]#Id): Session[T] = { | ||
_CACHE.get(sessionId).asInstanceOf[Session[T]] | ||
} | ||
|
||
def set[T](sessionId: Session[T]#Id, session: Session[T]): Unit = { | ||
_CACHE = _CACHE.updated(sessionId, session.asInstanceOf[Session[Any]]) | ||
} | ||
} |
The initialState shouldn't be List[AtomicObjectState[ChatMessage]]?
This way it contradicts to our AtomicState and Log implementation.