Tweets Module Clean Up:

In this step, spilt /twittme-web/src/tweets/components.js into multiple files.

move “ActionBtn” out:

  • create a /twittme-web/src/tweets/buttons.js
    • cut ActionBtn() from /twittme-web/src/tweets/components.js to here
    • add import modules ```javascript import React from “react”;

import { apiTweetAction } from “./lookup”;

export function ActionBtn(props) { const { tweet, action, didPerformAction } = props; const likes = tweet.likes ? tweet.likes : 0;

  1. const className = props.className
  2. ? props.className
  3. : "btn btn-primary btn-sm";
  4. const actionDisplay = action.display ? action.display : "Action"; //ensure that we have an action
  5. const handleActionBackendEvent = (response, status) => {
  6. console.log(response, status);
  7. if ((status === 200 || status === 201) && didPerformAction) {
  8. didPerformAction(response, status);
  9. }
  10. };
  11. const handleClick = (event) => {
  12. event.preventDefault();
  13. apiTweetAction(tweet.id, action.type, handleActionBackendEvent);
  14. };
  15. const display =
  16. action.type === "like" ? `${likes} ${actionDisplay}` : actionDisplay;
  17. return (
  18. <button className={className} onClick={handleClick}>
  19. {display}
  20. </button>
  21. );

}

  1. - import ActionBtn() in /twittme-web/src/tweets/components.js
  2. ```javascript
  3. //...
  4. import {
  5. apiTweetCreate,
  6. apiTweetList,
  7. //apiTweetAction
  8. } from "./lookup";
  9. import {ActionBtn} from "./buttons" //new
  10. //...
  • import and export ActionBtn() in /twittme-web/src/tweets/index.js ```javascript import { //ActionBtn, Tweet, TweetsList, TweetsComponent, } from “./components”;

import { ActionBtn } from “./buttons”; //new

export { ActionBtn, Tweet, TweetsList, TweetsComponent };

  1. <a name="t4g5x"></a>
  2. #### move list and detail outside:
  3. - create /twittme-web/src/tweets/list.js and /twittme-web/src/tweets/detail.js to send out more functions
  4. - detail.js
  5. - **cut** ParentTweet() and Tweet() from /twittme-web/src/tweets/components.js to here
  6. - import required modules
  7. ```javascript
  8. import React, { useState } from "react";
  9. import { ActionBtn } from "./buttons";
  10. export function ParentTweet(props) {
  11. const { tweet } = props;
  12. return tweet.parent ? (
  13. <div className="row">
  14. <div className="col-11 mx-auto p-3 border rounded">
  15. <p className="mb-0 text-muted small">Retweet</p>
  16. <Tweet hideActions className={" "} tweet={tweet.parent} />
  17. </div>
  18. </div>
  19. ) : null;
  20. }
  21. export function Tweet(props) {
  22. const { tweet, didRetweet, hideActions } = props;
  23. const [actionTweet, setActionTweet] = useState(
  24. props.tweet ? props.tweet : null
  25. );
  26. const className = props.className
  27. ? props.className
  28. : "col-10 mx-auto col-md-6";
  29. const handlePerformAction = (newActionTweet, status) => {
  30. if (status === 200) {
  31. setActionTweet(newActionTweet);
  32. } else if (status === 201) {
  33. if (didRetweet) {
  34. didRetweet(newActionTweet);
  35. }
  36. }
  37. };
  38. return (
  39. <div className={className}>
  40. <div>
  41. <p>
  42. {tweet.id} - {tweet.content}
  43. </p>
  44. <ParentTweet tweet={tweet} />
  45. </div>
  46. {actionTweet && hideActions !== true && (
  47. <div className="btn btn-group">
  48. <ActionBtn
  49. tweet={actionTweet}
  50. didPerformAction={handlePerformAction}
  51. action={{ type: "like", display: "Likes" }}
  52. />
  53. <ActionBtn
  54. tweet={actionTweet}
  55. didPerformAction={handlePerformAction}
  56. action={{ type: "unlike", display: "Unlike" }}
  57. />
  58. <ActionBtn
  59. tweet={actionTweet}
  60. didPerformAction={handlePerformAction}
  61. action={{ type: "retweet", display: "Retweet" }}
  62. />
  63. </div>
  64. )}
  65. </div>
  66. );
  67. }
  • list.js
    • cut TweetsList() from /twittme-web/src/tweets/components.js to here
    • import required modules ```javascript import React, { useEffect, useState } from “react”;

import { apiTweetList } from “./lookup”;

import { Tweet } from “./detail”;

export function TweetsList(props) { //console.log(props.username) const [tweetsInit, setTweetsInit] = useState([]); const [tweets, setTweets] = useState([]);

const [tweetsDidSet, setTweetsDidSet] = useState(false);

useEffect(() => { const final = […props.newTweets].concat(tweetsInit); //[…content] means a new list with the content if (final.length !== tweets.length) { setTweets(final); } }, [props.newTweets, tweets, tweetsInit]);

useEffect(() => { if (tweetsDidSet === false) { const handleTweetListLookup = (response, status) => { if (status === 200) { setTweetsInit(response); setTweetsDidSet(true); } else { alert(“There was an error”); } }; apiTweetList(props.username, handleTweetListLookup); } }, [tweetsInit, tweetsDidSet, setTweetsDidSet, props.username]);

//new const handleDidRetweet = (newTweet) => { const updateTweetsInit = […tweetsInit]; //grabbing tweetsInit list updateTweetsInit.unshift(newTweet); //add newTweet to the beginning of updateTweetsInit setTweetsInit(updateTweetsInit); //update status const updateFinalTweets = […tweets]; //grabbing tweets list updateFinalTweets.unshift(tweets); //add tweets to the beginning of updateFinalTweets setTweets(updateFinalTweets); //update status };

return tweets.map((item, index) => { return ( ); }); }

  1. - import and export functions in /twittme-web/src/tweets/index.js
  2. ```javascript
  3. import {
  4. //Tweet,
  5. //TweetsList,
  6. TweetsComponent,
  7. } from "./components";
  8. import { ActionBtn } from "./buttons";
  9. import {Tweet} from "./detail"; //new
  10. import {TweetsList} from "./list"; //new
  11. export { ActionBtn, Tweet, TweetsList, TweetsComponent };
  • and remove some imports in /twittme-web/src/tweets/components.js ```javascript import React, { //useEffect, useState, } from “react”;

import { apiTweetCreate, //apiTweetList } from “./lookup”;

//import { ActionBtn } from “./buttons”;

import { TweetsList } from “./list”;

  1. <a name="J1hjn"></a>
  2. ####
  3. <a name="42o4W"></a>
  4. #### move create outside:
  5. - in /twittme-web/src/tweets/components.js
  6. - split TweetsComponent() into TweetsComponent() and TweetCreate()
  7. ```javascript
  8. import React, { useState } from "react";
  9. import { apiTweetCreate } from "./lookup";
  10. import { TweetsList } from "./list";
  11. function TweetCreate(props) {
  12. const textAreaRef = React.createRef();
  13. const { didTweet } = props;
  14. //const [newTweets, setNewTweets] = useState([]);
  15. //const canTweet = props.canTweet === "false" ? false : true;
  16. const handleBackendUpdate = (response, status) => {
  17. //let tempNewTweets = [...newTweets];
  18. if (status === 201) {
  19. //tempNewTweets.unshift(response);
  20. //setNewTweets(tempNewTweets);
  21. //didTweet(tempNewTweets);
  22. didTweet(response);
  23. } else {
  24. console.log(response);
  25. alert("An error occured please try again");
  26. }
  27. };
  28. const handleSubmit = (event) => {
  29. event.preventDefault();
  30. const newVal = textAreaRef.current.value;
  31. console.log("new value", newVal);
  32. apiTweetCreate(newVal, handleBackendUpdate);
  33. textAreaRef.current.value = ""; //after the submission, clear the textarea
  34. };
  35. return (
  36. //<div className={"col-12 mb-3"}>
  37. <div className={props.className}>
  38. <form onSubmit={handleSubmit}>
  39. <textarea
  40. ref={textAreaRef}
  41. required={true}
  42. className="form-control"
  43. name="tweet"
  44. ></textarea>
  45. <button type="submit" className="btn btn-primary my-3">
  46. Tweet
  47. </button>
  48. </form>
  49. </div>
  50. );
  51. }
  52. export function TweetsComponent(props) {
  53. //const textAreaRef = React.createRef();
  54. const [newTweets, setNewTweets] = useState([]);
  55. //passed canTweet is a string, not a boolean
  56. const canTweet = props.canTweet === "false" ? false : true;
  57. //const handleBackendUpdate = (response, status) => {
  58. const handleNewTweet = (newTweet) => {
  59. let tempNewTweets = [...newTweets];
  60. tempNewTweets.unshift(newTweet);
  61. setNewTweets(tempNewTweets);
  62. /*
  63. if (status === 201) {
  64. tempNewTweets.unshift(response);
  65. setNewTweets(tempNewTweets);
  66. } else {
  67. console.log(response);
  68. alert("An error occured please try again");
  69. }
  70. */
  71. };
  72. /*
  73. const handleSubmit = (event) => {
  74. event.preventDefault();
  75. const newVal = textAreaRef.current.value;
  76. console.log("new value", newVal);
  77. apiTweetCreate(newVal, handleBackendUpdate);
  78. textAreaRef.current.value = ""; //after the submission, clear the textarea
  79. };
  80. */
  81. return (
  82. <div className={props.className}>
  83. {canTweet === true && (
  84. <TweetCreate didTweet={handleNewTweet} className="col-12 mb-3" />
  85. )}
  86. <TweetsList newTweets={newTweets} {...props} />
  87. </div>
  88. );
  89. /*
  90. return (
  91. <div className={props.className}>
  92. {canTweet === true && (
  93. <div className="col-12 mb-3">
  94. <form onSubmit={handleSubmit}>
  95. <textarea
  96. ref={textAreaRef}
  97. required={true}
  98. className="form-control"
  99. name="tweet"
  100. ></textarea>
  101. <button type="submit" className="btn btn-primary my-3">
  102. Tweet
  103. </button>
  104. </form>
  105. </div>
  106. )}
  107. <TweetsList newTweets={newTweets} {...props} />
  108. </div>
  109. );
  110. */
  111. }
  • create /twittme-web/src/tweets/create.js
    • cut TweetCreate() from /twittme-web/src/tweets/components.js
    • import necessary modules ```javascript import React from “react”;

import { apiTweetCreate } from “./lookup”;

//function TweetCreate(props) { export function TweetCreate(props) { const textAreaRef = React.createRef();

const { didTweet } = props;

const handleBackendUpdate = (response, status) => { if (status === 201) { didTweet(response); } else { console.log(response); alert(“An error occured please try again”); } };

const handleSubmit = (event) => { event.preventDefault(); const newVal = textAreaRef.current.value;

  1. console.log("new value", newVal);
  2. apiTweetCreate(newVal, handleBackendUpdate);
  3. textAreaRef.current.value = ""; //after the submission, clear the textarea

};

return (

<textarea ref={textAreaRef} required={true} className=”form-control” name=”tweet”

  1. ></textarea>
  2. <button type="submit" className="btn btn-primary my-3">
  3. Tweet
  4. </button>
  5. </form>
  6. </div>

); }

  1. - change import in /twittme-web/src/tweets/components.js
  2. ```javascript
  3. import React, { useState } from "react";
  4. //import { apiTweetCreate } from "./lookup";
  5. import { TweetsList } from "./list";
  6. import { TweetCreate } from "./create" //new
  7. //...
  • import and export in /twittme-web/src/tweets/index.js ```javascript import { TweetsComponent } from “./components”;

import { ActionBtn } from “./buttons”;

import { Tweet } from “./detail”;

import { TweetsList } from “./list”;

import { TweetCreate } from “./create”; // new

export { ActionBtn, Tweet, TweetsList, TweetsComponent, TweetCreate //new };

  1. after 3 steps, we have a cleaner module with separate functional files.
  2. <a name="mvtbY"></a>
  3. ### Lookup & Render & Embed Individual Tweets:
  4. <a name="4E3oF"></a>
  5. #### render only one tweet:
  6. This step focus on find and render an individual tweet.
  7. - in /twittme-web/src/tweets/components.js
  8. - add a new function TweetDetailComponent() to render individual tweet with a tweet id:
  9. ```javascript
  10. import React, {
  11. useState,
  12. useEffect, //new
  13. } from "react";
  14. import { TweetsList } from "./list";
  15. import { TweetCreate } from "./create";
  16. import { apiTweetDetail } from "./lookup"; //new
  17. import { Tweet } from "./detail"; //new
  18. //...
  19. export function TweetDetailComponent(props) {
  20. const { tweetId } = props; //grab id from props
  21. const [didLookup, setDidLookup] = useState(false);
  22. const [tweet, setTweet] = useState(null);
  23. const handleBackendLookup = (response, status) => {
  24. if (status === 200) {
  25. setTweet(response);
  26. } else {
  27. alert("There was an error finding your tweet.");
  28. }
  29. };
  30. useEffect(() => {
  31. if (didLookup === false) {
  32. apiTweetDetail(tweetId, handleBackendLookup);
  33. setDidLookup(true);
  34. }
  35. }, [tweetId, didLookup, setDidLookup]);
  36. return tweet === null ? null : (
  37. <Tweet tweet={tweet} className={props.className} />
  38. ); //return the requested tweet with tweetID found, or return null
  39. }
  • As usual, import and export it in /twittme-web/src/tweets/index.js ```javascript import { TweetsComponent, TweetDetailComponent, // new } from “./components”;

import { ActionBtn } from “./buttons”;

import { Tweet } from “./detail”;

import { TweetsList } from “./list”;

import { TweetCreate } from “./create”;

export { ActionBtn, Tweet, TweetsList, TweetsComponent, TweetCreate, TweetDetailComponent, //new };

  1. - in /twittme-web/src/index.js
  2. ```javascript
  3. import React from "react";
  4. import ReactDOM from "react-dom";
  5. import "./index.css";
  6. import App from "./App";
  7. import * as serviceWorker from "./serviceWorker";
  8. import {
  9. TweetsComponent,
  10. TweetDetailComponent, //new
  11. } from "./tweets";
  12. //...
  13. const tweetDetailElements = document.querySelectorAll(".tweetme-2-detail"); //get all elements with this class
  14. //render for each tweet detail element
  15. tweetDetailElements.forEach((container) => {
  16. ReactDOM.render(e(TweetDetailComponent, container.dataset), container);
  17. });
  • apply these codes in /twittme-web/public/index.html

    1. <body>
    2. <noscript>You need to enable JavaScript to run this app.</noscript>
    3. <!--
    4. <div id="tweetme-2" data-username="cfe" data-can-tweet="false"></div>
    5. -->
    6. <!--
    7. class name is same as in /twittme-web/src/index.js
    8. tweet-id here is 12
    9. -->
    10. <div class='tweetme-2-detail' data-tweet-id="12" data-class-name='col-6 mx-auto'></div>
    11. <!-- "d-none" to ignore -->
    12. <div class='d-none' id="tweetme-2" data-username="cfe" data-can-tweet="false"></div>
    13. <!--
    14. ...
    15. -->
    16. </body>

    to test, runserver and npm start, access localhost:30
    image.png
    only tweet with id 12 is shown here.

