Kotlinでの練習:ReactおよびKotlin / JSでのWebアプリケーションの作成

翻訳者から







ねえ!ステレオタイプがありますについてKotlinそれが唯一のAndroidのための開発のための言語です。実際、これはまったく当てはまりません。この言語は、いくつかのプラットフォーム(JVMJSNative)を公式にサポートしており、他の言語で記述されたこれらのプラットフォームのライブラリを操作する方法も知っています。このような「マルチプラットフォーム性」のサポートにより、あらゆる種類のプロジェクトを1つの言語で単一の形式で作成できるだけでなく、異なるプラットフォーム用に1つのプロジェクトを作成するときにコードを再利用することもできます。







この記事では、KotlinでWebサイトを構築するための公式のKotlinハンズオンチュートリアル翻訳しています。Kotlin / JSプログラミングの多くの側面をカバーし、純粋なDOM以上のものを操作する方法を理解します。主にReactJSについて説明しますがNPMからの依存関係を使用しREST APIを呼び出し、Herokuデプロイし、最終的にビデオプレーヤーアプリケーションを作成するGradleビルドシステムについても触れます







このテキストは、Kotlinを少し知っていて、Reactを知らないかほとんど知らない人を対象としています。これらの問題についてより経験がある場合は、チュートリアルの一部が過度に噛まれているように見える場合があります。







kotlin-react







, .







. , 09.04.2021.









  1. React – .
  2. .
  3. !
  4. NPM
  5. REST API
  6. :
  7. ?


1.



, Kotlin/JS React . React , . , .







React , - . JavaScript.







Kotlin/JS React, Gradle org.jetbrains.kotlin.js



. , React .







, - (DSL) , . , , .







, , HTML CSS. , .









KotlinConf , . KotlinConf 2018 - 1300 . YouTube, – "". – KotlinConf Explorer (. ).







結果







, , GitHub. , .







, .







2.





, , . , – IntelliJ IDEA ( 2020.3



, Community Edition) (1.4.30



) – . , ( Windows, MacOS Linux).









, .







GitHub IntelliJ IDEA (, File | New | Project from Version Control... Git | Clone...).







Kotlin/JS Gradle , - . Gradle , .







, , .







: , , Gradle , – .







Gradle



React, , . Gradle , .







, build.gradle.kts



repositories



. .







dependencies



:







dependencies {
    // React, React DOM + Wrappers ( 3)
    implementation("org.jetbrains:kotlin-react:17.0.1-pre.148-kotlin-1.4.21")
    implementation("org.jetbrains:kotlin-react-dom:17.0.1-pre.148-kotlin-1.4.21")
    implementation(npm("react", "17.0.1"))
    implementation(npm("react-dom", "17.0.1"))

    // Kotlin Styled ( 3)
    implementation("org.jetbrains:kotlin-styled:5.2.1-pre.148-kotlin-1.4.21")
    implementation(npm("styled-components", "~5.2.1"))

    // Video Player ( 7)
    implementation(npm("react-youtube-lite", "1.0.1"))

    // Share Buttons ( 7)
    implementation(npm("react-share", "~4.2.1"))

    // Coroutines ( 8)
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3")
}
      
      





, IDEA Gradle . , Reimport All Gradle Projects - Gradle ( ).







HTML



JavaScript , JS HTML , . src/main/resources/index.html



:







<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Hello, Kotlin/JS!</title>
</head>
<body>
    <div id="root"></div>
    <script src="confexplorer.js"></script>
</body>
</html>
      
      





Kotlin/JS Gradle , ("") JavaScript , . HTML confexplorer.js



(, , , followingAlong



, followingAlong.js



).







JavaScript, ( #root



) . , , .







: HTML, , onLoad



body



. Kotlin/JS body



.







"Hello, World" , – , . , . src/main/kotlin/Main.kt



:







import kotlinx.browser.document

fun main() {
    document.bgColor = "red"
}
      
      





.









Kotlin/JS Gradle webpack-dev-server, IDE .







, run



browserDevelopmentRun



- Gradle. other



( ), kotlin browser



:

サーバー開始タスク







IDE, , ./gradlew run



( Windows Gradle -: .\gradlew.bat run



).







, , , :

赤いページ







(hot reload) a.k.a.



