IC开发:Microblog 博客

简单记录 IC 开发 Microblog 博客

 

要求
1. 显示消息的发布者名字
  a. 给 Message 增加 author 字段
  b. 增加 set_name 和 get_name 公共方法
2. 增加以下新 UI
  a. 显示目前 follow 的作者列表
  b. 在 timeline 中显示消息作者和时间
3. 关注(follow)其它发布者的 canister
  a. 关注三个以上作者
  b. 点击作者名字,会显示对方发布的消息列表

 

  前端页面:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width" />
    <title>Microblog</title>
    <base href="/" />
    <link rel="icon" href="favicon.ico" />
    <link rel="stylesheet" href="https://cdn.simplecss.org/simple.min.css"/>
  </head>
  <body>
    <header><h1>Microblog</h1></header>
    <main>
      <div>
        <label for="message">What do you want to post?</label>
        <textarea id="message"></textarea>
        <label for="pwd">password</label>
        <input id="pwd"/><br/>
        <button id="post">Post</button>
        <span id="error"></span>
        <section id="posts"></section>
      </div>
      <br/>
      <div>
        <label for="name">Get or set name:</label>
        <input id="name"/>
        <span id="info"></span><br/>
        <button id="getName">Get</button>
        <button id="setName">Set</button>
      </div>
      <br/>
      <div> 
        <section id="follow"></section>
      </div>
    </main>
  </body>
</html>

 

  JS:

import { Int } from "@dfinity/candid/lib/cjs/idl";
import { goodbye_backend } from "../../declarations/goodbye_backend";

async function post() {
  let post_button = document.getElementById("post");
  let err = document.getElementById("error");
  err.innerText = "";
  post_button.ariaDisabled = true;
  let textarea = document.getElementById("message");
  let text = textarea.value;
  let pwd = document.getElementById("pwd").value;
  try { 
    await goodbye_backend.post(pwd, text);
    textarea.value = "";
  } catch (error) {
    console.error(error);
    err.innerText = "Failed";
  }
  post_button.ariaDisabled = false;
}

var num_posts = 0;
async function load_posts() {
  let posts_section = document.getElementById("posts");
  let posts = await goodbye_backend.posts(1);
  if (num_posts == posts.length) return;
  posts_section.replaceChildren([]);
  num_posts = posts.length;
  for(var i = num_posts - 1; i >= 0; i --) {
    let post = document.createElement("p");
    post.innerText = posts[i].author + " SAY: " +  posts[i].text + " - " + Date((BigInt(posts[i].time) / BigInt(1000000000))).toLocaleString(); 
    posts_section.appendChild(post);
  }
}

async function getName() {
  let get_button = document.getElementById("getName");
  get_button.disabled = true;
  let name = await goodbye_backend.get_name();
  document.getElementById("name").value = name;
  get_button.disabled = false;
}

async function setName() {
  let set_button = document.getElementById("setName");
  let info = document.getElementById("info");
  info.innerText = "";
  set_button.disabled = true;
  let nameIn = document.getElementById("name");
  let name = nameIn.value;
  try { 
    await goodbye_backend.set_name(name);
    nameIn.value = "";
    info.innerText = "Success";
  } catch (error) {
    console.error(error);
    info.innerText = "Failed";
  }
  set_button.disabled = false;
}

var num_follows = 0;
async function follows() {
  let follows = await goodbye_backend.follows();
  if (num_follows == follows.length) return;
  num_follows = follows.length;
  let info = "";
  for(var i = 0; i < num_follows; i ++) {
    let id = follows[i]['uid'];
    info += "<button class='info_btn' id= " + id + ">" + follows[i]['uname'] + "</button>";
    info += "<section id=" + id + "-msg" + "></section></br>";
  }
  document.getElementById("follow").innerHTML = info;
  for(var i = 0; i < num_follows; i ++) {
    let id = follows[i]['uid'];
    document.getElementById(id).addEventListener('click', function handleClick(event) {
      load_posts2(id);
    })
  }
}

var num_posts2 = 0;
async function load_posts2(id) {
  alert("Id: " + id + " , Please wait");
  let posts_section2 = document.getElementById(id + "-msg");
  try {
    let posts2 = await goodbye_backend.posts2(id, 1);
    if (num_posts2 == posts2.length) return;
    posts_section2.replaceChildren([]);
    num_posts2 = posts2.length;
    for(var i = num_posts2 - 1; i >= 0; i --) {
      let post2 = document.createElement("p");
      post2.innerText = posts2[i].author + " SAY: " +  posts2[i].text + " - " + Date((BigInt(posts2[i].time) / BigInt(1000000000))).toLocaleString(); 
      posts_section2.appendChild(post2);
    }
  } catch (error) {
    console.error(error);
  }
}

