Switch to new Metadata API

This commit is contained in:
Max Leiter 2023-02-24 18:07:40 -08:00
parent afd18d19a9
commit 6d184906b1
21 changed files with 258 additions and 209 deletions

View file

@ -14,7 +14,7 @@
}, },
"dependencies": { "dependencies": {
"@next-auth/prisma-adapter": "^1.0.5", "@next-auth/prisma-adapter": "^1.0.5",
"@next/eslint-plugin-next": "13.2.1-canary.0", "@next/eslint-plugin-next": "13.2.2-canary.0",
"@prisma/client": "^4.9.0", "@prisma/client": "^4.9.0",
"@radix-ui/react-dialog": "^1.0.2", "@radix-ui/react-dialog": "^1.0.2",
"@radix-ui/react-dropdown-menu": "^2.0.2", "@radix-ui/react-dropdown-menu": "^2.0.2",
@ -27,7 +27,7 @@
"client-zip": "2.3.0", "client-zip": "2.3.0",
"jest": "^29.4.1", "jest": "^29.4.1",
"lodash.debounce": "^4.0.8", "lodash.debounce": "^4.0.8",
"next": "13.2.1-canary.0", "next": "13.2.2-canary.0",
"next-auth": "^4.19.2", "next-auth": "^4.19.2",
"next-themes": "^0.2.1", "next-themes": "^0.2.1",
"react": "18.2.0", "react": "18.2.0",

View file

@ -3,7 +3,7 @@ lockfileVersion: 5.4
specifiers: specifiers:
'@next-auth/prisma-adapter': ^1.0.5 '@next-auth/prisma-adapter': ^1.0.5
'@next/bundle-analyzer': 13.1.7-canary.26 '@next/bundle-analyzer': 13.1.7-canary.26
'@next/eslint-plugin-next': 13.2.1-canary.0 '@next/eslint-plugin-next': 13.2.2-canary.0
'@prisma/client': ^4.9.0 '@prisma/client': ^4.9.0
'@radix-ui/react-dialog': ^1.0.2 '@radix-ui/react-dialog': ^1.0.2
'@radix-ui/react-dropdown-menu': ^2.0.2 '@radix-ui/react-dropdown-menu': ^2.0.2
@ -30,7 +30,7 @@ specifiers:
eslint-config-next: 13.1.7-canary.26 eslint-config-next: 13.1.7-canary.26
jest: ^29.4.1 jest: ^29.4.1
lodash.debounce: ^4.0.8 lodash.debounce: ^4.0.8
next: 13.2.1-canary.0 next: 13.2.2-canary.0
next-auth: ^4.19.2 next-auth: ^4.19.2
next-themes: ^0.2.1 next-themes: ^0.2.1
next-unused: 0.0.6 next-unused: 0.0.6
@ -54,7 +54,7 @@ specifiers:
dependencies: dependencies:
'@next-auth/prisma-adapter': 1.0.5_77bhi65b6v5jbrzr36rs2ojwe4 '@next-auth/prisma-adapter': 1.0.5_77bhi65b6v5jbrzr36rs2ojwe4
'@next/eslint-plugin-next': 13.2.1-canary.0 '@next/eslint-plugin-next': 13.2.2-canary.0
'@prisma/client': 4.9.0_prisma@4.9.0 '@prisma/client': 4.9.0_prisma@4.9.0
'@radix-ui/react-dialog': 1.0.2_5ndqzdd6t4rivxsukjv3i3ak2q '@radix-ui/react-dialog': 1.0.2_5ndqzdd6t4rivxsukjv3i3ak2q
'@radix-ui/react-dropdown-menu': 2.0.2_5ndqzdd6t4rivxsukjv3i3ak2q '@radix-ui/react-dropdown-menu': 2.0.2_5ndqzdd6t4rivxsukjv3i3ak2q
@ -67,9 +67,9 @@ dependencies:
client-zip: 2.3.0 client-zip: 2.3.0
jest: 29.4.1_@types+node@18.11.18 jest: 29.4.1_@types+node@18.11.18
lodash.debounce: 4.0.8 lodash.debounce: 4.0.8
next: 13.2.1-canary.0_biqbaboplfbrettd7655fr4n2y next: 13.2.2-canary.0_biqbaboplfbrettd7655fr4n2y
next-auth: 4.19.2_avkzbs57los6fogzead7rr4u74 next-auth: 4.19.2_kc5cm3uhxzg4ew7l4d27zpo4ia
next-themes: 0.2.1_avkzbs57los6fogzead7rr4u74 next-themes: 0.2.1_kc5cm3uhxzg4ew7l4d27zpo4ia
react: 18.2.0 react: 18.2.0
react-datepicker: 4.8.0_biqbaboplfbrettd7655fr4n2y react-datepicker: 4.8.0_biqbaboplfbrettd7655fr4n2y
react-dom: 18.2.0_react@18.2.0 react-dom: 18.2.0_react@18.2.0
@ -817,7 +817,7 @@ packages:
next-auth: ^4 next-auth: ^4
dependencies: dependencies:
'@prisma/client': 4.9.0_prisma@4.9.0 '@prisma/client': 4.9.0_prisma@4.9.0
next-auth: 4.19.2_avkzbs57los6fogzead7rr4u74 next-auth: 4.19.2_kc5cm3uhxzg4ew7l4d27zpo4ia
dev: false dev: false
/@next/bundle-analyzer/13.1.7-canary.26: /@next/bundle-analyzer/13.1.7-canary.26:
@ -829,8 +829,8 @@ packages:
- utf-8-validate - utf-8-validate
dev: true dev: true
/@next/env/13.2.1-canary.0: /@next/env/13.2.2-canary.0:
resolution: {integrity: sha512-6e5sszeQUWRFZpy/HXVWs18DKbjErcF/pMKS8pIsknejN1DQPRxNFEI+QuXIf76N+r87K1qR/qWQx4WKQof8Rw==} resolution: {integrity: sha512-EDy4UF4oXGmWgMq9w8P7Wg7JoYbrGY7EVnZcNoyMDU8o9KeiviYjK2IR3yFBulG0/1I2UbVu2wIM1xPYsQIonQ==}
dev: false dev: false
/@next/eslint-plugin-next/13.1.7-canary.26: /@next/eslint-plugin-next/13.1.7-canary.26:
@ -839,14 +839,14 @@ packages:
glob: 7.1.7 glob: 7.1.7
dev: true dev: true
/@next/eslint-plugin-next/13.2.1-canary.0: /@next/eslint-plugin-next/13.2.2-canary.0:
resolution: {integrity: sha512-OwJgZrTGj/WovColJHOUd2RB+2uju12cYL2oL5CnyNTz999GJ/z/esghfCAqL5fh4YaiQqlZCRJEbbymTsiU0Q==} resolution: {integrity: sha512-XBeBfEqB/XMyGHyYGG+S+KDA0xlcs+DtXgcfohJXUTD+Hiwjk4Zn4pCuopDGHlO49td5/xdCKznE0LhSDcqhoA==}
dependencies: dependencies:
glob: 7.1.7 glob: 7.1.7
dev: false dev: false
/@next/swc-android-arm-eabi/13.2.1-canary.0: /@next/swc-android-arm-eabi/13.2.2-canary.0:
resolution: {integrity: sha512-irsC3EdtmroNaHrKeqZDJTcQ15wrTWp5JJ8IIMJ6GvW/vhVENk+unnVoxD4BQLmgelCbzi7udusISDCkUUWCkg==} resolution: {integrity: sha512-/si1jk3wtrarhdVPQloSubTJjLeuTpgT7V2R2w+acWzvBBsrs2ThhZodLz0fJRKcYKmeDZebhtYGUkxkcm48Tw==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm] cpu: [arm]
os: [android] os: [android]
@ -854,8 +854,8 @@ packages:
dev: false dev: false
optional: true optional: true
/@next/swc-android-arm64/13.2.1-canary.0: /@next/swc-android-arm64/13.2.2-canary.0:
resolution: {integrity: sha512-3AyZ4THkuamCyOhFkrqFY38IMI+YFmxomOcFHK+67hrqfHQRkH0boL15SnHAzURlAeruI/Mw9/PuYshYXn3Usg==} resolution: {integrity: sha512-MKImVjggMFvPJju48fvz/KqjiqXaKoimGz3Vmc3c12WaSIEa5O1sevw0dQKPI6sv+1Mf5MuP7XlLQ9bWJo+72Q==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [android] os: [android]
@ -863,8 +863,8 @@ packages:
dev: false dev: false
optional: true optional: true
/@next/swc-darwin-arm64/13.2.1-canary.0: /@next/swc-darwin-arm64/13.2.2-canary.0:
resolution: {integrity: sha512-CN5e/C9WLGa8EdAmnndGzKh14ALfMHwjoRZYHBfFmM1Z+SArz89VrXeOmAMVgpcANBNC4ShvXp4rIUD9PveDQA==} resolution: {integrity: sha512-Sj+hCut5c6K2lOIJpV9KDsDJNe1dVacAE8WWmvotoeu4ab1W0//axZOxksq0S3240oF9CJ8QPZo+q5lPV6Gn2Q==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [darwin] os: [darwin]
@ -872,8 +872,8 @@ packages:
dev: false dev: false
optional: true optional: true
/@next/swc-darwin-x64/13.2.1-canary.0: /@next/swc-darwin-x64/13.2.2-canary.0:
resolution: {integrity: sha512-eP7hKzI84MrG54M3207pynGmJHqf2V6Ex0CXpht0WSMrrv7G6W9iHyLPqk18AgxzgjA5QH4lDP+FpTaoDmpdkg==} resolution: {integrity: sha512-/lv1J5ts5UhQ5V2V0PpIkQJw8kJywMgvPegZ/yf0fy9QOOdGCAw1dXtZsKUISrJmuzPcCZo0F56Zjbyb6lCLsw==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [darwin] os: [darwin]
@ -881,8 +881,8 @@ packages:
dev: false dev: false
optional: true optional: true
/@next/swc-freebsd-x64/13.2.1-canary.0: /@next/swc-freebsd-x64/13.2.2-canary.0:
resolution: {integrity: sha512-rloppA9VtxDFruDxMQJqrISwKzOip9/1hksljlXTBg+/fu2dJyufuARwhfksDNH3fBT0fQC10esFO+WjhLEp7g==} resolution: {integrity: sha512-ECQyYOYd1AKu/oTNeI5pWDXebgKTIWXCs84IN9rXsM7f+FOQmVU8V+gK6Oisw3jya68B1vmfMqLDGQClDDIMAg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [freebsd] os: [freebsd]
@ -890,8 +890,8 @@ packages:
dev: false dev: false
optional: true optional: true
/@next/swc-linux-arm-gnueabihf/13.2.1-canary.0: /@next/swc-linux-arm-gnueabihf/13.2.2-canary.0:
resolution: {integrity: sha512-Fb0d7znHY3RlaJFCc07X+x3NGQw3Sp0UOsCOtalG2LSgMKFTt0dMc47WBZez+ratWI7dc6B3H8kmsgntBd4Skw==} resolution: {integrity: sha512-/UQf0yoIwJJhgV8dpDmgWq/Q3/IxqFRsUHfBiy2M6kGVYwB9CnsjZzHwUKmg7sASdv+atW+oc4PIXXN6W9CCgg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
@ -899,8 +899,8 @@ packages:
dev: false dev: false
optional: true optional: true
/@next/swc-linux-arm64-gnu/13.2.1-canary.0: /@next/swc-linux-arm64-gnu/13.2.2-canary.0:
resolution: {integrity: sha512-ATkCdBfHpTtB+7VWgsib2O98sUAa9vyB7uyHTFDWPMB9qTnTXMUfPoYxFJmYXrjwB/xoW97AKrB3FGxMM+swHw==} resolution: {integrity: sha512-pv0m+4hnKyq0MwEVAvrJScoYme4GKoPieP0Tj32oRK6P1gafFK4uJxF6zMVPlO0D/tGI7EkoHZjv/7sU5pSYSA==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
@ -908,8 +908,8 @@ packages:
dev: false dev: false
optional: true optional: true
/@next/swc-linux-arm64-musl/13.2.1-canary.0: /@next/swc-linux-arm64-musl/13.2.2-canary.0:
resolution: {integrity: sha512-2x2i0dgcyhE9wycHYLAZiiHkQlzambV0qdjsG7DqQhc23UhVvkqG6dB9PHPFrkxMFPq2QDLpYGlKF9sdnnEQbw==} resolution: {integrity: sha512-Bpc7nLJoP6dVPG9lOqCGqWkfiMo143YRx7+cvxSl1FVupkGP7Ntldvl3zS/m2CnU/5egQEjC2C5jEgQRY92Zhw==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
@ -917,8 +917,8 @@ packages:
dev: false dev: false
optional: true optional: true
/@next/swc-linux-x64-gnu/13.2.1-canary.0: /@next/swc-linux-x64-gnu/13.2.2-canary.0:
resolution: {integrity: sha512-nRQfotbWl4V9sIS+nx4ZXtJxe8/+naeUrJSoD+Wi9PrIyF3uuLE+xi1WMHS5RhGydyduvFSyIjQO2qGzZ0Lhsw==} resolution: {integrity: sha512-2RPr5AxawTUVaQGrkhWWpjVdExPiA8wEJL6E1itI2gNav8LU3FEsIr9juQURv47Xn2KE286fw8214D6+D0RExg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
@ -926,8 +926,8 @@ packages:
dev: false dev: false
optional: true optional: true
/@next/swc-linux-x64-musl/13.2.1-canary.0: /@next/swc-linux-x64-musl/13.2.2-canary.0:
resolution: {integrity: sha512-hdM2Cds8yppGSzql2A4tCYdzjVheDh/Ei45GOz94gshO95u7SSS9UQNTaOKZTWOUfRnZYZrEbAVMhDxz10qqdQ==} resolution: {integrity: sha512-gKr5tJoJKGSlmDdpmoO1fe6oUeVMz2TluspKPM4ulc0rqyXjrRmQ9pd/oqEoypUKTUkqL49lt4hTL98MkhvXWg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
@ -935,8 +935,8 @@ packages:
dev: false dev: false
optional: true optional: true
/@next/swc-win32-arm64-msvc/13.2.1-canary.0: /@next/swc-win32-arm64-msvc/13.2.2-canary.0:
resolution: {integrity: sha512-2SkgCWZbDLBgKbgJ+slVpqQLNwOzp2/ja2C8nNzwUbDB4cbD9fTyWrUngZBgg8J1OaP5KR7qHOQ8JQoHqiTOjA==} resolution: {integrity: sha512-0N7+lcV8ycqzgJIye40+Vl0iUk/mu3919T2kfnt20WU7gP+rpRJkPfP44yxBdt7U7XYtnqldM2Ox969y+0qJVw==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [win32] os: [win32]
@ -944,8 +944,8 @@ packages:
dev: false dev: false
optional: true optional: true
/@next/swc-win32-ia32-msvc/13.2.1-canary.0: /@next/swc-win32-ia32-msvc/13.2.2-canary.0:
resolution: {integrity: sha512-1j1BJ2wtzDnbn6uOUPsPhOgjIWESr8TE7aLUAlxtTYgsrYdPYxe4uowa9M04mEw6UxZHatrrOA6zvTVoFUtB8g==} resolution: {integrity: sha512-vqdsKfJcfvf37P7YcJeIaPP5E7iSR7yTaHwgBpvY6Q0tDRxPv0gr4nRXElBs/V1VcxMaSx9PyyiQkKYB6UzEhg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [ia32] cpu: [ia32]
os: [win32] os: [win32]
@ -953,8 +953,8 @@ packages:
dev: false dev: false
optional: true optional: true
/@next/swc-win32-x64-msvc/13.2.1-canary.0: /@next/swc-win32-x64-msvc/13.2.2-canary.0:
resolution: {integrity: sha512-87QjJxWWYIPKBx/FoaqEd2bxPFBdTUZ6OdEnCeuBW2XjN6qZpwL1H2dIEvh6EXJMjQYW/DAhOqgr9cvpw9fu+Q==} resolution: {integrity: sha512-Hf1XQaP/hpi9ddS128u8+npotRzX5EOt9y4nxzHRKn6BmjgAKY7CEBsFQMsjVHWqit2Jt3jXspgaCVIFOCE9Ng==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [win32] os: [win32]
@ -5467,7 +5467,7 @@ packages:
dev: true dev: true
optional: true optional: true
/next-auth/4.19.2_avkzbs57los6fogzead7rr4u74: /next-auth/4.19.2_kc5cm3uhxzg4ew7l4d27zpo4ia:
resolution: {integrity: sha512-6V2YG3IJQVhgCAH7mvT3yopTW92gMdUrcwGX7NQ0dCreT/+axGua/JmVdarjec0C/oJukKpIYRgjMlV+L5ZQOQ==} resolution: {integrity: sha512-6V2YG3IJQVhgCAH7mvT3yopTW92gMdUrcwGX7NQ0dCreT/+axGua/JmVdarjec0C/oJukKpIYRgjMlV+L5ZQOQ==}
peerDependencies: peerDependencies:
next: ^12.2.5 || ^13 next: ^12.2.5 || ^13
@ -5482,7 +5482,7 @@ packages:
'@panva/hkdf': 1.0.2 '@panva/hkdf': 1.0.2
cookie: 0.5.0 cookie: 0.5.0
jose: 4.11.0 jose: 4.11.0
next: 13.2.1-canary.0_biqbaboplfbrettd7655fr4n2y next: 13.2.2-canary.0_biqbaboplfbrettd7655fr4n2y
oauth: 0.9.15 oauth: 0.9.15
openid-client: 5.3.0 openid-client: 5.3.0
preact: 10.11.2 preact: 10.11.2
@ -5492,14 +5492,14 @@ packages:
uuid: 8.3.2 uuid: 8.3.2
dev: false dev: false
/next-themes/0.2.1_avkzbs57los6fogzead7rr4u74: /next-themes/0.2.1_kc5cm3uhxzg4ew7l4d27zpo4ia:
resolution: {integrity: sha512-B+AKNfYNIzh0vqQQKqQItTS8evEouKD7H5Hj3kmuPERwddR2TxvDSFZuTj6T7Jfn1oyeUyJMydPl1Bkxkh0W7A==} resolution: {integrity: sha512-B+AKNfYNIzh0vqQQKqQItTS8evEouKD7H5Hj3kmuPERwddR2TxvDSFZuTj6T7Jfn1oyeUyJMydPl1Bkxkh0W7A==}
peerDependencies: peerDependencies:
next: '*' next: '*'
react: '*' react: '*'
react-dom: '*' react-dom: '*'
dependencies: dependencies:
next: 13.2.1-canary.0_biqbaboplfbrettd7655fr4n2y next: 13.2.2-canary.0_biqbaboplfbrettd7655fr4n2y
react: 18.2.0 react: 18.2.0
react-dom: 18.2.0_react@18.2.0 react-dom: 18.2.0_react@18.2.0
dev: false dev: false
@ -5515,8 +5515,8 @@ packages:
- supports-color - supports-color
dev: true dev: true
/next/13.2.1-canary.0_biqbaboplfbrettd7655fr4n2y: /next/13.2.2-canary.0_biqbaboplfbrettd7655fr4n2y:
resolution: {integrity: sha512-4dXz9jinbUmxOCnXxDPxcFHmwhR+YDCLRP0kiYUpu1GiTlTYbfHAb0HosmA58ZL76W1tmqS6x8/3qkiDNtZWrg==} resolution: {integrity: sha512-sAzQCPI06df+TY7NI6txOkc7sOC7sErnOGF7vXA28GcQ0r4V09s4GDe8XTlPckxJ7o2Ky4OU1eN4NJ3vnDCcZQ==}
engines: {node: '>=14.6.0'} engines: {node: '>=14.6.0'}
hasBin: true hasBin: true
peerDependencies: peerDependencies:
@ -5536,7 +5536,7 @@ packages:
sass: sass:
optional: true optional: true
dependencies: dependencies:
'@next/env': 13.2.1-canary.0 '@next/env': 13.2.2-canary.0
'@swc/helpers': 0.4.14 '@swc/helpers': 0.4.14
caniuse-lite: 1.0.30001449 caniuse-lite: 1.0.30001449
postcss: 8.4.14 postcss: 8.4.14
@ -5544,19 +5544,19 @@ packages:
react-dom: 18.2.0_react@18.2.0 react-dom: 18.2.0_react@18.2.0
styled-jsx: 5.1.1_react@18.2.0 styled-jsx: 5.1.1_react@18.2.0
optionalDependencies: optionalDependencies:
'@next/swc-android-arm-eabi': 13.2.1-canary.0 '@next/swc-android-arm-eabi': 13.2.2-canary.0
'@next/swc-android-arm64': 13.2.1-canary.0 '@next/swc-android-arm64': 13.2.2-canary.0
'@next/swc-darwin-arm64': 13.2.1-canary.0 '@next/swc-darwin-arm64': 13.2.2-canary.0
'@next/swc-darwin-x64': 13.2.1-canary.0 '@next/swc-darwin-x64': 13.2.2-canary.0
'@next/swc-freebsd-x64': 13.2.1-canary.0 '@next/swc-freebsd-x64': 13.2.2-canary.0
'@next/swc-linux-arm-gnueabihf': 13.2.1-canary.0 '@next/swc-linux-arm-gnueabihf': 13.2.2-canary.0
'@next/swc-linux-arm64-gnu': 13.2.1-canary.0 '@next/swc-linux-arm64-gnu': 13.2.2-canary.0
'@next/swc-linux-arm64-musl': 13.2.1-canary.0 '@next/swc-linux-arm64-musl': 13.2.2-canary.0
'@next/swc-linux-x64-gnu': 13.2.1-canary.0 '@next/swc-linux-x64-gnu': 13.2.2-canary.0
'@next/swc-linux-x64-musl': 13.2.1-canary.0 '@next/swc-linux-x64-musl': 13.2.2-canary.0
'@next/swc-win32-arm64-msvc': 13.2.1-canary.0 '@next/swc-win32-arm64-msvc': 13.2.2-canary.0
'@next/swc-win32-ia32-msvc': 13.2.1-canary.0 '@next/swc-win32-ia32-msvc': 13.2.2-canary.0
'@next/swc-win32-x64-msvc': 13.2.1-canary.0 '@next/swc-win32-x64-msvc': 13.2.2-canary.0
transitivePeerDependencies: transitivePeerDependencies:
- '@babel/core' - '@babel/core'
- babel-plugin-macros - babel-plugin-macros

View file

@ -1,5 +0,0 @@
import PageSeo from "@components/page-seo"
export default function AuthHead() {
return <PageSeo title="Sign In" />
}

View file

@ -1,3 +1,4 @@
import { getMetadata } from "src/app/lib/metadata"
import config from "@lib/config" import config from "@lib/config"
import Auth from "../components" import Auth from "../components"
@ -10,3 +11,7 @@ function isGithubEnabled() {
export default function SignInPage() { export default function SignInPage() {
return <Auth page="signin" isGithubEnabled={isGithubEnabled()} /> return <Auth page="signin" isGithubEnabled={isGithubEnabled()} />
} }
export const metadata = getMetadata({
title: "Sign in",
})

View file

@ -1,5 +0,0 @@
import PageSeo from "@components/page-seo"
export default function AuthHead() {
return <PageSeo title="Sign Up" />
}

View file

@ -1,6 +1,7 @@
import Auth from "../components" import Auth from "../components"
import { getRequiresPasscode } from "src/pages/api/auth/requires-passcode" import { getRequiresPasscode } from "src/pages/api/auth/requires-passcode"
import config from "@lib/config" import config from "@lib/config"
import { getMetadata } from "src/app/lib/metadata"
async function getPasscode() { async function getPasscode() {
return await getRequiresPasscode() return await getRequiresPasscode()
@ -22,3 +23,7 @@ export default async function SignUpPage() {
/> />
) )
} }
export const metadata = getMetadata({
title: "Sign up",
})

View file

@ -1,6 +1,5 @@
"use client"
import Note from "@components/note" import Note from "@components/note"
import { getMetadata } from "src/app/lib/metadata"
export default function ExpiredPage() { export default function ExpiredPage() {
return ( return (
@ -9,3 +8,8 @@ export default function ExpiredPage() {
</Note> </Note>
) )
} }
export const metadata = getMetadata({
title: "Post expired",
hidden: true
})

View file

@ -1,5 +0,0 @@
import PageSeo from "@components/page-seo"
export default function NewPostHead() {
return <PageSeo title="New" />
}

View file

@ -1,3 +1,4 @@
import { getMetadata } from "src/app/lib/metadata"
import NewPost from "src/app/(drift)/(posts)/new/components/new" import NewPost from "src/app/(drift)/(posts)/new/components/new"
import "./react-datepicker.css" import "./react-datepicker.css"
@ -6,3 +7,8 @@ export default function New() {
} }
export const dynamic = "force-static" export const dynamic = "force-static"
export const metadata = getMetadata({
title: "New post",
hidden: true
})

View file

@ -1,29 +0,0 @@
import PageSeo from "@components/page-seo"
import { getPostById } from "@lib/server/prisma"
export default async function Head({
params
}: {
params: {
id: string
}
}) {
const post = await getPostById(params.id, {
select: {
title: true,
description: true
}
})
if (!post) {
return null
}
return (
<PageSeo
title={post.title}
description={post.description || undefined}
isPrivate={false}
/>
)
}

View file

@ -1,4 +1,5 @@
import VisibilityControl from "@components/badges/visibility-control" import VisibilityControl from "@components/badges/visibility-control"
import { getMetadata } from "src/app/lib/metadata"
import { import {
PostWithFilesAndAuthor, PostWithFilesAndAuthor,
serverPostToClientPost, serverPostToClientPost,
@ -28,3 +29,28 @@ export default async function PostPage({
</> </>
) )
} }
export const generateMetadata = async ({
params
}: {
params: {
id: string
}
}) => {
const post = (await getPost(params.id)) as ServerPostWithFilesAndAuthor
return getMetadata({
title: post.title,
description: post.description || undefined,
hidden: post.visibility === "public",
overrides: {
openGraph: {
title: post.title,
description: post.description || undefined,
type: "website",
siteName: "Drift",
// TODO: og images
}
}
})
}

View file

@ -1,3 +1,4 @@
import { getMetadata } from "src/app/lib/metadata"
import { getCurrentUser } from "@lib/server/session" import { getCurrentUser } from "@lib/server/session"
import { redirect } from "next/navigation" import { redirect } from "next/navigation"
@ -15,3 +16,9 @@ export default async function AdminLayout({
return children return children
} }
export const metadata = getMetadata({
title: "Admin",
hidden: true
})

View file

@ -1,5 +0,0 @@
import PageSeo from "@components/page-seo"
export default function RootHead() {
return <PageSeo />
}

View file

@ -4,6 +4,8 @@ import Layout from "@components/layout"
import { Toasts } from "@components/toasts" import { Toasts } from "@components/toasts"
import Header from "@components/header" import Header from "@components/header"
import { Inter } from "next/font/google" import { Inter } from "next/font/google"
import type { Metadata } from 'next'
import { getMetadata } from "src/app/lib/metadata"
const inter = Inter({ subsets: ["latin"], variable: "--inter-font" }) const inter = Inter({ subsets: ["latin"], variable: "--inter-font" })
@ -28,3 +30,5 @@ export default async function RootLayout({
</html> </html>
) )
} }
export const metadata = getMetadata()

View file

@ -1,5 +0,0 @@
import PageSeo from "@components/page-seo"
export default function Head() {
return <PageSeo title="Your profile" isPrivate />
}

View file

@ -5,6 +5,7 @@ import { getCurrentUser } from "@lib/server/session"
import { authOptions } from "@lib/server/auth" import { authOptions } from "@lib/server/auth"
import { Suspense } from "react" import { Suspense } from "react"
import ErrorBoundary from "@components/error/fallback" import ErrorBoundary from "@components/error/fallback"
import { getMetadata } from "src/app/lib/metadata"
export default async function Mine() { export default async function Mine() {
const userId = (await getCurrentUser())?.id const userId = (await getCurrentUser())?.id
@ -29,3 +30,8 @@ export default async function Mine() {
} }
export const revalidate = 0 export const revalidate = 0
export const metadata = getMetadata({
title: "Your profile",
hidden: true
})

View file

@ -1,5 +0,0 @@
import PageSeo from "@components/page-seo"
export default function Head() {
return <PageSeo title="Settings" isPrivate />
}

View file

@ -1,3 +1,4 @@
import { getMetadata } from "src/app/lib/metadata"
import SettingsGroup from "../../components/settings-group" import SettingsGroup from "../../components/settings-group"
import APIKeys from "./components/sections/api-keys" import APIKeys from "./components/sections/api-keys"
import Profile from "./components/sections/profile" import Profile from "./components/sections/profile"
@ -14,3 +15,8 @@ export default async function SettingsPage() {
</> </>
) )
} }
export const metadata = getMetadata({
title: "Settings",
hidden: true
})

View file

@ -1,89 +0,0 @@
import config from "@lib/config"
import React from "react"
type PageSeoProps = {
title?: string
description?: string
isLoading?: boolean
isPrivate?: boolean
}
const PageSeo = ({
title: pageTitle,
description = "A self-hostable clone of GitHub Gist",
isPrivate = false
}: PageSeoProps) => {
const title = `Drift${pageTitle ? ` - ${pageTitle}` : ""}`
return (
<>
<title>{title}</title>
<meta charSet="utf-8" />
{!isPrivate && <meta name="description" content={description} />}
{isPrivate && <meta name="robots" content="noindex" />}
{/* TODO: verify the correct meta tags */}
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<ThemeAndIcons />
<URLs />
</>
)
}
export default PageSeo
const ThemeAndIcons = () => (
<>
<link
rel="apple-touch-icon"
sizes="180x180"
href="/assets/apple-touch-icon.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="/assets/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="/assets/favicon-16x16.png"
/>
<link rel="manifest" href="/site.webmanifest" />
<link
rel="mask-icon"
href="/assets/safari-pinned-tab.svg"
color="#5bbad5"
/>
<meta name="apple-mobile-web-app-title" content="Drift" />
<meta name="application-name" content="Drift" />
<meta name="msapplication-TileColor" content="#da532c" />
<meta
name="theme-color"
content="#ffffff"
media="(prefers-color-scheme: light)"
/>
<meta
name="theme-color"
content="#000"
media="(prefers-color-scheme: dark)"
/>
</>
)
const URLs = () => (
<>
<meta property="twitter:card" content="summary_large_image" />
<meta property="twitter:url" content={config.url} />
{/* TODO: OG image */}
<meta property="twitter:image" content={`${config.url}/assets/og.png`} />
<meta property="twitter:site" content="@" />
<meta property="twitter:creator" content="@drift" />
<meta property="og:type" content="website" />
<meta property="og:url" content={config.url} />
</>
)

View file

@ -99,6 +99,7 @@ const ListItem = ({
<Button <Button
iconRight={<ArrowUpCircle />} iconRight={<ArrowUpCircle />}
onClick={viewParentClick} onClick={viewParentClick}
// TODO: not perfect on mobile
height={38} height={38}
/> />
</Tooltip> </Tooltip>

127
src/app/lib/metadata.tsx Normal file
View file

@ -0,0 +1,127 @@
import config from "@lib/config"
import { Metadata } from "next"
import React from "react"
type PageSeoProps = {
title?: string
description?: string
isLoading?: boolean
isPrivate?: boolean
}
const PageSeo = ({
title: pageTitle,
description = "A self-hostable clone of GitHub Gist",
isPrivate = false
}: PageSeoProps) => {
const title = `Drift${pageTitle ? ` - ${pageTitle}` : ""}`
return (
<>
<title>{title}</title>
<meta charSet="utf-8" />
{!isPrivate && <meta name="description" content={description} />}
{isPrivate && <meta name="robots" content="noindex" />}
<ThemeAndIcons />
</>
)
}
export default PageSeo
const ThemeAndIcons = () => (
<>
<link />
<meta name="apple-mobile-web-app-title" content="Drift" />
<meta name="application-name" content="Drift" />
<meta
name="theme-color"
content="#ffffff"
media="(prefers-color-scheme: light)"
/>
<meta
name="theme-color"
content="#000"
media="(prefers-color-scheme: dark)"
/>
</>
)
export function getMetadata({
title,
description = "A self-hostable clone of GitHub Gist",
hidden = false,
overrides
}: {
overrides?: Metadata
title?: string
description?: string
hidden?: boolean
} = {}): Metadata {
function undefinedIfHidden<T>(value: T): T | undefined {
return hidden ? undefined : value
}
title = `Drift${title ? ` - ${title}` : ""}`
return {
title,
description: undefinedIfHidden(description),
themeColor: "#000",
manifest: "/site.webmanifest",
viewport: "width=device-width, initial-scale=1, shrink-to-fit=no",
robots: hidden ? "noindex" : undefined,
twitter: undefinedIfHidden({
card: "summary_large_image",
title,
description,
...overrides?.twitter
}),
applicationName: "Drift",
icons: [
{
rel: "icon",
sizes: "32x32",
type: "image/png",
url: "/assets/favicon-32x32.png"
},
{
rel: "apple-touch-icon",
sizes: "180x180",
url: "/assets/apple-touch-icon.png"
},
{
rel: "icon",
sizes: "16x16",
url: "/assets/favicon-16x16.png"
},
{
rel: "mask-icon",
url: "/assets/safari-pinned-tab.svg"
// TODO: re-enable this when we have a better color
// color: "#5bbad5"
}
],
openGraph: undefinedIfHidden({
type: "website",
url: config.url,
siteName: "Drift",
title,
description,
...overrides?.openGraph
}),
keywords: undefinedIfHidden([
"gist",
"github",
"drift",
"next.js",
"self-hosted",
"paste",
"pastebin",
"clone",
"code",
"snippet"
]),
...overrides
}
}