, – Kotlin/JS . run



Gradle.







, ( IDE – Stop; – Ctrl+C



).







IDEA, . IDEA , Gradle , :

編集を開く







Run/Debug Configurations --continuous



:

引数を追加する







Run (|>



) .







, : ./gradlew run --continuous



.







, Gradle . , :







document.bgColor = "blue"
      
      





, , – .







. . , .









, , , . -, , - . -, , . -, - , , Gradle , – - .







, Kotlin/JS, . , : HTML . browserDevelopmentWebpack



, build/distributions



build/developmentExecutable



. index.html



, .







, ...



Kotlin/JS , . !







master



.


3. –



Hello, World. .







src/main/kotlin/Main.kt



:







import react.dom.*
import kotlinx.browser.document

fun main() {
    render(document.getElementById("root")) {
        h1 {
            +"Hello, React+Kotlin/JS!"
        }
    }
}
      
      





:

こんにちは世界







, ! , . render



kotlin-react-dom ( ) . , src/main/resources/index.html



ID root



, . – . , HTML , DSL.







HTML



kotlin-react DSL, HTML . , DSL .







, . , - , , !







+



:







+



. . h1



– , . +



, unaryPlus



, HTML .







, +



" ".







HTML



, , () HTML. HTML, . , HTML:







<h1>KotlinConf Explorer</h1>
<div>
    <h3>Videos to watch</h3>
    <p>John Doe: Building and breaking things</p>
    <p>Jane Smith: The development process</p>
    <p>Matt Miller: The Web 7.0</p>

    <h3>Videos watched</h3>
    <p>Tom Jerry: Mouseless development</p>
</div>
<div>
    <h3>John Doe: Building and breaking things</h3>
    <img src="https://via.placeholder.com/640x360.png?text=Video+Player+Placeholder">
</div>
      
      





Kotlin DSL. . , , :







h1 {
    +"KotlinConf Explorer"
}
div {
    h3 {
        +"Videos to watch"
    }
    p {
        +"John Doe: Building and breaking things"
    }
    p {
        +"Jane Smith: The development process"
    }
    p {
        +"Matt Miller: The Web 7.0"
    }

    h3 {
        +"Videos watched"
    }
    p {
        +"Tom Jerry: Mouseless development"
    }
}
div {
    h3 {
        +"John Doe: Building and breaking things"
    }
    img {
       attrs {
           src = "https://via.placeholder.com/640x360.png?text=Video+Player+Placeholder"
       }
    }
}
      
      





render



. IntelliJ IDEA , (quick-fixes) Alt+Enter



. , :

kotlinconf-プレースホルダー









HTML DSL HTML. – , . , , , – HTML DSL , .







- . KotlinVideo



, ( Main.kt



, – ), external



– , API:







external interface Video {
    val id: Int
    val title: String
    val speaker: String
    val videoUrl: String
}

data class KotlinVideo(
    override val id: Int,
    override val title: String,
    override val speaker: String,
    override val videoUrl: String
) : Video
      
      





: . Main.kt



:







val unwatchedVideos = listOf(
    KotlinVideo(1, "Building and breaking things", "John Doe", "https://youtu.be/PsaFVLr8t4E"),
    KotlinVideo(2, "The development process", "Jane Smith", "https://youtu.be/PsaFVLr8t4E"),
    KotlinVideo(3, "The Web 7.0", "Matt Miller", "https://youtu.be/PsaFVLr8t4E")
)

val watchedVideos = listOf(
    KotlinVideo(4, "Mouseless development", "Tom Jerry", "https://youtu.be/PsaFVLr8t4E")
)
      
      





HTML, , ! HTML . p



, :







for (video in unwatchedVideos) {
    p {
        +"${video.speaker}: ${video.title}"
    }
}
      
      





watchedVideos



. , . , , , , .







CSS



, , : , . - .css



index.html



, , Kotlin DSL – CSS.







kotlin-styled styled-components , . CSS-in-JS. , , .







CSS DSL, Gradle. :







dependencies {
    //...
    // Kotlin Styled ( 3)
    implementation("org.jetbrains:kotlin-styled:5.2.1-pre.148-kotlin-1.4.21")
    implementation(npm("styled-components", "~5.2.1"))
    //...
}
      
      





div



h3



