ASTRO Camp 7th
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Write
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Help
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Write
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # 0423 Rails 與 JS、現有專案新增工作表與關聯 ###### tags: `Ruby / Rails` ## 在專案中寫 JS - 回顧:[webpack](https://hackmd.io/zPpaNDW6SyW2Y8tFGQVSow?view#webpack-gt-JavaScript-%E5%B7%A5%E5%85%B7) - [foreman 套件](https://rubygems.org/gems/foreman/versions/0.82.0?locale=zh-TW) - 把所有需要使用的套件都同時開起來 - 問題:使用 debugger 時會有不知道關哪個套件的問題 - 安裝好之後放進 Gemfile 的 group :develop :test - 執行 `$ foreman s` - 預設在 Procfile 裡面寫上要跑哪些服務 - 或是建立 procfile.dev 或其他檔案 - 執行指令換成 `$ foreman s -f 建立檔案的名字` ```ruby= a: bin/rails server -p 3000 b: bin/webpack-dev-server # a 跟 b 可以換成套件的名稱 # 例: web: bin/rails server -p 3000 pack: bin/webpack-dev-server ``` ## uchef 新增評論系統 ### 建立 model #### 設計欄位 - Comment Model - comment: text - restaurant: belongs_to - user: belongs_to - deleted_at: datetime:index (假刪除) * 階層回覆? - self references 自我參照 (關鍵字:rails model self references) - Comment Model - comment: text - restaurant: belongs_to - user: belongs_to - comment: belongs_to (針對留言再留言) - 加好友? - User has_many :users - Comment Model - comment: text - restaurant: belongs_to - user: belongs_to - reply_to: interger default: 0 => 如果是 0 就是最上層的回應 #### 用 rails 內建產生器產出 - `$ rails g model Comment content:text restaurant:belongs_to user:belongs_to deleted_at:datetime:index` #### 放進資料庫 - `$ rails db:migrate` #### 修改 controller - 在 controller 裡面建立新的 comment 物件 controllers/restaurants_controller.rb ```ruby= def show @restaurant = Restaurant.find(params[:id]) @comment = Comment.new end ``` - 在 model 裡新增關聯 user model ```ruby= class User < ApplicationRecord has_many :comments ``` restaurant model ```ruby= class Restaurant < ApplicationRecord has_many :comments ``` - 資料表關聯時,呼叫指令輸入的順序代表從哪個角度出發 - 所以最好把 controller 改寫成 controllers/restaurants_controller.rb ```ruby= def show @restaurant = Restaurant.find(params[:id]) @comment = @restaurant.comments.new end ``` --- ### 修改 routes - 思考 routes 的寫法 - 適當的控制網址的長度 - POST /restaurants/restaurant_id/comments - create - 如果沒有 restaurant 的話可能會有問題,找不到關聯 - GET /restaurants/restaurant_id/comments - index - GET /rsetaurants/restaurant_id/comments/餐廳的第幾個留言 - show => 很像不需要 - 可以改成 GET /comments/comment_id - DELETE /restaurants/restaurant_id/comments/餐廳的第幾個留言 - destroy - 刪除很像只要指定要刪除哪一個 comment 就好 - DELETE /comments/comment_id config/routes.rb ```ruby= Rails.application.routes.draw do resources :restaurants do resources :comments, only: [:index, :new, :create] end resources :comments, except: [:index, :new, :create] ``` - 甚至可以 ```ruby= Rails.application.routes.draw do resources :restaurants do resources :comments, only: [:index, :create] # 讓 POST restaurants#show 變成 comments new # 讓 GET restaurants#show 也顯示 comments#index end resources :comments, except: [:index, :new, :create] ``` - 或是讓它變膚淺 (shallow) [官方文件說明](https://guides.rubyonrails.org/routing.html#shallow-nesting) ```ruby= Rails.application.routes.draw do resources :restaurants do resources :comments, shallow: true end # 等於下面寫法 # resources :restaurants do # resources :comments, only: [:index, :new, :create] # end # resources :comments, except: [:index, :new, :create] ``` - 還可以把多餘的拔掉 ```ruby= Rails.application.routes.draw do resources :restaurants do resources :comments, shallow: true, except: [:index, :new] end ``` - 多思考一下: ```ruby= Rails.application.routes.draw do resources :restaurants do resources :comments, shallow: true, only: [:create, :destroy] end ``` - 其實有套件可以直接處理歷史紀錄 - [paper trail](https://github.com/paper-trail-gem/paper_trail) --- ### 建立 view views/restaurants/show.html.erb - 直覺的作法 ```ruby= <h1><%= @restaurant.title %></h1> <h3>留言</h3> # POST /comments <= 想要做到 <%= form_for(@comment) do |form| %> <% end %> # 實際會去找 /restaurant ``` - 所以要改成 ```ruby= <h1><%= @restaurant.title %></h1> <h3>留言</h3> <%= form_for(@comment, url: restaurant_comments_path(@restaurant)) do |form| %> <% end %> # 加 url 讓送出的地方變成新的位置 ``` - 架出完整表單 ```ruby= <h1><%= @restaurant.title %></h1> <h3>留言</h3> <%= form_for(@comment, url: restaurant_comments_path(@restaurant)) do |form| %> <div class="field"> <%= form.label :content, '留言內容' %> <%= form.text_area :content %> </div> <%= form.submit %> <% end %> ``` --- ### 建立 controller 跟 action /app/controllers/comments_controller.rb ```ruby= class CommentsController < ApplicationController before_action :check_user! def create @restaurant = Restaurant.find(params[:restaurant_id]) # 找到那間餐廳 @comment = @restaurant.comments.new(comment_params) # 建立出新留言 @comment.user = current_user # 找到那個留言對應的使用者 if @comment.save redirect_to @restaurant # 餐廳的show # redirect_to restaurant_path(@restaurant) # 跟上面那行一樣 else # .. end end def destroy end private def comment_params params.require(:comment).permit(:content) # 清洗想要的資料 end end ``` - 因為目前使用者 id 是從 session 那邊過來的,所以可以相信 - 可把程式碼修改成: ```ruby= class CommentsController < ApplicationController before_action :check_user! def create @restaurant = Restaurant.find(params[:restaurant_id]) @comment = @restaurant.comments.new(comment_params) # @comment.user = current_user if @comment.save redirect_to restaurant_path(@restaurant) # 餐廳的 show else # .. end end def destroy end private def comment_params permited_params = params.require(:comment).permit(:content) permited_params[:user] = current_user return permited_params end end ``` - 在物件中加一組 key value 的概念 ```ruby= a = {a: 1, b: 2} a.merge({c: 3}) p a # {a: 1, b: 2, c: 3} {a: a, b: b}.merge({c: c}) #回傳值{a: a, b: b, c: c} ``` - 把程式碼改寫(直接從源頭的 params 下手塞東西進去) ```ruby= class CommentsController < ApplicationController before_action :check_user! def create @restaurant = Restaurant.find(params[:restaurant_id]) @comment = @restaurant.comments.new(comment_params) # @comment.user = current_user if @comment.save redirect_to restaurant_path(@restaurant) # 餐廳的 show else render 'restaurants/show' # 檢查 view 裡面的變數 => 剛好都一樣 # 變數名稱一樣其實是刻意營造的巧合(@restaurant, @comment) end end def destroy end private def comment_params params.require(:comment).permit(:content) .merge({user: current_user}) # 不是使用者給的 # 還可以把 ip_address 加進去 # permited_params = params.require(:comment).permit(:content) # permited_params[:user] = current_user # return permited_params end end ``` ### 再次修改 view - 把全部的 comment 加上去 /views/restaurants/show.html.erb ```ruby= <h1><%= @restaurant.title %></h1> <h3>留言</h3> <%= form_for(@comment, url: restaurant_comments_path(@restaurant)) do |form| %> <div class="field"> <%= form.label :content, '留言內容' %> <%= form.text_area :content %> </div> <%= form.submit %> <% end %> <% @comments.each do |comment| %> <li><%= comment.content %></li> <% end %> ``` - 因為需要 @comments 所以修改 controller /app/controllers/restaurant_controller.rb ```ruby= class RestaurantsController < ApplicationController def show @restaurant = Restaurant.find(params[:id]) @comment = @restaurant.comments.new @comments = @restaurant.comments.order(id: :desc) # order 讓最新的在前面 end ``` - ==思考!== 如果要加檢舉功能怎麼辦? - ugc(user generated content) 功能通常要加 * order + SQL 語法 * 用字串只是使用 SQL 去執行 * 在 SQL 中大小寫一樣效果 * 在實務上還是都用 ruby/rails 語言比較好 ```ruby= Comment.where(user_id: 2) Comment.where('user_id = 2') # 效果相同 @comments = @restaurant.comments.order(id: :desc) # 但寫這個比較好啦 @comments = @restaurant.comments.order('id desc') @comments = @restaurant.comments.order('id DESC') # 效果相同 ``` ## 修改資料傳送方式 ### remote 模式 [Ajax](https://ihower.tw/rails/ajax.html) ```ruby= <%= form_for(@comment, url: restaurant_comments_path(@restaurant), remote: true) # <%= form_for(@comment, url: restaurant_comments_path(@restaurant)) do |form| %> ``` - 在瀏覽器渲染時:form 的屬性會增加 data-remote: true - 如果沒增加這個屬性 => 使用基本的 post 方法傳出 request - 有了之後 => 網頁使用 xhr 方法傳出 request => 再用 js 去接它 controllers/comments.rb ```ruby= def create @restaurant = Restaurant.find(params[:restaurant_id]) @comment = @restaurant.comments.new(comment_params) # @comment.user = current_user if @comment.save # redirect_to restaurant_path(@restaurant) => 把這行刪掉 else render 'restaurants/show' end end ``` - 因為 @comment.save 後面沒東西所以會去找 create 的同名 view - ==給他一個view!!!!== --- ### 在 js 裡面塞 ruby 原始碼 - 把本來的 html.erb 換成 js.erb /views/comments/create.js.erb ```ruby= alert('你輸入了:<%= @comment.content %>') => 會跳出一個小視窗 ``` - 所以把它認真換掉: ```ruby= var area = document.querySelector('#comment_area') var li = document.createElement('li') li.textContent = '<%= @comment.content %>' area.prepend(li) document.querySelector('#comment_content').value = '' ``` - 把 restaurant 的 show 改一下 ```ruby= <h1><%= @restaurant.title %></h1> <h3>留言</h3> <%= form_for(@comment, url: restaurant_comments_path(@restaurant), remote: true) do |form| %> <div class="field"> <%= form.label :content, '留言內容' %> <%= form.text_area :content %> </div> <%= form.submit %> <% end %> <ul id="comment_area"> ===> 加上 id 跟 ul, li <% @comments.each do |comment| %> <li><%= comment.content %></li> <% end %> </ul> ``` - 資料庫已經寫進去了但是沒有重新整理網頁 - 後端寫入資料之後讀取 js - 前端收到 js 檔之後就直接在網頁上渲染(演)給使用者看 - 但是其他使用者的頁面不會同時更新 - rails 的 ujs 才會把後端來的 js 渲染在前端 ## 在 rails 專案裡使用 npm/yarn ### 以 fontawesome 為例 - [官網介紹](https://fontawesome.com/how-to-use/on-the-web/setup/using-package-managers) - 到 rails 專案資料夾底下執行 yarn... - 下完指令會安裝相關套件到 rails/ujs/lib/assets/package.json * node_modules/@fortawesome/fontawsome-free/pachage.json 注意裡面的 main SVG JavaScript core app/javascript/packs/application.js 只用一個部分 webpacker.yml 裡面有寫位置 [webpacker 筆記](https://hackmd.io/zPpaNDW6SyW2Y8tFGQVSow?both#webpack-gt-JavaScript-%E5%B7%A5%E5%85%B7) * app / javascript裡新增一個icon資料夾,再新增fontawesome的js檔 ```ruby= import { library, dom } from '@fortawesome/fontawesome-svg-core' import { faUserAstronaut } from '@fortawesome/free-solid-svg-icons' # 把套件 import 進來 library.add(faUserAstronaut) # 把套件加進 library dom.watch() # 掃描整個畫面,有用到 library 的都渲染 # 注意 icon 的大小寫 ``` yarn 安裝套件 => 跑進專案底下的 node_module 資料夾底下 => 放物件 => 去 application.js 裡 import 套件 或是在 js 資料夾底下創新的資料夾跟檔案,再去application.js import 下載別人的專案下來後: `$ yarn install --check-files` --- ### 以 tailwind 為例 - 用法:在 html tag 的 class 裡面加東西 - [官網介紹](https://tailwindcss.com/) - [玩玩看 tailwind](https://play.tailwindcss.com/) --- ## 題外話 ### 使用上層方法 - super 尋找上層的同名方法,前提是上層有同名方法 - alias不是方法,僅是特殊語法 - [文件說明](https://ruby-doc.org/core-3.0.0/doc/syntax/miscellaneous_rdoc.html) * 範例一 ```ruby= class Animal def destroy puts '真delete' end end class Cat < Animal alias :super_destroy :destroy # 做一個別名,指向後方 (destroy) # 因為 Cat 的 destroy 還沒被執行,所以會找上層 (Animal) 的 destroy # 要放在定義新的同名方法前才會有效 def destroy # update deleted_at -> now puts '假刪除' end def really_destroy super_destroy # self.class.superclass.instance_method(:destroy).bind(self).call # 或是直接使用這行,不寫 alias # 自己的物件(self.class),的上層class(superclass), # 再用(instance_method)呼喚他(上層)的實體方法, # 再去綁到自己身上(bind),再呼叫(call) end end kitty.Cat kitty.destroy kitty.really_destroy ``` - 範例二 ```ruby= class Integer alias :old_plus :+ # alias不是方法,僅是特殊語法 def +(n) puts "hi" end def add(n) self.old_plus(n) end end puts 1.add(2) ``` - 範例三 ```ruby= alias :log :puts log(123) # 123 ``` ### 製作會員系統的套件 devise - [GitHub 連結](https://github.com/heartcombo/devise) - 短短幾行字讓你做完會員登入系統 ### meta programming 元編程 - 寫一段 code 去修改原本既定的行為 ```ruby= class Cat def method_missing(method_name, args) puts "yaya" end # def method_missing(method_name, args) # super # end end kitty = Cat.new kitty.find_by_id(1) # 舊式的 find_by 寫法 ``` - ruby 找方法的順序 - 找不到方法 => 往上找 => 到底也找不到 => 回頭重新再找特別的方法 => 再依序往上找看有沒有特別的方法 - ruby 中每一個錯誤訊息也是這樣來的 - 所以可以依照這個機制更改上層的反應 ```ruby= class Cat def method_missing(method_name, *args) if method_name.to_s.start_with?('find_by') puts "you just called #{method_name} with #{args}" else super end end end kitty = Cat.new kitty.find_by_id(1) ``` - [過陣子可以找來看的書(最好看原文的)](https://pragprog.com/titles/ppmetr2/metaprogramming-ruby-2/) - JS 的 metaprogramming: - reflect - proxy (vue.js相關) --- ### 套件安裝指令的不同 - `$ gem install xxx` - 在系統裡面安裝某個套件 - `$ bundle install` - 會產生 Gemfile 跟 Gemfile.lock - 在專案裡使用特別的套件版本 - 根據 Gemfile 裡的東西去執行 gem install 然後建立 Gemfile.lock - lock 檔:描述目前專案使用的套件(包含版本名 ?.?.?) --- ### JS 沒寫好讓連結壞掉 rails 裡面有一支 JS 去掃描攔截超連結做特殊處理, frontend/packs/application.js 如果沒寫好會讓檔案壞掉不能正常編譯 --- ### SSR vs. SPA - SSR = Server Side Render - 從伺服器撈資料之後渲染成頁面 - SPA = Single Page Application - 通過動態重寫當前頁面來與使用者互動,而非傳統的從伺服器重新載入整個新頁面。 - [WIKI 說明](https://zh.wikipedia.org/wiki/%E5%8D%95%E9%A1%B5%E5%BA%94%E7%94%A8) --- ### form_for vs. form_tag vs. form_with - form_for(@model) - 預設 remote: false - form_tag(....) - 單純產出 <form> </form> - form_with(model: @model) - 結合前兩種用法 - 如果 @model 是 nil 的話不會出錯,會跳出單純的 form tag - 預設 remote: true - 後面其他參數的加法跟 form_for 一樣 - url: ... - method: ... - 可能會看不懂 view 裡面的 js.erb ==提醒龍哥補充 form_with== --- ### npm & yarn * rails 內建使用 yarn - 裝好 yarn - 執行 `$ yarn init` 初始化 - 會在指定的資料夾產生 - `yarn.lock` - `node-modules` - 裡面就會放很多指令 - `package.json` - 可以用 npm 資料庫 - npm: `$ npm i 套件名稱 --save` - yarn: `$ yarn add 套件名稱` - 只要加在開發者模式 - `$ yarn add 套件名稱 -D` - package.json 底下會被丟進 devDependencies => 上線之後不會使用 - 下完指令之後會產生很多相依性相關的檔案 - 位置:node-modules --- ### bundle add - bundle add 套件名稱 - 自動安裝最新版本 - 自動放在 Gemfile 檔案最底端

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully