WebView

import { Button, HStack, Image, List, Navigation, NavigationStack, Script, Section, Text, TextField, useMemo, useState, VStack, WebView } from "scripting"

function WebViewControllerExample() {
  const [logs, setLogs] = useState<{
    content: string
    error: boolean
  }[]>([])

  function addLog(content: string, error = false) {
    setLogs(logs => [...logs, { content, error }])
  }

  async function runCode() {
    setLogs([])
    const controller = new WebViewController()

    addLog("WebViewController created.")
    addLog("Start loading...")

    if (await controller.loadURL("https://github.com")) {
      addLog("Website is loaded.")
      addLog("Calling controller.evaluateJavaScript...")
      const title = await controller.evaluateJavaScript<string>("document.title")

      if (title != null) {
        addLog(`Title: ${title}`)
      } else {
        addLog("Failed to get the title.", true)
      }
    } else {
      addLog("Failed to load the website.", true)
    }

    controller.dispose()
    addLog("The controller is disposed.")
  }

  return <Section
    header={
      <Text>WebView controller</Text>
    }
  >
    <VStack
      frame={{
        maxWidth: "infinity"
      }}
      alignment={"leading"}
    >
      <Text font={"headline"}>This example will follow these steps:</Text>
      <VStack
        padding={{
          leading: 16
        }}
        spacing={16}
        foregroundStyle={"secondaryLabel"}
        alignment={"leading"}
      >
        <Text>Create a WebViewController instane</Text>
        <Text>Load https://github.com</Text>
        <Text>Call evaluateJavaScript and get the title of the website</Text>
      </VStack>
      <HStack
        alignment={"center"}
        frame={{
          maxWidth: "infinity"
        }}
      >
        <Button
          title={"Run"}
          action={runCode}
        />
      </HStack>

      <VStack
        alignment={"leading"}
        spacing={8}
      >
        {logs.map(log =>
          <Text
            font={"caption"}
            monospaced
            padding={{
              leading: 16
            }}
            foregroundStyle={log.error ? "systemRed" : "systemGreen"}
          >{log.content}</Text>
        )}
      </VStack>
    </VStack>
  </Section>
}

function PresentWebViewExample() {

  function run() {
    const controller = new WebViewController()
    controller.loadURL("https://github.com")

    controller.present({
      fullscreen: true,
      navigationTitle: "Github"
    }).then(() => {
      console.log("WebView is dismissed")
      controller.dispose()
    })
  }

  return <Section
    header={
      <Text>Present a WebView as a independent page</Text>
    }
  >
    <Button
      title={"Present"}
      action={run}
    />
  </Section>
}

function EmbedAWebViewExample() {
  const controller = useMemo(() => new WebViewController(), [])
  const [url, setUrl] = useState("")

  return <Section
    header={
      <Text>Embed a WebView</Text>
    }
  >
    <VStack>
      <HStack>
        <Button action={() => {
          controller.goBack()
        }}>
          <Image
            systemName={"arrow.left"}
          />
        </Button>
        <Button action={() => {
          controller.goForward()
        }}>
          <Image
            systemName={"arrow.right"}
          />
        </Button>
        <Button action={() => {
          controller.reload()
        }}>
          <Image
            systemName={"arrow.clockwise"}
          />
        </Button>
        <TextField
          title={"Website URL"}
          textFieldStyle={"roundedBorder"}
          value={url}
          onChanged={setUrl}
          keyboardType={"URL"}
          textInputAutocapitalization={"never"}
          frame={{
            maxWidth: "infinity"
          }}
        />
        <Button
          action={() => controller.loadURL(url)}
        >
          <Image
            systemName={"arrow.right.circle"}
          />
        </Button>
      </HStack>
      <WebView
        controller={controller}
        frame={{
          height: 400
        }}
      />
    </VStack>
  </Section>
}

function Example() {
  return <NavigationStack>
    <List
      navigationTitle={"WebView"}
      navigationBarTitleDisplayMode={"inline"}
    >
      <WebViewControllerExample />
      <PresentWebViewExample />
      <EmbedAWebViewExample />
    </List>
  </NavigationStack>
}

async function run() {
  await Navigation.present({
    element: <Example />
  })

  Script.exit()
}

run()