async function load() {
  let post_button = document.getElementById("post");
  post_button.onclick = post;
  load_posts();
  setInterval(load_posts, 5000);

  let get_button = document.getElementById("getName");
  get_button.onclick = getName;

  let set_button = document.getElementById("setName");
  set_button.onclick = setName;

  follows();
}

window.onload = load;

 

  Motoko:

import List "mo:base/List";
import Iter "mo:base/Iter";
import Principal "mo:base/Principal";
import Time "mo:base/Time";
import Nat "mo:base/Nat";

actor {
  public type Message = {
    author: ?Text;
    text: Text; 
    time: Time.Time;
  };

  public type User  = {
    uid: Principal;
    uname: ?Text;
  };

  var king: Text = "Linnuo";

  public type Microblog = actor {
    follow: shared(Principal) -> async (); // add关注对象
    follows: shared query () -> async [User]; // return关注对象列表
    post: shared (Text) -> async (); // 发布新消息
    posts: shared (Time.Time) -> async [Message]; // return 发布消息列表
    posts2: shared (Text) -> async [Message]; // return 所有关注对象发布的消息
    timeline: shared (Time.Time) -> async [Message]; // return 所有关注对象发布的消息
    set_name: shared (Text) -> async ();  // set 名字
    get_name: shared query () -> async ?Text; // get 名字
    unfollow: shared () -> async ();  // 清空跟随
  };

  // stable 修饰:升级不清空内存
  stable var followed : List.List<User> = List.nil();

  public shared func follow(id: Principal) : async (){
    let canister : Microblog = actor(Principal.toText(id));
    let name: ?Text = await canister.get_name();
    let u = {
      uid = id;
      uname = name;
    };
    followed := List.push(u, followed);
  };

  public shared query func follows() : async [User] {
    List.toArray(followed);
  };

  stable var messages : List.List<Message> = List.nil();

  // (msg):获取消息属性
  public shared (msg) func post(pwd: Text, text: Text) : async (){
    // 获取发送者: dfx identity get-principal
    // assert(Principal.toText(msg.caller) == "vw7ov-537vk-abslh-s2gx7-gw2ff-v4u6y-thlcs-hejxf-hkkc5-bjiq7-pqe"); //消息发送者
    assert(pwd == "qwe123");
    let m = {
      author = ?king;
      text = text;
      time = Time.now();
    };
    messages := List.push(m, messages);
    // 用钱包调用正常返回失败:dfx canister --wallet=$(dfx identity get-wallet) call microblog_backend post "(\"Second post\")"
  };

  public shared func posts(since: Time.Time) : async [Message] {
    var list : List.List<Message> = List.nil();

    for (m in Iter.fromList(messages)) {
      if (m.time >= since){
        list := List.push(m, list);
      }
    };

    List.toArray(list);
  };

  public shared func posts2(pid: Principal, since: Time.Time) : async [Message] {
    try {
      let canister : Microblog = actor(Principal.toText(pid));
      await canister.posts(since);
    } catch (err) {
      []
    }
  };

  public shared func timeline(since: Time.Time) : async [Message] {
    var all : List.List<Message> = List.nil();

    for (user in Iter.fromList(followed)){
      let canister : Microblog = actor(Principal.toText(user.uid));
      let msgs = await canister.posts(since);

      for(msg in Iter.fromArray(msgs)){
        all := List.push(msg, all);
      };
    };

    List.toArray(all);
  };

  public shared func set_name(name: Text) {
    king := name;
  };

  public shared query func get_name() : async ?Text {
    return ?king;
  };

  public shared func unfollow() : async (){
    followed := List.nil();
  };

  // 发送消息:id和名称可以呼唤
  //  dfx canister call rrkah-fqaaa-aaaaa-aaaaq-cai post "(\"First post\")"   // 用Id发消息
  //  dfx canister call microblog_backend post "(\"Second post\")"            // 用名称发消息
  //  dfx canister call rrkah-fqaaa-aaaaa-aaaaq-cai posts "()"                // 获取消息列表
  //  dfx canister call microblog_backend2 follow "(principal \"$(dfx canister id microblog_backend)\")"  // 添加关注对象
  //  dfx canister call microblog_backend2 follows "()"                       // 获取关注对象列表
  //  dfx canister call microblog_backend2 timeline  "()"                     // 获取所有关注对象发布的消息
};

 

代码的实现并不复杂,欢迎童鞋们指导哈

为了避免直接下载项目,这里就不贴 Github 入口了,好好学习,加油

 

posted @ 2022-09-29 10:00  林诺欧巴  阅读(128)  评论(0编辑  收藏  举报