“capitalize”:

  • add more lines in /twittme-web/public/index.html

    1. <body>
    2. <noscript>You need to enable JavaScript to run this app.</noscript>
    3. <div class='tweetme-2-detail' data-tweet-id="12" data-class-name='col-6 mx-auto'></div>
    4. <div class='tweetme-2-detail' data-tweet-id="45" data-class-name='col-6 mx-auto'></div>
    5. <div class='tweetme-2-detail' data-tweet-id="108" data-class-name='col-6 mx-auto'></div>
    6. <!-- "d-none" to ignore -->
    7. <div class='d-none' id="tweetme-2" data-username="cfe" data-can-tweet="false"></div>
    8. <!--
    9. ...
    10. -->
    11. </body>

    refresh:
    image.png
    3 tweets with requested ids are shown.

  • remove ‘d-none’ for main tweet lists in /twittme-web/public/index.html

    1. <body>
    2. <noscript>You need to enable JavaScript to run this app.</noscript>
    3. <div class='tweetme-2-detail' data-tweet-id="12" data-class-name='col-6 mx-auto'></div>
    4. <div class='tweetme-2-detail' data-tweet-id="45" data-class-name='col-6 mx-auto'></div>
    5. <div class='tweetme-2-detail' data-tweet-id="108" data-class-name='col-6 mx-auto'></div>
    6. <!--
    7. <div class='d-none' id="tweetme-2" data-username="cfe" data-can-tweet="false"></div>
    8. -->
    9. <div id="tweetme-2" data-username="cfe" data-can-tweet="false"></div>
    10. <!--
    11. ...
    12. -->
    13. </body>

    refresh:
    image.png
    we can use this method to capitalize some tweets for some cases.

