├── .gitignore └── html-templates ├── 404.html ├── chat-visible.html ├── chat.js ├── create-post.html ├── docker-nginx.txt ├── fallback-avatar.jpg ├── home-guest.html ├── home-logged-in-no-results.html ├── home-logged-in-results.html ├── live-search.js ├── main.css ├── post.html ├── profile-posts.html ├── profile-users.html ├── search-visible.html ├── spa-profile.js └── vps-nginx.txt /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store -------------------------------------------------------------------------------- /html-templates/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | OurApp 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 |

OurApp

19 |
20 | 21 | 22 | 23 | Create Post 24 |
25 | 26 |
27 |
28 |
29 |
30 | 31 | 32 |
33 |
34 |

Whoops, we cannot find that page.

35 |

You can always visit the homepage to get a fresh start.

36 |
37 |
38 | 39 | 40 | 43 | 44 | 45 | 46 | 47 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /html-templates/chat-visible.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | OurApp 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 |

OurApp

19 |
20 | 21 | 22 | 23 | Create Post 24 |
25 | 26 |
27 |
28 |
29 |
30 | 31 | 32 |
33 |

The Latest From Those You Follow

34 |
35 | 36 | 37 | Example Post #1 38 | by kittydoe on 1/3/2019 39 | 40 | 41 | 42 | Example Post #2 43 | by barksalot on 1/3/2019 44 | 45 | 46 | 47 | Example Post #3 48 | by kittydoe on 1/3/2019 49 | 50 | 51 | 52 | Example Post #4 53 | by barksalot on 1/3/2019 54 | 55 |
56 |
57 | 58 | 59 |
60 |
61 | Chat 62 |
63 |
64 | 65 |
66 |
67 |
Hello, how are you?
68 |
69 | 70 |
71 | 72 | 73 | 74 |
75 | 76 |
77 |
78 | barksalot: 79 | I am doing well. How about you? 80 |
81 |
82 |
83 | 84 |
85 | 86 |
87 | 88 |
89 |
90 | 91 | 92 | 93 | 96 | 97 | 98 | 99 | 100 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /html-templates/chat.js: -------------------------------------------------------------------------------- 1 | import DOMPurify from "dompurify" 2 | 3 | export default class Chat { 4 | constructor() { 5 | this.openedYet = false 6 | this.chatWrapper = document.querySelector("#chat-wrapper") 7 | this.avatar = document.querySelector("#chat-wrapper").dataset.avatar 8 | this.openIcon = document.querySelector(".header-chat-icon") 9 | this.injectHTML() 10 | this.chatLog = document.querySelector("#chat") 11 | this.chatField = document.querySelector("#chatField") 12 | this.chatForm = document.querySelector("#chatForm") 13 | this.closeIcon = document.querySelector(".chat-title-bar-close") 14 | this.events() 15 | } 16 | 17 | // Events 18 | events() { 19 | this.chatForm.addEventListener("submit", e => { 20 | e.preventDefault() 21 | this.sendMessageToServer() 22 | }) 23 | this.openIcon.addEventListener("click", () => this.showChat()) 24 | this.closeIcon.addEventListener("click", () => this.hideChat()) 25 | } 26 | 27 | // Methods 28 | sendMessageToServer() { 29 | const test = document.createElement("div") 30 | test.innerHTML = DOMPurify.sanitize(this.chatField.value) 31 | 32 | axios.post("/send-chat-message", { "textvalue": this.chatField.value }) 33 | 34 | this.chatLog.insertAdjacentHTML( 35 | "beforeend", 36 | DOMPurify.sanitize(` 37 |
38 |
39 |
40 | ${test.textContent} 41 |
42 |
43 | 44 |
45 | `) 46 | ) 47 | this.chatLog.scrollTop = this.chatLog.scrollHeight 48 | this.chatField.value = "" 49 | this.chatField.focus() 50 | } 51 | 52 | hideChat() { 53 | this.chatWrapper.classList.remove("chat--visible") 54 | } 55 | 56 | showChat() { 57 | if (!this.openedYet) { 58 | this.openConnection() 59 | } 60 | this.openedYet = true 61 | this.chatWrapper.classList.add("chat--visible") 62 | this.chatField.focus() 63 | } 64 | 65 | openConnection() { 66 | Echo.private("chatchannel").listen("ChatMessage", e => { 67 | this.displayMessageFromServer(e.chat) 68 | }) 69 | } 70 | 71 | displayMessageFromServer(data) { 72 | this.chatLog.insertAdjacentHTML( 73 | "beforeend", 74 | DOMPurify.sanitize(` 75 |
76 | 77 |
78 | ${data.username}: 79 | ${data.textvalue} 80 |
81 |
82 | `) 83 | ) 84 | this.chatLog.scrollTop = this.chatLog.scrollHeight 85 | } 86 | 87 | injectHTML() { 88 | this.chatWrapper.classList.add("chat-wrapper--ready") 89 | this.chatWrapper.innerHTML = ` 90 |
Chat
91 |
92 | 93 |
94 | 95 |
96 | ` 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /html-templates/create-post.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | OurApp 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 |

OurApp

19 |
20 | 21 | 22 | 23 | Create Post 24 |
25 | 26 |
27 |
28 |
29 |
30 | 31 | 32 |
33 |
34 |
35 | 36 | 37 |
38 | 39 |
40 | 41 | 42 |
43 | 44 | 45 |
46 |
47 | 48 | 49 | 52 | 53 | 54 | 55 | 56 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /html-templates/docker-nginx.txt: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80 default_server; 3 | listen [::]:80 default_server; 4 | root /var/www/ourapp/public; 5 | 6 | add_header X-Frame-Options "SAMEORIGIN"; 7 | add_header X-XSS-Protection "1; mode=block"; 8 | add_header X-Content-Type-Options "nosniff"; 9 | client_max_body_size 10M; 10 | 11 | index index.html index.htm index.php; 12 | 13 | charset utf-8; 14 | 15 | location / { 16 | try_files $uri $uri/ /index.php?$query_string; 17 | } 18 | 19 | location = /favicon.ico { access_log off; log_not_found off; } 20 | location = /robots.txt { access_log off; log_not_found off; } 21 | 22 | error_page 404 /index.php; 23 | 24 | location ~ \.php$ { 25 | fastcgi_pass unix:/var/run/php/php8.1-fpm.sock; 26 | fastcgi_index index.php; 27 | fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; 28 | include fastcgi_params; 29 | } 30 | 31 | location ~ /\.(?!well-known).* { 32 | deny all; 33 | } 34 | } -------------------------------------------------------------------------------- /html-templates/fallback-avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LearnWebCode/laravel-course/27bf0490fc32e899d452308a8fdaa6d661cc77c9/html-templates/fallback-avatar.jpg -------------------------------------------------------------------------------- /html-templates/home-guest.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | OurApp 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 |

OurApp

19 |
20 |
21 |
22 | 23 |
24 |
25 | 26 |
27 |
28 | 29 |
30 |
31 |
32 |
33 |
34 | 35 | 36 |
37 |
38 |
39 |

Remember Writing?

40 |

Are you sick of short tweets and impersonal “shared” posts that are reminiscent of the late 90’s email forwards? We believe getting back to actually writing is the key to enjoying the internet again.

41 |
42 |
43 |
44 |
45 | 46 | 47 |
48 | 49 |
50 | 51 | 52 |
53 | 54 |
55 | 56 | 57 |
58 | 59 |
60 | 61 | 62 |
63 | 64 | 65 |
66 |
67 |
68 |
69 | 70 | 71 | 74 | 75 | 76 | 77 | 78 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /html-templates/home-logged-in-no-results.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | OurApp 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 |

OurApp

19 |
20 | 21 | 22 | 23 | Create Post 24 |
25 | 26 |
27 |
28 |
29 |
30 | 31 | 32 |
33 |
34 |

Hello username, your feed is empty.

35 |

Your feed displays the latest posts from the people you follow. If you don’t have any friends to follow that’s okay; you can use the “Search” feature in the top menu bar to find content written by people with similar interests and then follow them.

36 |
37 |
38 | 39 | 40 | 43 | 44 | 45 | 46 | 47 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /html-templates/home-logged-in-results.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | OurApp 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 |

OurApp

19 |
20 | 21 | 22 | 23 | Create Post 24 |
25 | 26 |
27 |
28 |
29 |
30 | 31 | 32 |
33 |

The Latest From Those You Follow

34 |
35 | 36 | 37 | Example Post #1 38 | by kittydoe on 1/3/2019 39 | 40 | 41 | 42 | Example Post #2 43 | by barksalot on 1/3/2019 44 | 45 | 46 | 47 | Example Post #3 48 | by kittydoe on 1/3/2019 49 | 50 | 51 | 52 | Example Post #4 53 | by barksalot on 1/3/2019 54 | 55 |
56 |
57 | 58 | 59 | 62 | 63 | 64 | 65 | 66 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /html-templates/live-search.js: -------------------------------------------------------------------------------- 1 | import DOMPurify from "dompurify" 2 | 3 | export default class Search { 4 | // 1. Select DOM elements, and keep track of any useful data 5 | constructor() { 6 | this.injectHTML() 7 | this.headerSearchIcon = document.querySelector(".header-search-icon") 8 | this.overlay = document.querySelector(".search-overlay") 9 | this.closeIcon = document.querySelector(".close-live-search") 10 | this.inputField = document.querySelector("#live-search-field") 11 | this.resultsArea = document.querySelector(".live-search-results") 12 | this.loaderIcon = document.querySelector(".circle-loader") 13 | this.typingWaitTimer 14 | this.previousValue = "" 15 | this.events() 16 | } 17 | 18 | // 2. Events 19 | events() { 20 | this.inputField.addEventListener("keyup", () => this.keyPressHandler()) 21 | this.closeIcon.addEventListener("click", () => this.closeOverlay()) 22 | this.headerSearchIcon.addEventListener("click", e => { 23 | e.preventDefault() 24 | this.openOverlay() 25 | }) 26 | document.addEventListener("keydown", e => { 27 | if (e.key.toUpperCase() == "S" && !this.overlay.classList.contains("search-overlay--visible") && document.activeElement.nodeName != "INPUT" && document.activeElement.nodeName != "TEXTAREA") { 28 | this.openOverlay() 29 | } 30 | 31 | if (e.key == "Escape" && this.overlay.classList.contains("search-overlay--visible")) { 32 | this.closeOverlay() 33 | } 34 | }) 35 | } 36 | 37 | // 3. Methods 38 | keyPressHandler() { 39 | let value = this.inputField.value 40 | 41 | if (value == "") { 42 | clearTimeout(this.typingWaitTimer) 43 | this.hideLoaderIcon() 44 | this.hideResultsArea() 45 | } 46 | 47 | if (value != "" && value != this.previousValue) { 48 | clearTimeout(this.typingWaitTimer) 49 | this.showLoaderIcon() 50 | this.hideResultsArea() 51 | this.typingWaitTimer = setTimeout(() => this.sendRequest(), 750) 52 | } 53 | 54 | this.previousValue = value 55 | } 56 | 57 | async sendRequest() { 58 | const results = await axios(`/search/${this.inputField.value}`) 59 | this.renderResultsHTML(results.data) 60 | } 61 | 62 | renderResultsHTML(posts) { 63 | if (posts.length) { 64 | this.resultsArea.innerHTML = DOMPurify.sanitize(`
65 |
Search Results (${posts.length > 1 ? `${posts.length} items found` : "1 item found"})
66 | ${posts 67 | .map(post => { 68 | let postDate = new Date(post.created_at) 69 | return ` 70 | ${post.title} 71 | by ${post.user.username} on ${postDate.getMonth() + 1}/${postDate.getDate()}/${postDate.getFullYear()} 72 | ` 73 | }) 74 | .join("")} 75 |
`) 76 | } else { 77 | this.resultsArea.innerHTML = `

Sorry, we could not find any results for that search.

` 78 | } 79 | this.hideLoaderIcon() 80 | this.showResultsArea() 81 | } 82 | 83 | showLoaderIcon() { 84 | this.loaderIcon.classList.add("circle-loader--visible") 85 | } 86 | 87 | hideLoaderIcon() { 88 | this.loaderIcon.classList.remove("circle-loader--visible") 89 | } 90 | 91 | showResultsArea() { 92 | this.resultsArea.classList.add("live-search-results--visible") 93 | } 94 | 95 | hideResultsArea() { 96 | this.resultsArea.classList.remove("live-search-results--visible") 97 | } 98 | 99 | openOverlay() { 100 | this.overlay.classList.add("search-overlay--visible") 101 | setTimeout(() => this.inputField.focus(), 50) 102 | } 103 | 104 | closeOverlay() { 105 | this.overlay.classList.remove("search-overlay--visible") 106 | } 107 | 108 | injectHTML() { 109 | document.body.insertAdjacentHTML( 110 | "beforeend", 111 | `
112 |
113 |
114 | 115 | 116 | 117 |
118 |
119 | 120 |
121 |
122 |
123 |
124 |
125 |
126 |
` 127 | ) 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /html-templates/main.css: -------------------------------------------------------------------------------- 1 | body, 2 | .tooltip { 3 | font-family: "Source Sans Pro", sans-serif; 4 | -webkit-font-smoothing: antialiased !important; 5 | -moz-osx-font-smoothing: grayscale !important; 6 | } 7 | 8 | .header-bar { 9 | background-color: #f9322c; 10 | } 11 | 12 | .container--narrow { 13 | max-width: 732px; 14 | } 15 | 16 | .header-search-icon { 17 | position: relative; 18 | top: 3px; 19 | } 20 | 21 | .header-chat-icon { 22 | cursor: pointer; 23 | position: relative; 24 | top: 3px; 25 | } 26 | 27 | .avatar-small { 28 | width: 32px; 29 | height: 32px; 30 | border-radius: 16px; 31 | margin-right: 5px; 32 | position: relative; 33 | top: -3px; 34 | } 35 | 36 | .avatar-tiny { 37 | width: 24px; 38 | height: 24px; 39 | border-radius: 12px; 40 | margin-right: 4px; 41 | position: relative; 42 | top: -1px; 43 | } 44 | 45 | .form-control-title { 46 | font-size: 2rem; 47 | font-weight: 500; 48 | } 49 | 50 | .body-content { 51 | font-size: 1.2rem; 52 | line-height: 1.75; 53 | color: #292929; 54 | } 55 | 56 | .body-content p, 57 | .body-content ul, 58 | .body-content ol { 59 | margin-bottom: 1.75rem; 60 | } 61 | 62 | /* .input-dark { 63 | background-color: #444; 64 | border-color: transparent; 65 | color: #ffffff; 66 | } 67 | 68 | .input-dark:focus { 69 | color: #ffffff; 70 | background-color: #555; 71 | border-color: #80bdff; 72 | outline: 0; 73 | box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); 74 | } 75 | 76 | .input-dark::-webkit-input-placeholder { 77 | color: #888; 78 | } 79 | .input-dark::-moz-placeholder { 80 | color: #888; 81 | } 82 | .input-dark:-ms-input-placeholder { 83 | color: #888; 84 | } 85 | .input-dark:-moz-placeholder { 86 | color: #888; 87 | } */ 88 | 89 | @media (min-width: 768px) { 90 | .input-dark { 91 | width: auto; 92 | } 93 | } 94 | 95 | .display-3 { 96 | font-size: 4.35rem; 97 | } 98 | 99 | @media (max-width: 768px) { 100 | .display-3 { 101 | font-size: 2.5rem; 102 | } 103 | } 104 | 105 | .form-group { 106 | position: relative; 107 | } 108 | 109 | .liveValidateMessage { 110 | transition: all 0.5s ease-out; 111 | top: -6px; 112 | position: absolute; 113 | z-index: 1; 114 | padding-top: 6px; 115 | padding-bottom: 16px; 116 | opacity: 0; 117 | transform: translateY(100%); 118 | } 119 | 120 | .liveValidateMessage--visible { 121 | opacity: 1; 122 | transform: translateY(0); 123 | } 124 | 125 | .form-group input, 126 | .form-group textarea { 127 | position: relative; 128 | z-index: 2; 129 | } 130 | 131 | textarea.tall-textarea { 132 | height: 160px; 133 | } 134 | 135 | @media (min-width: 768px) { 136 | textarea.tall-textarea { 137 | height: 320px; 138 | } 139 | } 140 | 141 | .delete-post-button { 142 | cursor: pointer; 143 | background: none; 144 | border: none; 145 | padding: 0; 146 | margin: 0; 147 | } 148 | 149 | /* Search Overaly */ 150 | .search-overlay { 151 | display: flex; 152 | flex-direction: column; 153 | position: fixed; 154 | z-index: 500; 155 | top: 0; 156 | left: 0; 157 | bottom: 0; 158 | right: 0; 159 | background-color: rgba(215, 215, 215, 0.911); 160 | visibility: hidden; 161 | opacity: 0; 162 | transform: scale(1.3); 163 | transition: 0.33s visibility ease-in-out, 0.33s opacity ease-in-out, 0.33s transform ease-in-out; 164 | will-change: visibility, transform, opacity; 165 | } 166 | 167 | .search-overlay--visible { 168 | visibility: visible; 169 | opacity: 1; 170 | transform: scale(1); 171 | } 172 | 173 | .search-overlay-icon { 174 | color: #007bff; 175 | font-size: 1.4rem; 176 | margin: 0; 177 | margin-right: 10px; 178 | } 179 | 180 | .live-search-field { 181 | background-color: transparent; 182 | border: none; 183 | font-size: 1.1rem; 184 | outline: none; 185 | flex: 1; 186 | color: #007bff; 187 | } 188 | 189 | .live-search-results { 190 | opacity: 0; 191 | transition: all 0.3s ease-out; 192 | transform: scale(1.07); 193 | } 194 | 195 | .live-search-results--visible { 196 | opacity: 1; 197 | transform: scale(1); 198 | } 199 | 200 | .search-overlay-top { 201 | background-color: #fff; 202 | /* background-color: rgba(0, 0, 0, .79); */ 203 | } 204 | 205 | .search-overlay-top .container { 206 | position: relative; 207 | display: flex; 208 | align-items: center; 209 | padding-top: 15px; 210 | padding-bottom: 15px; 211 | } 212 | 213 | .search-overlay-bottom { 214 | overflow: auto; 215 | } 216 | 217 | .close-live-search { 218 | font-size: 1.5rem; 219 | cursor: pointer; 220 | opacity: 0.75; 221 | line-height: 1; 222 | color: #292929; 223 | } 224 | 225 | @media (min-width: 700px) { 226 | .live-search-field { 227 | font-size: 2.5rem; 228 | } 229 | 230 | .close-live-search { 231 | font-size: 3rem; 232 | } 233 | 234 | .search-overlay-icon { 235 | font-size: 3rem; 236 | } 237 | } 238 | 239 | .close-live-search:hover { 240 | opacity: 1; 241 | } 242 | 243 | @-webkit-keyframes spin { 244 | 100% { 245 | -webkit-transform: rotate(360deg); 246 | transform: rotate(360deg); 247 | } 248 | } 249 | @keyframes spin { 250 | 100% { 251 | -webkit-transform: rotate(360deg); 252 | transform: rotate(360deg); 253 | } 254 | } 255 | 256 | .circle-loader { 257 | opacity: 0; 258 | transition: opacity 0.45s ease-out, visibility 0.45s ease-out; 259 | visibility: hidden; 260 | position: absolute; 261 | left: 50%; 262 | box-sizing: border-box; 263 | width: 65px; 264 | height: 65px; 265 | border-radius: 100%; 266 | border: 10px solid rgba(73, 80, 87, 0.2); 267 | border-top-color: #495057; 268 | will-change: -webkit-transform, transform; 269 | -webkit-transform: rotate(0deg); 270 | transform: rotate(0deg); 271 | -webkit-animation: spin 1s infinite linear; 272 | animation: spin 1s infinite linear; 273 | } 274 | 275 | .circle-loader--visible { 276 | visibility: visible; 277 | opacity: 1; 278 | } 279 | 280 | /* End Search Overlay */ 281 | 282 | /* Chat */ 283 | .chat-wrapper { 284 | position: fixed; 285 | z-index: 5; 286 | bottom: 0; 287 | right: 20px; 288 | width: 290px; 289 | height: 350px; 290 | background-color: #fff; 291 | display: flex; 292 | flex-direction: column; 293 | opacity: 0; 294 | transform: translateY(100%); 295 | } 296 | 297 | .chat-wrapper--ready { 298 | transition: all 0.4s ease-in-out; 299 | } 300 | 301 | .chat--visible { 302 | opacity: 1; 303 | transform: translateY(0); 304 | } 305 | 306 | .chat-title-bar { 307 | background-color: #292929; 308 | color: #fff; 309 | padding: 4px 7px; 310 | display: flex; 311 | justify-content: space-between; 312 | } 313 | 314 | .chat-title-bar-close { 315 | opacity: 0.7; 316 | cursor: pointer; 317 | } 318 | 319 | .chat-title-bar-close:hover { 320 | opacity: 1; 321 | } 322 | 323 | .chat-log { 324 | padding: 8px; 325 | flex: 1; 326 | overflow: auto; 327 | } 328 | 329 | .chat-self, 330 | .chat-other { 331 | font-size: 0.75rem; 332 | display: flex; 333 | align-items: center; 334 | margin-bottom: 7px; 335 | } 336 | 337 | .chat-self { 338 | padding-left: 25%; 339 | } 340 | 341 | .chat-self .chat-avatar { 342 | margin-left: 6px; 343 | } 344 | 345 | .chat-self .chat-message { 346 | flex: 1; 347 | display: flex; 348 | justify-content: flex-end; 349 | } 350 | 351 | .chat-self .chat-message-inner { 352 | text-align: right; 353 | padding: 4px 7px; 354 | border-radius: 12px; 355 | background-color: #007bff; 356 | color: #fff; 357 | } 358 | 359 | .chat-other { 360 | padding-right: 25%; 361 | } 362 | 363 | .chat-other .chat-avatar { 364 | margin-right: 6px; 365 | } 366 | 367 | .chat-other .chat-message { 368 | flex: 1; 369 | display: flex; 370 | justify-content: flex-start; 371 | } 372 | 373 | .chat-other .chat-message-inner { 374 | padding: 4px 7px; 375 | border-radius: 12px; 376 | background-color: #f1f0f0; 377 | } 378 | 379 | .chat-message a { 380 | color: #212529; 381 | } 382 | 383 | .chat-field { 384 | width: 100%; 385 | box-sizing: border-box; 386 | padding: 10px 7px; 387 | border: none; 388 | outline: none; 389 | font-size: 0.75rem; 390 | } 391 | 392 | header .flex-row > div { 393 | display: inline; 394 | } 395 | 396 | header .flex-row > div > div { 397 | display: inline; 398 | } 399 | -------------------------------------------------------------------------------- /html-templates/post.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | OurApp 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 |

OurApp

19 |
20 | 21 | 22 | 23 | Create Post 24 |
25 | 26 |
27 |
28 |
29 |
30 | 31 | 32 |
33 |
34 |

Example Post Title Here

35 | 36 | 37 |
38 | 39 |
40 |
41 |
42 | 43 |

44 | 45 | Posted by kittydoe on 2/3/2019 46 |

47 | 48 |
49 |

My roommate yells at me when I destroy things, but I do what I want.

50 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Numquam praesentium laboriosam unde fuga accusamus reiciendis laudantium quis consequatur, beatae temporibus nemo, tempora voluptatum, perspiciatis accusantium ullam molestiae cupiditate incidunt architecto.

51 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Numquam praesentium laboriosam unde fuga accusamus reiciendis laudantium quis consequatur, beatae temporibus nemo, tempora voluptatum, perspiciatis accusantium ullam molestiae cupiditate incidunt architecto.

52 |
53 |
54 | 55 | 56 | 59 | 60 | 61 | 62 | 63 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /html-templates/profile-posts.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | OurApp 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 |

OurApp

19 |
20 | 21 | 22 | 23 | Create Post 24 |
25 | 26 |
27 |
28 |
29 |
30 | 31 | 32 |
33 |

34 | brad 35 |
36 | 37 | 38 |
39 |

40 | 41 | 46 | 47 |
48 | 49 | 50 | Example Post #1 on 0/13/2019 51 | 52 | 53 | 54 | Example Post #2 on 0/13/2019 55 | 56 | 57 | 58 | Example Post #3 on 0/13/2019 59 | 60 |
61 |
62 | 63 | 64 | 67 | 68 | 69 | 70 | 71 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /html-templates/profile-users.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | OurApp 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 |

OurApp

19 |
20 | 21 | 22 | 23 | Create Post 24 |
25 | 26 |
27 |
28 |
29 |
30 | 31 | 32 |
33 |

34 | brad 35 |
36 | 37 | 38 |
39 |

40 | 41 | 46 | 47 |
48 | User 1 49 | User 2 50 | User 3 51 |
52 |
53 | 54 | 55 | 58 | 59 | 60 | 61 | 62 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /html-templates/search-visible.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | OurApp 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 |

OurApp

19 |
20 | 21 | 22 | 23 | Create Post 24 |
25 | 26 |
27 |
28 |
29 |
30 | 31 | 32 |
33 |

The Latest From Those You Follow

34 |
35 | 36 | 37 | Example Post #1 38 | by kittydoe on 1/3/2019 39 | 40 | 41 | 42 | Example Post #2 43 | by barksalot on 1/3/2019 44 | 45 | 46 | 47 | Example Post #3 48 | by kittydoe on 1/3/2019 49 | 50 | 51 | 52 | Example Post #4 53 | by barksalot on 1/3/2019 54 | 55 |
56 |
57 | 58 | 59 | 62 | 63 | 64 |
65 |
66 |
67 | 68 | 69 | 70 |
71 |
72 | 73 |
74 | 99 |
100 |
101 | 102 | 103 | 104 | 105 | 106 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /html-templates/spa-profile.js: -------------------------------------------------------------------------------- 1 | import DOMPurify from "dompurify" 2 | 3 | export default class Profile { 4 | constructor() { 5 | this.links = document.querySelectorAll(".profile-nav a") 6 | this.contentArea = document.querySelector(".profile-slot-content") 7 | this.events() 8 | } 9 | 10 | // events 11 | events() { 12 | addEventListener("popstate", () => { 13 | this.handleChange() 14 | }) 15 | this.links.forEach(link => { 16 | link.addEventListener("click", e => this.handleLinkClick(e)) 17 | }) 18 | } 19 | 20 | handleChange() { 21 | this.links.forEach(link => link.classList.remove("active")) 22 | this.links.forEach(async link => { 23 | if (link.getAttribute("href") == window.location.pathname) { 24 | const response = await axios.get(link.href + "/raw") 25 | this.contentArea.innerHTML = DOMPurify.sanitize(response.data.theHTML) 26 | document.title = response.data.docTitle + " | OurApp" 27 | link.classList.add("active") 28 | } 29 | }) 30 | } 31 | 32 | // methods 33 | async handleLinkClick(e) { 34 | this.links.forEach(link => link.classList.remove("active")) 35 | e.target.classList.add("active") 36 | e.preventDefault() 37 | const response = await axios.get(e.target.href + "/raw") 38 | this.contentArea.innerHTML = DOMPurify.sanitize(response.data.theHTML) 39 | document.title = response.data.docTitle + " | OurApp" 40 | 41 | history.pushState({}, "", e.target.href) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /html-templates/vps-nginx.txt: -------------------------------------------------------------------------------- 1 | server { 2 | listen [::]:80; 3 | listen 80; 4 | server_name google.com www.google.com; 5 | root /var/www/ourapp/public; 6 | 7 | client_max_body_size 10M; 8 | 9 | add_header X-Frame-Options "SAMEORIGIN"; 10 | add_header X-XSS-Protection "1; mode=block"; 11 | add_header X-Content-Type-Options "nosniff"; 12 | 13 | index index.html index.htm index.php; 14 | 15 | charset utf-8; 16 | 17 | location / { 18 | try_files $uri $uri/ /index.php?$query_string; 19 | } 20 | 21 | location = /favicon.ico { access_log off; log_not_found off; } 22 | location = /robots.txt { access_log off; log_not_found off; } 23 | 24 | error_page 404 /index.php; 25 | 26 | location ~ \.php$ { 27 | fastcgi_pass unix:/var/run/php/php8.1-fpm.sock; 28 | fastcgi_index index.php; 29 | fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; 30 | include fastcgi_params; 31 | } 32 | 33 | location ~ /\.(?!well-known).* { 34 | deny all; 35 | } 36 | } 37 | --------------------------------------------------------------------------------