From f5a9eaa5dc7262f88eaf5fe5b4a62d1d8fbbda08 Mon Sep 17 00:00:00 2001 From: Arvid Lunnemark Date: Sun, 3 Apr 2022 14:10:26 -0400 Subject: [PATCH] Streaming (#22) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * update schema in daemon ts files * add stub for streaming endpoint * add failing streaming test! * use empty json array * fix test and make it pass!!!! * trunk fixes * 🔄 "improve readme " Update anysphere/asphr commit SHA 🔗 https://github.com/anysphere/asphr/commit/ef7929aae7a104e800a25e946339f3cbf760f20c * make it easier to run two things at once * save last_mono_index * use empty array not null * fix tests * 🔄 "Merge branch 'main' into arvid/streaming " Update anysphere/asphr commit SHA 🔗 https://github.com/anysphere/asphr/commit/256f3df950ac8736b64789af1ab6f31b68c2482d * small qol improvements * 🔄 "small qol improvements " Update anysphere/asphr commit SHA 🔗 https://github.com/anysphere/asphr/commit/f178ab6cd501970e2823a3d32ef3ea8f81e575eb * update gui proto * sort based on timestamp * fix race condition on independent things by removing forced coupling * 🔄 "fix race condition on independent things by removing forced coupling " Update anysphere/asphr commit SHA 🔗 https://github.com/anysphere/asphr/commit/05031765fb97aba9fb55e6297c5c89acdd10efa9 * get each message exactly once * not working! * 🔄 "not working! " Update anysphere/asphr commit SHA 🔗 https://github.com/anysphere/asphr/commit/69957a80733f0b58a74a4269b1f832583b7c2086 * update gui proto * make new messages also streamed * update tests for new filtering mechanism * fix problems' * only update when actuallynecessary * add wrap3 because useful * make sent and outbox work * comply with iso c++ * trunk fmt * clarifying comment * clarify sorting order * typescript super linter use prettier * fix shellcheck i love shellcheck * linting things * add a generated comment on top * add language to make linter happy * trunk fmt all * document scripts * linter --- .github/workflows/linter.yml | 2 + .github/workflows/stale.yml | 17 +- .vscode/settings.json | 3 + README.md | 43 +- cli/as_cli.cc | 56 +-- cli/friend_struct.cc | 8 +- cli/inbox.cc | 9 +- client_lib/client_lib.cc | 8 +- daemon/config.cc | 40 +- daemon/config.hpp | 21 +- daemon/crypto.cc | 2 +- daemon/crypto.hpp | 2 +- daemon/daemon_rpc.cc | 233 +++++++--- daemon/daemon_rpc.hpp | 12 +- daemon/friend.hpp | 3 + daemon/inbox.cc | 16 +- daemon/msgstore.cc | 83 +++- daemon/msgstore.hpp | 24 + daemon/transmitter.cc | 3 + gui/README.md | 5 +- gui/assets/pkg-scripts/postinstall | 27 +- gui/assets/pkg-scripts/preinstall | 22 +- gui/generate-icons.sh | 20 +- gui/package.json | 2 +- gui/src/daemon/schema/daemon_grpc_pb.d.ts | 21 +- gui/src/daemon/schema/daemon_grpc_pb.js | 78 ++-- gui/src/daemon/schema/daemon_pb.d.ts | 78 ++-- gui/src/daemon/schema/daemon_pb.js | 433 ++++-------------- gui/src/main/preload.js | 144 +++++- gui/src/renderer/components/MessageList.tsx | 53 ++- .../renderer/components/SelectableList.tsx | 2 - gui/src/renderer/types.ts | 1 + wrap2.sh | 12 + wrap3.sh | 12 + 34 files changed, 802 insertions(+), 693 deletions(-) mode change 100755 => 100644 gui/src/daemon/schema/daemon_grpc_pb.d.ts mode change 100755 => 100644 gui/src/daemon/schema/daemon_grpc_pb.js mode change 100755 => 100644 gui/src/daemon/schema/daemon_pb.d.ts mode change 100755 => 100644 gui/src/daemon/schema/daemon_pb.js create mode 100755 wrap2.sh create mode 100755 wrap3.sh diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 2dd398d5..091dbf0e 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -26,5 +26,7 @@ jobs: env: VALIDATE_ALL_CODEBASE: false JAVASCRIPT_DEFAULT_STYLE: prettier + TYPESCRIPT_DEFAULT_STYLE: prettier + IGNORE_GENERATED_FILES: true DEFAULT_BRANCH: main GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 2c7c52b9..942c02f4 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -7,21 +7,20 @@ name: Mark stale issues and pull requests on: schedule: - - cron: '42 3 * * *' + - cron: "42 3 * * *" jobs: stale: - runs-on: ubuntu-latest permissions: issues: write pull-requests: write steps: - - uses: actions/stale@v3 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - stale-issue-message: 'Stale issue message' - stale-pr-message: 'Stale pull request message' - stale-issue-label: 'no-issue-activity' - stale-pr-label: 'no-pr-activity' + - uses: actions/stale@v3 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + stale-issue-message: "Stale issue message" + stale-pr-message: "Stale pull request message" + stale-issue-label: "no-issue-activity" + stale-pr-label: "no-pr-activity" diff --git a/.vscode/settings.json b/.vscode/settings.json index 03776c8c..953fbd17 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -8,5 +8,8 @@ }, "[typescriptreact]": { "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "search.exclude": { + ".trunk": true } } diff --git a/README.md b/README.md index 71227b82..28680e6a 100644 --- a/README.md +++ b/README.md @@ -6,25 +6,25 @@ This repository, along with [anysphere/asphr](https://github.com/anysphere/asphr Clone anysphere/asphr and anysphere/client and put them in a directory side-by-side. Then, in the `client` directory, run: -``` +```bash bazel build //... ``` to build, and -``` +```bash bazel test //... ``` to test. -## Develop GUI. +## Develop GUI Checkout [the GUI Readme](gui/README.md)! ## Package -``` +```bash npm run package-mac ``` @@ -32,13 +32,13 @@ Make sure you have Xcode installed. To enable notarization, first run -``` +```bash security find-identity -p basic -v ``` to see the developer certificates (there should be Developer ID Application and Developer ID Installer). Make note of the ten character code at the end: this is the teamId. Then run -``` +```bash xcrun notarytool store-credentials --apple-id "name@example.com" --team-id "ABCD123456" --keychain ~/Library/Keychains/login.keychain-db ``` @@ -52,37 +52,30 @@ Let `.env` contain the environment variables specified by `helpers/scripts/packa Build: -``` +```bash bazel build //... ``` -Run normal daemon: +In one terminal, run normal daemon: -``` +```bash ./bazel-bin/daemon/daemon ``` -Run daemon 2: -Open a new terminal and run +In a new terminal, run a second daemon: -``` -export XDG_CONFIG_HOME=$HOME/.anysphere2/data -export XDG_RUNTIME_DIR=$HOME/.anysphere2/run -rm -rf ~/.anysphere2 && mkdir ~/.anysphere2 && ./bazel-bin/daemon/daemon -d "$HOME/.anysphere2/anysphere.sock" -c "$HOME/.anysphere2/config.json" +```bash +./wrap2.sh ./bazel-bin/daemon/daemon ``` -Next, in a third terminal, run +To connect to daemon 1, run: -``` -./bazel-bin/cli/asphr +```bash +./bazel-bin/cli/asphr [command] ``` -to connect to daemon 1, and in a fourth terminal, run +To connect to daemon 2, run: +```bash +./wrap2.sh ./bazel-bin/cli/asphr [command] ``` -export XDG_CONFIG_HOME=$HOME/.anysphere2/data -export XDG_RUNTIME_DIR=$HOME/.anysphere2/run -./bazel-bin/cli/asphr [command] -``` - -to connect to daemon 2. diff --git a/cli/as_cli.cc b/cli/as_cli.cc index 650e8948..c909793b 100644 --- a/cli/as_cli.cc +++ b/cli/as_cli.cc @@ -19,22 +19,22 @@ int main(int argc, char** argv) { static Friend::FriendMap kFriends_map_; - auto *const binary_name = argv[0]; - - auto help = StrCat("Usage: \n", - // register - binary_name, " register: {name}\n", - // init-friend - binary_name, " init-friend: {name}\n", - // TODO(unknown): add-friend must have an init-friend before - binary_name, " add-friend: {name} {key}\n", - // TODO(unknown): explain the options better. - binary_name, " (s | m | send | msg | message) {name}\n", - binary_name, " (get-friends | friends)\n", - // TODO(unknown): explain that -a is optional. - binary_name, " (inbox | i) [-a | -all]\n", binary_name, - " socket {address}\n", binary_name, " kill\n", binary_name, - " status\n"); + auto* const binary_name = argv[0]; + + auto help = StrCat( + "Usage: \n", + // register + binary_name, " register: {name}\n", + // init-friend + binary_name, " init-friend: {name}\n", + // TODO(unknown): add-friend must have an init-friend before + binary_name, " add-friend: {name} {key}\n", + // TODO(unknown): explain the options better. + binary_name, " (s | m | send | msg | message) {name}\n", binary_name, + " (get-friends | friends)\n", + // TODO(unknown): explain that -a is optional. + binary_name, " (inbox | i) [-a | -all]\n", binary_name, + " socket {address}\n", binary_name, " kill\n", binary_name, " status\n"); CommandLine cmd_line{argc, argv, help}; @@ -44,7 +44,7 @@ int main(int argc, char** argv) { cout << "Anysphere CLI." << endl << endl; cout << help << endl; return 0; - }; + } auto command = command_status.value(); @@ -197,10 +197,13 @@ int main(int argc, char** argv) { if (!status.ok()) { cout << "get status failed: " << status.error_message() << endl; return 0; - } cout << "Registered: " << (response.registered() ? "true" : "false") - << endl; - cout << "Release commit hash: " << response.release_hash() << endl; - + } + + cout << "Registered: " << (response.registered() ? "true" : "false") + << endl; + cout << "Release commit hash: " << response.release_hash() << endl; + cout << "Latency: " << response.latency_seconds() << endl; + } else if (command == "kill") { grpc::ClientContext context; asphrdaemon::KillRequest request; @@ -211,8 +214,9 @@ int main(int argc, char** argv) { if (!status.ok()) { cout << "kill failed: " << status.error_message() << endl; return 0; - } cout << "Successfully killed the daemon!" << endl; - + } + cout << "Successfully killed the daemon!" << endl; + } else if (command == "change-latency" || command == "cltcy") { auto latency_seconds = cmd_line.getArgument(2); if (!latency_seconds.ok()) { @@ -238,9 +242,9 @@ int main(int argc, char** argv) { if (!status.ok()) { cout << "change latency failed: " << status.error_message() << endl; return 0; - } cout << "Successfully changed latency to " << latency << " seconds!" - << endl; - + } + cout << "Successfully changed latency to " << latency << " seconds!" + << endl; } else { cout << "Unknown command: " << command << endl; diff --git a/cli/friend_struct.cc b/cli/friend_struct.cc index 409474a0..84caf3fb 100644 --- a/cli/friend_struct.cc +++ b/cli/friend_struct.cc @@ -18,8 +18,8 @@ auto Friend::generate_key(unique_ptr& stub) if (!status.ok()) { cout << "generate friend key failed: " << status.error_message() << endl; return absl::UnknownError("generate friend key failed"); - } return response.key(); - + } + return response.key(); } auto Friend::add(unique_ptr& stub, const string& key) @@ -38,8 +38,8 @@ auto Friend::add(unique_ptr& stub, const string& key) if (!status.ok()) { cout << "add friend failed: " << status.error_message() << endl; return absl::UnknownError("add friend failed"); - } return absl::OkStatus(); - + } + return absl::OkStatus(); } bool Friend::complete() const { return !name_is_empty(); } \ No newline at end of file diff --git a/cli/inbox.cc b/cli/inbox.cc index 4f5fb8ad..2ecd374b 100644 --- a/cli/inbox.cc +++ b/cli/inbox.cc @@ -20,10 +20,11 @@ void Inbox::update(unique_ptr& stub, vector> new_messages; grpc::ClientContext context; - asphrdaemon::GetAllMessagesRequest request; - asphrdaemon::GetAllMessagesResponse response; + asphrdaemon::GetMessagesRequest request; + request.set_filter(asphrdaemon::GetMessagesRequest::ALL); + asphrdaemon::GetMessagesResponse response; - grpc::Status status = stub->GetAllMessages(&context, request, &response); + grpc::Status status = stub->GetMessages(&context, request, &response); if (!status.ok()) { cout << "get all messages failed: " << status.error_message() << endl; @@ -38,4 +39,4 @@ void Inbox::update(unique_ptr& stub, } update(new_messages); -} \ No newline at end of file +} diff --git a/client_lib/client_lib.cc b/client_lib/client_lib.cc index e8d314bb..98415e64 100644 --- a/client_lib/client_lib.cc +++ b/client_lib/client_lib.cc @@ -40,10 +40,10 @@ extern constexpr size_t GUARANTEED_SINGLE_MESSAGE_SIZE = auto get_base_config_dir() noexcept(false) -> std::filesystem::path { // on Linux and on systems where XDG_CONFIG_HOME is defined, use it. std::filesystem::path anysphere_home; - auto *config_home_maybe = std::getenv("XDG_CONFIG_HOME"); + auto* config_home_maybe = std::getenv("XDG_CONFIG_HOME"); if (config_home_maybe == nullptr) { // if not defined, default to $HOME/.anysphere - auto *home = std::getenv("HOME"); + auto* home = std::getenv("HOME"); if (home == nullptr) { // don't know what to do now.... there is literally nothing we can do // without knowing the HOME directory. crash. @@ -84,7 +84,7 @@ auto get_gui_config_dir() noexcept(false) -> std::filesystem::path { // cli and gui processes. auto get_runtime_dir() noexcept(false) -> std::filesystem::path { std::filesystem::path runtime_home; - auto *runtime_home_maybe = std::getenv("XDG_RUNTIME_DIR"); + auto* runtime_home_maybe = std::getenv("XDG_RUNTIME_DIR"); if (runtime_home_maybe == nullptr) { // if not defined, there is no standard directory to put this in. // we therefore use the base config dir / run. @@ -115,7 +115,7 @@ auto get_socket_path() noexcept(false) -> std::filesystem::path { // this is only the default location. auto get_default_data_dir() noexcept(false) -> std::filesystem::path { std::filesystem::path data_home; - auto *data_home_maybe = std::getenv("XDG_DATA_HOME"); + auto* data_home_maybe = std::getenv("XDG_DATA_HOME"); if (data_home_maybe == nullptr) { // if not defined, we are using ~/.anysphere to store all data. // we therefore use the base config dir / run. diff --git a/daemon/config.cc b/daemon/config.cc index c1d12320..533b1a41 100644 --- a/daemon/config.cc +++ b/daemon/config.cc @@ -63,8 +63,7 @@ auto Config::server_address() -> std::string { Config::Config(const string& config_file_address) : Config(read_json_file(config_file_address), config_file_address) {} -Config::Config(const asphr::json& config_json_input, - string config_file_address) +Config::Config(const asphr::json& config_json_input, string config_file_address) : saved_file_address(std::move(config_file_address)), db_rows_(CLIENT_DB_ROWS), dummyMe("dummyMe", 0, "add_key", "", "", 0, false, 0, 0, 0, true), @@ -252,8 +251,7 @@ auto Config::remove_friend(const string& name) -> absl::Status { check_rep(); if (!friendTable.contains(name)) { - return {absl::StatusCode::kInvalidArgument, - "friend does not exist"}; + return {absl::StatusCode::kInvalidArgument, "friend does not exist"}; } friendTable.erase(name); @@ -290,13 +288,29 @@ auto Config::get_friend(const string& name) const -> absl::StatusOr { return friendTable.at(name); } -auto Config::update_friend(const Friend& f) -> void { +auto Config::update_friend(const string& name, const FriendUpdate& fs) -> void { const std::lock_guard l(config_mtx); check_rep(); - assert(friendTable.contains(f.name)); - friendTable.insert_or_assign(f.name, f); + assert(friendTable.contains(name)); + + auto old_friend = friendTable.at(name); + + auto new_friend = + Friend(name, fs.read_index.value_or(old_friend.read_index), + fs.add_key.value_or(old_friend.add_key), + fs.read_key.value_or(old_friend.read_key), + fs.write_key.value_or(old_friend.write_key), + fs.ack_index.value_or(old_friend.ack_index), + fs.enabled.value_or(old_friend.enabled), + fs.latest_ack_id.value_or(old_friend.latest_ack_id), + fs.latest_send_id.value_or(old_friend.latest_send_id), + fs.last_receive_id.value_or(old_friend.last_receive_id), + fs.dummy.value_or(old_friend.dummy)); + + friendTable.insert_or_assign(name, new_friend); + save(); check_rep(); @@ -369,7 +383,7 @@ auto Config::wait_until_killed_or_seconds(int seconds) -> bool { // private method; hence no check_rep, no lock auto Config::check_rep() const -> void { assert(!saved_file_address.empty()); - assert(data_dir != ""); + ASPHR_ASSERT_NEQ(data_dir, ""); assert(db_rows_ > 0); assert(latency_ >= 1); @@ -402,6 +416,9 @@ auto Config::check_rep() const -> void { // private method; hence, no check_rep, no lock auto Config::save() -> void { + // only save if rep is ok! + check_rep(); + asphr::json config_json; config_json["has_registered"] = has_registered_; config_json["data_dir"] = data_dir; @@ -434,5 +451,8 @@ auto Config::initialize_dummy_me() -> void { dummyMe = Friend("dummyMe", 0, "add_key", dummy_read_write_keys.first, dummy_read_write_keys.second, 0, false, 0, 0, 0, true); - save(); -} \ No newline at end of file + // don't save at the end because initialize_dummy_me is called before other + // things are initialized, so if we saved here we would save an invalid + // config. that's why we also don't have a check-rep here: check-rep is not + // guaranteed to pass! +} diff --git a/daemon/config.hpp b/daemon/config.hpp index c726e091..94f9afbc 100644 --- a/daemon/config.hpp +++ b/daemon/config.hpp @@ -33,7 +33,7 @@ struct RegistrationInfo { class Config { public: Config(const string& config_file_address); - Config(const asphr::json& config_json, string config_file_address); + Config(const asphr::json& config_json, string config_file_address); // precondition: friend_info.name is not in friendTable auto add_friend(const Friend& friend_info) -> void; @@ -41,7 +41,19 @@ class Config { auto friends() const -> vector; auto get_friend(const string& name) const -> asphr::StatusOr; // precondition: f.name is in friendTable - auto update_friend(const Friend& f) -> void; + struct FriendUpdate { + optional read_index = std::nullopt; + optional add_key = std::nullopt; + optional read_key = std::nullopt; + optional write_key = std::nullopt; + optional ack_index = std::nullopt; + optional enabled = std::nullopt; + optional latest_ack_id = std::nullopt; + optional latest_send_id = std::nullopt; + optional last_receive_id = std::nullopt; + optional dummy = std::nullopt; + }; + auto update_friend(const string& name, const FriendUpdate& f) -> void; auto has_space_for_friends() -> bool; auto num_enabled_friends() -> int; @@ -110,11 +122,12 @@ class Config { std::condition_variable kill_cv; bool kill_ = false; - // randomness source! NOT threadsafe, which is fine because we use monitor pattern + // randomness source! NOT threadsafe, which is fine because we use monitor + // pattern absl::BitGen rand_bitgen_; auto check_rep() const -> void; auto initialize_dummy_me() -> void; auto save() -> void; -}; \ No newline at end of file +}; diff --git a/daemon/crypto.cc b/daemon/crypto.cc index 826a541a..12e38775 100644 --- a/daemon/crypto.cc +++ b/daemon/crypto.cc @@ -16,7 +16,7 @@ auto Crypto::generate_keypair() -> std::pair { string(reinterpret_cast(secret_key), crypto_kx_SECRETKEYBYTES)}; } -auto Crypto::generate_friend_key(const string& my_public_key, int index) +auto Crypto::generate_friend_key(const string& my_public_key, int index) -> string { string public_key_b64; public_key_b64.resize(sodium_base64_ENCODED_LEN( diff --git a/daemon/crypto.hpp b/daemon/crypto.hpp index da0ecd7b..b1f6e710 100644 --- a/daemon/crypto.hpp +++ b/daemon/crypto.hpp @@ -33,7 +33,7 @@ class Crypto { // Generates a new keypair in the format . static auto generate_keypair() -> std::pair; - static auto generate_friend_key(const string& my_public_key, int index) + static auto generate_friend_key(const string& my_public_key, int index) -> string; auto decode_friend_key(const string& friend_key) const -> asphr::StatusOr>; diff --git a/daemon/daemon_rpc.cc b/daemon/daemon_rpc.cc index 991ca279..34f5fa00 100644 --- a/daemon/daemon_rpc.cc +++ b/daemon/daemon_rpc.cc @@ -7,14 +7,14 @@ #include "google/protobuf/util/time_util.h" -using namespace asphrdaemon; - using grpc::ServerContext; +using grpc::ServerWriter; using grpc::Status; -Status DaemonRpc::RegisterUser(ServerContext* context, - const RegisterUserRequest* registerUserRequest, - RegisterUserResponse* registerUserResponse) { +Status DaemonRpc::RegisterUser( + ServerContext* context, + const asphrdaemon::RegisterUserRequest* registerUserRequest, + asphrdaemon::RegisterUserResponse* registerUserResponse) { cout << "RegisterUser() called" << endl; if (config->has_registered()) { @@ -67,8 +67,9 @@ Status DaemonRpc::RegisterUser(ServerContext* context, } Status DaemonRpc::GetFriendList( - ServerContext* context, const GetFriendListRequest* getFriendListRequest, - GetFriendListResponse* getFriendListResponse) { + ServerContext* context, + const asphrdaemon::GetFriendListRequest* getFriendListRequest, + asphrdaemon::GetFriendListResponse* getFriendListResponse) { cout << "GetFriendList() called" << endl; if (!config->has_registered()) { @@ -87,8 +88,8 @@ Status DaemonRpc::GetFriendList( Status DaemonRpc::GenerateFriendKey( ServerContext* context, - const GenerateFriendKeyRequest* generateFriendKeyRequest, - GenerateFriendKeyResponse* generateFriendKeyResponse) { + const asphrdaemon::GenerateFriendKeyRequest* generateFriendKeyRequest, + asphrdaemon::GenerateFriendKeyResponse* generateFriendKeyResponse) { cout << "GenerateFriendKey() called" << endl; if (!config->has_registered()) { @@ -130,9 +131,10 @@ Status DaemonRpc::GenerateFriendKey( return Status::OK; } -Status DaemonRpc::AddFriend(ServerContext* context, - const AddFriendRequest* addFriendRequest, - AddFriendResponse* addFriendResponse) { +Status DaemonRpc::AddFriend( + ServerContext* context, + const asphrdaemon::AddFriendRequest* addFriendRequest, + asphrdaemon::AddFriendResponse* addFriendResponse) { cout << "AddFriend() called" << endl; cout << "name: " << addFriendRequest->name() << endl; @@ -149,8 +151,6 @@ Status DaemonRpc::AddFriend(ServerContext* context, "friend not found; call generatefriendkey first!"); } - auto friend_info = friend_info_status.value(); - auto decoded_friend_key = crypto.decode_friend_key(addFriendRequest->key()); if (!decoded_friend_key.ok()) { cout << "invalid friend key" << endl; @@ -162,19 +162,19 @@ Status DaemonRpc::AddFriend(ServerContext* context, auto [read_key, write_key] = crypto.derive_read_write_keys( config->registration_info().public_key, config->registration_info().private_key, friend_public_key); - friend_info.read_key = read_key; - friend_info.write_key = write_key; - friend_info.read_index = read_index; - friend_info.enabled = true; - config->update_friend(friend_info); + config->update_friend(addFriendRequest->name(), {.read_index = read_index, + .read_key = read_key, + .write_key = write_key, + .enabled = true}); return Status::OK; } -Status DaemonRpc::RemoveFriend(ServerContext* context, - const RemoveFriendRequest* removeFriendRequest, - RemoveFriendResponse* removeFriendResponse) { +Status DaemonRpc::RemoveFriend( + ServerContext* context, + const asphrdaemon::RemoveFriendRequest* removeFriendRequest, + asphrdaemon::RemoveFriendResponse* removeFriendResponse) { cout << "RemoveFriend() called" << endl; if (!config->has_registered()) { @@ -200,9 +200,10 @@ Status DaemonRpc::RemoveFriend(ServerContext* context, return Status::OK; } -Status DaemonRpc::SendMessage(ServerContext* context, - const SendMessageRequest* sendMessageRequest, - SendMessageResponse* sendMessageResponse) { +Status DaemonRpc::SendMessage( + ServerContext* context, + const asphrdaemon::SendMessageRequest* sendMessageRequest, + asphrdaemon::SendMessageResponse* sendMessageResponse) { cout << "SendMessage() called" << endl; if (!config->has_registered()) { @@ -235,21 +236,33 @@ Status DaemonRpc::SendMessage(ServerContext* context, return Status::OK; } -Status DaemonRpc::GetAllMessages( - ServerContext* context, const GetAllMessagesRequest* getAllMessagesRequest, - GetAllMessagesResponse* getAllMessagesResponse) { +Status DaemonRpc::GetMessages( + ServerContext* context, + const asphrdaemon::GetMessagesRequest* getMessagesRequest, + asphrdaemon::GetMessagesResponse* getMessagesResponse) { using TimeUtil = google::protobuf::util::TimeUtil; - cout << "GetAllMessages() called" << endl; + cout << "GetMessages() called" << endl; if (!config->has_registered()) { cout << "need to register first!" << endl; return Status(grpc::StatusCode::UNAUTHENTICATED, "not registered"); } - auto messages = msgstore->get_all_incoming_messages_sorted(); + auto filter = getMessagesRequest->filter(); + + vector messages; + + if (filter == asphrdaemon::GetMessagesRequest::ALL) { + messages = msgstore->get_all_incoming_messages_sorted(); + } else if (filter == asphrdaemon::GetMessagesRequest::NEW) { + messages = msgstore->get_new_incoming_messages_sorted(); + } else { + cout << "filter: INVALID" << endl; + return Status(grpc::StatusCode::INVALID_ARGUMENT, "invalid filter"); + } for (auto& m : messages) { - auto message_info = getAllMessagesResponse->add_messages(); + auto message_info = getMessagesResponse->add_messages(); auto baseMessage = message_info->mutable_m(); baseMessage->set_id(m.id); @@ -257,7 +270,7 @@ Status DaemonRpc::GetAllMessages( message_info->set_from(m.from); message_info->set_seen(m.seen); - // TODO: do this conversion not through strings.... + // TODO(arvid): do this conversion not through strings.... auto timestamp_str = absl::FormatTime(m.received_timestamp); auto timestamp = message_info->mutable_received_timestamp(); auto success = TimeUtil::FromString(timestamp_str, timestamp); @@ -270,36 +283,121 @@ Status DaemonRpc::GetAllMessages( return Status::OK; } -Status DaemonRpc::GetNewMessages( - ServerContext* context, const GetNewMessagesRequest* getNewMessagesRequest, - GetNewMessagesResponse* getNewMessagesResponse) { +// WARNING: this method is subtle. please take a moment to understand +// what's going on before modifying it. +Status DaemonRpc::GetMessagesStreamed( + ServerContext* context, + const asphrdaemon::GetMessagesRequest* getMessagesRequest, + ServerWriter* writer) { using TimeUtil = google::protobuf::util::TimeUtil; - cout << "GetNewMessages() called" << endl; + cout << "GetMessagesStreamed() called" << endl; if (!config->has_registered()) { cout << "need to register first!" << endl; return Status(grpc::StatusCode::UNAUTHENTICATED, "not registered"); } - auto messages = msgstore->get_new_incoming_messages_sorted(); + auto filter = getMessagesRequest->filter(); + + // the messages may not be added in the right order. so what we do is + // we wait until the last_mono_index has changed, and then get all messages + // from that point on. + // this needs to happen before we get all the messages below. + // + // the caller will always get *each message EXACTLY ONCE*, in the order of + // the mono index. *this may or may not correspond to the timestamp order.* + int last_mono_index; + { + std::lock_guard l(msgstore->add_cv_mtx); + last_mono_index = msgstore->last_mono_index; + } - for (auto& m : messages) { - auto message_info = getNewMessagesResponse->add_messages(); + { + vector messages; - auto baseMessage = message_info->mutable_m(); - baseMessage->set_id(m.id); - baseMessage->set_message(m.message); - message_info->set_from(m.from); - message_info->set_seen(m.seen); + if (filter == asphrdaemon::GetMessagesRequest::ALL) { + messages = msgstore->get_all_incoming_messages_sorted(); + } else if (filter == asphrdaemon::GetMessagesRequest::NEW) { + messages = msgstore->get_new_incoming_messages_sorted(); + } else { + cout << "filter: INVALID" << endl; + return Status(grpc::StatusCode::INVALID_ARGUMENT, "invalid filter"); + } - // TODO: do this conversion not through strings.... - auto timestamp_str = absl::FormatTime(m.received_timestamp); - auto timestamp = message_info->mutable_received_timestamp(); - auto success = TimeUtil::FromString(timestamp_str, timestamp); - if (!success) { - cout << "invalid timestamp" << endl; - return Status(grpc::StatusCode::UNKNOWN, "invalid timestamp"); + asphrdaemon::GetMessagesResponse response; + + for (auto& m : messages) { + // let last_mono_index be the maximum + last_mono_index = + m.mono_index > last_mono_index ? m.mono_index : last_mono_index; + + auto message_info = response.add_messages(); + + auto baseMessage = message_info->mutable_m(); + baseMessage->set_id(m.id); + baseMessage->set_message(m.message); + message_info->set_from(m.from); + message_info->set_seen(m.seen); + + // TODO(arvid): do this conversion not through strings.... + auto timestamp_str = absl::FormatTime(m.received_timestamp); + auto timestamp = message_info->mutable_received_timestamp(); + auto success = TimeUtil::FromString(timestamp_str, timestamp); + if (!success) { + cout << "invalid timestamp" << endl; + return Status(grpc::StatusCode::UNKNOWN, "invalid timestamp"); + } } + + writer->Write(response); + } + + // keep the connection open forever + while (!context->IsCancelled()) { + int last_mono_index_here; + { + std::unique_lock l(msgstore->add_cv_mtx); + msgstore->add_cv.wait_for(l, std::chrono::seconds(60 * 60), [&] { + return msgstore->last_mono_index != last_mono_index; + }); + if (msgstore->last_mono_index == last_mono_index) { + continue; // continue the loop to check if we are cancelled + } + last_mono_index_here = msgstore->last_mono_index; + } + + auto messages = + msgstore->get_incoming_messages_sorted_after(last_mono_index); + + asphrdaemon::GetMessagesResponse response; + + for (auto& m : messages) { + // let last_mono_index_here be the maximum + last_mono_index_here = m.mono_index > last_mono_index_here + ? m.mono_index + : last_mono_index_here; + + auto message_info = response.add_messages(); + + auto baseMessage = message_info->mutable_m(); + baseMessage->set_id(m.id); + baseMessage->set_message(m.message); + message_info->set_from(m.from); + message_info->set_seen(m.seen); + + // TODO(arvid): do this conversion not through strings.... + auto timestamp_str = absl::FormatTime(m.received_timestamp); + auto timestamp = message_info->mutable_received_timestamp(); + auto success = TimeUtil::FromString(timestamp_str, timestamp); + if (!success) { + cout << "invalid timestamp" << endl; + return Status(grpc::StatusCode::UNKNOWN, "invalid timestamp"); + } + } + + writer->Write(response); + + last_mono_index = last_mono_index_here; } return Status::OK; @@ -307,8 +405,8 @@ Status DaemonRpc::GetNewMessages( Status DaemonRpc::GetOutboxMessages( ServerContext* context, - const GetOutboxMessagesRequest* getOutboxMessagesRequest, - GetOutboxMessagesResponse* getOutboxMessagesResponse) { + const asphrdaemon::GetOutboxMessagesRequest* getOutboxMessagesRequest, + asphrdaemon::GetOutboxMessagesResponse* getOutboxMessagesResponse) { using TimeUtil = google::protobuf::util::TimeUtil; cout << "GetOutboxMessages() called" << endl; @@ -342,8 +440,8 @@ Status DaemonRpc::GetOutboxMessages( Status DaemonRpc::GetSentMessages( ServerContext* context, - const GetSentMessagesRequest* getSentMessagesRequest, - GetSentMessagesResponse* getSentMessagesResponse) { + const asphrdaemon::GetSentMessagesRequest* getSentMessagesRequest, + asphrdaemon::GetSentMessagesResponse* getSentMessagesResponse) { using TimeUtil = google::protobuf::util::TimeUtil; cout << "GetSentMessages() called" << endl; @@ -375,9 +473,10 @@ Status DaemonRpc::GetSentMessages( return Status::OK; } -Status DaemonRpc::MessageSeen(ServerContext* context, - const MessageSeenRequest* messageSeenRequest, - MessageSeenResponse* messageSeenResponse) { +Status DaemonRpc::MessageSeen( + ServerContext* context, + const asphrdaemon::MessageSeenRequest* messageSeenRequest, + asphrdaemon::MessageSeenResponse* messageSeenResponse) { cout << "MessageSeen() called" << endl; if (!config->has_registered()) { @@ -397,19 +496,22 @@ Status DaemonRpc::MessageSeen(ServerContext* context, } auto DaemonRpc::GetStatus(ServerContext* context, - const GetStatusRequest* getStatusRequest, - GetStatusResponse* getStatusResponse) -> Status { + const asphrdaemon::GetStatusRequest* getStatusRequest, + asphrdaemon::GetStatusResponse* getStatusResponse) + -> Status { cout << "GetStatus() called" << endl; getStatusResponse->set_registered(config->has_registered()); getStatusResponse->set_release_hash(RELEASE_COMMIT_HASH); + getStatusResponse->set_latency_seconds(config->get_latency_seconds()); return Status::OK; } -auto DaemonRpc::GetLatency(ServerContext* context, - const GetLatencyRequest* getLatencyRequest, - GetLatencyResponse* getLatencyResponse) -> Status { +auto DaemonRpc::GetLatency( + ServerContext* context, + const asphrdaemon::GetLatencyRequest* getLatencyRequest, + asphrdaemon::GetLatencyResponse* getLatencyResponse) -> Status { cout << "GetLatency() called" << endl; getLatencyResponse->set_latency_seconds(config->get_latency_seconds()); @@ -439,8 +541,9 @@ auto DaemonRpc::ChangeLatency( return Status::OK; } -auto DaemonRpc::Kill(ServerContext* context, const KillRequest* killRequest, - KillResponse* killResponse) -> Status { +auto DaemonRpc::Kill(ServerContext* context, + const asphrdaemon::KillRequest* killRequest, + asphrdaemon::KillResponse* killResponse) -> Status { cout << "Kill() called" << endl; config->kill(); cout << "Will kill daemon ASAP" << endl; diff --git a/daemon/daemon_rpc.hpp b/daemon/daemon_rpc.hpp index d7597e6a..a28bfe9d 100644 --- a/daemon/daemon_rpc.hpp +++ b/daemon/daemon_rpc.hpp @@ -49,15 +49,15 @@ class DaemonRpc final : public asphrdaemon::Daemon::Service { const asphrdaemon::SendMessageRequest* sendMessageRequest, asphrdaemon::SendMessageResponse* sendMessageResponse) override; - grpc::Status GetAllMessages( + grpc::Status GetMessages( grpc::ServerContext* context, - const asphrdaemon::GetAllMessagesRequest* getAllMessagesRequest, - asphrdaemon::GetAllMessagesResponse* getAllMessagesResponse) override; + const asphrdaemon::GetMessagesRequest* getMessagesRequest, + asphrdaemon::GetMessagesResponse* getMessagesResponse) override; - grpc::Status GetNewMessages( + grpc::Status GetMessagesStreamed( grpc::ServerContext* context, - const asphrdaemon::GetNewMessagesRequest* getNewMessagesRequest, - asphrdaemon::GetNewMessagesResponse* getNewMessagesResponse) override; + const asphrdaemon::GetMessagesRequest* request, + grpc::ServerWriter* writer) override; grpc::Status GetOutboxMessages( grpc::ServerContext* context, diff --git a/daemon/friend.hpp b/daemon/friend.hpp index bd37a5e3..981d565c 100644 --- a/daemon/friend.hpp +++ b/daemon/friend.hpp @@ -44,6 +44,9 @@ class Friend { check_rep(); } + + // Be very careful when changing the order of these parameters... please + // search for all usages. Friend(const string& name, const int read_index, const string& add_key, const string& read_key, const string& write_key, const int ack_index, const bool enabled, const uint32_t latest_ack_id, diff --git a/daemon/inbox.cc b/daemon/inbox.cc index 7a1beaf2..ee2d171d 100644 --- a/daemon/inbox.cc +++ b/daemon/inbox.cc @@ -113,12 +113,12 @@ auto Inbox::update_ack_from_friend(Config& config, pir_value_t& pir_acks, if (!ack.ok()) { continue; } - if (ack.value() >= friend_info.latest_ack_id) { - auto new_friend_info = friend_info; - new_friend_info.latest_ack_id = ack.value(); - config.update_friend(new_friend_info); - cout << "updating friend " << new_friend_info.name << " with ack id " - << new_friend_info.latest_ack_id << endl; + if (ack.value() > friend_info.latest_ack_id) { + config.update_friend(friend_info.name, {.latest_ack_id = ack.value()}); + cout << "updating friend " << friend_info.name << " with ack id " + << ack.value() << endl; + } else if (ack.value() == friend_info.latest_ack_id) { + // everything is fine :))) } else { cout << "something weird is going on.... ACKing is older than latest ack " "id. look into this" @@ -186,13 +186,11 @@ auto Inbox::receive_message(FastPIRClient& client, Config& config, // only ACK this message if it is exactly the next ID we expect. otherwise, we // still need to await more messages if (message.id() == friend_info.last_receive_id + 1) { - auto new_friend_info = friend_info; - new_friend_info.last_receive_id = message.id(); // TODO: update the last_receive_id atomically with adding the message to // the inbox. in particular, we need to guard this update as well as the // later inbox update with a lock on the config (not inbox because it is not // threadsafe). - config.update_friend(new_friend_info); + config.update_friend(friend_info.name, {.last_receive_id = message.id()}); // friend_info has been updated! auto friend_info_status = config.get_friend(friend_info.name); if (!friend_info_status.ok()) { diff --git a/daemon/msgstore.cc b/daemon/msgstore.cc index d6655e76..1ca11676 100644 --- a/daemon/msgstore.cc +++ b/daemon/msgstore.cc @@ -1,3 +1,7 @@ +// +// Copyright 2022 Anysphere, Inc. +// SPDX-License-Identifier: GPL-3.0-only +// #include "msgstore.hpp" @@ -7,6 +11,7 @@ auto IncomingMessage::to_json() const -> asphr::json { {"message", message}, {"from", from}, {"received_timestamp", absl::FormatTime(received_timestamp)}, + {"mono_index", mono_index}, {"seen", seen}, }; } @@ -23,6 +28,7 @@ auto IncomingMessage::from_json(const asphr::json& j) -> IncomingMessage { throw std::runtime_error("error parsing time: " + err); } msg.seen = j.at("seen").get(); + msg.mono_index = j.at("mono_index").get(); return msg; } @@ -58,7 +64,9 @@ auto read_msgstore_json(const string& file_address) -> asphr::json { std::filesystem::path(file_address).parent_path().u8string(); std::filesystem::create_directories(dir_path); cout << "creating new msgstore asphr::json!" << endl; - asphr::json j = {{"incoming", {}}, {"outgoing", {}}}; + asphr::json j = {{"incoming", asphr::json::array()}, + {"outgoing", asphr::json::array()}, + {"last_mono_index", 0}}; std::ofstream o(file_address); o << std::setw(4) << j.dump(4) << std::endl; } @@ -71,7 +79,7 @@ Msgstore::Msgstore(const string& file_address, shared_ptr config) Msgstore::Msgstore(const asphr::json& serialized_json, const string& file_address, shared_ptr config) - : saved_file_address(file_address), config(config) { + : last_mono_index(0), saved_file_address(file_address), config(config) { for (auto& messageJson : serialized_json.at("incoming")) { auto message = IncomingMessage::from_json(messageJson); incoming.push_back(message); @@ -80,19 +88,24 @@ Msgstore::Msgstore(const asphr::json& serialized_json, auto message = OutgoingMessage::from_json(messageJson); outgoing.push_back(message); } + if (serialized_json.contains("last_mono_index")) { + last_mono_index = serialized_json.at("last_mono_index").get(); + } check_rep(); } auto Msgstore::save() noexcept(false) -> void { check_rep(); - asphr::json j = {{"incoming", {}}, {"outgoing", {}}}; + asphr::json j = {{"incoming", asphr::json::array()}, + {"outgoing", asphr::json::array()}}; for (auto& m : incoming) { j.at("incoming").push_back(m.to_json()); } for (auto& m : outgoing) { j.at("outgoing").push_back(m.to_json()); } + j["last_mono_index"] = last_mono_index; std::ofstream o(saved_file_address); o << std::setw(4) << j.dump(4) << std::endl; check_rep(); @@ -113,12 +126,12 @@ auto Msgstore::add_outgoing_message(const string& to, const string& message) << "; ignoring message" << endl; return f_status.status(); } + auto friend_info = f_status.value(); - const auto id = message_id(false, to, f_status.value().latest_send_id); + const auto id = message_id(false, to, friend_info.latest_send_id); - auto new_friend = f_status.value(); - new_friend.latest_send_id++; - config->update_friend(new_friend); + config->update_friend(friend_info.name, + {.latest_send_id = friend_info.latest_send_id + 1}); OutgoingMessage outgoing_message{{id, message}, to, absl::Now(), false}; outgoing.push_back(outgoing_message); @@ -161,13 +174,23 @@ auto Msgstore::add_incoming_message(int sequence_number, const string& from, check_rep(); + auto mono_index = last_mono_index + 1; + const auto id = message_id(true, from, sequence_number); - IncomingMessage incoming_message{{id, message}, from, absl::Now(), false}; + IncomingMessage incoming_message{ + {id, message}, from, absl::Now(), false, mono_index}; incoming.push_back(incoming_message); save(); check_rep(); + + // only after everything is committed (i.e. saved), we can notify the add_cv + { + std::lock_guard l(add_cv_mtx); + last_mono_index = mono_index; + add_cv.notify_all(); + } } auto Msgstore::mark_message_as_seen(const string& id) -> asphr::Status { @@ -191,6 +214,21 @@ auto Msgstore::mark_message_as_seen(const string& id) -> asphr::Status { return absl::NotFoundError("id not found"); } +auto Msgstore::get_incoming_message_by_id(const string& id) + -> asphr::StatusOr { + const std::lock_guard l(msgstore_mtx); + + check_rep(); + + for (auto& m : incoming) { + if (m.id == id) { + return m; + } + } + + return absl::NotFoundError("id not found"); +} + // TODO: we want some kind of message querying interface here..... maybe we // should just expose the raw SQL??? otherwise we kinda need to invent our own // querying system that we then parse into SQL which seems a little @@ -212,6 +250,27 @@ auto Msgstore::get_all_incoming_messages_sorted() -> vector { }); return sorted_incoming; } + +auto Msgstore::get_incoming_messages_sorted_after(int after_mono_index) + -> vector { + const std::lock_guard l(msgstore_mtx); + + check_rep(); + + vector sorted_incoming; + sorted_incoming.reserve(incoming.size()); + for (auto& m : incoming) { + if (m.mono_index > after_mono_index) { + sorted_incoming.push_back(m); + } + } + std::sort(sorted_incoming.begin(), sorted_incoming.end(), + [](const IncomingMessage& a, const IncomingMessage& b) { + return a.received_timestamp > b.received_timestamp; + }); + return sorted_incoming; +} + auto Msgstore::get_new_incoming_messages_sorted() -> vector { const std::lock_guard l(msgstore_mtx); @@ -246,7 +305,7 @@ auto Msgstore::get_undelivered_outgoing_messages_sorted() } std::sort(undelivered_outgoing.begin(), undelivered_outgoing.end(), [](const OutgoingMessage& a, const OutgoingMessage& b) { - return a.written_timestamp < b.written_timestamp; + return a.written_timestamp > b.written_timestamp; }); return undelivered_outgoing; } @@ -266,7 +325,7 @@ auto Msgstore::get_delivered_outgoing_messages_sorted() } std::sort(delivered_outgoing.begin(), delivered_outgoing.end(), [](const OutgoingMessage& a, const OutgoingMessage& b) { - return a.written_timestamp < b.written_timestamp; + return a.written_timestamp > b.written_timestamp; }); return delivered_outgoing; } @@ -289,5 +348,7 @@ auto Msgstore::check_rep() const -> void { for (const auto& m : outgoing) { ids.insert(m.id); } - assert(ids.size() == incoming.size() + outgoing.size()); + ASPHR_ASSERT_EQ_MSG(ids.size(), incoming.size() + outgoing.size(), + "incoming size: " << incoming.size() << " outgoing size: " + << outgoing.size()); } \ No newline at end of file diff --git a/daemon/msgstore.hpp b/daemon/msgstore.hpp index 99a2e334..34948f6e 100644 --- a/daemon/msgstore.hpp +++ b/daemon/msgstore.hpp @@ -1,3 +1,8 @@ +// +// Copyright 2022 Anysphere, Inc. +// SPDX-License-Identifier: GPL-3.0-only +// + #pragma once #include "asphr/asphr.hpp" @@ -14,6 +19,11 @@ struct IncomingMessage : BaseMessage { // is NOT when the message was sent. absl::Time received_timestamp; bool seen; + // mono_index is a monotonically increasing index for each message, + // in the order they were *added to the msgstore*. note that this is + // not necessarily the same order as the timestamps the messages were + // received in + int mono_index; auto to_json() const -> asphr::json; static auto from_json(const asphr::json& j) -> IncomingMessage; @@ -58,6 +68,9 @@ class Msgstore { const string& message) -> void; auto mark_message_as_seen(const string& id) -> asphr::Status; + auto get_incoming_message_by_id(const string& id) + -> asphr::StatusOr; + // TODO: we want some kind of message querying interface here..... maybe we // should just expose the raw SQL??? otherwise we kinda need to invent our own // querying system that we then parse into SQL which seems a little @@ -65,10 +78,21 @@ class Msgstore { // received auto get_all_incoming_messages_sorted() -> vector; auto get_new_incoming_messages_sorted() -> vector; + // i.e., calling this with v[0].mono_index if v was previous response will + // result in non-overlap + auto get_incoming_messages_sorted_after(int after_mono_index) + -> vector; + // sorted means newest to oldest auto get_undelivered_outgoing_messages_sorted() -> vector; auto get_delivered_outgoing_messages_sorted() -> vector; + // the following three are for notifying someone when an incoming message has + // been added + std::mutex add_cv_mtx; + std::condition_variable add_cv; + int last_mono_index; + private: // TODO: remove this mutex! eventually, all data should only ever be read and // written to the database on every call, and never stored in memory. diff --git a/daemon/transmitter.cc b/daemon/transmitter.cc index fcef0068..7dccc60c 100644 --- a/daemon/transmitter.cc +++ b/daemon/transmitter.cc @@ -118,6 +118,9 @@ auto Transmitter::send_messages() -> void { auto undelivered_messages = msgstore->get_undelivered_outgoing_messages_sorted(); + // we want to send messages in chronological order, not reverse chronological + // order hence, we need to reverse this array + std::reverse(undelivered_messages.begin(), undelivered_messages.end()); auto authentication_token = config->registration_info().authentication_token; diff --git a/gui/README.md b/gui/README.md index 9d92c62d..f4abdf93 100644 --- a/gui/README.md +++ b/gui/README.md @@ -7,8 +7,9 @@ ## Storybook To start the storybook of our components: -``` + +```bash npm run storybook ``` -In general, if you build a component, you should be able to see it in the storybook! Even better, if you can interact with it with kinda real, and it has tests! +In general, if you build a component, you should be able to see it in the storybook! Even better, if you can interact with it with kinda real, and it has tests! diff --git a/gui/assets/pkg-scripts/postinstall b/gui/assets/pkg-scripts/postinstall index d9335753..10d0b23b 100755 --- a/gui/assets/pkg-scripts/postinstall +++ b/gui/assets/pkg-scripts/postinstall @@ -9,30 +9,31 @@ # here we set up the daemon! # we also link the CLI to /usr/local/bin -create_user_dir () { - install -d -o "$USER" -g "$(id -g "$USER")" "$1" +create_user_dir() { + install -d -o "$USER" -g "$(id -g "$USER")" "$1" } LOG_DIR="" # set to XDG_CACHE_HOME/logs if it exists, otherwise use .anysphere directory as always if [ -d "${XDG_CACHE_HOME}" ]; then - LOG_DIR="${XDG_CACHE_HOME}/anysphere/logs" - create_user_dir "${XDG_CACHE_HOME}/anysphere" - create_user_dir "${XDG_CACHE_HOME}/anysphere/logs" + LOG_DIR="${XDG_CACHE_HOME}/anysphere/logs" + create_user_dir "${XDG_CACHE_HOME}/anysphere" + create_user_dir "${XDG_CACHE_HOME}/anysphere/logs" else - LOG_DIR="${HOME}/.anysphere/cache/logs" - create_user_dir "${HOME}/.anysphere" - create_user_dir "${HOME}/.anysphere/cache" - create_user_dir "${HOME}/.anysphere/cache/logs" + LOG_DIR="${HOME}/.anysphere/cache/logs" + create_user_dir "${HOME}/.anysphere" + create_user_dir "${HOME}/.anysphere/cache" + create_user_dir "${HOME}/.anysphere/cache/logs" fi -exec 2>&1 > "$LOG_DIR"/postinstall.log +exec 2>&1 >"$LOG_DIR"/postinstall.log echo "hello from anysphere postinstall script!" INSTALL_DIR=$2 -PLIST="$(cat << EOF +PLIST="$( + cat < @@ -58,11 +59,11 @@ EOF uid=$(id -u "$USER") echo "user: $USER, uid: $uid" PLIST_PATH="/Library/LaunchAgents/co.anysphere.anysphered.plist" -echo "$PLIST" > "$PLIST_PATH" +echo "$PLIST" >"$PLIST_PATH" chmod 644 "$PLIST_PATH" # if already existed, unload launchctl asuser "$uid" launchctl unload "$PLIST_PATH" launchctl asuser "$uid" launchctl load "$PLIST_PATH" mkdir -p /usr/local/bin -ln -sf "$INSTALL_DIR/Anysphere.app/Contents/Resources/anysphere" /usr/local/bin/anysphere \ No newline at end of file +ln -sf "$INSTALL_DIR/Anysphere.app/Contents/Resources/anysphere" /usr/local/bin/anysphere diff --git a/gui/assets/pkg-scripts/preinstall b/gui/assets/pkg-scripts/preinstall index c403fbb2..6bd3dc2a 100755 --- a/gui/assets/pkg-scripts/preinstall +++ b/gui/assets/pkg-scripts/preinstall @@ -9,23 +9,23 @@ # here, we should check if a current version exists, and clean it up if it does. # for example, we should stop the daemon if it is running. -create_user_dir () { - install -d -o "$USER" -g "$(id -g "$USER")" "$1" +create_user_dir() { + install -d -o "$USER" -g "$(id -g "$USER")" "$1" } LOG_DIR="" # set to XDG_CACHE_HOME/logs if it exists, otherwise use .anysphere directory as always if [ -d "${XDG_CACHE_HOME}" ]; then - LOG_DIR="${XDG_CACHE_HOME}/anysphere/logs" - create_user_dir "${XDG_CACHE_HOME}/anysphere" - create_user_dir "${XDG_CACHE_HOME}/anysphere/logs" + LOG_DIR="${XDG_CACHE_HOME}/anysphere/logs" + create_user_dir "${XDG_CACHE_HOME}/anysphere" + create_user_dir "${XDG_CACHE_HOME}/anysphere/logs" else - LOG_DIR="${HOME}/.anysphere/cache/logs" - create_user_dir "${HOME}/.anysphere" - create_user_dir "${HOME}/.anysphere/cache" - create_user_dir "${HOME}/.anysphere/cache/logs" + LOG_DIR="${HOME}/.anysphere/cache/logs" + create_user_dir "${HOME}/.anysphere" + create_user_dir "${HOME}/.anysphere/cache" + create_user_dir "${HOME}/.anysphere/cache/logs" fi -exec 2>&1 > "$LOG_DIR"/preinstall.log +exec 2>&1 >"$LOG_DIR"/preinstall.log -echo "hello from anysphere preinstall script!" \ No newline at end of file +echo "hello from anysphere preinstall script!" diff --git a/gui/generate-icons.sh b/gui/generate-icons.sh index 1b878430..1a187014 100755 --- a/gui/generate-icons.sh +++ b/gui/generate-icons.sh @@ -12,19 +12,19 @@ rm -rf assets/icons rm -rf assets/icons.iconset mkdir -p assets/icons.iconset -sips -z 16 16 "$INPUT_FILE" --out "assets/icons.iconset/icon_16x16.png" -sips -z 32 32 "$INPUT_FILE" --out "assets/icons.iconset/icon_16x16@2x.png" -sips -z 32 32 "$INPUT_FILE" --out "assets/icons.iconset/icon_32x32.png" -sips -z 64 64 "$INPUT_FILE" --out "assets/icons.iconset/icon_32x32@2x.png" -sips -z 128 128 "$INPUT_FILE" --out "assets/icons.iconset/icon_128x128.png" -sips -z 256 256 "$INPUT_FILE" --out "assets/icons.iconset/icon_128x128@2x.png" -sips -z 256 256 "$INPUT_FILE" --out "assets/icons.iconset/icon_256x256.png" -sips -z 512 512 "$INPUT_FILE" --out "assets/icons.iconset/icon_256x256@2x.png" -sips -z 512 512 "$INPUT_FILE" --out "assets/icons.iconset/icon_512x512.png" +sips -z 16 16 "$INPUT_FILE" --out "assets/icons.iconset/icon_16x16.png" +sips -z 32 32 "$INPUT_FILE" --out "assets/icons.iconset/icon_16x16@2x.png" +sips -z 32 32 "$INPUT_FILE" --out "assets/icons.iconset/icon_32x32.png" +sips -z 64 64 "$INPUT_FILE" --out "assets/icons.iconset/icon_32x32@2x.png" +sips -z 128 128 "$INPUT_FILE" --out "assets/icons.iconset/icon_128x128.png" +sips -z 256 256 "$INPUT_FILE" --out "assets/icons.iconset/icon_128x128@2x.png" +sips -z 256 256 "$INPUT_FILE" --out "assets/icons.iconset/icon_256x256.png" +sips -z 512 512 "$INPUT_FILE" --out "assets/icons.iconset/icon_256x256@2x.png" +sips -z 512 512 "$INPUT_FILE" --out "assets/icons.iconset/icon_512x512.png" iconutil -c icns -o assets/icon.icns assets/icons.iconset mv assets/icons.iconset assets/icons cp assets/icons/icon_256x256.png assets/icon.png -cp assets/icons/icon_256x256.png assets/icon.ico \ No newline at end of file +cp assets/icons/icon_256x256.png assets/icon.ico diff --git a/gui/package.json b/gui/package.json index 97d3ab82..a575ddd0 100644 --- a/gui/package.json +++ b/gui/package.json @@ -1,7 +1,7 @@ { "name": "anysphere", "scripts": { - "update": "cd ../../asphr && bazel build //schema:daemon_js_grpc && cd ../client/gui && rm -rf src/daemon && cp -r ../../asphr/bazel-bin/schema/daemon_js_grpc_pb src/daemon", + "update": "cd ../../asphr && bazel build //schema:daemon_js_grpc && cd ../client/gui && rm -rf src/daemon && cp -r ../../asphr/bazel-bin/schema/daemon_js_grpc_pb src/daemon && for f in src/daemon/**/*; do echo -e \"// @generated\\n\\n$(cat ${f})\" > ${f}2; mv -f ${f}2 ${f}; echo 'this package.json file is @not-generated' > /dev/null; done;", "build-binaries-mac-arm64": "rm -rf binaries && bazel build //:asphr-release -c opt && mkdir binaries && cp $(bazel info output_path)/darwin_arm64-opt/bin/cli/asphr binaries/anysphere && cp $(bazel info output_path)/darwin_arm64-opt/bin/daemon/daemon binaries/anysphered", "build-binaries-mac-x86_64": "rm -rf binaries && arch --x86_64 /usr/local/bin/bazel build //:asphr-release -c opt && mkdir binaries && cp $(bazel info output_path)/darwin-opt/bin/cli/asphr binaries/anysphere && cp $(bazel info output_path)/darwin-opt/bin/daemon/daemon binaries/anysphered", "build": "concurrently \"npm run build:main\" \"npm run build:renderer\"", diff --git a/gui/src/daemon/schema/daemon_grpc_pb.d.ts b/gui/src/daemon/schema/daemon_grpc_pb.d.ts old mode 100755 new mode 100644 index 159c0f8b..d41728f2 --- a/gui/src/daemon/schema/daemon_grpc_pb.d.ts +++ b/gui/src/daemon/schema/daemon_grpc_pb.d.ts @@ -1,3 +1,5 @@ +// @generated + // GENERATED CODE -- DO NOT EDIT! // package: asphrdaemon @@ -13,8 +15,8 @@ interface IDaemonService extends grpc.ServiceDefinition; removeFriend: grpc.MethodDefinition; sendMessage: grpc.MethodDefinition; - getAllMessages: grpc.MethodDefinition; - getNewMessages: grpc.MethodDefinition; + getMessages: grpc.MethodDefinition; + getMessagesStreamed: grpc.MethodDefinition; getOutboxMessages: grpc.MethodDefinition; getSentMessages: grpc.MethodDefinition; messageSeen: grpc.MethodDefinition; @@ -33,8 +35,8 @@ export interface IDaemonServer extends grpc.UntypedServiceImplementation { addFriend: grpc.handleUnaryCall; removeFriend: grpc.handleUnaryCall; sendMessage: grpc.handleUnaryCall; - getAllMessages: grpc.handleUnaryCall; - getNewMessages: grpc.handleUnaryCall; + getMessages: grpc.handleUnaryCall; + getMessagesStreamed: grpc.handleServerStreamingCall; getOutboxMessages: grpc.handleUnaryCall; getSentMessages: grpc.handleUnaryCall; messageSeen: grpc.handleUnaryCall; @@ -64,12 +66,11 @@ export class DaemonClient extends grpc.Client { sendMessage(argument: schema_daemon_pb.SendMessageRequest, callback: grpc.requestCallback): grpc.ClientUnaryCall; sendMessage(argument: schema_daemon_pb.SendMessageRequest, metadataOrOptions: grpc.Metadata | grpc.CallOptions | null, callback: grpc.requestCallback): grpc.ClientUnaryCall; sendMessage(argument: schema_daemon_pb.SendMessageRequest, metadata: grpc.Metadata | null, options: grpc.CallOptions | null, callback: grpc.requestCallback): grpc.ClientUnaryCall; - getAllMessages(argument: schema_daemon_pb.GetAllMessagesRequest, callback: grpc.requestCallback): grpc.ClientUnaryCall; - getAllMessages(argument: schema_daemon_pb.GetAllMessagesRequest, metadataOrOptions: grpc.Metadata | grpc.CallOptions | null, callback: grpc.requestCallback): grpc.ClientUnaryCall; - getAllMessages(argument: schema_daemon_pb.GetAllMessagesRequest, metadata: grpc.Metadata | null, options: grpc.CallOptions | null, callback: grpc.requestCallback): grpc.ClientUnaryCall; - getNewMessages(argument: schema_daemon_pb.GetNewMessagesRequest, callback: grpc.requestCallback): grpc.ClientUnaryCall; - getNewMessages(argument: schema_daemon_pb.GetNewMessagesRequest, metadataOrOptions: grpc.Metadata | grpc.CallOptions | null, callback: grpc.requestCallback): grpc.ClientUnaryCall; - getNewMessages(argument: schema_daemon_pb.GetNewMessagesRequest, metadata: grpc.Metadata | null, options: grpc.CallOptions | null, callback: grpc.requestCallback): grpc.ClientUnaryCall; + getMessages(argument: schema_daemon_pb.GetMessagesRequest, callback: grpc.requestCallback): grpc.ClientUnaryCall; + getMessages(argument: schema_daemon_pb.GetMessagesRequest, metadataOrOptions: grpc.Metadata | grpc.CallOptions | null, callback: grpc.requestCallback): grpc.ClientUnaryCall; + getMessages(argument: schema_daemon_pb.GetMessagesRequest, metadata: grpc.Metadata | null, options: grpc.CallOptions | null, callback: grpc.requestCallback): grpc.ClientUnaryCall; + getMessagesStreamed(argument: schema_daemon_pb.GetMessagesRequest, metadataOrOptions?: grpc.Metadata | grpc.CallOptions | null): grpc.ClientReadableStream; + getMessagesStreamed(argument: schema_daemon_pb.GetMessagesRequest, metadata?: grpc.Metadata | null, options?: grpc.CallOptions | null): grpc.ClientReadableStream; getOutboxMessages(argument: schema_daemon_pb.GetOutboxMessagesRequest, callback: grpc.requestCallback): grpc.ClientUnaryCall; getOutboxMessages(argument: schema_daemon_pb.GetOutboxMessagesRequest, metadataOrOptions: grpc.Metadata | grpc.CallOptions | null, callback: grpc.requestCallback): grpc.ClientUnaryCall; getOutboxMessages(argument: schema_daemon_pb.GetOutboxMessagesRequest, metadata: grpc.Metadata | null, options: grpc.CallOptions | null, callback: grpc.requestCallback): grpc.ClientUnaryCall; diff --git a/gui/src/daemon/schema/daemon_grpc_pb.js b/gui/src/daemon/schema/daemon_grpc_pb.js old mode 100755 new mode 100644 index 41199fe3..79deeaad --- a/gui/src/daemon/schema/daemon_grpc_pb.js +++ b/gui/src/daemon/schema/daemon_grpc_pb.js @@ -1,3 +1,5 @@ +// @generated + // GENERATED CODE -- DO NOT EDIT! 'use strict'; @@ -71,28 +73,6 @@ function deserialize_asphrdaemon_GenerateFriendKeyResponse(buffer_arg) { return schema_daemon_pb.GenerateFriendKeyResponse.deserializeBinary(new Uint8Array(buffer_arg)); } -function serialize_asphrdaemon_GetAllMessagesRequest(arg) { - if (!(arg instanceof schema_daemon_pb.GetAllMessagesRequest)) { - throw new Error('Expected argument of type asphrdaemon.GetAllMessagesRequest'); - } - return Buffer.from(arg.serializeBinary()); -} - -function deserialize_asphrdaemon_GetAllMessagesRequest(buffer_arg) { - return schema_daemon_pb.GetAllMessagesRequest.deserializeBinary(new Uint8Array(buffer_arg)); -} - -function serialize_asphrdaemon_GetAllMessagesResponse(arg) { - if (!(arg instanceof schema_daemon_pb.GetAllMessagesResponse)) { - throw new Error('Expected argument of type asphrdaemon.GetAllMessagesResponse'); - } - return Buffer.from(arg.serializeBinary()); -} - -function deserialize_asphrdaemon_GetAllMessagesResponse(buffer_arg) { - return schema_daemon_pb.GetAllMessagesResponse.deserializeBinary(new Uint8Array(buffer_arg)); -} - function serialize_asphrdaemon_GetFriendListRequest(arg) { if (!(arg instanceof schema_daemon_pb.GetFriendListRequest)) { throw new Error('Expected argument of type asphrdaemon.GetFriendListRequest'); @@ -137,26 +117,26 @@ function deserialize_asphrdaemon_GetLatencyResponse(buffer_arg) { return schema_daemon_pb.GetLatencyResponse.deserializeBinary(new Uint8Array(buffer_arg)); } -function serialize_asphrdaemon_GetNewMessagesRequest(arg) { - if (!(arg instanceof schema_daemon_pb.GetNewMessagesRequest)) { - throw new Error('Expected argument of type asphrdaemon.GetNewMessagesRequest'); +function serialize_asphrdaemon_GetMessagesRequest(arg) { + if (!(arg instanceof schema_daemon_pb.GetMessagesRequest)) { + throw new Error('Expected argument of type asphrdaemon.GetMessagesRequest'); } return Buffer.from(arg.serializeBinary()); } -function deserialize_asphrdaemon_GetNewMessagesRequest(buffer_arg) { - return schema_daemon_pb.GetNewMessagesRequest.deserializeBinary(new Uint8Array(buffer_arg)); +function deserialize_asphrdaemon_GetMessagesRequest(buffer_arg) { + return schema_daemon_pb.GetMessagesRequest.deserializeBinary(new Uint8Array(buffer_arg)); } -function serialize_asphrdaemon_GetNewMessagesResponse(arg) { - if (!(arg instanceof schema_daemon_pb.GetNewMessagesResponse)) { - throw new Error('Expected argument of type asphrdaemon.GetNewMessagesResponse'); +function serialize_asphrdaemon_GetMessagesResponse(arg) { + if (!(arg instanceof schema_daemon_pb.GetMessagesResponse)) { + throw new Error('Expected argument of type asphrdaemon.GetMessagesResponse'); } return Buffer.from(arg.serializeBinary()); } -function deserialize_asphrdaemon_GetNewMessagesResponse(buffer_arg) { - return schema_daemon_pb.GetNewMessagesResponse.deserializeBinary(new Uint8Array(buffer_arg)); +function deserialize_asphrdaemon_GetMessagesResponse(buffer_arg) { + return schema_daemon_pb.GetMessagesResponse.deserializeBinary(new Uint8Array(buffer_arg)); } function serialize_asphrdaemon_GetOutboxMessagesRequest(arg) { @@ -403,27 +383,27 @@ var DaemonService = exports.DaemonService = { responseSerialize: serialize_asphrdaemon_SendMessageResponse, responseDeserialize: deserialize_asphrdaemon_SendMessageResponse, }, - getAllMessages: { - path: '/asphrdaemon.Daemon/GetAllMessages', + getMessages: { + path: '/asphrdaemon.Daemon/GetMessages', requestStream: false, responseStream: false, - requestType: schema_daemon_pb.GetAllMessagesRequest, - responseType: schema_daemon_pb.GetAllMessagesResponse, - requestSerialize: serialize_asphrdaemon_GetAllMessagesRequest, - requestDeserialize: deserialize_asphrdaemon_GetAllMessagesRequest, - responseSerialize: serialize_asphrdaemon_GetAllMessagesResponse, - responseDeserialize: deserialize_asphrdaemon_GetAllMessagesResponse, + requestType: schema_daemon_pb.GetMessagesRequest, + responseType: schema_daemon_pb.GetMessagesResponse, + requestSerialize: serialize_asphrdaemon_GetMessagesRequest, + requestDeserialize: deserialize_asphrdaemon_GetMessagesRequest, + responseSerialize: serialize_asphrdaemon_GetMessagesResponse, + responseDeserialize: deserialize_asphrdaemon_GetMessagesResponse, }, - getNewMessages: { - path: '/asphrdaemon.Daemon/GetNewMessages', + getMessagesStreamed: { + path: '/asphrdaemon.Daemon/GetMessagesStreamed', requestStream: false, - responseStream: false, - requestType: schema_daemon_pb.GetNewMessagesRequest, - responseType: schema_daemon_pb.GetNewMessagesResponse, - requestSerialize: serialize_asphrdaemon_GetNewMessagesRequest, - requestDeserialize: deserialize_asphrdaemon_GetNewMessagesRequest, - responseSerialize: serialize_asphrdaemon_GetNewMessagesResponse, - responseDeserialize: deserialize_asphrdaemon_GetNewMessagesResponse, + responseStream: true, + requestType: schema_daemon_pb.GetMessagesRequest, + responseType: schema_daemon_pb.GetMessagesResponse, + requestSerialize: serialize_asphrdaemon_GetMessagesRequest, + requestDeserialize: deserialize_asphrdaemon_GetMessagesRequest, + responseSerialize: serialize_asphrdaemon_GetMessagesResponse, + responseDeserialize: deserialize_asphrdaemon_GetMessagesResponse, }, getOutboxMessages: { path: '/asphrdaemon.Daemon/GetOutboxMessages', diff --git a/gui/src/daemon/schema/daemon_pb.d.ts b/gui/src/daemon/schema/daemon_pb.d.ts old mode 100755 new mode 100644 index f1933a79..99d0345c --- a/gui/src/daemon/schema/daemon_pb.d.ts +++ b/gui/src/daemon/schema/daemon_pb.d.ts @@ -1,3 +1,5 @@ +// @generated + // package: asphrdaemon // file: schema/daemon.proto @@ -358,77 +360,50 @@ export namespace OutgoingMessage { } } -export class GetAllMessagesRequest extends jspb.Message { - serializeBinary(): Uint8Array; - toObject(includeInstance?: boolean): GetAllMessagesRequest.AsObject; - static toObject(includeInstance: boolean, msg: GetAllMessagesRequest): GetAllMessagesRequest.AsObject; - static extensions: {[key: number]: jspb.ExtensionFieldInfo}; - static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; - static serializeBinaryToWriter(message: GetAllMessagesRequest, writer: jspb.BinaryWriter): void; - static deserializeBinary(bytes: Uint8Array): GetAllMessagesRequest; - static deserializeBinaryFromReader(message: GetAllMessagesRequest, reader: jspb.BinaryReader): GetAllMessagesRequest; -} - -export namespace GetAllMessagesRequest { - export type AsObject = { - } -} - -export class GetAllMessagesResponse extends jspb.Message { - clearMessagesList(): void; - getMessagesList(): Array; - setMessagesList(value: Array): void; - addMessages(value?: IncomingMessage, index?: number): IncomingMessage; +export class GetMessagesRequest extends jspb.Message { + getFilter(): GetMessagesRequest.FilterMap[keyof GetMessagesRequest.FilterMap]; + setFilter(value: GetMessagesRequest.FilterMap[keyof GetMessagesRequest.FilterMap]): void; serializeBinary(): Uint8Array; - toObject(includeInstance?: boolean): GetAllMessagesResponse.AsObject; - static toObject(includeInstance: boolean, msg: GetAllMessagesResponse): GetAllMessagesResponse.AsObject; + toObject(includeInstance?: boolean): GetMessagesRequest.AsObject; + static toObject(includeInstance: boolean, msg: GetMessagesRequest): GetMessagesRequest.AsObject; static extensions: {[key: number]: jspb.ExtensionFieldInfo}; static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; - static serializeBinaryToWriter(message: GetAllMessagesResponse, writer: jspb.BinaryWriter): void; - static deserializeBinary(bytes: Uint8Array): GetAllMessagesResponse; - static deserializeBinaryFromReader(message: GetAllMessagesResponse, reader: jspb.BinaryReader): GetAllMessagesResponse; + static serializeBinaryToWriter(message: GetMessagesRequest, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): GetMessagesRequest; + static deserializeBinaryFromReader(message: GetMessagesRequest, reader: jspb.BinaryReader): GetMessagesRequest; } -export namespace GetAllMessagesResponse { +export namespace GetMessagesRequest { export type AsObject = { - messagesList: Array, + filter: GetMessagesRequest.FilterMap[keyof GetMessagesRequest.FilterMap], } -} - -export class GetNewMessagesRequest extends jspb.Message { - serializeBinary(): Uint8Array; - toObject(includeInstance?: boolean): GetNewMessagesRequest.AsObject; - static toObject(includeInstance: boolean, msg: GetNewMessagesRequest): GetNewMessagesRequest.AsObject; - static extensions: {[key: number]: jspb.ExtensionFieldInfo}; - static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; - static serializeBinaryToWriter(message: GetNewMessagesRequest, writer: jspb.BinaryWriter): void; - static deserializeBinary(bytes: Uint8Array): GetNewMessagesRequest; - static deserializeBinaryFromReader(message: GetNewMessagesRequest, reader: jspb.BinaryReader): GetNewMessagesRequest; -} -export namespace GetNewMessagesRequest { - export type AsObject = { + export interface FilterMap { + ALL: 0; + NEW: 1; } + + export const Filter: FilterMap; } -export class GetNewMessagesResponse extends jspb.Message { +export class GetMessagesResponse extends jspb.Message { clearMessagesList(): void; getMessagesList(): Array; setMessagesList(value: Array): void; addMessages(value?: IncomingMessage, index?: number): IncomingMessage; serializeBinary(): Uint8Array; - toObject(includeInstance?: boolean): GetNewMessagesResponse.AsObject; - static toObject(includeInstance: boolean, msg: GetNewMessagesResponse): GetNewMessagesResponse.AsObject; + toObject(includeInstance?: boolean): GetMessagesResponse.AsObject; + static toObject(includeInstance: boolean, msg: GetMessagesResponse): GetMessagesResponse.AsObject; static extensions: {[key: number]: jspb.ExtensionFieldInfo}; static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; - static serializeBinaryToWriter(message: GetNewMessagesResponse, writer: jspb.BinaryWriter): void; - static deserializeBinary(bytes: Uint8Array): GetNewMessagesResponse; - static deserializeBinaryFromReader(message: GetNewMessagesResponse, reader: jspb.BinaryReader): GetNewMessagesResponse; + static serializeBinaryToWriter(message: GetMessagesResponse, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): GetMessagesResponse; + static deserializeBinaryFromReader(message: GetMessagesResponse, reader: jspb.BinaryReader): GetMessagesResponse; } -export namespace GetNewMessagesResponse { +export namespace GetMessagesResponse { export type AsObject = { messagesList: Array, } @@ -569,6 +544,9 @@ export class GetStatusResponse extends jspb.Message { getReleaseHash(): string; setReleaseHash(value: string): void; + getLatencySeconds(): number; + setLatencySeconds(value: number): void; + serializeBinary(): Uint8Array; toObject(includeInstance?: boolean): GetStatusResponse.AsObject; static toObject(includeInstance: boolean, msg: GetStatusResponse): GetStatusResponse.AsObject; @@ -583,6 +561,7 @@ export namespace GetStatusResponse { export type AsObject = { registered: boolean, releaseHash: string, + latencySeconds: number, } } @@ -689,4 +668,3 @@ export namespace KillResponse { export type AsObject = { } } - diff --git a/gui/src/daemon/schema/daemon_pb.js b/gui/src/daemon/schema/daemon_pb.js old mode 100755 new mode 100644 index 186d0144..20bbe9a3 --- a/gui/src/daemon/schema/daemon_pb.js +++ b/gui/src/daemon/schema/daemon_pb.js @@ -1,3 +1,5 @@ +// @generated + // source: schema/daemon.proto /** * @fileoverview @@ -25,14 +27,13 @@ goog.exportSymbol('proto.asphrdaemon.ChangeLatencyResponse', null, global); goog.exportSymbol('proto.asphrdaemon.FriendInfo', null, global); goog.exportSymbol('proto.asphrdaemon.GenerateFriendKeyRequest', null, global); goog.exportSymbol('proto.asphrdaemon.GenerateFriendKeyResponse', null, global); -goog.exportSymbol('proto.asphrdaemon.GetAllMessagesRequest', null, global); -goog.exportSymbol('proto.asphrdaemon.GetAllMessagesResponse', null, global); goog.exportSymbol('proto.asphrdaemon.GetFriendListRequest', null, global); goog.exportSymbol('proto.asphrdaemon.GetFriendListResponse', null, global); goog.exportSymbol('proto.asphrdaemon.GetLatencyRequest', null, global); goog.exportSymbol('proto.asphrdaemon.GetLatencyResponse', null, global); -goog.exportSymbol('proto.asphrdaemon.GetNewMessagesRequest', null, global); -goog.exportSymbol('proto.asphrdaemon.GetNewMessagesResponse', null, global); +goog.exportSymbol('proto.asphrdaemon.GetMessagesRequest', null, global); +goog.exportSymbol('proto.asphrdaemon.GetMessagesRequest.Filter', null, global); +goog.exportSymbol('proto.asphrdaemon.GetMessagesResponse', null, global); goog.exportSymbol('proto.asphrdaemon.GetOutboxMessagesRequest', null, global); goog.exportSymbol('proto.asphrdaemon.GetOutboxMessagesResponse', null, global); goog.exportSymbol('proto.asphrdaemon.GetSentMessagesRequest', null, global); @@ -397,37 +398,16 @@ if (goog.DEBUG && !COMPILED) { * @extends {jspb.Message} * @constructor */ -proto.asphrdaemon.GetAllMessagesRequest = function(opt_data) { +proto.asphrdaemon.GetMessagesRequest = function(opt_data) { jspb.Message.initialize(this, opt_data, 0, -1, null, null); }; -goog.inherits(proto.asphrdaemon.GetAllMessagesRequest, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.asphrdaemon.GetAllMessagesRequest.displayName = 'proto.asphrdaemon.GetAllMessagesRequest'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.asphrdaemon.GetAllMessagesResponse = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, proto.asphrdaemon.GetAllMessagesResponse.repeatedFields_, null); -}; -goog.inherits(proto.asphrdaemon.GetAllMessagesResponse, jspb.Message); +goog.inherits(proto.asphrdaemon.GetMessagesRequest, jspb.Message); if (goog.DEBUG && !COMPILED) { /** * @public * @override */ - proto.asphrdaemon.GetAllMessagesResponse.displayName = 'proto.asphrdaemon.GetAllMessagesResponse'; + proto.asphrdaemon.GetMessagesRequest.displayName = 'proto.asphrdaemon.GetMessagesRequest'; } /** * Generated by JsPbCodeGenerator. @@ -439,37 +419,16 @@ if (goog.DEBUG && !COMPILED) { * @extends {jspb.Message} * @constructor */ -proto.asphrdaemon.GetNewMessagesRequest = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); +proto.asphrdaemon.GetMessagesResponse = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, proto.asphrdaemon.GetMessagesResponse.repeatedFields_, null); }; -goog.inherits(proto.asphrdaemon.GetNewMessagesRequest, jspb.Message); +goog.inherits(proto.asphrdaemon.GetMessagesResponse, jspb.Message); if (goog.DEBUG && !COMPILED) { /** * @public * @override */ - proto.asphrdaemon.GetNewMessagesRequest.displayName = 'proto.asphrdaemon.GetNewMessagesRequest'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.asphrdaemon.GetNewMessagesResponse = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, proto.asphrdaemon.GetNewMessagesResponse.repeatedFields_, null); -}; -goog.inherits(proto.asphrdaemon.GetNewMessagesResponse, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.asphrdaemon.GetNewMessagesResponse.displayName = 'proto.asphrdaemon.GetNewMessagesResponse'; + proto.asphrdaemon.GetMessagesResponse.displayName = 'proto.asphrdaemon.GetMessagesResponse'; } /** * Generated by JsPbCodeGenerator. @@ -3160,116 +3119,8 @@ if (jspb.Message.GENERATE_TO_OBJECT) { * http://goto/soy-param-migration * @return {!Object} */ -proto.asphrdaemon.GetAllMessagesRequest.prototype.toObject = function(opt_includeInstance) { - return proto.asphrdaemon.GetAllMessagesRequest.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.asphrdaemon.GetAllMessagesRequest} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.asphrdaemon.GetAllMessagesRequest.toObject = function(includeInstance, msg) { - var f, obj = { - - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.asphrdaemon.GetAllMessagesRequest} - */ -proto.asphrdaemon.GetAllMessagesRequest.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.asphrdaemon.GetAllMessagesRequest; - return proto.asphrdaemon.GetAllMessagesRequest.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.asphrdaemon.GetAllMessagesRequest} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.asphrdaemon.GetAllMessagesRequest} - */ -proto.asphrdaemon.GetAllMessagesRequest.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.asphrdaemon.GetAllMessagesRequest.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.asphrdaemon.GetAllMessagesRequest.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.asphrdaemon.GetAllMessagesRequest} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.asphrdaemon.GetAllMessagesRequest.serializeBinaryToWriter = function(message, writer) { - var f = undefined; -}; - - - -/** - * List of repeated fields within this message type. - * @private {!Array} - * @const - */ -proto.asphrdaemon.GetAllMessagesResponse.repeatedFields_ = [1]; - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.asphrdaemon.GetAllMessagesResponse.prototype.toObject = function(opt_includeInstance) { - return proto.asphrdaemon.GetAllMessagesResponse.toObject(opt_includeInstance, this); +proto.asphrdaemon.GetMessagesRequest.prototype.toObject = function(opt_includeInstance) { + return proto.asphrdaemon.GetMessagesRequest.toObject(opt_includeInstance, this); }; @@ -3278,14 +3129,13 @@ proto.asphrdaemon.GetAllMessagesResponse.prototype.toObject = function(opt_inclu * @param {boolean|undefined} includeInstance Deprecated. Whether to include * the JSPB instance for transitional soy proto support: * http://goto/soy-param-migration - * @param {!proto.asphrdaemon.GetAllMessagesResponse} msg The msg instance to transform. + * @param {!proto.asphrdaemon.GetMessagesRequest} msg The msg instance to transform. * @return {!Object} * @suppress {unusedLocalVariables} f is only used for nested messages */ -proto.asphrdaemon.GetAllMessagesResponse.toObject = function(includeInstance, msg) { +proto.asphrdaemon.GetMessagesRequest.toObject = function(includeInstance, msg) { var f, obj = { - messagesList: jspb.Message.toObjectList(msg.getMessagesList(), - proto.asphrdaemon.IncomingMessage.toObject, includeInstance) + filter: jspb.Message.getFieldWithDefault(msg, 1, 0) }; if (includeInstance) { @@ -3299,23 +3149,23 @@ proto.asphrdaemon.GetAllMessagesResponse.toObject = function(includeInstance, ms /** * Deserializes binary data (in protobuf wire format). * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.asphrdaemon.GetAllMessagesResponse} + * @return {!proto.asphrdaemon.GetMessagesRequest} */ -proto.asphrdaemon.GetAllMessagesResponse.deserializeBinary = function(bytes) { +proto.asphrdaemon.GetMessagesRequest.deserializeBinary = function(bytes) { var reader = new jspb.BinaryReader(bytes); - var msg = new proto.asphrdaemon.GetAllMessagesResponse; - return proto.asphrdaemon.GetAllMessagesResponse.deserializeBinaryFromReader(msg, reader); + var msg = new proto.asphrdaemon.GetMessagesRequest; + return proto.asphrdaemon.GetMessagesRequest.deserializeBinaryFromReader(msg, reader); }; /** * Deserializes binary data (in protobuf wire format) from the * given reader into the given message object. - * @param {!proto.asphrdaemon.GetAllMessagesResponse} msg The message object to deserialize into. + * @param {!proto.asphrdaemon.GetMessagesRequest} msg The message object to deserialize into. * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.asphrdaemon.GetAllMessagesResponse} + * @return {!proto.asphrdaemon.GetMessagesRequest} */ -proto.asphrdaemon.GetAllMessagesResponse.deserializeBinaryFromReader = function(msg, reader) { +proto.asphrdaemon.GetMessagesRequest.deserializeBinaryFromReader = function(msg, reader) { while (reader.nextField()) { if (reader.isEndGroup()) { break; @@ -3323,9 +3173,8 @@ proto.asphrdaemon.GetAllMessagesResponse.deserializeBinaryFromReader = function( var field = reader.getFieldNumber(); switch (field) { case 1: - var value = new proto.asphrdaemon.IncomingMessage; - reader.readMessage(value,proto.asphrdaemon.IncomingMessage.deserializeBinaryFromReader); - msg.addMessages(value); + var value = /** @type {!proto.asphrdaemon.GetMessagesRequest.Filter} */ (reader.readEnum()); + msg.setFilter(value); break; default: reader.skipField(); @@ -3340,9 +3189,9 @@ proto.asphrdaemon.GetAllMessagesResponse.deserializeBinaryFromReader = function( * Serializes the message to binary data (in protobuf wire format). * @return {!Uint8Array} */ -proto.asphrdaemon.GetAllMessagesResponse.prototype.serializeBinary = function() { +proto.asphrdaemon.GetMessagesRequest.prototype.serializeBinary = function() { var writer = new jspb.BinaryWriter(); - proto.asphrdaemon.GetAllMessagesResponse.serializeBinaryToWriter(this, writer); + proto.asphrdaemon.GetMessagesRequest.serializeBinaryToWriter(this, writer); return writer.getResultBuffer(); }; @@ -3350,159 +3199,45 @@ proto.asphrdaemon.GetAllMessagesResponse.prototype.serializeBinary = function() /** * Serializes the given message to binary data (in protobuf wire * format), writing to the given BinaryWriter. - * @param {!proto.asphrdaemon.GetAllMessagesResponse} message + * @param {!proto.asphrdaemon.GetMessagesRequest} message * @param {!jspb.BinaryWriter} writer * @suppress {unusedLocalVariables} f is only used for nested messages */ -proto.asphrdaemon.GetAllMessagesResponse.serializeBinaryToWriter = function(message, writer) { +proto.asphrdaemon.GetMessagesRequest.serializeBinaryToWriter = function(message, writer) { var f = undefined; - f = message.getMessagesList(); - if (f.length > 0) { - writer.writeRepeatedMessage( + f = message.getFilter(); + if (f !== 0.0) { + writer.writeEnum( 1, - f, - proto.asphrdaemon.IncomingMessage.serializeBinaryToWriter + f ); } }; /** - * repeated IncomingMessage messages = 1; - * @return {!Array} + * @enum {number} */ -proto.asphrdaemon.GetAllMessagesResponse.prototype.getMessagesList = function() { - return /** @type{!Array} */ ( - jspb.Message.getRepeatedWrapperField(this, proto.asphrdaemon.IncomingMessage, 1)); +proto.asphrdaemon.GetMessagesRequest.Filter = { + ALL: 0, + NEW: 1 }; - -/** - * @param {!Array} value - * @return {!proto.asphrdaemon.GetAllMessagesResponse} returns this -*/ -proto.asphrdaemon.GetAllMessagesResponse.prototype.setMessagesList = function(value) { - return jspb.Message.setRepeatedWrapperField(this, 1, value); -}; - - /** - * @param {!proto.asphrdaemon.IncomingMessage=} opt_value - * @param {number=} opt_index - * @return {!proto.asphrdaemon.IncomingMessage} + * optional Filter filter = 1; + * @return {!proto.asphrdaemon.GetMessagesRequest.Filter} */ -proto.asphrdaemon.GetAllMessagesResponse.prototype.addMessages = function(opt_value, opt_index) { - return jspb.Message.addToRepeatedWrapperField(this, 1, opt_value, proto.asphrdaemon.IncomingMessage, opt_index); +proto.asphrdaemon.GetMessagesRequest.prototype.getFilter = function() { + return /** @type {!proto.asphrdaemon.GetMessagesRequest.Filter} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); }; /** - * Clears the list making it empty but non-null. - * @return {!proto.asphrdaemon.GetAllMessagesResponse} returns this + * @param {!proto.asphrdaemon.GetMessagesRequest.Filter} value + * @return {!proto.asphrdaemon.GetMessagesRequest} returns this */ -proto.asphrdaemon.GetAllMessagesResponse.prototype.clearMessagesList = function() { - return this.setMessagesList([]); -}; - - - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.asphrdaemon.GetNewMessagesRequest.prototype.toObject = function(opt_includeInstance) { - return proto.asphrdaemon.GetNewMessagesRequest.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.asphrdaemon.GetNewMessagesRequest} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.asphrdaemon.GetNewMessagesRequest.toObject = function(includeInstance, msg) { - var f, obj = { - - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.asphrdaemon.GetNewMessagesRequest} - */ -proto.asphrdaemon.GetNewMessagesRequest.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.asphrdaemon.GetNewMessagesRequest; - return proto.asphrdaemon.GetNewMessagesRequest.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.asphrdaemon.GetNewMessagesRequest} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.asphrdaemon.GetNewMessagesRequest} - */ -proto.asphrdaemon.GetNewMessagesRequest.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.asphrdaemon.GetNewMessagesRequest.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.asphrdaemon.GetNewMessagesRequest.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.asphrdaemon.GetNewMessagesRequest} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.asphrdaemon.GetNewMessagesRequest.serializeBinaryToWriter = function(message, writer) { - var f = undefined; +proto.asphrdaemon.GetMessagesRequest.prototype.setFilter = function(value) { + return jspb.Message.setProto3EnumField(this, 1, value); }; @@ -3512,7 +3247,7 @@ proto.asphrdaemon.GetNewMessagesRequest.serializeBinaryToWriter = function(messa * @private {!Array} * @const */ -proto.asphrdaemon.GetNewMessagesResponse.repeatedFields_ = [1]; +proto.asphrdaemon.GetMessagesResponse.repeatedFields_ = [1]; @@ -3529,8 +3264,8 @@ if (jspb.Message.GENERATE_TO_OBJECT) { * http://goto/soy-param-migration * @return {!Object} */ -proto.asphrdaemon.GetNewMessagesResponse.prototype.toObject = function(opt_includeInstance) { - return proto.asphrdaemon.GetNewMessagesResponse.toObject(opt_includeInstance, this); +proto.asphrdaemon.GetMessagesResponse.prototype.toObject = function(opt_includeInstance) { + return proto.asphrdaemon.GetMessagesResponse.toObject(opt_includeInstance, this); }; @@ -3539,11 +3274,11 @@ proto.asphrdaemon.GetNewMessagesResponse.prototype.toObject = function(opt_inclu * @param {boolean|undefined} includeInstance Deprecated. Whether to include * the JSPB instance for transitional soy proto support: * http://goto/soy-param-migration - * @param {!proto.asphrdaemon.GetNewMessagesResponse} msg The msg instance to transform. + * @param {!proto.asphrdaemon.GetMessagesResponse} msg The msg instance to transform. * @return {!Object} * @suppress {unusedLocalVariables} f is only used for nested messages */ -proto.asphrdaemon.GetNewMessagesResponse.toObject = function(includeInstance, msg) { +proto.asphrdaemon.GetMessagesResponse.toObject = function(includeInstance, msg) { var f, obj = { messagesList: jspb.Message.toObjectList(msg.getMessagesList(), proto.asphrdaemon.IncomingMessage.toObject, includeInstance) @@ -3560,23 +3295,23 @@ proto.asphrdaemon.GetNewMessagesResponse.toObject = function(includeInstance, ms /** * Deserializes binary data (in protobuf wire format). * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.asphrdaemon.GetNewMessagesResponse} + * @return {!proto.asphrdaemon.GetMessagesResponse} */ -proto.asphrdaemon.GetNewMessagesResponse.deserializeBinary = function(bytes) { +proto.asphrdaemon.GetMessagesResponse.deserializeBinary = function(bytes) { var reader = new jspb.BinaryReader(bytes); - var msg = new proto.asphrdaemon.GetNewMessagesResponse; - return proto.asphrdaemon.GetNewMessagesResponse.deserializeBinaryFromReader(msg, reader); + var msg = new proto.asphrdaemon.GetMessagesResponse; + return proto.asphrdaemon.GetMessagesResponse.deserializeBinaryFromReader(msg, reader); }; /** * Deserializes binary data (in protobuf wire format) from the * given reader into the given message object. - * @param {!proto.asphrdaemon.GetNewMessagesResponse} msg The message object to deserialize into. + * @param {!proto.asphrdaemon.GetMessagesResponse} msg The message object to deserialize into. * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.asphrdaemon.GetNewMessagesResponse} + * @return {!proto.asphrdaemon.GetMessagesResponse} */ -proto.asphrdaemon.GetNewMessagesResponse.deserializeBinaryFromReader = function(msg, reader) { +proto.asphrdaemon.GetMessagesResponse.deserializeBinaryFromReader = function(msg, reader) { while (reader.nextField()) { if (reader.isEndGroup()) { break; @@ -3601,9 +3336,9 @@ proto.asphrdaemon.GetNewMessagesResponse.deserializeBinaryFromReader = function( * Serializes the message to binary data (in protobuf wire format). * @return {!Uint8Array} */ -proto.asphrdaemon.GetNewMessagesResponse.prototype.serializeBinary = function() { +proto.asphrdaemon.GetMessagesResponse.prototype.serializeBinary = function() { var writer = new jspb.BinaryWriter(); - proto.asphrdaemon.GetNewMessagesResponse.serializeBinaryToWriter(this, writer); + proto.asphrdaemon.GetMessagesResponse.serializeBinaryToWriter(this, writer); return writer.getResultBuffer(); }; @@ -3611,11 +3346,11 @@ proto.asphrdaemon.GetNewMessagesResponse.prototype.serializeBinary = function() /** * Serializes the given message to binary data (in protobuf wire * format), writing to the given BinaryWriter. - * @param {!proto.asphrdaemon.GetNewMessagesResponse} message + * @param {!proto.asphrdaemon.GetMessagesResponse} message * @param {!jspb.BinaryWriter} writer * @suppress {unusedLocalVariables} f is only used for nested messages */ -proto.asphrdaemon.GetNewMessagesResponse.serializeBinaryToWriter = function(message, writer) { +proto.asphrdaemon.GetMessagesResponse.serializeBinaryToWriter = function(message, writer) { var f = undefined; f = message.getMessagesList(); if (f.length > 0) { @@ -3632,7 +3367,7 @@ proto.asphrdaemon.GetNewMessagesResponse.serializeBinaryToWriter = function(mess * repeated IncomingMessage messages = 1; * @return {!Array} */ -proto.asphrdaemon.GetNewMessagesResponse.prototype.getMessagesList = function() { +proto.asphrdaemon.GetMessagesResponse.prototype.getMessagesList = function() { return /** @type{!Array} */ ( jspb.Message.getRepeatedWrapperField(this, proto.asphrdaemon.IncomingMessage, 1)); }; @@ -3640,9 +3375,9 @@ proto.asphrdaemon.GetNewMessagesResponse.prototype.getMessagesList = function() /** * @param {!Array} value - * @return {!proto.asphrdaemon.GetNewMessagesResponse} returns this + * @return {!proto.asphrdaemon.GetMessagesResponse} returns this */ -proto.asphrdaemon.GetNewMessagesResponse.prototype.setMessagesList = function(value) { +proto.asphrdaemon.GetMessagesResponse.prototype.setMessagesList = function(value) { return jspb.Message.setRepeatedWrapperField(this, 1, value); }; @@ -3652,16 +3387,16 @@ proto.asphrdaemon.GetNewMessagesResponse.prototype.setMessagesList = function(va * @param {number=} opt_index * @return {!proto.asphrdaemon.IncomingMessage} */ -proto.asphrdaemon.GetNewMessagesResponse.prototype.addMessages = function(opt_value, opt_index) { +proto.asphrdaemon.GetMessagesResponse.prototype.addMessages = function(opt_value, opt_index) { return jspb.Message.addToRepeatedWrapperField(this, 1, opt_value, proto.asphrdaemon.IncomingMessage, opt_index); }; /** * Clears the list making it empty but non-null. - * @return {!proto.asphrdaemon.GetNewMessagesResponse} returns this + * @return {!proto.asphrdaemon.GetMessagesResponse} returns this */ -proto.asphrdaemon.GetNewMessagesResponse.prototype.clearMessagesList = function() { +proto.asphrdaemon.GetMessagesResponse.prototype.clearMessagesList = function() { return this.setMessagesList([]); }; @@ -4553,7 +4288,8 @@ proto.asphrdaemon.GetStatusResponse.prototype.toObject = function(opt_includeIns proto.asphrdaemon.GetStatusResponse.toObject = function(includeInstance, msg) { var f, obj = { registered: jspb.Message.getBooleanFieldWithDefault(msg, 1, false), - releaseHash: jspb.Message.getFieldWithDefault(msg, 2, "") + releaseHash: jspb.Message.getFieldWithDefault(msg, 2, ""), + latencySeconds: jspb.Message.getFieldWithDefault(msg, 3, 0) }; if (includeInstance) { @@ -4598,6 +4334,10 @@ proto.asphrdaemon.GetStatusResponse.deserializeBinaryFromReader = function(msg, var value = /** @type {string} */ (reader.readString()); msg.setReleaseHash(value); break; + case 3: + var value = /** @type {number} */ (reader.readInt32()); + msg.setLatencySeconds(value); + break; default: reader.skipField(); break; @@ -4641,6 +4381,13 @@ proto.asphrdaemon.GetStatusResponse.serializeBinaryToWriter = function(message, f ); } + f = message.getLatencySeconds(); + if (f !== 0) { + writer.writeInt32( + 3, + f + ); + } }; @@ -4680,6 +4427,24 @@ proto.asphrdaemon.GetStatusResponse.prototype.setReleaseHash = function(value) { }; +/** + * optional int32 latency_seconds = 3; + * @return {number} + */ +proto.asphrdaemon.GetStatusResponse.prototype.getLatencySeconds = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.asphrdaemon.GetStatusResponse} returns this + */ +proto.asphrdaemon.GetStatusResponse.prototype.setLatencySeconds = function(value) { + return jspb.Message.setProto3IntField(this, 3, value); +}; + + diff --git a/gui/src/main/preload.js b/gui/src/main/preload.js index 1819e868..4055ab03 100644 --- a/gui/src/main/preload.js +++ b/gui/src/main/preload.js @@ -68,17 +68,35 @@ contextBridge.exposeInMainWorld("copyToClipboard", async (s) => { clipboard.writeText(s, "selection"); }); -function convertProtobufMessageToTypedMessage(m) { - console.log("seconds", m.getTimestamp().getSeconds()); +function convertProtobufIncomingMessageToTypedMessage(m) { + console.log("seconds", m.getReceivedTimestamp().getSeconds()); var d = new Date( - m.getTimestamp().getSeconds() * 1e3 + m.getTimestamp().getNanos() / 1e6 + m.getReceivedTimestamp().getSeconds() * 1e3 + + m.getReceivedTimestamp().getNanos() / 1e6 ); return { - id: m.getId(), - from: m.getSender(), + id: m.getM().getId(), + from: m.getFrom(), to: "me", - message: m.getMessage(), + message: m.getM().getMessage(), timestamp: d, + type: "incoming", + }; +} + +function convertProtobufOutgoingMessageToTypedMessage(m) { + console.log("seconds", m.getWrittenTimestamp().getSeconds()); + var d = new Date( + m.getWrittenTimestamp().getSeconds() * 1e3 + + m.getWrittenTimestamp().getNanos() / 1e6 + ); + return { + id: m.getM().getId(), + from: "me", + to: m.getTo(), + message: m.getM().getMessage(), + timestamp: d, + type: "outgoing", }; } @@ -91,6 +109,7 @@ contextBridge.exposeInMainWorld("getNewMessages", async () => { to: "me", message: "hello!\n\nthis is a test message\n\nbest,\nalice", timestamp: new Date(), + type: "incoming", }, { id: "2", @@ -98,17 +117,19 @@ contextBridge.exposeInMainWorld("getNewMessages", async () => { to: "me", message: "hi", timestamp: new Date(), + type: "incoming", }, ]; } - const request = new daemonM.GetNewMessagesRequest(); + const request = new daemonM.GetMessagesRequest(); + request.setFilter(daemonM.GetMessagesRequest.Filter.NEW); const getNewMessages = promisify(daemonClient.getNewMessages).bind( daemonClient ); try { const response = await getNewMessages(request); const lm = response.getMessagesList(); - const l = lm.map(convertProtobufMessageToTypedMessage); + const l = lm.map(convertProtobufIncomingMessageToTypedMessage); return l; } catch (e) { console.log(`error in getNewMessages: ${e}`); @@ -169,6 +190,7 @@ contextBridge.exposeInMainWorld("getAllMessages", async () => { to: "me", message: "hello", timestamp: new Date(), + type: "incoming", }, { id: "2", @@ -176,6 +198,7 @@ contextBridge.exposeInMainWorld("getAllMessages", async () => { to: "me", message: "hi", timestamp: new Date(), + type: "incoming", }, { id: "3", @@ -183,6 +206,7 @@ contextBridge.exposeInMainWorld("getAllMessages", async () => { to: "me", message: "hi this is my second message", timestamp: new Date(), + type: "incoming", }, { id: "4", @@ -190,17 +214,19 @@ contextBridge.exposeInMainWorld("getAllMessages", async () => { to: "me", message: "hi this is my first message", timestamp: new Date(), + type: "incoming", }, ]; } - const request = new daemonM.GetAllMessagesRequest(); + const request = new daemonM.GetMessagesRequest(); + request.setFilter(daemonM.GetMessagesRequest.Filter.ALL); const getAllMessages = promisify(daemonClient.getAllMessages).bind( daemonClient ); try { const response = await getAllMessages(request); const lm = response.getMessagesList(); - const l = lm.map(convertProtobufMessageToTypedMessage); + const l = lm.map(convertProtobufIncomingMessageToTypedMessage); return l; } catch (e) { console.log(`error in getAllMessages: ${e}`); @@ -208,36 +234,104 @@ contextBridge.exposeInMainWorld("getAllMessages", async () => { } }); +contextBridge.exposeInMainWorld("getAllMessagesStreamed", (f) => { + const request = new daemonM.GetMessagesRequest(); + request.setFilter(daemonM.GetMessagesRequest.Filter.ALL); + var call = daemonClient.getMessagesStreamed(request); + call.on("data", function (r) { + try { + const lm = r.getMessagesList(); + const l = lm.map(convertProtobufIncomingMessageToTypedMessage); + f(l); + } catch (e) { + console.log(`error in getAllMessagesStreamed: ${e}`); + } + console.log("got all messages streamed", r); + }); + call.on("end", function () { + // The server has finished sending + console.log("getAllMessagesStreamed end"); + }); + call.on("error", function (e) { + // An error has occurred and the stream has been closed. + console.log("getAllMessagesStreamed error", e); + }); + call.on("status", function (status) { + // process status + console.log("getAllMessagesStreamed status", status); + }); + return () => { + console.log("cancelling grpc!"); + call.cancel(); + }; +}); + +contextBridge.exposeInMainWorld("getNewMessagesStreamed", (f) => { + const request = new daemonM.GetMessagesRequest(); + request.setFilter(daemonM.GetMessagesRequest.Filter.NEW); + var call = daemonClient.getMessagesStreamed(request); + call.on("data", function (r) { + try { + const lm = r.getMessagesList(); + const l = lm.map(convertProtobufIncomingMessageToTypedMessage); + f(l); + } catch (e) { + console.log(`error in getNewMessagesStreamed: ${e}`); + } + console.log("got all messages streamed", r); + }); + call.on("end", function () { + // The server has finished sending + console.log("getNewMessagesStreamed end"); + }); + call.on("error", function (e) { + // An error has occurred and the stream has been closed. + console.log("getNewMessagesStreamed error", e); + }); + call.on("status", function (status) { + // process status + console.log("getNewMessagesStreamed status", status); + }); + return () => { + console.log("cancelling grpc!"); + call.cancel(); + }; +}); + contextBridge.exposeInMainWorld("getOutboxMessages", async () => { if (FAKE_DATA) { return [ { id: "1", - from: "SuAlEh", - to: "me", + from: "me", + to: "SUelAh", message: "hello!\n\nthis is a test message\n\nnot the best,\nSuAlEh", timestamp: new Date(), + type: "outgoing", }, { id: "2", - from: "HI", - to: "me", + from: "me", + to: "HI", message: "HIHI", timestamp: new Date(), + type: "outgoing", }, { id: "3", - from: "Bob", - to: "me", + from: "me", + to: "Bob", message: "hi this is my second message", timestamp: new Date(), + type: "outgoing", }, { id: "4", - from: "Bob", - to: "me", + from: "me", + to: "Bob", message: "hi this is my first message", timestamp: new Date(), + type: "outgoing", }, ]; } @@ -248,7 +342,7 @@ contextBridge.exposeInMainWorld("getOutboxMessages", async () => { try { const response = await getOutboxMessages(request); const lm = response.getMessagesList(); - const l = lm.map(convertProtobufMessageToTypedMessage); + const l = lm.map(convertProtobufOutgoingMessageToTypedMessage); return l; } catch (e) { console.log(`error in getOutboxMessages: ${e}`); @@ -261,18 +355,20 @@ contextBridge.exposeInMainWorld("getSentMessages", async () => { return [ { id: "1", - from: "SuAlEh", - to: "me", + from: "me", + to: "SuAlEh", message: "Send send!\n\nthis is a test message\n\nnot the best,\nSuAlEh", timestamp: new Date(), + type: "outgoing", }, { id: "2", - from: "sent happy happy", - to: "me", + from: "me", + to: "sent happy happy", message: "HIHI", timestamp: new Date(), + type: "outgoing", }, ]; } @@ -283,7 +379,7 @@ contextBridge.exposeInMainWorld("getSentMessages", async () => { try { const response = await getSentMessages(request); const lm = response.getMessagesList(); - const l = lm.map(convertProtobufMessageToTypedMessage); + const l = lm.map(convertProtobufOutgoingMessageToTypedMessage); return l; } catch (e) { console.log(`error in getSentMessages: ${e}`); diff --git a/gui/src/renderer/components/MessageList.tsx b/gui/src/renderer/components/MessageList.tsx index 0a9f893b..7e5853ba 100644 --- a/gui/src/renderer/components/MessageList.tsx +++ b/gui/src/renderer/components/MessageList.tsx @@ -17,7 +17,6 @@ function MessageBlurb({ }) { let timestamp_string = ""; try { - console.log(message.timestamp); timestamp_string = formatTime(message.timestamp); } catch {} @@ -28,7 +27,9 @@ function MessageBlurb({ } border-l-4 px-4 py-4 rounded-sm my-2`} >
-
{message.from}
+
+ {message.type === "incoming" ? message.from : `To: ${message.to}`} +
{truncate(message.message, 70)}
@@ -57,13 +58,49 @@ function MessageList(props: { React.useEffect(() => { if (props.messages === "new") { - (window as any).getNewMessages().then((messages: Message[]) => { - setMessages(messages); - }); + setMessages([]); + let cancel = (window as any).getNewMessagesStreamed( + (messages: Message[]) => { + setMessages((prev: Message[]) => { + // merge new messages with old messages, and sort them by timestamp + let new_messages = messages.concat(prev); + new_messages.sort((a, b) => { + // sort based on timestamp + if (a.timestamp > b.timestamp) { + return -1; + } else if (a.timestamp < b.timestamp) { + return 1; + } else { + return 0; + } + }); + return new_messages; + }); + } + ); + return cancel; } else if (props.messages === "all") { - (window as any).getAllMessages().then((messages: Message[]) => { - setMessages(messages); - }); + setMessages([]); + let cancel = (window as any).getAllMessagesStreamed( + (messages: Message[]) => { + setMessages((prev: Message[]) => { + // merge new messages with old messages, and sort them by timestamp + let new_messages = messages.concat(prev); + new_messages.sort((a, b) => { + // sort based on timestamp + if (a.timestamp > b.timestamp) { + return -1; + } else if (a.timestamp < b.timestamp) { + return 1; + } else { + return 0; + } + }); + return new_messages; + }); + } + ); + return cancel; } else if (props.messages === "outbox") { (window as any).getOutboxMessages().then((messages: Message[]) => { setMessages(messages); diff --git a/gui/src/renderer/components/SelectableList.tsx b/gui/src/renderer/components/SelectableList.tsx index c8f8d58b..cc20c814 100644 --- a/gui/src/renderer/components/SelectableList.tsx +++ b/gui/src/renderer/components/SelectableList.tsx @@ -138,8 +138,6 @@ export function SelectableList(props: SelectableListProps) { const pointerMoved = usePointerMovedSinceMount(); - console.log("ACTIVE INDEX", activeIndex); - return (
diff --git a/gui/src/renderer/types.ts b/gui/src/renderer/types.ts index babfb6e5..1b62c928 100644 --- a/gui/src/renderer/types.ts +++ b/gui/src/renderer/types.ts @@ -9,6 +9,7 @@ export type Message = { to: string; message: string; timestamp: Date; + type: "outgoing" | "incoming"; }; export type Friend = { diff --git a/wrap2.sh b/wrap2.sh new file mode 100755 index 00000000..6f9f1b29 --- /dev/null +++ b/wrap2.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +# +# Copyright 2022 Anysphere, Inc. +# SPDX-License-Identifier: GPL-3.0-only +# + +# Prepend this script to any command to have it use the .anysphere2 directory as its source of truth. +# Useful to test two people on the same computer. +# E.g.: ./wrap2.sh ./bazel-bin/daemon/daemon + +XDG_CONFIG_HOME=${HOME}/.anysphere2/data XDG_RUNTIME_DIR=${HOME}/.anysphere2/run XDG_CACHE_HOME=${HOME}/.anysphere2/cache "$@" diff --git a/wrap3.sh b/wrap3.sh new file mode 100755 index 00000000..62bba116 --- /dev/null +++ b/wrap3.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +# +# Copyright 2022 Anysphere, Inc. +# SPDX-License-Identifier: GPL-3.0-only +# + +# Prepend this script to any command to have it use the .anysphere3 directory as its source of truth. +# Useful to test two people on the same computer. +# E.g.: ./wrap3.sh ./bazel-bin/daemon/daemon + +XDG_CONFIG_HOME=${HOME}/.anysphere3/data XDG_RUNTIME_DIR=${HOME}/.anysphere3/run XDG_CACHE_HOME=${HOME}/.anysphere3/cache "$@"