Merge branch 'main' into typesafe_i18n

This commit is contained in:
notmd
2023-01-23 19:29:24 +07:00
44 changed files with 259 additions and 177 deletions
@@ -95,8 +95,8 @@ def query_frontend_user_messages(
def query_frontend_user_messages_cursor(
auth_method: str,
username: str,
lt: Optional[str] = None,
gt: Optional[str] = None,
before: Optional[str] = None,
after: Optional[str] = None,
only_roots: Optional[bool] = False,
include_deleted: Optional[bool] = False,
max_count: Optional[int] = Query(10, gt=0, le=1000),
@@ -105,8 +105,8 @@ def query_frontend_user_messages_cursor(
db: Session = Depends(deps.get_db),
):
return get_messages_cursor(
lt=lt,
gt=gt,
before=before,
after=after,
auth_method=auth_method,
username=username,
only_roots=only_roots,
+34 -14
View File
@@ -50,8 +50,8 @@ def query_messages(
@router.get("/cursor", response_model=protocol.MessagePage)
def get_messages_cursor(
lt: Optional[str] = None,
gt: Optional[str] = None,
before: Optional[str] = None,
after: Optional[str] = None,
user_id: Optional[UUID] = None,
auth_method: Optional[str] = None,
username: Optional[str] = None,
@@ -63,6 +63,8 @@ def get_messages_cursor(
api_client: ApiClient = Depends(deps.get_api_client),
db: Session = Depends(deps.get_db),
):
assert max_count is not None
def split_cursor(x: str | None) -> tuple[datetime, UUID]:
if not x:
return None, None
@@ -74,11 +76,21 @@ def get_messages_cursor(
except ValueError:
raise OasstError("Invalid cursor value", OasstErrorCode.INVALID_CURSOR_VALUE)
lte_created_date, lt_id = split_cursor(lt)
gte_created_date, gt_id = split_cursor(gt)
if desc:
gte_created_date, gt_id = split_cursor(before)
lte_created_date, lt_id = split_cursor(after)
query_desc = not (before is not None and not after)
else:
lte_created_date, lt_id = split_cursor(before)
gte_created_date, gt_id = split_cursor(after)
query_desc = before is not None and not after
print(f"{desc=} {query_desc=} {gte_created_date=} {lte_created_date=}")
qry_max_count = max_count + 1 if before is None or after is None else max_count
pr = PromptRepository(db, api_client)
messages = pr.query_messages_ordered_by_created_date(
items = pr.query_messages_ordered_by_created_date(
user_id=user_id,
auth_method=auth_method,
username=username,
@@ -89,22 +101,30 @@ def get_messages_cursor(
lt_id=lt_id,
only_roots=only_roots,
deleted=None if include_deleted else False,
desc=desc,
limit=max_count,
desc=query_desc,
limit=qry_max_count,
)
items = utils.prepare_message_list(messages)
num_rows = len(items)
if qry_max_count > max_count and num_rows == qry_max_count:
assert not (before and after)
items = items[:-1]
if desc != query_desc:
items.reverse()
items = utils.prepare_message_list(items)
n, p = None, None
if len(items) > 0:
if len(items) == max_count or gte_created_date:
if (num_rows > max_count and before) or after:
p = str(items[0].id) + "$" + items[0].created_date.isoformat()
if len(items) == max_count or lte_created_date:
if num_rows > max_count or before:
n = str(items[-1].id) + "$" + items[-1].created_date.isoformat()
else:
if gte_created_date:
p = gte_created_date.isoformat()
if lte_created_date:
n = lte_created_date.isoformat()
if after:
p = lte_created_date.isoformat() if desc else gte_created_date.isoformat()
if before:
n = gte_created_date.isoformat() if desc else lte_created_date.isoformat()
order = "desc" if desc else "asc"
return protocol.MessagePage(prev=p, next=n, sort_key="created_date", order=order, items=items)
+13 -13
View File
@@ -78,8 +78,8 @@ def get_users_ordered_by_display_name(
@router.get("/cursor", response_model=protocol.FrontEndUserPage)
def get_users_cursor(
lt: Optional[str] = None,
gt: Optional[str] = None,
before: Optional[str] = None,
after: Optional[str] = None,
sort_key: Optional[str] = Query("username", max_length=32),
max_count: Optional[int] = Query(100, gt=0, le=10000),
api_client_id: Optional[UUID] = None,
@@ -99,8 +99,8 @@ def get_users_cursor(
return x, None
items: list[protocol.FrontEndUser]
qry_max_count = max_count + 1 if lt is None or gt is None else max_count
desc = lt and not gt
qry_max_count = max_count + 1 if before is None or after is None else max_count
desc = before is not None and not after
def get_next_prev(num_rows: int, lt: str | None, gt: str | None, key_fn: Callable[[protocol.FrontEndUser], str]):
p, n = None, None
@@ -119,7 +119,7 @@ def get_users_cursor(
def remove_extra_item(items: list[protocol.FrontEndUser], lt: str | None, gt: str | None):
num_rows = len(items)
if qry_max_count > max_count and num_rows == qry_max_count:
assert not (lt and gt)
assert not (lt is not None and gt is not None)
items = items[:-1]
if desc:
items.reverse()
@@ -127,8 +127,8 @@ def get_users_cursor(
n, p = None, None
if sort_key == "username":
lte_username, lt_id = split_cursor(lt)
gte_username, gt_id = split_cursor(gt)
lte_username, lt_id = split_cursor(before)
gte_username, gt_id = split_cursor(after)
items = get_users_ordered_by_username(
api_client_id=api_client_id,
gte_username=gte_username,
@@ -146,8 +146,8 @@ def get_users_cursor(
p, n = get_next_prev(num_rows, lte_username, gte_username, lambda x: x.id)
elif sort_key == "display_name":
lte_display_name, lt_id = split_cursor(lt)
gte_display_name, gt_id = split_cursor(gt)
lte_display_name, lt_id = split_cursor(before)
gte_display_name, gt_id = split_cursor(after)
items = get_users_ordered_by_display_name(
api_client_id=api_client_id,
gte_display_name=gte_display_name,
@@ -247,8 +247,8 @@ def query_user_messages(
@router.get("/{user_id}/messages/cursor", response_model=protocol.MessagePage)
def query_user_messages_cursor(
user_id: Optional[UUID],
lt: Optional[str] = None,
gt: Optional[str] = None,
before: Optional[str] = None,
after: Optional[str] = None,
only_roots: Optional[bool] = False,
include_deleted: Optional[bool] = False,
max_count: Optional[int] = Query(10, gt=0, le=1000),
@@ -257,8 +257,8 @@ def query_user_messages_cursor(
db: Session = Depends(deps.get_db),
):
return get_messages_cursor(
lt=lt,
gt=gt,
before=before,
after=after,
user_id=user_id,
only_roots=only_roots,
include_deleted=include_deleted,
@@ -43,7 +43,7 @@ def get_one_dataset(conf, dataset_name):
if dataset_name == "debate_sum":
train, eval = train_val_dataset(train, val_split=0.2)
else:
val_name = "validation" if dataset_name not in ["billsum"] else "test"
val_name = "validation" if dataset_name not in ["billsum", "tldr_news"] else "test"
eval = SummarizationDataset(dataset_name, conf.cache_dir, val_name)
elif "ted_trans" in dataset_name:
language_pair = dataset_name.split("_")[-1]
@@ -3,7 +3,6 @@ from typing import Optional, Union
import numpy as np
import torch
from custom_datasets.qa_datasets import QA_SPECIAL_TOKENS
from torch.nn import functional as F
from transformers.tokenization_utils_base import PaddingStrategy, PreTrainedTokenizerBase
@@ -23,15 +22,8 @@ class DialogueDataCollator:
flatten_messages = []
label_masks = []
for feature_one in features:
assert len(feature_one) % 2 == 0, "Number of messages must be even"
# TODO: we should push this to dataset __getitem__
messages = [
(QA_SPECIAL_TOKENS["Question"] if i % 2 == 0 else "")
+ x
+ (QA_SPECIAL_TOKENS["Answer"] if i % 2 == 0 else "")
for i, x in enumerate(feature_one)
]
for messages in features:
messages = list(messages)
# Add a way for the model to terminate generation
# When we predict the start of a new expected question, we want to be able to stop generation
@@ -0,0 +1,5 @@
QA_SPECIAL_TOKENS = {"Question": "<human>", "Answer": "<bot>", "StartPrefix": "<prefix>", "EndPrefix": "</prefix>"}
def format_pair(pair):
return "{}{}{}".format(QA_SPECIAL_TOKENS["Question"], pair[0], QA_SPECIAL_TOKENS["Answer"]), pair[1]
@@ -2,6 +2,7 @@ import json
import os
from urllib.request import urlopen
from custom_datasets.formatting import format_pair
from torch.utils.data import Dataset
@@ -49,8 +50,7 @@ class PromptGeneratedDataset(Dataset):
return len(self.pairs)
def __getitem__(self, index):
question, answer = self.pairs[index]
return question, answer
return format_pair(self.pairs[index])
class InstructionTuning(Dataset):
@@ -101,5 +101,4 @@ class InstructionTuning(Dataset):
return len(self.pairs)
def __getitem__(self, index):
question, answer = self.pairs[index]
return question, answer
return format_pair(self.pairs[index])
@@ -7,14 +7,13 @@ import re
from urllib.request import urlopen
import numpy as np
from custom_datasets.formatting import QA_SPECIAL_TOKENS, format_pair
from datasets import load_dataset
from torch.utils.data import Dataset
# @agoryuno contributed this
re_reference_remove = re.compile(r"\[\d+(?:,\s*\d+)*?\]")
QA_SPECIAL_TOKENS = {"Question": "<human>", "Answer": "<bot>", "StartPrefix": "<prefix>", "EndPrefix": "</prefix>"}
def index_squad_v2(example):
if len(example["answers"]["text"]):
@@ -78,7 +77,7 @@ class QADataset(Dataset):
def __getitem__(self, idx):
data = self.dataset[idx]
return self.index_fn(data)
return format_pair(self.index_fn(data))
class WebGPT(Dataset):
@@ -111,7 +110,7 @@ class WebGPT(Dataset):
def __getitem__(self, index):
question = self.index2question[index]
answer = self.questions[question]
return [question, answer]
return format_pair((question, answer))
class SODA(Dataset):
@@ -121,14 +120,14 @@ class SODA(Dataset):
def process_soda_convo(self, data):
pairs = []
play_as = data["speakers"][1]
prefix = "{}{}. {}{}".format(
QA_SPECIAL_TOKENS["StartPrefix"],
data["narrative"],
"your name {}".format(play_as),
QA_SPECIAL_TOKENS["EndPrefix"],
)
question, answer = "", ""
prefix, postfix = "", ""
dialogue_bg = "{}{} {}{}".format(
QA_SPECIAL_TOKENS["StartPrefix"],
data["narrative"],
"your are {}".format(play_as),
QA_SPECIAL_TOKENS["EndPrefix"],
)
previous_chat = []
for idx, convo in enumerate(data["dialogue"]):
@@ -138,14 +137,20 @@ class SODA(Dataset):
else:
answer = convo
postfix = data["speakers"][idx]
if len(question) and len(answer) and prefix != postfix and postfix == play_as:
history = "<sep>".join(
["{}{}{}".format(p[0], QA_SPECIAL_TOKENS["Answer"], p[1]) for p in previous_chat]
[
"{}{}{}{}".format(QA_SPECIAL_TOKENS["Question"], p[0], QA_SPECIAL_TOKENS["Answer"], p[1])
for p in previous_chat
]
)
if len(history):
history += "<sep>"
pairs.append((prefix + history + question, answer))
prompt = QA_SPECIAL_TOKENS["Question"] + question + QA_SPECIAL_TOKENS["Answer"]
pairs.append((dialogue_bg + history + prompt, answer))
previous_chat.append((question, answer))
return pairs
def __init__(self, cache_dir, max_sample_size=10000, input_max_length=1024) -> None:
@@ -166,8 +171,8 @@ class SODA(Dataset):
return len(self.pairs)
def __getitem__(self, index):
question, answer = self.pairs[index]
return question, answer
# special token added during preprocess
return self.pairs[index]
class SODADialogue(Dataset):
@@ -218,7 +223,7 @@ class SODADialogue(Dataset):
return len(self.pairs)
def __getitem__(self, index):
return self.pairs[index]
return format_pair(self.pairs[index])
class JokeExplaination(Dataset):
@@ -253,8 +258,7 @@ class JokeExplaination(Dataset):
return len(self.pairs)
def __getitem__(self, index):
question, answer = self.pairs[index]
return question, answer
return format_pair(self.pairs[index])
# https://huggingface.co/datasets/aquamuse
@@ -3,6 +3,7 @@
"""
import random
from custom_datasets.formatting import format_pair
from datasets import load_dataset
from torch.utils.data import Dataset
@@ -54,11 +55,12 @@ def index_summary_merge(text, summary):
class SummarizationDataset(Dataset):
def __init__(self, dataset, cache_dir, split):
def __init__(self, dataset, cache_dir, split, max_words=512):
self.name = dataset
self.dataset = load_dataset(*summarization_config_mapping[dataset], cache_dir=cache_dir, split=split)
self.text_column, self.summary_column = summarization_name_mapping[dataset]
self.preprocess_fn = index_summary_merge if dataset == "scitldr" else index_summary_default
self.max_words = max_words
def __len__(self):
return len(self.dataset)
@@ -72,4 +74,5 @@ class SummarizationDataset(Dataset):
else:
prompt = random.choice(SUMMARIZATION_SPECIAL_TOKENS["Summary"])
return ("".join([SUMMARIZATION_SPECIAL_TOKENS["Text"], " ".join(text.split(" ")[:256]), prompt]), summary)
context = "".join([SUMMARIZATION_SPECIAL_TOKENS["Text"], " ".join(text.split(" ")[: self.max_words]), prompt])
return format_pair((context, summary))
@@ -4,12 +4,13 @@
"""
import random
from custom_datasets.formatting import QA_SPECIAL_TOKENS, format_pair
from datasets import load_dataset
from torch.utils.data import Dataset
class ProsocialDialogueExplaination(Dataset):
name = "prosocial_explain"
name = "explain_prosocial"
TEMPLATE = [
# 0 : reply or sentence of interest, 1 : reason of caution
("'{0}' Why is this sentence {1}", "This sentence is {1} because {0}"),
@@ -36,7 +37,7 @@ class ProsocialDialogueExplaination(Dataset):
return len(self.pairs)
def __getitem__(self, idx):
return self.pairs[idx]
return format_pair(self.pairs[idx])
class ProsocialDialogue(Dataset):
@@ -58,8 +59,9 @@ class ProsocialDialogue(Dataset):
dataset = load_dataset("allenai/prosocial-dialog", cache_dir=cache_dir)[split]
self.pairs = []
for row in dataset:
prompt = QA_SPECIAL_TOKENS["Question"] + row["context"] + QA_SPECIAL_TOKENS["Answer"]
for answer in row["rots"]:
self.pairs.append((self.PREFIX + row["context"], answer))
self.pairs.append((self.PREFIX + prompt, answer))
def __len__(self):
return len(self.pairs)
@@ -8,6 +8,7 @@
"""
import random
from custom_datasets.formatting import format_pair
from datasets import load_dataset
from torch.utils.data import Dataset
@@ -82,7 +83,7 @@ class TranslationPair(Dataset):
return len(self.pairs)
def __getitem__(self, index):
return self.pairs[index]
return format_pair(self.pairs[index])
class WMT2019(TranslationPair):
@@ -99,6 +100,8 @@ class WMT2019(TranslationPair):
else: # translating in reverse direction
source = random.choice(TRANSLATION_PROMPT[src]).format(row[tgt])
self.pairs.append((source, row[src]))
if len(self.pairs) > 100000:
break
class DiveMT(TranslationPair):
@@ -7,8 +7,8 @@ from custom_datasets.dialogue_collator import DialogueDataCollator
def test_all_datasets():
qa_base = QA_DATASETS
summarize_base = SUMMARIZATION_DATASETS
others = ["prompt_dialogue", "webgpt", "soda", "joke", "instruct_tuning"]
translation = ["dive_mt", "wmt2019_zh-en", "wmt2019_ru-en", "wmt2019_de-en", "ted_trans_de-ja", "ted_trans_nl-en"]
others = ["prompt_dialogue", "webgpt", "soda", "joke", "instruct_tuning", "explain_prosocial", "prosocial_dialogue"]
translation = ["dive_mt", "wmt2019_zh-en", "wmt2019_ru-en", "ted_trans_de-ja", "ted_trans_nl-en"]
config = Namespace(cache_dir=".cache")
for dataset_name in translation + others + summarize_base + qa_base:
@@ -31,7 +31,6 @@ def test_collate_fn():
qa_base = QA_DATASETS
summarize_base = SUMMARIZATION_DATASETS
others = ["prompt_dialogue", "webgpt", "soda", "joke", "gsm8k"]
trains, evals = [], []
for dataset_name in others + qa_base + summarize_base:
print(dataset_name)
@@ -41,10 +40,10 @@ def test_collate_fn():
dataloader = DataLoader(ConcatDataset(trains), collate_fn=collate_fn, batch_size=128)
for batch in dataloader:
# print(batch.keys())
# print(tokenizer.decode(batch['input_ids'][0]))
# print('-----')
# print(tokenizer.decode(batch['targets'][0][batch['label_masks'][0]]))
print(batch.keys())
print(tokenizer.decode(batch["input_ids"][0]))
print("-----")
print(tokenizer.decode(batch["targets"][0][batch["label_masks"][0]]))
assert batch["targets"].shape[1] <= 512
dataloader = DataLoader(ConcatDataset(evals), collate_fn=collate_fn, batch_size=128)
for batch in dataloader:
+4
View File
@@ -25,6 +25,10 @@ def get_tokenizer(conf):
tokenizer.add_special_tokens({"pad_token": tokenizer.eos_token, "sep_token": "<|extratoken_100|>"})
elif "codegen" in conf.model_name:
tokenizer.add_special_tokens({"pad_token": "<|endoftext|>", "sep_token": "<|endoftext|>"})
elif "pythia" in conf.model_name:
tokenizer.add_special_tokens(
{"pad_token": "<|padding|>", "sep_token": "<|endoftext|>", "eos_token": "<|endoftext|>"}
)
additional_special_tokens = (
[]
+15 -15
View File
@@ -33,6 +33,7 @@
"focus-visible": "^5.2.0",
"framer-motion": "^6.5.1",
"install": "^0.13.0",
"lucide-react": "^0.105.0",
"next": "13.0.6",
"next-auth": "^4.18.6",
"next-i18next": "^13.0.3",
@@ -45,7 +46,6 @@
"react-feature-flags": "^1.0.0",
"react-hook-form": "^7.42.1",
"react-i18next": "^12.1.4",
"react-icons": "^4.7.1",
"react-table": "^7.8.0",
"sharp": "^0.31.3",
"swr": "^2.0.0",
@@ -26726,6 +26726,14 @@
"yallist": "^3.0.2"
}
},
"node_modules/lucide-react": {
"version": "0.105.0",
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.105.0.tgz",
"integrity": "sha512-iHaIkd4Wq6aNIVrFMXt3If8E/+2lnJd4WlCyntoJNIzZ8nWhdSSHWpsw7XM4rlw2319LZ2t4WLdnM8Z0ECDTOQ==",
"peerDependencies": {
"react": "^16.5.1 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/lz-string": {
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz",
@@ -32657,14 +32665,6 @@
}
}
},
"node_modules/react-icons": {
"version": "4.7.1",
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.7.1.tgz",
"integrity": "sha512-yHd3oKGMgm7zxo3EA7H2n7vxSoiGmHk5t6Ou4bXsfcgWyhfDKMpyKfhHR6Bjnn63c+YXBLBPUql9H4wPJM6sXw==",
"peerDependencies": {
"react": "*"
}
},
"node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
@@ -57914,6 +57914,12 @@
"yallist": "^3.0.2"
}
},
"lucide-react": {
"version": "0.105.0",
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.105.0.tgz",
"integrity": "sha512-iHaIkd4Wq6aNIVrFMXt3If8E/+2lnJd4WlCyntoJNIzZ8nWhdSSHWpsw7XM4rlw2319LZ2t4WLdnM8Z0ECDTOQ==",
"requires": {}
},
"lz-string": {
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz",
@@ -62143,12 +62149,6 @@
"html-parse-stringify": "^3.0.1"
}
},
"react-icons": {
"version": "4.7.1",
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.7.1.tgz",
"integrity": "sha512-yHd3oKGMgm7zxo3EA7H2n7vxSoiGmHk5t6Ou4bXsfcgWyhfDKMpyKfhHR6Bjnn63c+YXBLBPUql9H4wPJM6sXw==",
"requires": {}
},
"react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+1 -1
View File
@@ -50,6 +50,7 @@
"focus-visible": "^5.2.0",
"framer-motion": "^6.5.1",
"install": "^0.13.0",
"lucide-react": "^0.105.0",
"next": "13.0.6",
"next-auth": "^4.18.6",
"next-i18next": "^13.0.3",
@@ -62,7 +63,6 @@
"react-feature-flags": "^1.0.0",
"react-hook-form": "^7.42.1",
"react-i18next": "^12.1.4",
"react-icons": "^4.7.1",
"react-table": "^7.8.0",
"sharp": "^0.31.3",
"swr": "^2.0.0",
+4 -3
View File
@@ -1,9 +1,10 @@
import { Box, Link, Text, useColorMode } from "@chakra-ui/react";
import { Github } from "lucide-react";
import { useTranslation } from "next-i18next";
import { useId } from "react";
import { FaDiscord, FaGithub } from "react-icons/fa";
import { Container } from "./Container";
import { Discord } from "./Icons/Discord";
const CIRCLE_HEIGHT = 558;
const CIRCLE_WIDTH = 558;
@@ -70,7 +71,7 @@ export function CallToAction() {
type="button"
className="mb-2 ml-6 flex items-center rounded-md border border-transparent bg-blue-600 px-6 py-3 text-base font-medium text-white shadow-sm hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"
>
<FaDiscord size={25} />
<Discord size={25} />
<Text as="span" className="text-lg ml-3">
{t("discord")}
</Text>
@@ -81,7 +82,7 @@ export function CallToAction() {
type="button"
className="mb-2 ml-6 flex items-center rounded-md border border-transparent bg-blue-600 px-6 py-3 text-base font-medium text-white shadow-sm hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"
>
<FaGithub size={25} />
<Github size={25} />
<Text as="span" className="text-lg ml-3">
{t("github")}
</Text>
+2 -2
View File
@@ -25,8 +25,8 @@ import {
useDisclosure,
} from "@chakra-ui/react";
import { ColumnDef, flexRender, getCoreRowModel, useReactTable } from "@tanstack/react-table";
import { Filter } from "lucide-react";
import { ChangeEvent, ReactNode } from "react";
import { FaFilter } from "react-icons/fa";
import { useDebouncedCallback } from "use-debounce";
export type DataTableColumnDef<T> = ColumnDef<T> & {
@@ -148,7 +148,7 @@ const FilterModal = ({
<Popover isOpen={isOpen} onOpen={onOpen} onClose={onClose}>
<PopoverTrigger>
<Button variant={"unstyled"} ml="2">
<FaFilter></FaFilter>
<Filter size="1em"></Filter>
</Button>
</PopoverTrigger>
<PopoverContent w="fit-content">
+3 -4
View File
@@ -1,11 +1,10 @@
import { Box, Text, useColorModeValue } from "@chakra-ui/react";
import { AlertTriangle, LucideIcon } from "lucide-react";
import NextLink from "next/link";
import { FiAlertTriangle } from "react-icons/fi";
import { IconType } from "react-icons/lib";
type EmptyStateProps = {
text: string;
icon: IconType;
icon: LucideIcon;
};
export const EmptyState = (props: EmptyStateProps) => {
@@ -25,5 +24,5 @@ export const EmptyState = (props: EmptyStateProps) => {
};
export const TaskEmptyState = () => {
return <EmptyState text="Looks like no tasks were found." icon={FiAlertTriangle} />;
return <EmptyState text="Looks like no tasks were found." icon={AlertTriangle} />;
};
+2 -2
View File
@@ -22,8 +22,8 @@ import {
} from "@chakra-ui/react";
import { QuestionMarkCircleIcon } from "@heroicons/react/20/solid";
import clsx from "clsx";
import { AlertCircle } from "lucide-react";
import { useEffect, useReducer } from "react";
import { FiAlertCircle } from "react-icons/fi";
import { get, post } from "src/lib/api";
import { colors } from "src/styles/Theme/colors";
import { Message } from "src/types/Conversation";
@@ -154,7 +154,7 @@ export const FlaggableElement = (props: FlaggableElementProps) => {
<Box>
<PopoverTrigger>
<Box as="button" display="flex" alignItems="center" justifyContent="center" borderRadius="full" p="1">
<FiAlertCircle size="20" className="text-red-400" aria-hidden="true" />
<AlertCircle size="20" className="text-red-400" aria-hidden="true" />
</Box>
</PopoverTrigger>
</Box>
+2 -2
View File
@@ -1,10 +1,10 @@
import { Box, Button, Flex, Text } from "@chakra-ui/react";
import { User } from "lucide-react";
import Image from "next/image";
import Link from "next/link";
import { useSession } from "next-auth/react";
import { useTranslation } from "next-i18next";
import { Flags } from "react-feature-flags";
import { FaUser } from "react-icons/fa";
import { LanguageSelector } from "src/components/LanguageSelector";
import { UserMenu } from "./UserMenu";
@@ -17,7 +17,7 @@ function AccountButton() {
return (
<Link href="/auth/signin" aria-label="Home">
<Flex alignItems="center">
<Button variant="outline" leftIcon={<FaUser />}>
<Button variant="outline" leftIcon={<User size={"20"} />}>
Sign in
</Button>
</Flex>
+7 -7
View File
@@ -11,11 +11,11 @@ import {
Text,
useColorModeValue,
} from "@chakra-ui/react";
import { AlertTriangle, Layout, LogOut, Settings, Shield } from "lucide-react";
import NextLink from "next/link";
import { signOut, useSession } from "next-auth/react";
import { useTranslation } from "next-i18next";
import React, { ElementType, useCallback } from "react";
import { FiAlertTriangle, FiLayout, FiLogOut, FiSettings, FiShield } from "react-icons/fi";
interface MenuOption {
name: string;
@@ -39,19 +39,19 @@ export function UserMenu() {
{
name: t("dashboard"),
href: "/dashboard",
icon: FiLayout,
icon: Layout,
isExternal: false,
},
{
name: t("account_settings"),
href: "/account",
icon: FiSettings,
icon: Settings,
isExternal: false,
},
{
name: t("report_a_bug"),
href: "https://github.com/LAION-AI/Open-Assistant/issues/new/choose",
icon: FiAlertTriangle,
icon: AlertTriangle,
isExternal: true,
},
];
@@ -60,7 +60,7 @@ export function UserMenu() {
options.unshift({
name: t("admin_dashboard"),
href: "/admin",
icon: FiShield,
icon: Shield,
isExternal: false,
});
}
@@ -93,7 +93,7 @@ export function UserMenu() {
_hover={{ textDecoration: "none" }}
>
<MenuItem gap="3" borderRadius="md" p="4">
<item.icon className="text-blue-500" aria-hidden="true" />
<item.icon size="1em" className="text-blue-500" aria-hidden="true" />
<Text>{item.name}</Text>
</MenuItem>
</Link>
@@ -101,7 +101,7 @@ export function UserMenu() {
</MenuGroup>
<MenuDivider />
<MenuItem gap="3" borderRadius="md" p="4" onClick={handleSignOut}>
<FiLogOut className="text-blue-500" aria-hidden="true" />
<LogOut size="1em" className="text-blue-500" aria-hidden="true" />
<Text>{t("sign_out")}</Text>
</MenuItem>
</MenuList>
+16
View File
@@ -0,0 +1,16 @@
import { LucideIcon } from "lucide-react";
export const Discord: LucideIcon = ({ size = 24, ...rest }) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 127.14 96.36"
fill="currentColor"
height={size}
width={size}
{...rest}
>
<path d="M107.7 8.07A105.15 105.15 0 0 0 81.47 0a72.06 72.06 0 0 0-3.36 6.83 97.68 97.68 0 0 0-29.11 0A72.37 72.37 0 0 0 45.64 0a105.89 105.89 0 0 0-26.25 8.09C2.79 32.65-1.71 56.6.54 80.21a105.73 105.73 0 0 0 32.17 16.15 77.7 77.7 0 0 0 6.89-11.11 68.42 68.42 0 0 1-10.85-5.18c.91-.66 1.8-1.34 2.66-2a75.57 75.57 0 0 0 64.32 0c.87.71 1.76 1.39 2.66 2a68.68 68.68 0 0 1-10.87 5.19 77 77 0 0 0 6.89 11.1 105.25 105.25 0 0 0 32.19-16.14c2.64-27.38-4.51-51.11-18.9-72.15ZM42.45 65.69C36.18 65.69 31 60 31 53s5-12.74 11.43-12.74S54 46 53.89 53s-5.05 12.69-11.44 12.69Zm42.24 0C78.41 65.69 73.25 60 73.25 53s5-12.74 11.44-12.74S96.23 46 96.12 53s-5.04 12.69-11.43 12.69Z" />
</svg>
);
};
@@ -8,12 +8,13 @@ const LanguageSelector = () => {
const router = useRouter();
const { i18n } = useTranslation();
const { language: currentLanguage } = i18n;
const languageNames = useMemo(() => {
return new Intl.DisplayNames([currentLanguage], {
type: "language",
});
}, [currentLanguage]);
// Memo the set of locales and their display names.
const localesAndNames = useMemo(() => {
return router.locales.map((locale) => ({
locale,
name: new Intl.DisplayNames([locale], { type: "language" }).of(locale),
}));
}, [router.locales]);
const languageChanged = useCallback(
async (option) => {
@@ -25,12 +26,12 @@ const LanguageSelector = () => {
[router]
);
const locales = router.locales;
const { language: currentLanguage } = i18n;
return (
<Select onChange={languageChanged} defaultValue={currentLanguage}>
{locales.map((locale) => (
{localesAndNames.map(({ locale, name }) => (
<option key={locale} value={locale}>
{languageNames.of(locale) ?? locale}
{name}
</option>
))}
</Select>
+6 -6
View File
@@ -1,8 +1,8 @@
// https://nextjs.org/docs/basic-features/layouts
import { Box, Grid } from "@chakra-ui/react";
import { Activity, BarChart2, Layout, MessageSquare, Users } from "lucide-react";
import type { NextPage } from "next";
import { FiBarChart2, FiLayout, FiMessageSquare, FiUsers, FiActivity } from "react-icons/fi";
import { Header } from "src/components/Header";
import { SlimFooter } from "./Dashboard/SlimFooter";
@@ -38,19 +38,19 @@ export const getDashboardLayout = (page: React.ReactElement) => (
label: "Dashboard",
pathname: "/dashboard",
desc: "Dashboard Home",
icon: FiLayout,
icon: Layout,
},
{
label: "Messages",
pathname: "/messages",
desc: "Messages Dashboard",
icon: FiMessageSquare,
icon: MessageSquare,
},
{
label: "Leaderboard",
pathname: "/leaderboard",
desc: "User Leaderboard",
icon: FiBarChart2,
icon: BarChart2,
},
]}
>
@@ -73,13 +73,13 @@ export const getAdminLayout = (page: React.ReactElement) => (
label: "Users",
pathname: "/admin",
desc: "Users Dashboard",
icon: FiUsers,
icon: Users,
},
{
label: "Status",
pathname: "/admin/status",
desc: "Status Dashboard",
icon: FiActivity,
icon: Activity,
},
]}
>
@@ -5,13 +5,19 @@ import { Message } from "src/types/Conversation";
interface MessageTableProps {
messages: Message[];
enableLink?: boolean;
highlightLastMessage?: boolean;
}
export function MessageTable({ messages, enableLink }: MessageTableProps) {
export function MessageTable({ messages, enableLink, highlightLastMessage }: MessageTableProps) {
return (
<Stack spacing="4">
{messages.map((item) => (
<MessageTableEntry enabled={enableLink} item={item} key={item.id + item.frontend_message_id} />
{messages.map((item, idx) => (
<MessageTableEntry
enabled={enableLink}
item={item}
key={item.id + item.frontend_message_id}
highlight={highlightLastMessage && idx === messages.length - 1}
/>
))}
</Stack>
);
@@ -1,14 +1,15 @@
import { Avatar, Box, HStack, LinkBox, useBreakpoint, useBreakpointValue, useColorModeValue } from "@chakra-ui/react";
import { Avatar, Box, HStack, useBreakpointValue, useColorModeValue } from "@chakra-ui/react";
import { boolean } from "boolean";
import Link from "next/link";
import { useRouter } from "next/router";
import { useCallback, useMemo } from "react";
import { FlaggableElement } from "src/components/FlaggableElement";
import { Message } from "src/types/Conversation";
import { colors } from "styles/Theme/colors";
interface MessageTableEntryProps {
item: Message;
enabled?: boolean;
highlight?: boolean;
}
export function MessageTableEntry(props: MessageTableEntryProps) {
@@ -37,6 +38,7 @@ export function MessageTableEntry(props: MessageTableEntryProps) {
),
[borderColor, inlineAvatar, item.is_assistant]
);
const highlightColor = useColorModeValue(colors.light.highlight, colors.dark.highlight);
return (
<FlaggableElement message={item}>
@@ -48,6 +50,8 @@ export function MessageTableEntry(props: MessageTableEntryProps) {
p="4"
borderRadius="md"
bg={item.is_assistant ? backgroundColor : backgroundColor2}
outline={props.highlight && "2px solid black"}
outlineColor={highlightColor}
onClick={props.enabled && goToMessage}
_hover={props.enabled && { cursor: "pointer", opacity: 0.9 }}
whiteSpace="pre-wrap"
+4 -5
View File
@@ -1,15 +1,14 @@
import { Box, Button, Text, Tooltip, useColorMode } from "@chakra-ui/react";
import { LucideIcon, Sun } from "lucide-react";
import Link from "next/link";
import { useRouter } from "next/router";
import { FiSun } from "react-icons/fi";
import { IconType } from "react-icons/lib";
import { colors } from "styles/Theme/colors";
export interface MenuButtonOption {
label: string;
pathname: string;
desc: string;
icon: IconType;
icon: LucideIcon;
}
export interface SideMenuProps {
@@ -47,7 +46,7 @@ export function SideMenu(props: SideMenuProps) {
bg={router.pathname === item.pathname ? "blue.500" : null}
_hover={router.pathname === item.pathname ? { bg: "blue.600" } : null}
>
<item.icon className={router.pathname === item.pathname ? "text-blue-200" : null} />
<item.icon size={"1em"} className={router.pathname === item.pathname ? "text-blue-200" : null} />
<Text
fontWeight="normal"
color={router.pathname === item.pathname ? "white" : null}
@@ -63,7 +62,7 @@ export function SideMenu(props: SideMenuProps) {
<div>
<Tooltip fontFamily="inter" label="Toggle Dark Mode" placement="right" className="hidden lg:hidden sm:block">
<Button size="lg" width="full" justifyContent="center" onClick={toggleColorMode} gap="2">
<FiSun />
<Sun size={"1em"} />
<Text fontWeight="normal" className="hidden lg:block">
{colorMode === "light" ? "Dark Mode" : "Light Mode"}
</Text>
@@ -1,8 +1,8 @@
import { Box, useColorModeValue } from "@chakra-ui/react";
import { useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import { GripVertical } from "lucide-react";
import { PropsWithChildren, useState } from "react";
import { RxDragHandleDots2 } from "react-icons/rx";
export const SortableItem = ({
children,
@@ -45,7 +45,7 @@ export const SortableItem = ({
style={style}
shadow="base"
>
<Box pr="4">{isEditable ? <RxDragHandleDots2 size="20px" /> : `${index + 1}.`}</Box>
<Box pr="4">{isEditable ? <GripVertical size="20px" /> : `${index + 1}.`}</Box>
{children}
</Box>
);
@@ -1,5 +1,5 @@
import { Box, Flex, IconButton, Tooltip, useColorModeValue } from "@chakra-ui/react";
import { FiEdit2 } from "react-icons/fi";
import { Edit2 } from "lucide-react";
import { SkipButton } from "src/components/Buttons/Skip";
import { SubmitButton } from "src/components/Buttons/Submit";
import { TaskInfo } from "src/components/TaskInfo/TaskInfo";
@@ -36,7 +36,13 @@ export const TaskControls = (props: TaskControlsProps) => {
{props.taskStatus === "REVIEW" || props.taskStatus === "SUBMITTED" ? (
<>
<Tooltip label="Edit">
<IconButton size="lg" data-cy="edit" aria-label="edit" onClick={props.onEdit} icon={<FiEdit2 />} />
<IconButton
size="lg"
data-cy="edit"
aria-label="edit"
onClick={props.onEdit}
icon={<Edit2 size="1em" />}
/>
</Tooltip>
<SubmitButton
colorScheme="green"
+1 -1
View File
@@ -36,7 +36,7 @@ export const CreateTask = ({
<TaskHeader taskType={taskType} />
{task.conversation ? (
<Box mt="4" borderRadius="lg" bg={cardColor} className="p-3 sm:p-6">
<MessageTable messages={task.conversation.messages} />
<MessageTable messages={task.conversation.messages} highlightLastMessage />
</Box>
) : null}
</>
@@ -38,7 +38,7 @@ export const EvaluateTask = ({
<SurveyCard>
<TaskHeader taskType={taskType} />
<Box mt="4" p="6" borderRadius="lg" bg={cardColor}>
<MessageTable messages={messages} />
<MessageTable messages={messages} highlightLastMessage />
</Box>
<Sortable
items={task[sortables]}
@@ -53,6 +53,7 @@ export const LabelTask = ({
message_id: task.message_id,
},
]}
highlightLastMessage
/>
</Box>
) : (
@@ -1,5 +1,5 @@
import { HStack, IconButton, Link, Stack, Text, useColorModeValue } from "@chakra-ui/react";
import { FiHelpCircle } from "react-icons/fi";
import { HelpCircle } from "lucide-react";
import type { TaskInfo } from "src/components/Tasks/TaskTypes";
interface TaskHeaderProps {
@@ -22,7 +22,7 @@ const TaskHeader = ({ taskType }: TaskHeaderProps) => {
{taskType.label}
</Text>
<Link href={taskType.help_link} isExternal>
<IconButton variant="ghost" aria-label="More Information" icon={<FiHelpCircle />} />
<IconButton variant="ghost" aria-label="More Information" icon={<HelpCircle size="1em" />} />
</Link>
</HStack>
<Text fontSize="md" color={labelColor}>
+2 -2
View File
@@ -1,8 +1,8 @@
import { IconButton } from "@chakra-ui/react";
import { createColumnHelper } from "@tanstack/react-table";
import { Pencil } from "lucide-react";
import Link from "next/link";
import { memo, useState } from "react";
import { FaPen } from "react-icons/fa";
import { get } from "src/lib/api";
import { FetchUsersResponse } from "src/lib/oasst_api_client";
import type { User } from "src/types/Users";
@@ -49,7 +49,7 @@ const columns: DataTableColumnDef<User>[] = [
as={Link}
href={`/admin/manage_user/${getValue()}`}
aria-label="Manage"
icon={<FaPen></FaPen>}
icon={<Pencil size="1em"></Pencil>}
></IconButton>
),
header: "Update",
+3 -3
View File
@@ -221,7 +221,7 @@ export class OasstApiClient {
// pagination direction but they both take the same cursor value.
// Depending on direction, pick the right query param.
if (cursor !== "") {
params.append(direction === "forward" ? "gt" : "lt", cursor);
params.append(direction === "forward" ? "after" : "before", cursor);
}
const BASE_URL = `/api/v1/users/cursor`;
const url = `${BASE_URL}/?${params.toString()}`;
@@ -269,8 +269,8 @@ export class OasstApiClient {
/**
* Returns the counts of all tasks (some might be zero)
*/
async fetch_available_tasks(user: BackendUserCore): Promise<AvailableTasks> {
return this.post(`/api/v1/tasks/availability`, user);
async fetch_available_tasks(user: BackendUserCore, lang: string): Promise<AvailableTasks> {
return this.post(`/api/v1/tasks/availability`, { ...user, lang });
}
}
+1 -1
View File
@@ -15,7 +15,7 @@ const LOCALE_SET = new Set(i18n.locales);
* the i18n module.
* 3. "en" as a final fallback.
*/
const getUserLanguage = (req: NextApiRequest) => {
const getUserLanguage = (req: NextApiRequest): string => {
const cookieLanguage = req.cookies["NEXT_LOCALE"];
if (cookieLanguage) {
return cookieLanguage;
+3 -3
View File
@@ -1,6 +1,6 @@
import { Box, Button, Center, Link, Text } from "@chakra-ui/react";
import { AlertTriangle } from "lucide-react";
import Head from "next/head";
import { FiAlertTriangle } from "react-icons/fi";
import { EmptyState } from "src/components/EmptyState";
import { getTransparentHeaderLayout } from "src/components/Layout";
export { getDefaultStaticProps as getStaticProps } from "src/lib/default_static_props";
@@ -13,12 +13,12 @@ function Error() {
<meta name="404" content="Sorry, this page doesn't exist." />
</Head>
<Center flexDirection="column" gap="4" fontSize="lg" className="subpixel-antialiased">
<EmptyState text="Sorry, the page you are looking for does not exist." icon={FiAlertTriangle} />
<EmptyState text="Sorry, the page you are looking for does not exist." icon={AlertTriangle} />
<Box display="flex" flexDirection="column" alignItems="center" gap="2" mt="6">
<Text fontSize="sm">If you were trying to contribute data but ended up here, please file a bug.</Text>
<Button
width="fit-content"
leftIcon={<FiAlertTriangle className="text-blue-500" aria-hidden="true" />}
leftIcon={<AlertTriangle size={"1em"} className="text-blue-500" aria-hidden="true" />}
variant="solid"
size="xs"
>
+3 -6
View File
@@ -1,6 +1,6 @@
import { Box, Button, Center, Link, Text } from "@chakra-ui/react";
import { AlertTriangle } from "lucide-react";
import Head from "next/head";
import { FiAlertTriangle } from "react-icons/fi";
import { EmptyState } from "src/components/EmptyState";
import { getTransparentHeaderLayout } from "src/components/Layout";
export { getDefaultStaticProps as getStaticProps } from "src/lib/default_static_props";
@@ -13,15 +13,12 @@ function ServerError() {
<meta name="404" content="Sorry, this page doesn't exist." />
</Head>
<Center flexDirection="column" gap="4" fontSize="lg" className="subpixel-antialiased">
<EmptyState
text="Sorry, we encountered a server error. We're not sure what went wrong."
icon={FiAlertTriangle}
/>
<EmptyState text="Sorry, we encountered a server error. We're not sure what went wrong." icon={AlertTriangle} />
<Box display="flex" flexDirection="column" alignItems="center" gap="2" mt="6">
<Text fontSize="sm">If you were trying to contribute data but ended up here, please file a bug.</Text>
<Button
width="fit-content"
leftIcon={<FiAlertTriangle className="text-blue-500" aria-hidden="true" />}
leftIcon={<AlertTriangle size="1em" className="text-blue-500" aria-hidden="true" />}
variant="solid"
size="xs"
>
+3 -3
View File
@@ -1,10 +1,10 @@
import { Button, Divider, Flex, Grid, Icon, Text } from "@chakra-ui/react";
import { Divider, Flex, Grid, Icon, Text } from "@chakra-ui/react";
import Head from "next/head";
import Link from "next/link";
import { useSession } from "next-auth/react";
import React from "react";
export { getDefaultStaticProps as getStaticProps } from "src/lib/default_static_props";
import { MdOutlineEdit } from "react-icons/md";
import { Pencil } from "lucide-react";
import { SurveyCard } from "src/components/Survey/SurveyCard";
export default function Account() {
@@ -34,7 +34,7 @@ export default function Account() {
<Flex gap={2}>
{session.user.name ?? "(No username)"}
<Link href="/account/edit">
<Icon boxSize={5} as={MdOutlineEdit} />
<Icon boxSize={5} as={Pencil} size="1em" />
</Link>
</Flex>
<Text as="b">Email</Text>
+3 -2
View File
@@ -4,12 +4,12 @@ import {
CardBody,
CircularProgress,
SimpleGrid,
Text,
Table,
TableCaption,
TableContainer,
Tbody,
Td,
Text,
Th,
Thead,
Tr,
@@ -19,9 +19,10 @@ import Head from "next/head";
import { useRouter } from "next/router";
import { useSession } from "next-auth/react";
import { useEffect } from "react";
import useSWRImmutable from "swr/immutable";
import { getAdminLayout } from "src/components/Layout";
import { get } from "src/lib/api";
import useSWRImmutable from "swr/immutable";
export { getDefaultStaticProps as getStaticProps } from "src/lib/default_static_props";
/**
* Provides the admin status page that shows result of calls to several backend API endpoints,
+3 -2
View File
@@ -1,10 +1,11 @@
import { withoutRole } from "src/lib/auth";
import { oasstApiClient } from "src/lib/oasst_api_client";
import { getBackendUserCore } from "src/lib/users";
import { getBackendUserCore, getUserLanguage } from "src/lib/users";
const handler = withoutRole("banned", async (req, res, token) => {
const user = await getBackendUserCore(token.sub);
const availableTasks = await oasstApiClient.fetch_available_tasks(user);
const userLanguage = getUserLanguage(req);
const availableTasks = await oasstApiClient.fetch_available_tasks(user, userLanguage);
res.status(200).json(availableTasks);
});
+8 -7
View File
@@ -1,17 +1,18 @@
import { Button, ButtonProps, Input, Stack, useColorModeValue } from "@chakra-ui/react";
import { useColorMode } from "@chakra-ui/react";
import { Bug, Github, Mail } from "lucide-react";
import { GetServerSideProps } from "next";
import Head from "next/head";
import Link from "next/link";
import { useRouter } from "next/router";
import { ClientSafeProvider, getProviders, signIn } from "next-auth/react";
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
import React, { useEffect, useRef, useState } from "react";
import React, { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import { FaBug, FaDiscord, FaEnvelope, FaGithub } from "react-icons/fa";
import { AuthLayout } from "src/components/AuthLayout";
import { Footer } from "src/components/Footer";
import { Header } from "src/components/Header";
import { Discord } from "src/components/Icons/Discord";
import { Role, RoleSelect } from "src/components/RoleSelect";
export type SignInErrorTypes =
@@ -89,7 +90,7 @@ function Signin({ providers }: SigninProps) {
placeholder="Email Address"
{...register("email")}
/>
<SigninButton data-cy="signin-email-button" leftIcon={<FaEnvelope />}>
<SigninButton data-cy="signin-email-button" leftIcon={<Mail />}>
Continue with Email
</SigninButton>
</Stack>
@@ -103,7 +104,7 @@ function Signin({ providers }: SigninProps) {
bg: "#454FBF",
}}
size="lg"
leftIcon={<FaDiscord />}
leftIcon={<Discord />}
color="white"
onClick={() => signIn(discord.id, { callbackUrl: "/" })}
// isDisabled="false"
@@ -119,7 +120,7 @@ function Signin({ providers }: SigninProps) {
bg: "#101010",
}}
size={"lg"}
leftIcon={<FaGithub />}
leftIcon={<Github />}
colorScheme="blue"
// isDisabled="false"
>
@@ -165,7 +166,7 @@ const SigninButton = (props: ButtonProps) => {
return (
<Button
size={"lg"}
leftIcon={<FaEnvelope />}
leftIcon={<Mail />}
type="submit"
colorScheme={buttonColorScheme}
color="white"
@@ -203,7 +204,7 @@ const DebugSigninForm = ({ credentials, bgColorClass }: { credentials: ClientSaf
<Stack>
<Input variant="outline" size="lg" placeholder="Username" {...register("username")} />
<RoleSelect {...register("role")}></RoleSelect>
<SigninButton leftIcon={<FaBug />}>Continue with Debug User</SigninButton>
<SigninButton leftIcon={<Bug />}>Continue with Debug User</SigninButton>
</Stack>
</form>
);
+19 -3
View File
@@ -1,16 +1,32 @@
import { Flex } from "@chakra-ui/react";
import Head from "next/head";
import { useMemo } from "react";
import { useEffect, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { LeaderboardTable, TaskOption, WelcomeCard } from "src/components/Dashboard";
import { getDashboardLayout } from "src/components/Layout";
import { TaskCategory } from "src/components/Tasks/TaskTypes";
import { get } from "src/lib/api";
import { AvailableTasks, TaskType } from "src/types/Task";
export { getDefaultStaticProps as getStaticProps } from "src/lib/default_static_props";
import useSWRImmutable from "swr/immutable";
import useSWR from "swr";
const Dashboard = () => {
const { data } = useSWRImmutable<AvailableTasks>("/api/available_tasks", get);
const {
i18n: { language },
} = useTranslation();
const [activeLang, setLang] = useState<string>(null);
const { data, mutate: fetchTasks } = useSWR<AvailableTasks>("/api/available_tasks", get, {
refreshInterval: 2 * 60 * 1000, //2 minutes
revalidateOnMount: false, // triggered in the hook below
});
useEffect(() => {
// re-fetch tasks if the language has changed
if (activeLang !== language) {
setLang(language);
fetchTasks();
}
}, [activeLang, setLang, language, fetchTasks]);
const availableTaskTypes = useMemo(() => {
const taskTypes = filterAvailableTasks(data ?? {});
+2
View File
@@ -4,11 +4,13 @@ export const colors = {
btn: "gray.50",
div: "white",
text: "black",
highlight: "blue.400",
},
dark: {
bg: "gray.900",
btn: "gray.600",
div: "gray.700",
text: "gray.200",
highlight: "blue.500",
},
};