Linking Individual Tweets:

Right now in reactjs page in the project, even specify tweet number such as http://localhost:30/123
The page still shows everything:
image.png
so we need to create links to individuals tweets.

adding div dynamically:

  • use a script to finish rendering tweets individually in /twittme-web/public/index.html

    • (only render tweet with id 12 here as an example) ```html

  1. <!--
  2. ...
  3. -->

  1. refresh:<br />![image.png](https://cdn.nlark.com/yuque/0/2020/png/1243266/1595061558708-ed25d33a-21a7-4f86-820c-1e68b98343c3.png#align=left&display=inline&height=450&margin=%5Bobject%20Object%5D&name=image.png&originHeight=899&originWidth=1701&size=47691&status=done&style=none&width=850.5)<br />same as before, we have a tweet 12 at the top.
  2. <a name="OKYRp"></a>
  3. #### load id based on url:
  4. - in /twittme-web/public/index.html
  5. - add a switch "if not default path, hide the main tweet list"
  6. ```html
  7. <body>
  8. <noscript>You need to enable JavaScript to run this app.</noscript>
  9. <div id='tweet-container'></div>
  10. <div id="tweetme-2" data-username="cfe" data-can-tweet="false"></div>
  11. <script>
  12. var path = window.location.pathname
  13. if(path !== "/") {
  14. var tweetme2El = document.getElementById("tweetme-2")
  15. tweetme2El.className = 'd-none'
  16. var tweetId = 12
  17. var tweetEl = "<div class='tweetme-2-detail' data-tweet-id=" + tweetId + " data-class-name='col-6 mx-auto'></div>"
  18. var mainContainer = document.getElementById("tweet-container")
  19. mainContainer.innerHTML = tweetEl
  20. }
  21. /*
  22. var tweetId = 12
  23. var tweetEl = "<div class='tweetme-2-detail' data-tweet-id=" + tweetId + " data-class-name='col-6 mx-auto'></div>"
  24. var mainContainer = document.getElementById("tweet-container")
  25. mainContainer.innerHTML = tweetEl
  26. */
  27. </script>
  28. <!--
  29. ...
  30. -->
  31. </body>

