summary refs log tree commit diff
path: root/app/page.tsx
blob: 229a803192da8c65f3d8a54a9080141cd35cb159 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
import fs from 'node:fs/promises'
import path from 'node:path'

import Link from 'next/link'

import styles from '~/styles/index.module.css'
import sharp from 'sharp'

const mimes = {
  jpg: 'image/jpeg',
  jpeg: 'image/jpeg',
  png: 'image/png',
  bmp: 'image/bmp',
  gif: 'image/gif'
}

export default async function Index() {
  const buttonDir = 'images/buttons/'
  const buttonFiles = (await fs.readdir(path.join(process.cwd(), buttonDir)).catch(() => {}) || [])
    .filter(buttonName => buttonName.match(/\.(png|gif|jpg|jpeg)$/))

  const buttonLinks = (await fs.readFile(path.join(process.cwd(), 'images/buttons/urls.txt'), {encoding: 'utf8'}).catch(() => {}) || '')
    .split('\n').filter(line => !!line)
    .map(line => line.match(/^([^:]+):\s+(.*)$/))
    .filter(match => !!match)
    .reduce((acc,[_, name, url]) => ({...acc, [name]: url}), {})

  const buttons = await Promise.all(buttonFiles.map(async buttonFile => {
    const origImageData = await fs.readFile(path.join(process.cwd(), buttonDir, buttonFile))
    let preview = null

    const metadata = await sharp(origImageData, {pages: -1}).metadata()

    // Generate preview if there's more than one frame
    if(metadata.pages > 1) {
      preview = await sharp(origImageData, {pages: 1})
        .png({quality: 80, force: true})
        .toBuffer()
    }

    return {
      name: buttonFile.split('.').slice(0, -1).join('.'),
      mime: mimes[buttonFile.split('.').at(-1)] || 'image/*',
      url: buttonLinks[buttonFile],
      data: origImageData.toString('base64'),
      preview: preview?.toString('base64')
    }
  }))

  const friendButtons = buttons.filter(button => button.name !== 'tempest')
  const ourButton = buttons.filter(button => button.name === 'tempest')[0]

  return (
    <>
      <main className="mainColumn card">
        <p>
          Hi, we're tempest!  We're a median plural
          system of six members, but most of the time you'll probably see us
          operating as one
        </p>

        <p>We like coding, VR, and making CG art</p>

        <h2>At a glance</h2>
        <div className={styles.glance}>
          <span className={styles.label}>Pronouns:</span>
          <span>they/it</span>

          <span className={styles.label}>Cohort:</span>
          <span>millenial</span>

          <span className={styles.label}>Orientation:</span>
          <span>ace . . . ish</span>

          <span className={styles.label}>Partners:</span>
          <span>several</span>

          <span className={styles.label}>Children:</span>
          <span>two</span>

          <span className={styles.label}>Capitalize name:</span>
          <span>not unless we're at work</span>
        </div>

        <p>
          <em>Note:</em> This is the information for our system in aggregate,
          for individual info see our <Link href="/about">about</Link> page
        </p>
      </main>
      {friendButtons.length > 0 && (
        <div className="mainColumn postscript">
          <h2>Friends and other neighbors:
            {ourButton && <a target="_blank" href={`data:${ourButton.mime};base64,${ourButton.data}`} className="asideLink" aria-label="Link back to us">
              <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="currentcolor" d="M579.8 267.7c56.5-56.5 56.5-148 0-204.5c-50-50-128.8-56.5-186.3-15.4l-1.6 1.1c-14.4 10.3-17.7 30.3-7.4 44.6s30.3 17.7 44.6 7.4l1.6-1.1c32.1-22.9 76-19.3 103.8 8.6c31.5 31.5 31.5 82.5 0 114L422.3 334.8c-31.5 31.5-82.5 31.5-114 0c-27.9-27.9-31.5-71.8-8.6-103.8l1.1-1.6c10.3-14.4 6.9-34.4-7.4-44.6s-34.4-6.9-44.6 7.4l-1.1 1.6C206.5 251.2 213 330 263 380c56.5 56.5 148 56.5 204.5 0L579.8 267.7zM60.2 244.3c-56.5 56.5-56.5 148 0 204.5c50 50 128.8 56.5 186.3 15.4l1.6-1.1c14.4-10.3 17.7-30.3 7.4-44.6s-30.3-17.7-44.6-7.4l-1.6 1.1c-32.1 22.9-76 19.3-103.8-8.6C74 372 74 321 105.5 289.5L217.7 177.2c31.5-31.5 82.5-31.5 114 0c27.9 27.9 31.5 71.8 8.6 103.9l-1.1 1.6c-10.3 14.4-6.9 34.4 7.4 44.6s34.4 6.9 44.6-7.4l1.1-1.6C433.5 260.8 427 182 377 132c-56.5-56.5-148-56.5-204.5 0L60.2 244.3z"/></svg>
            </a>}
          </h2>
          <div className={styles.the88x31s}>
            {friendButtons.map(button => {
              const image = <img
                data-button-canonical
                alt={button.name}
                title={button.name}
                src={`data:${button.mime};base64,${button.data}`}
              />

              const preview = button.preview ? <img
                data-button-preview
                alt={button.name}
                title={button.name}
                src={`data:image/png;base64,${button.preview}`}
              /> : null

              if(button.url)
                return (
                  <a target="_blank" rel="noopener" key={button.name} href={button.url}>{image}{preview}</a>
                )
              else
                return (
                  <a key={button.name}>{image}{preview}</a>
                )
            })}
            <script type="text/javascript" dangerouslySetInnerHTML={{__html: `
              const buttons = document.currentScript.parentElement.querySelectorAll('a:has([data-button-preview])')

              // Slightly janky way of resetting GIF animations after they go back to the preview state
              buttons.forEach(button => button.addEventListener('mouseleave', () => {
                const image = button.querySelector('[data-button-canonical]')
                const src = image.getAttribute('src')
                image.setAttribute('src', '')
                setTimeout(() => {
                  image.setAttribute('src', src)
                }, 0)
              }))
            `}}/>
          </div>
        </div>
      )}
    </>
  )
}