Skip to content

Commit

Permalink
add online badge
Browse files Browse the repository at this point in the history
  • Loading branch information
adbenitez committed Aug 31, 2023
1 parent e5ecc42 commit 24f8053
Show file tree
Hide file tree
Showing 12 changed files with 381 additions and 131 deletions.
2 changes: 1 addition & 1 deletion frontend/public/version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.0.2
2.0.0
35 changes: 33 additions & 2 deletions frontend/src/components/BotItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,37 @@ import { languageOutline, openOutline } from "ionicons/icons";
import { Bot } from "../store";
import "./BotItem.css";

export default function BotItem({ bot }: { bot: Bot }) {
const longAgo = 1000 * 60 * 60 * 24 * 360 * 10;
const recently = 1000 * 60 * 15;

function displayLastSeen(lastSync: Date, lastSeen: Date) {
if (lastSeen) {
let label;
let color;
let timeAgo = lastSync.getTime() - lastSeen.getTime();
if (timeAgo <= recently) {
color = "success";
label = "online";
} else {
color = "danger";
if (timeAgo >= longAgo) {
label = "offline";
} else {
label = "offline (" + lastSeen.toLocaleString() + ")";
}
}
return <IonBadge color={color}>{label}</IonBadge>;
}
return false;
}

export default function BotItem({
bot,
lastSync,
}: {
bot: Bot;
lastSync: Date;
}) {
return (
<IonItem>
<IonLabel>
Expand All @@ -32,7 +62,8 @@ export default function BotItem({ bot }: { bot: Bot }) {
<br />
<IonBadge color="light">
<IonIcon icon={languageOutline} /> {bot.lang.label}
</IonBadge>
</IonBadge>{" "}
{displayLastSeen(lastSync || new Date(), bot.lastSeen)}
<p className="ion-text-wrap">{bot.description}</p>
<p>
<strong>Admin: </strong>
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/pages/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ const Home: React.FC = () => {
return (
<IonPage>
<IonContent fullscreen>
{state.syncing && state.lastUpdated && (
{state.syncing && state.hash && (
<IonProgressBar type="indeterminate"></IonProgressBar>
)}
{state.bots.length > 0 && (
Expand All @@ -69,10 +69,10 @@ const Home: React.FC = () => {
></IonSearchbar>
</>
)}
{state.lastUpdated ? (
{state.hash ? (
<IonList>
{results.map((bot) => (
<BotItem bot={bot} />
<BotItem bot={bot} lastSync={state.lastSync || new Date()} />
))}
</IonList>
) : (
Expand Down
84 changes: 45 additions & 39 deletions frontend/src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ const api = (() => {
return {
sync: () => {
const lastSyncReq = localStorage.getItem(lastSyncReqKey) || "";
const lastUpdated = localStorage.getItem(lastUpdatedKey) || null;
const hash = localStorage.getItem(hashKey) || null;
if (
lastSyncReq &&
new Date().getTime() - new Date(lastSyncReq).getTime() <=
1000 * 60 * (lastUpdated ? 10 : 1)
1000 * 60 * (hash ? 10 : 1)
) {
return;
}
Expand All @@ -20,19 +20,19 @@ const api = (() => {
payload: {
id: "sync",
method: "Sync",
params: [lastUpdated],
params: [hash],
},
},
"",
);
},
};
})();
const lastSyncKey = "LastSyncKey";
const lastSyncReqKey = "LastSyncReqKey";
const lastUpdatedKey = "LastUpdatedKey";
const botsKey = "BotsKey";
const maxSerialKey = "MaxSerialKey";
const lastSyncKey = "lastSyncKey";
const lastSyncReqKey = "lastSyncReqKey";
const hashKey = "hashKey";
const botsKey = "botsKey";
const maxSerialKey = "maxSerialKey";

type Response = {
id: string;
Expand All @@ -48,10 +48,11 @@ export type Bot = {
description: string;
lang: Lang;
admin: Admin;
lastSeen: Date;
};
interface State {
lastSync?: Date;
lastUpdated?: string;
hash?: string;
bots: Bot[];
error?: Error;
syncing: boolean;
Expand All @@ -71,32 +72,53 @@ export const useStore = create<State>()((set) => ({
};
}
localStorage.setItem(lastSyncKey, new Date().toString());
const result = update.result;
if (result) {
localStorage.setItem(lastUpdatedKey, result.lastUpdated);
const data = result.data;
data.bots.map((bot: any) => {
bot.admin = { ...data.admins[bot.admin], name: bot.admin };
bot.lang = { code: bot.lang, label: data.langs[bot.lang] };
const [botsData, statusData] = update.result || [null, null];
if (statusData) {
state.bots.map((bot: Bot) => {
if (statusData[bot.addr]) {
bot.lastSeen = new Date(statusData[bot.addr]);
}
});
data.bots.sort((a: Bot, b: Bot) => {
if (a.addr < b.addr) {
return -1;
} else if (botsData) {
localStorage.setItem(hashKey, botsData.hash);
botsData.bots.map((bot: any) => {
if (bot.lastSeen) {
bot.lastSeen = new Date(bot.lastSeen);
}
return 1;
});
localStorage.setItem(botsKey, JSON.stringify(data.bots));
localStorage.setItem(botsKey, JSON.stringify(botsData.bots));
return {
...state,
lastUpdated: result.lastUpdated,
bots: data.bots,
hash: botsData.hash,
bots: botsData.bots,
};
}
return state;
}),
}));

export async function init() {
// The first time the bot sends the state so no need to request
if (!localStorage.getItem(lastSyncReqKey)) {
localStorage.setItem(lastSyncReqKey, new Date().toString());
}
const hash = localStorage.getItem(hashKey);
if (hash) {
const lastSync = new Date(localStorage.getItem(lastSyncKey) || "");
const bots = JSON.parse(localStorage.getItem(botsKey) || "");
bots.map((bot: any) => {
if (bot.lastSeen) {
bot.lastSeen = new Date(bot.lastSeen);
}
});
useStore.setState({
...useStore.getState(),
lastSync: lastSync,
hash: hash,
bots: bots,
});
}

await window.webxdc.setUpdateListener(
(message) => {
if (message.serial === message.max_serial) {
Expand All @@ -110,22 +132,6 @@ export async function init() {
parseInt(localStorage.getItem(maxSerialKey) || "0"),
);

const lastUpdated = localStorage.getItem(lastUpdatedKey);
if (lastUpdated) {
const lastSync = new Date(localStorage.getItem(lastSyncKey) || "");
const bots = JSON.parse(localStorage.getItem(botsKey) || "");
useStore.setState({
...useStore.getState(),
lastSync: lastSync,
lastUpdated: lastUpdated,
bots: bots,
});
}

// The first time the bot sends the state so no need to request
if (!localStorage.getItem(lastSyncReqKey)) {
localStorage.setItem(lastSyncReqKey, new Date().toString());
}
api.sync();
setInterval(() => {
api.sync();
Expand Down
24 changes: 17 additions & 7 deletions frontend/webxdc.js
Original file line number Diff line number Diff line change
Expand Up @@ -260,25 +260,35 @@ function sendData() {
return response.json();
})
.then(json => {
json.bots.map((bot) => {
bot.admin = { ...json.admins[bot.admin], name: bot.admin };
bot.lang = { code: bot.lang, label: json.langs[bot.lang] };
});
json.bots.sort((a, b) => {
if (a.addr < b.addr) {
return -1;
}
return 1;
});
window.webxdc.sendUpdate({
payload: {
error: null,
id: "sync",
result: {
lastUpdated: new Date().toString(),
data: json
}
result: [{
hash: new Date().toString(),
bots: json.bots
}, null]
}
});
}).catch(function () {
}).catch(function (err) {
window.webxdc.sendUpdate({
payload: {
error: {code: -100, message: "Failed to fetch data"},
error: {code: -100, message: "Failed to fetch data: " + err},
id: "sync"
}
});
});
}
if (!localStorage.getItem("LastSyncReqKey")) {
if (!localStorage.getItem("lastSyncReqKey")) {
sendData();
}
49 changes: 39 additions & 10 deletions src/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,53 @@
package main

import (
"encoding/json"
"time"

"github.com/deltachat/deltachat-rpc-client-go/deltachat/xdcrpc"
)

type Metadata struct {
LastUpdated time.Time `json:"lastUpdated"`
Data json.RawMessage `json:"data"`
type StatusData map[string]time.Time

type BotsData struct {
Hash string `json:"hash"`
Bots []Bot `json:"bots"`

lastChecked time.Time
}

type Bot struct {
Addr string `json:"addr"`
Url string `json:"url"`
Description string `json:"description"`
Lang Lang `json:"lang"`
Admin Admin `json:"admin"`
LastSeen time.Time `json:"lastSeen"`
}

type Admin struct {
Name string `json:"name"`
Url string `json:"url"`
}

type Lang struct {
Label string `json:"label"`
Code string `json:"code"`
}

type API struct{}

// Sync app state.
func (api *API) Sync(lastUpdated *time.Time) (*Metadata, *xdcrpc.Error) {
data := cfg.GetMetadata()
if lastUpdated == nil || *lastUpdated != data.LastUpdated {
return data, nil
// Sync bot list and online status.
func (api *API) Sync(hash string) (*BotsData, StatusData, *xdcrpc.Error) {
data := cfg.GetBotsData()
if data.Hash == "" {
return nil, nil, nil
}
if hash != data.Hash {
return &data, nil, nil
}
statuses := make(StatusData)
for _, bot := range data.Bots {
statuses[bot.Addr] = bot.LastSeen
}
return nil, nil
return nil, statuses, nil
}
Loading

0 comments on commit 24f8053

Please sign in to comment.