styled



, , styledDiv



styledH3



. css



. , , :







styledDiv {
    css {
        position = Position.absolute
        top = 10.px
        right = 10.px
    }
    h3 {
        +"John Doe: Building and breaking things"
    }
    img {
        attrs {
            src = "https://via.placeholder.com/640x360.png?text=Video+Player+Placeholder"
        }
    }
}
      
      





, IDEA . , :







import kotlinx.css.*
import styled.*
      
      





Alt+Enter



.







. – , . CSS Grids, ( ). ( fontFamily



) ( sans-serif



), , , ( color



).







step-02-first-static-page



.


4. React – .





. , , . , , / .







, . , :

ルートコンポーネント







, , :

分割コンポーネント









. App



, . App.kt



src/main/kotlin



. App



, RComponent



( React Component). (RProps



RState



), :







import react.*

@JsExport
class App : RComponent<RProps, RState>() {

    override fun RBuilder.render() {
        //    HTML!
    }
}
      
      





HTML render



. . main



- App



. : App



, child



:







fun main() {
    render(document.getElementById("root")) {
        child(App::class) {}
    }
}
      
      





, . , .









? , – . , , .







VideoList.kt



. App



, VideoList



, RComponent



HTML DSL unwatchedVideos



:







import react.*
import react.dom.*

@JsExport
class VideoList : RComponent<RProps, RState>() {

    override fun RBuilder.render() {
        for (video in unwatchedVideos) {
            p {
                +"${video.speaker}: ${video.title}"
            }
        }
    }
}
      
      





App



:







div {
    h3 {
        +"Videos to watch"
    }
    child(VideoList::class) {}

    h3 {
        +"Videos watched"
    }
    child(VideoList::class) {}
}
      
      





: App



. . , .









, - . , , . props



. , .







, . . VideoList.kt



:







external interface VideoListProps : RProps {
    var videos: List<Video>
}
      
      





VideoList



, :







@JsExport
class VideoList : RComponent<VideoListProps, RState>() {

    override fun RBuilder.render() {
        for (video in props.videos) {
            p {
                key = video.id.toString()
                +"${video.speaker}: ${video.title}"
            }
        }
    }
}
      
      





( , ), key



. , , – ! , , .







, VideoList



( App



) . unwatchedVideos



watchedVideos



:







child(VideoList::class) {
    attrs.videos = unwatchedVideos
}
      
      





, , . , . , .









, , . , : , , :







fun RBuilder.videoList(handler: VideoListProps.() -> Unit): ReactElement {
    return child(VideoList::class) {
        attrs.handler()
    }
}
      
      





, : videoList



RBuilder



. handler



– - VideoListProps



, Unit



. child



( VideoList



), handler



attrs



.







– :







videoList {
    videos = unwatchedVideos
}
      
      





, child



, class



attrs



, . , . ! App



.









- – . , . : alert



.







VideoList.render



. , p



:







p {
    key = video.id.toString()
    attrs {
        onClickFunction = {
            window.alert("Clicked $video!")
        }
    }
    +"${video.speaker}: ${video.title}"
}
      
      





IntelliJ IDEA , Alt+Enter



. :







import kotlinx.html.js.onClickFunction
import kotlinx.browser.window
      
      





:

アラート







onClickFunction



, . Kotlin/JS . . , onClickFunction



.




?







. (|>



). – . – :







external interface VideoListState : RState {
    var selectedVideo: Video?
}
      
      





:







  • VideoList



    , VideoListState



    RComponent<..., VideoListState>



    .
  • .
  • onClickFunction



    selectedVideo



    , . , setState



    .


, :







@JsExport
class VideoList : RComponent<VideoListProps, VideoListState>() {

    override fun RBuilder.render() {
        for (video in props.videos) {
            p {
                key = video.id.toString()
                attrs {
                    onClickFunction = {
                        setState {
                            selectedVideo = video
                        }
                    }
                }
                if (video == state.selectedVideo) {
                    +"|> "
                }
                +"${video.speaker}: ${video.title}"
            }
        }
    }
}
      
      





setState



. UI .

, React FAQ.







step-03-first-component



.


5. .



. , , . , :)

ペア







-, – , . ( ) . ( , "" ).









-, : . , . , , . . ! , App



. , VideoList