refresh and access non-default paths, such as http://localhost:30/6
image.png
The main tweet list is hidden.
Because provided tweetId is still 12, so the shown tweet is still tweet 12.

  • in /twittme-web/public/index.html

    • use regular expression to find tweet id and look for that tweet ```html

  1. <!--
  2. ...
  3. -->

  1. refresh and access some new links: <br />![image.png](https://cdn.nlark.com/yuque/0/2020/png/1243266/1595062457605-203cbdc3-9b14-4338-8d74-9fb1b9952f7b.png#align=left&display=inline&height=366&margin=%5Bobject%20Object%5D&name=image.png&originHeight=731&originWidth=1621&size=32915&status=done&style=none&width=810.5)<br />![image.png](https://cdn.nlark.com/yuque/0/2020/png/1243266/1595062473603-aa6b3289-11b4-4a97-b9a3-d1e0040486b9.png#align=left&display=inline&height=350&margin=%5Bobject%20Object%5D&name=image.png&originHeight=699&originWidth=1670&size=34802&status=done&style=none&width=835)<br />![image.png](https://cdn.nlark.com/yuque/0/2020/png/1243266/1595062519147-693abee6-209f-49af-a33f-b5fe2642578f.png#align=left&display=inline&height=335&margin=%5Bobject%20Object%5D&name=image.png&originHeight=669&originWidth=1637&size=32587&status=done&style=none&width=818.5)
  2. <a name="SHOeT"></a>
  3. #### create link to the page:
  4. - in /twittme-web/src/tweets/detail.js
  5. - add the 4th button
  6. ```javascript
  7. export function Tweet(props) {
  8. const { tweet, didRetweet, hideActions } = props;
  9. //...
  10. //new
  11. const isDetail = false;
  12. //new
  13. const handleLink = (event) => {
  14. event.preventDefault();
  15. window.location.href = `/${tweet.id}`;
  16. };
  17. //...
  18. return (
  19. <div className={className}>
  20. <div>
  21. <p>
  22. {tweet.id} - {tweet.content}
  23. </p>
  24. <ParentTweet tweet={tweet} />
  25. </div>
  26. {actionTweet && hideActions !== true && (
  27. <div className="btn btn-group">
  28. <ActionBtn
  29. tweet={actionTweet}
  30. didPerformAction={handlePerformAction}
  31. action={{ type: "like", display: "Likes" }}
  32. />
  33. <ActionBtn
  34. tweet={actionTweet}
  35. didPerformAction={handlePerformAction}
  36. action={{ type: "unlike", display: "Unlike" }}
  37. />
  38. <ActionBtn
  39. tweet={actionTweet}
  40. didPerformAction={handlePerformAction}
  41. action={{ type: "retweet", display: "Retweet" }}
  42. />
  43. {isDetail === true ? null : ( //new
  44. <button className="btn btn-outline-primary" onClick={handleLink}>
  45. View
  46. </button>
  47. )}
  48. </div>
  49. )}
  50. </div>
  51. );
  52. }

refresh and click view for any tweet:
image.png
entered this page, click “view” on this page:
image.png
nothing happened, cause it’s already a detailed page now.

  • next we can move that regular expression searching method to /twittme-web/src/tweets/detail.js

    1. export function Tweet(props) {
    2. const { tweet, didRetweet, hideActions } = props;
    3. //...
    4. //new
    5. const path = window.location.pathname;
    6. const match = path.match(/(?<tweetid>\d+)/);
    7. const urlTweetId = match ? match.groups.tweetid : -1;
    8. //const isDetail = false;
    9. const isDetail = `${tweet.id}` === `${urlTweetId}`;
    10. const handleLink = (event) => {
    11. event.preventDefault();
    12. window.location.href = `/${tweet.id}`;
    13. };
    14. //...
    15. }

    React Fragment:

    we have a problem that
    image.png
    we have no view button for parents, and
    image.png
    even for detailed page, here we should have a view button.

to fix that:

  • in /twittme-web/src/tweets/detail.js

    • add a view button for every retweeted tweets in the retweet: ```javascript export function Tweet(props) { //…

    return (

    1. <div>
    2. <p>
    3. {tweet.id} - {tweet.content}
    4. </p>
    5. <ParentTweet tweet={tweet} />
    6. </div>
    7. <div className="btn btn-group">
    8. {actionTweet && hideActions !== true && (
    9. <React.Fragment>
    10. <ActionBtn
    11. tweet={actionTweet}
    12. didPerformAction={handlePerformAction}
    13. action={{ type: "like", display: "Likes" }}
    14. />
    15. <ActionBtn
    16. tweet={actionTweet}
    17. didPerformAction={handlePerformAction}
    18. action={{ type: "unlike", display: "Unlike" }}
    19. />
    20. <ActionBtn
    21. tweet={actionTweet}
    22. didPerformAction={handlePerformAction}
    23. action={{ type: "retweet", display: "Retweet" }}
    24. />
    25. </React.Fragment>
    26. )}
    27. {isDetail === true ? null : ( //new
    28. <button className="btn btn-outline-primary" onClick={handleLink}>
    29. View
    30. </button>
    31. )}
    32. </div>

    ); /* return (

    1. <div>
    2. <p>
    3. {tweet.id} - {tweet.content}
    4. </p>
    5. <ParentTweet tweet={tweet} />
    6. </div>
    7. {actionTweet && hideActions !== true &&
    8. <div className="btn btn-group">
    9. <ActionBtn
    10. tweet={actionTweet}
    11. didPerformAction={handlePerformAction}
    12. action={{ type: "like", display: "Likes" }}
    13. />
    14. <ActionBtn
    15. tweet={actionTweet}
    16. didPerformAction={handlePerformAction}
    17. action={{ type: "unlike", display: "Unlike" }}
    18. />
    19. <ActionBtn
    20. tweet={actionTweet}
    21. didPerformAction={handlePerformAction}
    22. action={{ type: "retweet", display: "Retweet" }}
    23. />
    24. {isDetail === true ? null : ( //new
    25. <button className="btn btn-outline-primary" onClick={handleLink}>
    26. View
    27. </button>
    28. )}
    29. </div>
    30. )}

    ); */ }

``` and now we have view button for parents.
refresh and test:
image.png
image.png