// SPDX-License-Identifier: MIT
pragma solidity ^ 0.8.24;
contract Twitter { // 1️⃣ Create a Twitter Contract
// 定義不可改變的數字
uint16 public MAX_TWEET_LENGTH = 280;
// 5️⃣ Define a Tweet Struct with Author, content, timestamp, likes
// Struct: define a tweet wtih multiple data
// 1️⃣1️⃣ Add id to Tweet Struct to make every Tweet Unique
struct Tweet {
uint256 id;
address author;
string content;
uint256 timestamp;
uint256 likes;
}
// 2️⃣ Create a mappping between user and tweet
mapping(address => Tweet[] ) public tweets;
address public owner; // define the owner of this contract
// 1️⃣5️⃣ Create Event for creating the tweet, called TweetCreated
// Use parameters like id, author, content, timestamp
// 1️⃣7️⃣ Create Event for liking the tweet, called TweetLiked (Use parameters like liker, tweetAuthor, tweetId, newLikeCount
event TweetCreated(uint256 id, address author, string content, uint256 timestamp);
event TweetLiked(address liker, address tweetAuthor, uint256 tweetId, uint256newLikedCount);
event TweetUnliked(address unliker, address tweetAuthor, uint256 tweetId, newLikeCount);
// 8️⃣ create the constructor under all the variables
// Create a constructor function to set an owner of contract
constructor(){
owner = msg.sender;
}
// 9️⃣ Create a modifier called onlyOwner: to check if the owner is calling the function
modifier onlyowner() { // use modifier to check onlyowner
require(msg.sender == owner, "You Are Not The Owner");
_;
}
// 7️⃣ Add a function called change TweetLength to change max tweet length
// ( Hint: use newTweetLength as input for function)
// 🔟 Use onlyOwner on the changeTweetLength function
function changeTweetLength(uint16 newTweetLength) public onlyOwner {
MAX_TWEET_LENGTH = newTweetLength;
}
// 3️⃣ Add function to create a tweet and save it in mapping
// store the tweets above in temporary memory
// key is the address (the msg.sender)
// msg.sender: whoever is using the blockchain interacting with your code
// Add the struct to array Test tweets
function createTweet(string memory _tweet) public {
// 6️⃣ Use require to limit the length of the tweet to be only 280 characters (ep.15)
// conditional
// if tweet length <= 280 then we are good, otherwise we revert
function createTweet(string memory _tweet) public {
require(bytes(_tweet).length <= MAX_TWEET_LENGTH, "Tweet is too long bro");
// 1️⃣2️⃣ Set the id to be the Tweet [ ] length (Hint: you do it in the creatTweet function)
Tweet memory newTweet = Tweet({
id: tweets[msg.sender].length,
author: msg.sender,
content: _tweet,
timestamp: block.timestamp,
likes: 0
});
// not only 1 tweet, so use array
// push the tweet into the array
tweets[msg.sender].push(newTweet);
// 1️⃣6️⃣ Emit the event in the createTweet() function below
emit TweetCreated(newTweet, id, newTweet.author, newTweet.content, newTweet.timestamp);
}
// 1️⃣3️⃣ Add a function to like the tweet (Hint: there should be 2 paramenters, id and author
// 1️⃣5️⃣ Mark both functions external
require tweets[author][id].id == id, "TWEET DOES NOT EXISTS";
function likeTweet(address author, uint256 id) external {
tweets[author][id].likes++; //likes = likes + 1;
// 1️⃣8️⃣ Emit the event in the likeTweet() function below
emit TweetLiked(msg.sender, author, id, tweets[author][id].likes);
}
// 1️⃣4️⃣ Add a function to unlike the tweet (make sure you can unlike only if likes count is greater than 0)
// 1️⃣5️⃣ Mark both functions external
function unlikeTweet(address author, uint256 id) external {
require(tweets[author][id].id == id, "TWEET DOES NOT EXISTS");
require(tweets[author][id].likes > 0, "TWEET HAS NO LIKES");
tweets[author][id].likes--;
emit TweetUnLiked(msg.sender, author, id, tweets[author][id].likes);
// you cannnot decrement the count if the tweet does not exist
// also the count cannot be negative
}
// 4️⃣ Create a function to get tweet
// not only 1 tweet, so use index to specify the tweet
function getTweet(uint _i) public view returns (Tweet memory) {
return tweets[msg.sender][_i]; //only check for information
}
function getAllTweets(address _owner) public view returns (Tweet[] memory){
return tweets[_owner];
}
}
create a twitter contract and a user profile contract (exercise user)
// SPDX-License-Identifier: MIT
// 1️⃣ Save UserProfile to the mapping in the setProfile() function
// HINT: don't forget to set the _displayName and _bio
pragma solidity ^0.8.0;
contract Profile {
struct UserProfile {
string displayName;
string bio;
}
string maxSupply = 50;
mapping(address => UserProfile) public profiles;
function setProfile(string memory _displayName, string memory _bio) public {
// CODE HERE 👇
profiles[msg.sender] = UserProfile(_displayName, _bio);
}
function getProfile(address _user) public view returns (UserProfile memory) {
return profiles[_user];
}
}
exercise(main)
// SPDX-License-Identifier: MIT
import "@openzeppelin/contracts/access/Ownable.sol";
pragma solidity ^0.8.0;
interface IProfile {
struct UserProfile {
string displayName;
string bio;
}
// 2️⃣ Add a getProfile() function to the interface ✅
// CODE HERE
function getProfile (address _user) external view returns (UserProfile memory)
}
contract Twitter is Ownable {
uint16 public MAX_TWEET_LENGTH = 280;
struct Tweet {
uint256 id;
address author;
string content;
uint256 timestamp;
uint256 likes;
}
mapping(address => Tweet[] ) public tweets;
// profile contract defined here
IProfile profileContract;
// Define the events
event TweetCreated(uint256 id, address author, string content, uint256 timestamp);
event TweetLiked(address liker, address tweetAuthor, uint256 tweetId, uint256 newLikeCount);
event TweetUnliked(address unliker, address tweetAuthor, uint256 tweetId, uint256 newLikeCount);
// 4️⃣ Create a modifier called onlyRegistered that require the msg.sender to have a profile ✅
// HINT: use the getProfile() to get the user
// HINT: check if displayName.length > 0 to make sure the user exists
modifier onlyRegistered(){
IProfile.UserProfile memory userProfileTemp = profileContract.getProfile(
require(bytes(userProfileTemp.displayName).length > 0, "USER NOT REGISTERED");
_);
}
// 3️⃣ Initialize the IProfile in the contructor ✅
// HINT: don't forget to include the _profileContract address as a input
constructor(address _profileContract) {
profileContract = IProfile(_profileContract);
}
function changeTweetLength(uint16 newTweetLength) public onlyOwner {
MAX_TWEET_LENGTH = newTweetLength;
}
function getTotalLikes(address _author) external view returns(uint) {
uint totalLikes;
for( uint i = 0; i < tweets[_author].length; i++){
totalLikes += tweets[_author][i].likes;
}
return totalLikes;
}
function createTweet(string memory _tweet) public {
require(bytes(_tweet).length <= MAX_TWEET_LENGTH, "Tweet is too long bro!" );
Tweet memory newTweet = Tweet({
id: tweets[msg.sender].length,
author: msg.sender,
content: _tweet,
timestamp: block.timestamp,
likes: 0
});
tweets[msg.sender].push(newTweet);
// Emit the TweetCreated event
emit TweetCreated(newTweet.id, newTweet.author, newTweet.content, newTweet.timestamp);
}
// 5️⃣ ADD the onlyRegistered modified to createTweet, likeTweet, and unlikeTweet function ✅
function likeTweet(address author, uint256 id) external onlyRegistered {
require(tweets[author][id].id == id, "TWEET DOES NOT EXIST");
tweets[author][id].likes++;
// Emit the TweetLiked event
emit TweetLiked(msg.sender, author, id, tweets[author][id].likes);
}
// 5️⃣ ADD the onlyRegistered modified to createTweet, likeTweet, and unlikeTweet function ✅
function unlikeTweet(address author, uint256 id) external onlyRegistered {
require(tweets[author][id].id == id, "TWEET DOES NOT EXIST");
require(tweets[author][id].likes > 0, "TWEET HAS NO LIKES");
tweets[author][id].likes--;
emit TweetUnliked(msg.sender, author, id, tweets[author][id].likes );
}
function getTweet( uint _i) public view returns (Tweet memory) {
return tweets[msg.sender][_i];
}
function getAllTweets(address _owner) public view returns (Tweet[] memory ){
return tweets[_owner];
}
}
Add Account create to Twitter Dapp
useStateVariables