.







:







external interface AppState : RState {
    var currentVideo: Video?
}
      
      





App



:







@JsExport
class App : RComponent<RProps, AppState>()
      
      





VideoListState



, . , , :







@JsExport
class VideoList : RComponent<VideoListProps, RState>()
      
      





App



VideoList



. VideoListProps



, :







external interface VideoListProps : RProps {
    var videos: List<Video>
    var selectedVideo: Video?
}
      
      





, :







if (video == props.selectedVideo) {
    +"|> "
}
      
      





, : , setState



onClickFunction



. , - .









, , . -: . , ? – , Video



Unit



:







external interface VideoListProps : RProps {
    var videos: List<Video>
    var selectedVideo: Video?
    var onSelectVideo: (Video) -> Unit
}
      
      





onClickFunction



:







onClickFunction = {
    props.onSelectVideo(video)
}
      
      





, . , . videoList



:







videoList {
    videos = unwatchedVideos
    selectedVideo = state.currentVideo
    onSelectVideo = { video ->
        setState {
            currentVideo = video
        }
    }
}
      
      





watchedVideos



.







, : , , . , , .







step-04-composing-components



.


6. !



, . .









, – ( -). , : , . , Video



, . VideoPlayer



VideoPlayer.kt



:







import kotlinx.css.*
import kotlinx.html.js.onClickFunction
import react.*
import react.dom.*
import styled.*

external interface VideoPlayerProps : RProps {
    var video: Video
}

@JsExport
class VideoPlayer : RComponent<VideoPlayerProps, RState>() {
    override fun RBuilder.render() {
        styledDiv {
            css {
                position = Position.absolute
                top = 10.px
                right = 10.px
            }
            h3 {
                +"${props.video.speaker}: ${props.video.title}"
            }
            img {
                attrs {
                    src = "https://via.placeholder.com/640x360.png?text=Video+Player+Placeholder"
                }
            }
        }
    }
}

fun RBuilder.videoPlayer(handler: VideoPlayerProps.() -> Unit): ReactElement {
    return child(VideoPlayer::class) {
        this.attrs(handler)
    }
}
      
      





styledDiv



( App.kt



) . , - – let



, let



, currentVideo



null



:







state.currentVideo?.let { currentVideo ->
    videoPlayer {
        video = currentVideo
    }
}
      
      







. VideoPlayer



.







, VideoPlayer



. , .







-. , , , , . .







VideoPlayerProps



:







external interface VideoPlayerProps : RProps {
    var video: Video
    var onWatchedButtonPressed: (Video) -> Unit
    var unwatchedVideo: Boolean
}
      
      





, . CSS : . HTML DSL render



, h3



img



:







styledButton {
    css {
        display = Display.block
        backgroundColor = if (props.unwatchedVideo) Color.lightGreen else Color.red
    }
    attrs {
        onClickFunction = {
            props.onWatchedButtonPressed(props.video)
        }
    }
    if (props.unwatchedVideo) {
        +"Mark as watched"
    } else {
        +"Mark as unwatched"
    }
}
      
      







VideoPlayer



, .







unwatched



watched



, .







. ! :







external interface AppState : RState {
    var currentVideo: Video?
    var unwatchedVideos: List<Video>
    var watchedVideos: List<Video>
}
      
      





init



. , App



:







override fun AppState.init() {
    unwatchedVideos = listOf(
        KotlinVideo(1, "Building and breaking things", "John Doe", "https://youtu.be/PsaFVLr8t4E"),
        KotlinVideo(2, "The development process", "Jane Smith", "https://youtu.be/PsaFVLr8t4E"),
        KotlinVideo(3, "The Web 7.0", "Matt Miller", "https://youtu.be/PsaFVLr8t4E")
    )
    watchedVideos = listOf(
        KotlinVideo(4, "Mouseless development", "Tom Jerry", "https://youtu.be/PsaFVLr8t4E")
    )
}
      
      





unwatchedVideos



watchedVideos



Main.kt



, Main.kt



(un



)watchedVideos



, IDE , state.



(un



)watchedVideos



.







, . :







