mirror of
https://github.com/PaiGramTeam/luoxu-api-pub.git
synced 2024-11-25 17:35:39 +00:00
it work!
This commit is contained in:
parent
feefc22121
commit
466ca11cbb
@ -1,16 +1,16 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="zh-CN">
|
||||||
<head>
|
<head>
|
||||||
<meta charset='utf-8'>
|
<meta charset='utf-8'>
|
||||||
<meta name='viewport' content='width=device-width,initial-scale=1'>
|
<meta name='viewport' content='width=device-width,initial-scale=1'>
|
||||||
|
|
||||||
<title>Svelte app</title>
|
<title>落絮</title>
|
||||||
|
|
||||||
<link rel='icon' type='image/png' href='/favicon.png'>
|
<link rel='icon' type='image/png' href='/favicon.png'>
|
||||||
<link rel='stylesheet' href='/global.css'>
|
<link rel='stylesheet' href='/global.css'>
|
||||||
<link rel='stylesheet' href='/build/bundle.css'>
|
<link rel='stylesheet' href='/build/bundle.css'>
|
||||||
|
|
||||||
<script defer src='/build/bundle.js'></script>
|
<script defer src='/build/bundle.js'></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
@ -18,8 +18,8 @@ function serve() {
|
|||||||
writeBundle() {
|
writeBundle() {
|
||||||
if (server) return;
|
if (server) return;
|
||||||
server = require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], {
|
server = require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], {
|
||||||
stdio: ['ignore', 'inherit', 'inherit'],
|
// stdio: ['ignore', 'inherit', 'inherit'],
|
||||||
shell: true
|
// shell: true
|
||||||
});
|
});
|
||||||
|
|
||||||
process.on('SIGTERM', toExit);
|
process.on('SIGTERM', toExit);
|
||||||
@ -71,6 +71,7 @@ export default {
|
|||||||
production && terser()
|
production && terser()
|
||||||
],
|
],
|
||||||
watch: {
|
watch: {
|
||||||
|
buildDelay: 500,
|
||||||
clearScreen: false
|
clearScreen: false
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
185
src/App.svelte
185
src/App.svelte
@ -1,30 +1,173 @@
|
|||||||
<script>
|
<script>
|
||||||
export let name;
|
import { onMount, setContext } from 'svelte'
|
||||||
|
import Message from './Message.svelte'
|
||||||
|
|
||||||
|
const LUOXU_URL = 'https://lab.lilydjwg.me/luoxu'
|
||||||
|
let groups = []
|
||||||
|
let group
|
||||||
|
let query
|
||||||
|
let error
|
||||||
|
let result
|
||||||
|
let now = new Date()
|
||||||
|
let loading = false
|
||||||
|
|
||||||
|
setContext('LUOXU_URL', LUOXU_URL)
|
||||||
|
|
||||||
|
function parse_hash() {
|
||||||
|
const hash = location.hash
|
||||||
|
if(hash) {
|
||||||
|
const info = new Map()
|
||||||
|
for(const pair of hash.substring(1).split('&')){
|
||||||
|
const [key, value] = pair.split('=')
|
||||||
|
info.set(key, decodeURIComponent(value))
|
||||||
|
}
|
||||||
|
return info
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
const res = await fetch(`${LUOXU_URL}/groups`)
|
||||||
|
groups = (await res.json()).groups
|
||||||
|
do_hash_search()
|
||||||
|
})
|
||||||
|
|
||||||
|
function update_title() {
|
||||||
|
let group_name
|
||||||
|
for(const g of groups) {
|
||||||
|
if(g.group_id == group) {
|
||||||
|
group_name = g.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(query && group_name) {
|
||||||
|
document.title = `搜索:${query} 于 ${group_name} - 落絮`
|
||||||
|
}else{
|
||||||
|
document.title = '落絮'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function do_hash_search() {
|
||||||
|
const info = parse_hash()
|
||||||
|
if(info) {
|
||||||
|
if(info.has('g')) {
|
||||||
|
group = info.get('g')|0
|
||||||
|
}
|
||||||
|
if(info.has('q')) {
|
||||||
|
query = info.get('q')
|
||||||
|
}
|
||||||
|
if(group && query) {
|
||||||
|
do_search()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function do_search(more) {
|
||||||
|
if(!group) {
|
||||||
|
error = '请选择要搜索的群组'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if(!query) {
|
||||||
|
error = '请输入搜索关键字'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
error = ''
|
||||||
|
console.log(`searching ${query} for group ${group}, older than ${more}`)
|
||||||
|
const q = `g=${group}&q=${encodeURIComponent(query)}`
|
||||||
|
let url
|
||||||
|
if(!more) {
|
||||||
|
location.hash = `#${q}`
|
||||||
|
update_title()
|
||||||
|
if(result) {
|
||||||
|
result.messages = []
|
||||||
|
}
|
||||||
|
url = `${LUOXU_URL}/search?${q}`
|
||||||
|
}else{
|
||||||
|
url = `${LUOXU_URL}/search?${q}&end=${more}`
|
||||||
|
}
|
||||||
|
|
||||||
|
now = new Date()
|
||||||
|
loading = true
|
||||||
|
try{
|
||||||
|
const res = await fetch(url)
|
||||||
|
const r = await res.json()
|
||||||
|
loading = false
|
||||||
|
if(more) {
|
||||||
|
return r
|
||||||
|
}else{
|
||||||
|
result = r
|
||||||
|
}
|
||||||
|
}catch(e){
|
||||||
|
error = e
|
||||||
|
loading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function do_search_more() {
|
||||||
|
const more = result.messages[result.messages.length-1].t
|
||||||
|
const old_msgs = result.messages
|
||||||
|
const new_result = await do_search(more)
|
||||||
|
result.messages = [...old_msgs, ...new_result.messages]
|
||||||
|
result.has_more = new_result.has_more
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<svelte:window on:hashchange={do_hash_search}/>
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
<h1>Hello {name}!</h1>
|
<div id="searchbox">
|
||||||
<p>Visit the <a href="https://svelte.dev/tutorial">Svelte tutorial</a> to learn how to build Svelte apps.</p>
|
<select bind:value={group} on:change={() => error = ''}>
|
||||||
|
{#each groups as group}
|
||||||
|
<option value={group.group_id}>{group.name}</option>
|
||||||
|
{:else}
|
||||||
|
<option selected value=''>正在加载群组信息...</option>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
<input type="search" bind:value={query}
|
||||||
|
on:input={() => error = ''}
|
||||||
|
on:keyup={e => {if(e.key == 'Enter'){do_search()}}}
|
||||||
|
/>
|
||||||
|
<button on:click={do_search}>搜索</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if error}
|
||||||
|
<div class="error">{error}</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if result}
|
||||||
|
{#each result.messages as message}
|
||||||
|
<Message msg={message} group={result.group_pub_id} now={now} />
|
||||||
|
{/each}
|
||||||
|
{/if}
|
||||||
|
{#if loading}
|
||||||
|
<div id="loading"><p>正在加载...</p></div>
|
||||||
|
{:else if result && result.has_more}
|
||||||
|
<div id="more"><button on:click={do_search_more}>加载更多</button></div>
|
||||||
|
{/if}
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
main {
|
main {
|
||||||
text-align: center;
|
margin: 1em;
|
||||||
padding: 1em;
|
}
|
||||||
max-width: 240px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
#searchbox {
|
||||||
color: #ff3e00;
|
display: flex;
|
||||||
text-transform: uppercase;
|
}
|
||||||
font-size: 4em;
|
#searchbox input[type=search] {
|
||||||
font-weight: 100;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 640px) {
|
.error {
|
||||||
main {
|
color: red;
|
||||||
max-width: none;
|
text-align: center;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</style>
|
#loading, #more {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
#loading > * {
|
||||||
|
border: 1px #bfbfbf solid;
|
||||||
|
border-radius: 2em;
|
||||||
|
padding: 0.5em 1em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
93
src/Message.svelte
Normal file
93
src/Message.svelte
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
<script>
|
||||||
|
import { onMount, getContext } from 'svelte'
|
||||||
|
|
||||||
|
export let msg
|
||||||
|
export let group
|
||||||
|
export let now
|
||||||
|
|
||||||
|
const formatter = new Intl.DateTimeFormat(undefined, {
|
||||||
|
timeStyle: "full",
|
||||||
|
dateStyle: "full",
|
||||||
|
hour12: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
let dt = new Date(msg.t * 1000)
|
||||||
|
let edited = msg.edited ? new Date(msg.edited * 1000) : null
|
||||||
|
let title = format_dt(dt) + (edited ? `\n最后编辑于:${format_dt(edited)}` : '')
|
||||||
|
let relative_dt = format_relative_time(dt, now)
|
||||||
|
let iso_date = dt.toISOString()
|
||||||
|
|
||||||
|
function format_relative_time(d1, d2) {
|
||||||
|
// in miliseconds
|
||||||
|
const units = {
|
||||||
|
year : 24 * 60 * 60 * 1000 * 365,
|
||||||
|
month : 24 * 60 * 60 * 1000 * 365 / 12,
|
||||||
|
day : 24 * 60 * 60 * 1000,
|
||||||
|
hour : 60 * 60 * 1000,
|
||||||
|
minute: 60 * 1000,
|
||||||
|
second: 1000,
|
||||||
|
}
|
||||||
|
|
||||||
|
const rtf = new Intl.RelativeTimeFormat()
|
||||||
|
|
||||||
|
const elapsed = d1 - d2
|
||||||
|
|
||||||
|
for(const u in units) {
|
||||||
|
if(Math.abs(elapsed) > units[u] || u == 'second') {
|
||||||
|
return rtf.format(Math.round(elapsed/units[u]), u)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function format_dt(t) {
|
||||||
|
return formatter.format(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div class="message">
|
||||||
|
<img src="{getContext('LUOXU_URL')}/avatar/{msg.from_id}.jpg" height="64" width="64" alt="{msg.from_name} 的头像"/>
|
||||||
|
<div>
|
||||||
|
<div class="name">{msg.from_name}</div>
|
||||||
|
<div class="body"><pre>{msg.text}</pre></div>
|
||||||
|
<div class="time"><a href="tg://resolve?domain={group}&post={msg.id}"><time datetime={iso_date} title={title}>{relative_dt}</time></a></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.message {
|
||||||
|
margin: 1em;
|
||||||
|
padding: 0.5em;
|
||||||
|
max-width: max-content;
|
||||||
|
border: 1px #eeeeee solid;
|
||||||
|
box-shadow: 0 0 3px gray;
|
||||||
|
border-radius: 5px;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.message::after {
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
img {
|
||||||
|
padding-right: 0.5em;
|
||||||
|
}
|
||||||
|
.name {
|
||||||
|
white-space: nowrap;
|
||||||
|
color: #1e90ff;
|
||||||
|
}
|
||||||
|
pre {
|
||||||
|
white-space: break-spaces;
|
||||||
|
margin: 0.2em 0;
|
||||||
|
}
|
||||||
|
.time {
|
||||||
|
font-size: 0.75em;
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
.time > a {
|
||||||
|
color: gray;
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,10 +1,7 @@
|
|||||||
import App from './App.svelte';
|
import App from './App.svelte';
|
||||||
|
|
||||||
const app = new App({
|
const app = new App({
|
||||||
target: document.body,
|
target: document.body
|
||||||
props: {
|
|
||||||
name: 'world'
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export default app;
|
export default app;
|
||||||
|
Loading…
Reference in New Issue
Block a user