Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 89 additions & 3 deletions contract/talk.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
#include <eosio/eosio.hpp>

using react_t = uint8_t;
constexpr react_t REACT_TYPE_MAX = 5;

// Message table
struct [[eosio::table("message"), eosio::contract("talk")]] message {
uint64_t id = {}; // Non-0
Expand All @@ -11,8 +14,36 @@ struct [[eosio::table("message"), eosio::contract("talk")]] message {
uint64_t get_reply_to() const { return reply_to; }
};

uint128_t makeLikesUniqSecondary( uint64_t post_id, eosio::name user) {
return uint128_t{post_id} << 64 | user.value;
}

// Likes table
struct [[eosio::table("likes"), eosio::contract("talk")]] likes {
uint64_t id = {}; // Non-0
uint64_t post_id = {}; // Non-0
eosio::name user = {};
react_t react = {}; // reaction type

uint64_t primary_key() const { return id; }
uint128_t get_uniq_secondary() const { return makeLikesUniqSecondary(post_id, user); }
};

using message_table = eosio::multi_index<
"message"_n, message, eosio::indexed_by<"by.reply.to"_n, eosio::const_mem_fun<message, uint64_t, &message::get_reply_to>>>;
"message"_n, message,
eosio::indexed_by<
"by.reply.to"_n,
eosio::const_mem_fun<message, uint64_t, &message::get_reply_to>
>
>;

using likes_table = eosio::multi_index<
"likes"_n, likes,
eosio::indexed_by<
"by.uniq.secondary"_n,
eosio::const_mem_fun<likes, uint128_t, &likes::get_uniq_secondary>
>
>;

// The contract
class talk : eosio::contract {
Expand All @@ -28,9 +59,10 @@ class talk : eosio::contract {
require_auth(user);

// Check reply_to exists
if (reply_to)
if (reply_to) {
table.get(reply_to);

}

// Create an ID if user didn't specify one
eosio::check(id < 1'000'000'000ull, "user-specified id is too big");
if (!id)
Expand All @@ -44,4 +76,58 @@ class talk : eosio::contract {
message.content = content;
});
}

// Like or unlike a message, or change reaction
// to like a message, react must be > 0 and <+ REACT_MAX
// to unlike a message, react must be 0 and like must exist in likes_table
[[eosio::action]] void likepost(uint64_t post_id, eosio::name user, react_t react) {
// Check user authentication
require_auth(user);

auto msg = message_table{get_self(), 0};
auto likes = likes_table{get_self(), 0};

// check for valid reaction
eosio::check(react <= REACT_TYPE_MAX, "Bad value for reaction");

// post_id must be > 0
eosio::check(post_id > 0, "Must have valid post_id-to");

// Check reply_to exists
auto it_msg = msg.find(post_id);

// cannot like your own post
eosio::check(it_msg->user != user, "Cannot like yur own post");

// determien if user already liked this post
auto idx = likes.get_index<"by.uniq.secondary"_n>();
auto it = idx.find(makeLikesUniqSecondary(post_id, user));
bool likeExists = (it != idx.end());

// if reaction is 0, post must already exist (it will be deleted)
if(react == 0)
eosio::check(likeExists, "Like not found in table");

// like does not exist, so insert new record
if (!likeExists) {
likes.emplace(get_self(), [&](auto& like) {
like.id = likes.available_primary_key();
like.post_id = post_id;
like.user = user;
like.react = react;
});
}
// othwerise like already exists in table, so either erase it, if reaction is 0 or update it it reaction is changed
else {
auto like_it = likes.find(it->id);
if (react == 0) {
likes.erase(like_it);
} else if (react != like_it->react) {
likes.modify(like_it, get_self(), [&](auto& like) {
like.react = react;
});
}
}

}
};
9 changes: 9 additions & 0 deletions talk_run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/bash

eosio-cpp contract/talk.cpp
cleos create account eosio talk EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV
cleos create account eosio bob EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV
cleos create account eosio jane EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV
cleos create account eosio alice EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV
cleos set code talk talk.wasm
cleos set abi talk talk.abi
90 changes: 90 additions & 0 deletions tests/talk_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ BOOST_AUTO_TEST_CASE(post) try {
// Create users
t.create_account(N(john));
t.create_account(N(jane));
t.create_account(N(jack));

// Test "post" action
t.push_action(
Expand Down Expand Up @@ -50,6 +51,43 @@ BOOST_AUTO_TEST_CASE(post) try {
("content", "post 3: reply") //
);


// jane likes johns post
t.push_action(
N(talk), N(likepost), N(jane),
mutable_variant_object //
("post_id", 1) //
("user", "jane") //
("react", 1) //
);

// jane changes reaction type from 'like' to 'love'
t.push_action(
N(talk), N(likepost), N(jane),
mutable_variant_object //
("post_id", 1) //
("user", "jane") //
("react", 2) //
);

// jack also reacts to johns post
t.push_action(
N(talk), N(likepost), N(jack),
mutable_variant_object //
("post_id", 1) //
("user", "jack") //
("react", 4) //
);

// jane deletes like
t.push_action(
N(talk), N(likepost), N(jane),
mutable_variant_object //
("post_id", 1) //
("user", "jane") //
("react", 0) //
);

// Can't reply to non-existing message
BOOST_CHECK_THROW(
[&] {
Expand All @@ -63,6 +101,58 @@ BOOST_AUTO_TEST_CASE(post) try {
);
}(),
fc::exception);

BOOST_CHECK_THROW(
[&] {
t.push_action(
N(talk), N(post), N(john),
mutable_variant_object //
("id", 4) //
("reply_to", 99) //
("user", "john") //
("content", "post 3: reply") //
);
}(),
fc::exception);

// must have proper authorization
BOOST_CHECK_THROW(
[&] {
t.push_action(
N(talk), N(likepost), N(john),
mutable_variant_object //
("post_id", 1) //
("user", "jane") //
("react", 1) //
);
}(),
fc::exception);

// cannot like yoru own post
BOOST_CHECK_THROW(
[&] {
t.push_action(
N(talk), N(likepost), N(john),
mutable_variant_object //
("post_id", 1) //
("user", "john") //
("react", 1) //
);
}(),
fc::exception);

// cannot like a post which does not exist
BOOST_CHECK_THROW(
[&] {
t.push_action(
N(talk), N(likepost), N(john),
mutable_variant_object //
("post_id", 3) //
("user", "john") //
("react", 1) //
);
}(),
fc::exception);
}
FC_LOG_AND_RETHROW()

Expand Down
Loading