videoPlayer {
    video = currentVideo
    unwatchedVideo = currentVideo in state.unwatchedVideos
    onWatchedButtonPressed = {
        if (video in state.unwatchedVideos) {
            setState {
                unwatchedVideos -= video
                watchedVideos += video
            }
        } else {
            setState {
                watchedVideos -= video
                unwatchedVideos += video
            }
        }
    }
}
      
      





, , , .







, . , , , . !







. .







step-05-more-components



.


7. NPM



, . , , . , , .







– .









, . react-youtube-lite



. API README.







. react-youtube-lite



, Gradle. :







dependencies {
    // ...
    // Video Player ( 7)
    implementation(npm("react-youtube-lite", "1.0.1"))
    // ...
}
      
      





– NPM Gradle npm



. yarn



, Kotlin/JS Gradle , , .







NPM , : , . , IDE . . ReactYouTube.kt



:







@file:JsModule("react-youtube-lite")
@file:JsNonModule

import react.*

@JsName("ReactYouTubeLite")
external val reactPlayer: RClass<dynamic>
      
      





JavaScript – , , . – require("react-youtube-lite").default



JS. : " , , RClass<dynamic>



".









, , . dynamic



, . , , - (, ).







, , ( external



), README . – . , – . :







@file:JsModule("react-youtube-lite")
@file:JsNonModule

import react.*

@JsName("ReactYouTubeLite")
external val reactPlayer: RClass<ReactYouTubeProps>

external interface ReactYouTubeProps : RProps {
    var url: String
}
      
      





VideoPlayer



! img



:







reactPlayer {
    attrs.url = props.video.videoUrl
}
      
      







KotlinConf ( ). – . , , . , , react-share. Gradle:







dependencies {
    // ...
    // Share Buttons ( 7)
    implementation(npm("react-share", "~4.2.1"))
    // ...
}
      
      





. , , : , EmailShareButton



EmailIcon



. . ; ReactShare.kt



:







@file:JsModule("react-share")
@file:JsNonModule

import react.RClass
import react.RProps

@JsName("EmailIcon")
external val emailIcon: RClass<IconProps>

@JsName("EmailShareButton")
external val emailShareButton: RClass<ShareButtonProps>

@JsName("TelegramIcon")
external val telegramIcon: RClass<IconProps>

@JsName("TelegramShareButton")
external val telegramShareButton: RClass<ShareButtonProps>

external interface ShareButtonProps : RProps {
    var url: String
}

external interface IconProps : RProps {
    var size: Int
    var round: Boolean
}
      
      





. reactPlayer



( styledDiv



, ):







styledDiv {
    css {
        display = Display.flex
        marginBottom = 10.px
    }
    emailShareButton {
        attrs.url = props.video.videoUrl
        emailIcon {
            attrs.size = 32
            attrs.round = true
        }
    }
    telegramShareButton {
        attrs.url = props.video.videoUrl
        telegramIcon {
            attrs.size = 32
            attrs.round = true
        }
    }
}
      
      





, . , . , , .

ペア







, .







step-06-packages-from-npm



.


8. REST API



, . , REST API.







API, https://my-json-server.typicode.com/kotlin-hands-on/kotlinconf-json/videos/1. API – videos



, . API . , , Video



( ;)



). , .







JS



. Kotlin/JS , . Fetch API, HTTP REST API.







JavaScript – . , , . , - . , , , . , :







window.fetch("https://url...").then {
    it.json().then {
        it.unsafeCast<Video>()
        //...
    }
}
      
      





. , .









(structured concurrency) – . , . . .







, Gradle :







dependencies {
    //...
    // Coroutines ( 8)
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9")
}
      
      





, !









App.kt



, , REST API:







suspend fun fetchVideo(id: Int): Video {
    val response = window
        .fetch("https://my-json-server.typicode.com/kotlin-hands-on/kotlinconf-json/videos/$id")
        .await()
        .json()
        .await()
    return response as Video
}
      
      





. :







import kotlinx.browser.window
import kotlinx.coroutines.*
      
      





, suspend . fetch



, id



API. (await



), JSON, . external interface Video



. , IDE – JavaScript fetch



: , Video



. . : , @Suppress



, unsafeCast



(response.unsafeCast<Video>()



).







. window.fetch



json



. , . , (await



) . , , . await



, ( suspend



). , .







suspend



, , 25. fetchVideos



, 25 . , suspend – async



. :







suspend fun fetchVideos(): List<Video> = coroutineScope {
    (1..25).map { id ->
        async {
            fetchVideo(id)
        }
    }.awaitAll()
}
      
      





coroutineScope



. 25 , , .







, .







. , init



App



:







override fun AppState.init() {
    unwatchedVideos = listOf()
    watchedVideos = listOf()

    val mainScope = MainScope()
    mainScope.launch {
        val videos = fetchVideos()
        setState {
            unwatchedVideos = videos
        }
    }
}
      
      





, init



, setState



unwatchedVideos



. - , , , unwatchedVideos



. setState



, , .







:

実際のデータ







. , "Hello, World" .







, , , , .







step-07-using-external-rest-api



.


9.



.









, Gradle build



- IntelliJ IDEA ./gradlew build



. , , , DCE (dead code elimination – ).







, build/distributions



. JS , HTML , . , , , HTTP , GitHub Pages .







Heroku



Heroku . ; , .







git Heroku . :







git init
heroku create
git add .
git commit -m "initial commit"
      
      





JVM , Heroku (, Ktor Spring Boot), , . Heroku:







heroku buildpacks:set heroku/gradle
heroku buildpacks:add https://github.com/heroku/heroku-buildpack-static.git
      
      





heroku/gradle



stage



Gradle . , build



, , :







// Heroku Deployment ( 9)
tasks.register("stage") {
    dependsOn("build")
}
      
      





buildpack-static



, static.json



. root



:







{
    "root": "build/distributions"
}
      
      





, , :







git add -A
git commit -m "add stage task and static content root configuration"
git push heroku master
      
      





master (, , step*



), , master Heroku (, : git push heroku step-08-deploying-to-production:master



).

, , !

heroku







final



.


10. :



- , .







React 16.8 . . : !







, , – state effect. , .









, . , . – this



. , , :







external interface WelcomeProps : RProps {
    var name: String
}

val welcome = functionalComponent<WelcomeProps> { props -> 
    h1 {
        +"Hello, ${props.name}"
    }
}
      
      





, external interface



. . functionalComponent



render



.







: child



:







child(welcome) {
    attrs.name = "Kotlin"
}
      
      





:







fun RBuilder.welcome(handler: WelcomeProps.() -> Unit) = child(welcome) {
    attrs.handler()
}
      
      





4. welcome { name = "Kotlin" }



.







, - . .







State



, . :







val counter = functionalComponent<RProps> {
    val (count, setCount) = useState(0)
    button {
        attrs.onClickFunction = { setCount(count + 1) }
        +"$count"
    }
}
      
      





, :







  • useState



    0



    Int



    . , , (useState<String?>(null)



    ).
  • useState



    , :

    1. ( count



      Int



      );
    2. ( setCount



      RSetState<Int> /* = (Int) -> Unit */



      ).
  • , setState



    .


, count



, . , , , .







State .







: useState



– -. , , :







val counter = functionalComponent<RProps> {
    var count by useState(0)
    button {
        attrs.onClickFunction = { ++count }
        +"$count"
    }
}
      
      





Effect



, - – API WebSocket . , h3



:







val randomFact = functionalComponent<RProps> {
    val (randomFact, setRandomFact) = useState<String?>(null)
    useEffect(emptyList()) {
        GlobalScope.launch {
            val fortyTwoFact = window.fetch("http://numbersapi.com/42").await().text().await()
            setRandomFact(fortyTwoFact)
        }
    }
    h3 { +(randomFact ?: "Fetching...") }
}
      
      





, , , . useEffect



, setRandomFact



.







, useEffect



. – , – . , , useEffect



. API . .







, setRandomFact



, .







Effect, "" , .









, - , , videoList



, . useState



, useEffect



API, 8.







, .







11. ?





, . , Kotlin/JS .









. , . , HTML , .









. , -. - , ( Ktor), , . - .







APIs



APIs, . ? ( : )? , , !







:



, , . CSS (grids) ( : ).









kotlin-wrappers JS , . ( ):









,



YouTrack. , . Slack. , #javascript



#react



.









- , . , .









. , , .







, !









, ! Kotlin/JS , JS, JSX – , , .







, , Kotlin DSL. JSX , Kotlin DSL , . , , , – . , , . Kotlin/JS !








All Articles