From 41965bc0235a49955225656eba9aaa9663f5c664 Mon Sep 17 00:00:00 2001 From: pillar Date: Fri, 13 Oct 2023 23:41:58 -0700 Subject: [PATCH] fix: update page read --- bun.lockb | Bin 176953 -> 178794 bytes i18n.sqlite | Bin 61440 -> 61440 bytes package.json | 2 + src/lib/components/lazy-show.svelte | 44 ++++ src/lib/components/speech-cache.ts | 3 + src/lib/components/speech-controller.svelte | 83 +++++++ src/lib/components/speech.svelte | 75 ++++++ src/lib/components/tab.svelte | 1 + src/lib/components/translate-word.svelte | 32 +++ src/lib/helpers/loop-play-hooks.ts | 65 ++++++ src/lib/helpers/scroll-to-element.ts | 11 + src/lib/helpers/split-sentences.ts | 5 + src/lib/helpers/split-words.ts | 5 + src/lib/helpers/translate-google.ts | 26 +++ src/lib/i18n/i18n-source.ts | 15 ++ src/lib/stores/article.ts | 220 ++++++++++++++++++ src/lib/stores/brain-store.ts | 1 + src/lib/stores/learn-words.ts | 3 + src/lib/stores/storable.ts | 28 ++- src/lib/stores/user.ts | 1 + src/routes/article/+page.svelte | 17 ++ src/routes/article/learn/+page.svelte | 96 ++++++++ src/routes/brain/+page.svelte | 2 +- .../[learn]/analyze-response/+page.svelte | 4 + src/routes/setting/+page.svelte | 2 +- 25 files changed, 728 insertions(+), 13 deletions(-) create mode 100644 src/lib/components/lazy-show.svelte create mode 100644 src/lib/components/speech-cache.ts create mode 100644 src/lib/components/speech-controller.svelte create mode 100644 src/lib/components/speech.svelte create mode 100644 src/lib/components/translate-word.svelte create mode 100644 src/lib/helpers/loop-play-hooks.ts create mode 100644 src/lib/helpers/scroll-to-element.ts create mode 100644 src/lib/helpers/split-sentences.ts create mode 100644 src/lib/helpers/split-words.ts create mode 100644 src/lib/helpers/translate-google.ts create mode 100644 src/lib/stores/article.ts create mode 100644 src/lib/stores/learn-words.ts create mode 100644 src/routes/article/+page.svelte create mode 100644 src/routes/article/learn/+page.svelte diff --git a/bun.lockb b/bun.lockb index 3a22446535e19bce20a7c2393945e4a1d8784b47..7ecc9385d42e444364cb1c8c7355f65fae379c88 100755 GIT binary patch delta 30999 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 Ix_Q|D2gTgPng9R* diff --git a/i18n.sqlite b/i18n.sqlite index d8347ed1f988f017ca8d8445499cafb351485c27..9e609581ca5757709ec71d4a3bdff9e26026a5e0 100644 GIT binary patch delta 643 zcmZp8z})bFd4e?Kq=_=lf|D5Z;$?aHe=sodw=(dz@}J+VC?Lwexs|_u1Ec3;)&nhq zzgR9XZR2lePGv7+En`Yy+`M_#fp`{47I9`nrl)I{J=wY7#r~Pk)@^;-*7|haPKJq_ zg^qsWVsmFVV_8#ILEm@whWH4`Ij%8ZE zv9O(~zEP`Dkj1dc6w?{iN>v$3R!W!>N;*oZc|e)qlG2n)g_4ZSVuj@Vypq(s5}-_0 z0Z`^~mE04e%uU1ZD6wHanmHAS&tjHf%F#xRk;?Il-8$YCMu*Bm!v|C$V|xx zN~aY8r4M%k&Cg7&Qb>kLmMYbTH43muHmX7b0qQ~s2j(irqLR$yoK%=oUaXk$WLG!L zN}%N7waKM9a7*f8%4-|-8u@_%VT&22Fl#UZ8)ju>erb^cFcuUt5{ng5D+)4;Qd3~| zKW=P#+_>s-<9uL%0F^v$Tn~(;moq0k?P!18)bn_<_9kBa;8Lh=h~`8@7=-1Q6s3YC TfuRI4`0z@l+RYhHW^e-ltn(0p delta 142 zcmV;90CE3--~)i*1CSd5kC7Zh5sv^aS4a&H_y7VAg#ZtQ56`n95ET!zg%5wQ0Z5Yt zz=RRd0=5r_17!zx1$F{t0kaOrvzowP1OXh8K^U_V#qtUSMF&~{lM2u?vp>-H29ba$ wvs%}x2p$atXaElx4r&cS3}6a330(+^2mc3`1?L4h1mpy0v2jKOvuNa)3-I4A%K!iX diff --git a/package.json b/package.json index deee17c..535fed4 100644 --- a/package.json +++ b/package.json @@ -61,8 +61,10 @@ "dependencies": { "@faker-js/faker": "^8.0.2", "@google-cloud/text-to-speech": "^5.0.1", + "@minht11/solid-virtual-container": "^0.2.1", "@prisma/client": "5.2.0", "@sveltejs/adapter-node": "^1.3.1", + "@sveltejs/svelte-virtual-list": "^3.0.1", "@trpc/client": "^10.38.1", "@trpc/server": "^10.38.1", "@types/node": "^20.6.3", diff --git a/src/lib/components/lazy-show.svelte b/src/lib/components/lazy-show.svelte new file mode 100644 index 0000000..06127bc --- /dev/null +++ b/src/lib/components/lazy-show.svelte @@ -0,0 +1,44 @@ + + +
+ {#if isVisible} + + {/if} +
diff --git a/src/lib/components/speech-cache.ts b/src/lib/components/speech-cache.ts new file mode 100644 index 0000000..965e5de --- /dev/null +++ b/src/lib/components/speech-cache.ts @@ -0,0 +1,3 @@ +export const speechCache = { + lastAudio: null as HTMLAudioElement | null, +}; diff --git a/src/lib/components/speech-controller.svelte b/src/lib/components/speech-controller.svelte new file mode 100644 index 0000000..1546ccf --- /dev/null +++ b/src/lib/components/speech-controller.svelte @@ -0,0 +1,83 @@ + + +
+
+ {#if copy} + + {/if} +
+ {#if connect} + + {/if} + {#if loop} + + {/if} + +
+ + {#each speeds as item} + + {/each} + +
+
+
diff --git a/src/lib/components/speech.svelte b/src/lib/components/speech.svelte new file mode 100644 index 0000000..d5f340b --- /dev/null +++ b/src/lib/components/speech.svelte @@ -0,0 +1,75 @@ + + + + + handleSpeech(e)} class={className}> + diff --git a/src/lib/components/tab.svelte b/src/lib/components/tab.svelte index 0a8752c..b1793a2 100644 --- a/src/lib/components/tab.svelte +++ b/src/lib/components/tab.svelte @@ -5,6 +5,7 @@ export let selected = 0; const tabs = [ { title: i18n`添加单词`, icon: 'ci:table-add', href: '/home/' + $user.learn }, + { title: i18n`文章`, icon: 'grommet-icons:article', href: '/article' }, { title: i18n`我的大脑`, icon: 'icon-park-outline:brain', href: '/brain' }, { title: i18n`设置`, icon: 'mingcute:settings-3-line', href: '/setting' }, ]; diff --git a/src/lib/components/translate-word.svelte b/src/lib/components/translate-word.svelte new file mode 100644 index 0000000..60b7dfc --- /dev/null +++ b/src/lib/components/translate-word.svelte @@ -0,0 +1,32 @@ + + + + + + {text} + {#if translate} + ( {translate}) + {/if} + diff --git a/src/lib/helpers/loop-play-hooks.ts b/src/lib/helpers/loop-play-hooks.ts new file mode 100644 index 0000000..4d941ed --- /dev/null +++ b/src/lib/helpers/loop-play-hooks.ts @@ -0,0 +1,65 @@ +import { browser } from '$app/environment'; +import { speechCache } from '$lib/components/speech-cache'; +import { loopPlay, speechConnect, speechPeople, speedAudio } from '$lib/stores/brain-store'; +import { user } from '$lib/stores/user'; +import { onDestroy, onMount } from 'svelte'; +import { get } from 'svelte/store'; +import { scrollToElement } from './scroll-to-element'; + +export function loopPlayHooks() { + let loopUpdateTimer: ReturnType; + + onMount(() => { + if (browser) { + loopUpdateTimer = setInterval(() => { + if (speechCache.lastAudio?.paused) { + if (get(speechConnect)) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + if ((speechCache.lastAudio as any).nowConnect === void 0) { + return; + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (speechCache.lastAudio as any).nowConnect += 1; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const connectId = (speechCache.lastAudio as any).nowConnect; + const nextAudio = document.querySelector( + `[data-connect-audio="${connectId}"]`, + ) as HTMLAudioElement; + if (nextAudio) { + scrollToElement(nextAudio.parentElement?.parentElement); + const src = `/brain/audio?${new URLSearchParams({ + text: nextAudio.getAttribute('data-text') || '', + people: get(speechPeople), + learn: get(user).learn, + }).toString()}`; + if (speechCache.lastAudio.src === src) { + return; + } + speechCache.lastAudio.src = src; + speechCache.lastAudio.load(); + speechCache.lastAudio.playbackRate = get(speedAudio); + speechCache.lastAudio.currentTime = 0.1; + speechCache.lastAudio.play(); + } + } else if (get(loopPlay)) { + speechCache.lastAudio.currentTime = 0.1; + speechCache.lastAudio.playbackRate = 1; + speechCache.lastAudio.pause(); + speechCache.lastAudio.load(); + speechCache.lastAudio.currentTime = 0.1; + speechCache.lastAudio.playbackRate = get(speedAudio); + speechCache.lastAudio.play(); + } else { + speechCache.lastAudio.pause(); + speechCache.lastAudio = null; + } + } + }, 100); + } + }); + onDestroy(() => { + if (browser) { + clearInterval(loopUpdateTimer); + } + }); +} diff --git a/src/lib/helpers/scroll-to-element.ts b/src/lib/helpers/scroll-to-element.ts new file mode 100644 index 0000000..252517b --- /dev/null +++ b/src/lib/helpers/scroll-to-element.ts @@ -0,0 +1,11 @@ +export function scrollToElement(element?: HTMLElement | null) { + if (element) { + const offsetTop = element.offsetTop; + const windowHeight = window.innerHeight || document.documentElement.clientHeight; + const scrollPosition = offsetTop - windowHeight / 2; + window.scrollTo({ + top: scrollPosition, + behavior: 'smooth', + }); + } +} diff --git a/src/lib/helpers/split-sentences.ts b/src/lib/helpers/split-sentences.ts new file mode 100644 index 0000000..ebd1ecf --- /dev/null +++ b/src/lib/helpers/split-sentences.ts @@ -0,0 +1,5 @@ +export function splitSentences(text: string): string[] { + // 使用正则表达式将文本分割成句子,句子以句号、问号或感叹号结尾 + const sentences = text.split(/(?<=[.!?])\s+/); + return sentences; +} diff --git a/src/lib/helpers/split-words.ts b/src/lib/helpers/split-words.ts new file mode 100644 index 0000000..37e11e1 --- /dev/null +++ b/src/lib/helpers/split-words.ts @@ -0,0 +1,5 @@ +export function splitWords(sentence: string): string[] { + // 使用正则表达式将句子分割成单词,\w+ 匹配一个或多个字母、数字或下划线 + const words = sentence.split(/\s+/); + return words; +} diff --git a/src/lib/helpers/translate-google.ts b/src/lib/helpers/translate-google.ts new file mode 100644 index 0000000..0642b27 --- /dev/null +++ b/src/lib/helpers/translate-google.ts @@ -0,0 +1,26 @@ +function removePunctuation(inputString: string) { + // eslint-disable-next-line no-useless-escape + const regex = /[.,\/#!$%\^&\*;:{}=\-_`~()\[\]\p{P}]/gu; + return inputString.replace(regex, ''); +} + +export async function translateGoogle(q: string, to: string) { + if (typeof q !== 'string') { + return ''; + } + if (to === 'zh') { + to = 'zh-CN'; + } else if (to === 'es') { + to = 'es'; + } else if (to === 'jp') { + to = 'ja'; + } + q = removePunctuation(q); + const uri = `https://translate.googleapis.com/translate_a/single?client=gtx&dt=t&sl=auto&tl=${to}&q=${q}`; + + const res = await fetch(uri, { method: 'GET' }).then((v) => v.json()); + if (res[0] && res[0][0] && res[0][0][0]) { + return res[0][0][0]; + } + return 'Null'; +} diff --git a/src/lib/i18n/i18n-source.ts b/src/lib/i18n/i18n-source.ts index 2ad0720..578f422 100644 --- a/src/lib/i18n/i18n-source.ts +++ b/src/lib/i18n/i18n-source.ts @@ -195,6 +195,7 @@ export const i18nSource = { es: 'Agregar palabra', fr: 'Ajouter un mot', }, + 文章: { zh: '文章', en: 'Article', jp: '記事', es: 'Artículo', fr: 'Article' }, 我的大脑: { zh: '我的大脑', en: 'My brain', jp: '私の脳', es: 'Mi cerebro', fr: 'Mon cerveau' }, 设置: { zh: '设置', en: 'Settings', jp: '設定', es: 'Configuración', fr: 'Paramètres' }, 我要学习英语: { @@ -299,6 +300,13 @@ export const i18nSource = { es: 'El AI está generando contenido, recomendaciones y traducciones en el idioma nativo para usted. Tomará aproximadamente 30 segundos.', fr: "L'IA génère du contenu, des recommandations et des traductions dans votre langue maternelle. Cela prendra environ 30 secondes.", }, + 您的VIP已过期: { + zh: '您的VIP已过期', + en: 'Your VIP has expired', + jp: 'あなたのVIPは期限切れです', + es: 'Su VIP ha expirado', + fr: 'Votre VIP a expiré', + }, '分析失败, 对于 AI 自动分析来说这是有概率的,建议你点击下面的按钮重试': { zh: '分析失败,对于AI自动分析来说这是有概率的,建议你点击下面的按钮重试', en: 'Analysis failed. It is probabilistic for AI automatic analysis. We recommend you to click the button below and try again.', @@ -454,6 +462,13 @@ export const i18nSource = { es: 'Todavía tienes muchos días VIP en el mes actual. Te recomendamos cancelar la suscripción dentro de los 5 días restantes. ¿Estás seguro de que quieres cancelar la suscripción?', fr: 'Vous avez encore beaucoup de jours VIP dans le mois en cours. Nous vous recommandons de vous désabonner dans les 5 jours restants. Êtes-vous sûr de vouloir vous désabonner ?', }, + 学习这段内容: { + zh: '学习这段内容', + en: 'Study this content', + jp: 'このコンテンツを学ぶ', + es: 'Estudia este contenido', + fr: 'Étudiez ce contenu', + }, '剩余免费试用天数:': { zh: '剩余免费试用天数:', en: 'Remaining free trial days:', diff --git a/src/lib/stores/article.ts b/src/lib/stores/article.ts new file mode 100644 index 0000000..3166fc3 --- /dev/null +++ b/src/lib/stores/article.ts @@ -0,0 +1,220 @@ +import { storable } from './storable'; + +const baseArticle = `Interviewer: Good morning. Could you start by introducing yourself? +Interview Candidate: Good morning. My name is XYZ. I am a front-end engineer with 4 years +of experience in developing robust and user-friendly web applications. I specialize in using +JavaScript, CSS, HTML, and I have worked extensively with React.js and Vue.js frameworks. +Interviewer: That's great. Can you share an example of a project where you significantly +improved the performance? + + +Interview Candidate: Certainly. In one of my previous roles, we had a project where the +webpage was loading very slowly. I took the initiative to diagnose the problem and discovered the +main issue was bulky images. I then implemented an image compression solution which +significantly reduced the loading time. Furthermore, I used code splitting to lazy load parts of the +application which were not immediately necessary, resulting in a more than 50% decrease in the +initial load time. + +Interviewer: Very interesting. How do you approach debugging when you encounter an issue in your code? + +Interview Candidate: When I encounter a problem, my first step is always to replicate the issue +and understand the exact conditions under which it occurs. Then, I typically use the browser's +developer tools, particularly the console and network tabs, to identify errors or unexpected +behavior. I also leverage debugging tools provided by the frameworks I'm using, like Vue +Devtools or React Developer Tools. +Interviewer: And how do you ensure the quality of your code? +Interview Candidate: I am a strong advocate of code reviews - both giving and receiving. This +not only helps to catch potential bugs and maintain a clean codebase, but it also fosters knowledge +sharing within the team. Besides, I am a believer in test-driven development. I ensure to write unit +tests and integration tests using tools like Jest or Mocha to validate the functionality of my code. +Interviewer: Excellent! Do you have any questions for us? +Interview Candidate: Yes, I would like to know more about the current projects this role would be involved in. Could you share more about that? +Interviewer: Absolutely. The role would involve working on our main product, which is a web +application serving thousands of users daily. We are in the process of integrating more interactive +features and improving the overall user experience. There are also opportunities to work on side +projects and create innovative solutions. +This is a general dialogue but the exact questions would vary depending on the company, the +specific role, and the interviewer. Remember, interviews are not just about showing your technical +competence, but also about demonstrating your problem-solving skills, your ability to work as part +of a team, and your passion for the work. +Interview Candidate: Sounds exciting. I also wanted to ask, how do you handle code reviews and testing in the development process? +Interviewer: Good question. We have a two-step code review process in place where two different team members review the code before it gets merged. This helps us maintain high coding standards and minimizes the risk of introducing bugs into production. As for testing, we follow the approach of automated testing. We use Jest for unit testing and Cypress for end-to-end testing. +Interview Candidate: That's reassuring. My last question would be regarding the team. Could you share more about the team I'd be working with? +Interviewer: Of course. You'll be part of a cross-functional team of about 8 people, including front-end and back-end developers, a UX/UI designer, and a product manager. The team operates in an Agile framework with two-week sprints. We have daily stand-ups and retrospective meetings at the end of each sprint. +Interview Candidate: Thank you. This was very informative. +Interviewer: You're welcome. Do you have any other questions? +Interview Candidate: No, not for now. I appreciate the opportunity to interview for this position. +Interviewer: It was great talking to you. We will be in touch soon with next steps. Have a nice day! +Remember that an interview is not only about the company assessing your fit for the role but also +about you assessing if the company and role are a good fit for your career and personal goals. It's +essential to ask questions to understand the work culture, expectations, and opportunities for +growth. +Since the candidate didn't have any further questions, I would assume this wraps up the interview. However, if you're looking for more potential questions and responses, here's how a follow-up conversation might look like: +Interviewer: We're glad to hear you found the interview informative. As a front-end developer, what would you say is the most challenging part of your job? +Interview Candidate: In my experience, one of the biggest challenges is maintaining the balance +between meeting project deadlines and ensuring high-quality, maintainable code. There can be +pressure to deliver features quickly, but it's important not to compromise on code quality. I try to +manage this by communicating effectively with my team and utilizing efficient coding practices +like code reviews and comprehensive testing. +Interviewer: That's a good point. Now, can you tell us about a time when you had to learn a new technology or framework for a project? How did you go about it? + +Interview Candidate: Yes, at my previous job, I was tasked with a project that required me to learn TypeScript. I initially took some online courses and read the official documentation to understand the basics. Then, I built a small side project using TypeScript, which helped me get hands-on experience. During the project, I regularly consulted more experienced teammates and online forums whenever I faced challenges. +Interviewer: Great, it sounds like you're able to learn and adapt quickly. Now, can you explain how you ensure that the websites you develop are accessible? +Interview Candidate: Absolutely. Accessibility is crucial for the websites I develop. I always use semantic HTML to ensure that the content is easily interpreted by screen readers. I also focus on keyboard accessibility, ensuring all functionality is available through the keyboard. Additionally, I test color contrast to ensure it meets the WCAG standards, and I try to include options like a +text-resizing feature for better readability. +Interviewer: Excellent, we also consider accessibility as a top priority. Thank you for your time and your thoughtful responses. We will be in touch soon regarding the next steps in the process. +Remember that showing your thought process, problem-solving skills, and how you adapt to new situations can be very valuable in an interview, in addition to showing your technical knowledge. +As the conversation seems to have come to a natural close, and the Interviewer has mentioned +contacting the candidate with next steps, any further dialogue would likely take place at a second +interview or during a follow-up conversation. Here's an example of how that might look: +Interviewer (in a follow-up call): Hello again, thank you for your patience. We were impressed +with your skills and experience, and we would like to invite you for a second round interview +which would be a technical round where you'll be asked to solve coding challenges. How does that +sound? +Interview Candidate: That sounds great, thank you. I am looking forward to the opportunity to demonstrate my technical skills. +Interviewer: Excellent, we will send you the details via email. In the meantime, do you have any other questions about the role or the company? +Interview Candidate: Not at the moment, but I may reach out if anything comes up before the second round. Thank you for considering my application. +Interviewer: You're welcome. We'll look forward to your participation in the next round. Have a great day! +Interview Candidate: You too. Goodbye! +Remember, it's always good to show enthusiasm and interest in the role throughout the interviewing process. Also, feel free to ask for feedback, and always take the opportunity to learn and grow from the experience. +Since the conversation seems to have concluded the initial interview and established the steps for the next round, continuing this thread might not make much sense. But, if you are looking for an idea of how the technical round might go, here is an example: +Interviewer (during the second round): Hi again, are you ready to start the coding challenges? +Interview Candidate: Yes, I am ready. +Interviewer: Great, for the first task, could you please write a function in JavaScript that reverses a string without using any built-in reverse functions? +Interview Candidate: Sure, here is a simple implementation using a for loop: +Interviewer: Nice work! Now, can you explain how you would ensure a web application is responsive and works on various screen sizes? +Interview Candidate: I would use CSS media queries to change the layout based on different screen sizes. Flexbox and Grid are also very helpful to create responsive designs. I also make use of responsive images to ensure the page loads quickly on smaller devices. Lastly, I always test the application on various devices and screen sizes to ensure a consistent user experience. +Interviewer: Excellent! We'll wrap it up here. We'll be in touch soon with the results. Thanks for your time. +Remember, in a technical interview, clarity of thought, approach to problem-solving, and clear +communication are just as important as getting the correct solution. The interviewer is also +interested in understanding your thought process and how you handle problems. +At this point, the next logical step would be a feedback conversation or job offer. Here's how a final follow-up call might go: +Interviewer (in a follow-up call): Hello again, and thank you for your patience. After a thorough +review of your performance in the technical round, we are pleased to extend an offer to you for the +position of Front-End Engineer. We were impressed by your skills, problem-solving abilities, and +your passion for front-end development. +Interview Candidate: That's fantastic news! I'm really excited about this opportunity. Could you tell me more about the details of the offer, such as the salary, benefits, and starting date? +Interviewer: Of course. The starting salary is [salary details], and we offer a comprehensive +benefits package, including [details about benefits such as healthcare, retirement plan, vacation +days, etc]. If you accept, we'd like you to start on [proposed starting date]. How does that sound? +Interview Candidate: That sounds great, but I would like to take a couple of days to consider the +offer. Could I give you my final answer by [proposed date]? +Interviewer: Absolutely, we understand this is a big decision. Please take your time, and let us know your decision by [agreed date]. +This is a potential scenario, but the actual conversation could differ based on the company, their +processes, and the specifics of the offer. It's always a good idea to take some time to review the +offer and consider it carefully before giving your final answer. + +At this stage, the candidate is typically expected to get back to the company with a decision. Here's how it might proceed: +Interview Candidate (following up on the agreed date): Hello, thank you for giving me some +time to consider the offer. I am pleased to accept the position as a Front-End Engineer at your +company. I am excited about the opportunity and looking forward to contributing to the team. +Interviewer: That's excellent news! We're delighted to welcome you aboard. We'll send you the official offer letter and further details about the onboarding process via email. +Interview Candidate: Great, I'll look forward to receiving those. If there are any tasks or paperwork I need to complete prior to my start date, please let me know. +Interviewer: Absolutely, we will keep you posted. Welcome to the team, and we're looking forward to working with you. + +Interview Candidate: Thank you, I'm excited to start. Have a great day! +Interviewer: You too. Goodbye! + +Remember, once you accept a job offer, it's important to follow up on any next steps and be prepared for your first day. It's also generally a good practice to thank your interviewers for the opportunity, and anyone else who helped you during the application process. +At this point, it seems like we've covered the full process from the initial interview to the final job acceptance. However, here's how the onboarding process might start: +Onboarding Specialist (first day at work): Welcome to your first day! We're glad to have you on our team. Let me give you a brief introduction to our office and your workspace. +Interview Candidate (now Employee): Thank you, I'm excited to be here. +Onboarding Specialist: We have scheduled a series of introductory meetings today with various +team members. These should help you understand your role better and learn how our team +operates. +Employee: Sounds good. I'm eager to meet everyone and get started. +Onboarding Specialist: Great, and please remember, if you have any questions or need any help, +feel free to ask. Everyone here is willing to help you get settled. +Employee: I appreciate that. I'm sure I'll have lots of questions as I get started. +This could be an example of how your first day in your new job might look like. Remember, the +most important thing is to be open, eager to learn, and willing to ask questions when you're unsure. +This will help you adapt more quickly to your new environment. +At this point, it's your first week in the new job and you are getting to know the team and your tasks. Here's an example of how a check-in with your manager might go after your first week: +Manager: Hi, how was your first week? Do you have any questions or is there anything you're unsure about? +Employee: Hi, the first week has been really informative. Everyone has been very welcoming and +helpful. I am getting accustomed to the workflows here. I do have a question about the upcoming +project. Could you tell me more about my role in it? +Manager: Of course. In the upcoming project, you will be responsible for developing the user +interface of our new product feature. You'll be working closely with our UX designers and +back-end engineers. We'll start with a planning meeting where we will go over the project's +requirements in detail. +Employee: That sounds exciting. I'm looking forward to contributing to the project. I will prepare for the planning meeting. +Manager: Excellent! We're glad to have you on the team and are here to support you. If you have any more questions or if there's anything you need, don't hesitate to ask. + +Remember, being proactive, asking questions, and communicating with your team are key to +successfully settling into your new role. It's also important to take the time to understand the +company culture, workflows, and how different team members and departments work together. +After some weeks of getting adjusted and starting your first project, there may be a check-in to see how you're progressing. Here's an example of how that conversation might look: +Manager: Hi, I wanted to check in and see how things are going now that you're a few weeks in. How's the project coming along? +Employee: Hello, thanks for checking in. I'm making good progress on the project. I've managed +to complete the initial front-end design as per the UX mockups and am currently working on +integrating it with the back-end. I've been working closely with the back-end team to ensure +smooth integration. +Manager: That's great to hear. And how are you finding the team and the working environment so far? +Employee: Everyone has been very supportive and collaborative. I appreciate the open +communication culture here; it's made it easier to ask for help when needed and provide input +where I see fit. +Manager: I'm glad to hear that. If there are any issues or if there's anything you need to perform your role more effectively, don't hesitate to let me know. Our goal is to make sure you have the resources and support you need to succeed. +Employee: Thank you, I appreciate that. I'll certainly let you know if I need anything. +Remember, as an employee, it's important to regularly communicate your progress, challenges, +and needs to your manager. This not only helps your manager support you effectively but also +shows your proactive approach and dedication to your work. +At this stage, you're getting more involved in the project and starting to feel comfortable in your +role. You might have meetings with other teams or participate in code reviews. Here's how a +meeting with a UX designer might go: +UX Designer: Hi, I wanted to discuss the design interface you've been working on. Have you had any challenges implementing the design elements? +Employee: Hi, most of the design has been straightforward to implement. However, I've had a bit +of a challenge with the interactive carousel on the homepage. The design is a bit complex and I'm +trying to find a solution that's both user-friendly and efficient to load. +UX Designer: I see. How about we work together to simplify the design while still maintaining the interactive element? We can make a few adjustments that make it easier to implement without sacrificing the user experience. +Employee: That sounds like a great idea. I'm sure we can find a middle ground that works well. +UX Designer: Perfect. Let's schedule a time to work on this together. And always feel free to reach out if you're facing any challenges implementing the designs. + +Employee: Thank you, I appreciate your support. I'm looking forward to working together on this. + +Effective communication and collaboration across teams are crucial in any project. It's important to voice any challenges you're facing and be open to receiving help from others. Everyone brings a +unique skill set to the team and there's always an opportunity to learn from each other. + +As the project progresses, you may find yourself in meetings discussing the development status. Here's an example of a project status meeting: + +Project Manager: Hello team, let's begin our weekly project status meeting. We'll go around and each share updates on our progress. Why don't we start with you? + +Employee: Sure, over the past week, I've made significant progress on the user interface. We've resolved the issues we were having with the interactive carousel on the homepage, thanks to collaborative work with the UX team. Currently, I'm working on optimizing the page load times and ensuring our website is fully responsive and accessible. + +Project Manager: That's excellent progress. Do you foresee any potential issues in the coming week? + +Employee: At the moment, everything is on track. If any issues arise, I'll be sure to communicate them to the team. + +Project Manager: Perfect. Remember, it's important to communicate any blockers as soon as possible, so we can address them and keep the project on track. + +The project progresses with regular updates, issue tracking, and continued collaboration with your team. Remember, clear and regular communication is key in managing and delivering successful projects. + +At this stage, your project is coming to an end and you might have a project wrap-up or review meeting. Here's an example of how it could go: + +Project Manager: Hi team, as we've successfully completed the project, I wanted to conduct a +wrap-up meeting. We'll discuss what went well, what didn't, and what we can improve for future +projects. Let's start with you. + +Employee: Sure. I think the communication within the team was strong and we were quick to +resolve any issues or blockers. The collaboration with the UX team, in particular, was very helpful +in ensuring that we could implement the design elements effectively. + +Project Manager: That's great to hear. And what about areas where we could improve? + +Employee: One area we could potentially improve is in the initial planning phase. We faced some +challenges due to changes in the design midway through the project. Maybe in the future, we +could spend more time in the planning phase to solidify the design and requirements before +starting the implementation. + +Project Manager: That's a good point. We'll aim to enhance our planning process for the next project. Thank you for your feedback. + +Employee: You're welcome. Overall, I think the project was a success and I learned a lot from the experience. + +Project Manager: Thank you for your hard work and dedication. We look forward to more successful projects in the future. + +A project review or wrap-up meeting is a good opportunity to reflect on your work, celebrate +successes, and identify areas for improvement. Always be open to learning from every project, as +it's an opportunity to grow and improve your skills.`; + +export const article = storable('article', baseArticle); +export const translateSentence = storable>('translateSentence', {}); diff --git a/src/lib/stores/brain-store.ts b/src/lib/stores/brain-store.ts index dd8d177..48267da 100644 --- a/src/lib/stores/brain-store.ts +++ b/src/lib/stores/brain-store.ts @@ -2,6 +2,7 @@ import { NanoIndexed } from '$lib/helpers/nano-indexed'; import { storable } from './storable'; export const memoryTap = storable('memory-tab', 'Step'); +export const speechConnect = storable('speech-connect', false); export const speechPeople = storable('speech-people', 'Matthew'); export const speedAudio = storable('speed-audio', 1); export const loopPlay = storable('loop-play', false); diff --git a/src/lib/stores/learn-words.ts b/src/lib/stores/learn-words.ts new file mode 100644 index 0000000..3a9d54b --- /dev/null +++ b/src/lib/stores/learn-words.ts @@ -0,0 +1,3 @@ +import { storable } from './storable'; + +export const learnWorls = storable>('learnWorls', {}); diff --git a/src/lib/stores/storable.ts b/src/lib/stores/storable.ts index 0dae747..1d8180a 100644 --- a/src/lib/stores/storable.ts +++ b/src/lib/stores/storable.ts @@ -1,31 +1,37 @@ import { browser } from '$app/environment'; +import { NanoIndexed } from '$lib/helpers/nano-indexed'; import { get, writable } from 'svelte/store'; +const db = NanoIndexed({ + dbName: 'writeflowy', + store: 'db', +}); + export function storable(key: string, data: T) { const store = writable(data); const { subscribe, set } = store; - if (browser && localStorage[key]) { - try { - const old = JSON.parse(localStorage[key]); - if (old && old.v) { - set(old.v); + if (browser) { + db.get(key).then((res) => { + if (res) { + set(res); } - } catch (err) { - // - } + }); } return { subscribe, set: (n: T) => { - browser && (localStorage[key] = JSON.stringify({ v: n })); + if (browser) { + db.set(key, n); + } set(n); }, update: (cb: (v: T) => T) => { const updatedStore = cb(get(store)); - - browser && (localStorage[key] = JSON.stringify({ v: updatedStore })); + if (browser) { + db.set(key, updatedStore); + } set(updatedStore); }, onInit: () => {}, diff --git a/src/lib/stores/user.ts b/src/lib/stores/user.ts index a9e1d68..cab4c06 100644 --- a/src/lib/stores/user.ts +++ b/src/lib/stores/user.ts @@ -50,6 +50,7 @@ export const user = { if (checkTokenLock) { return; } + await new Promise((res) => setTimeout(res, 500)); checkTokenLock = true; const auth = user.getAuth(); diff --git a/src/routes/article/+page.svelte b/src/routes/article/+page.svelte new file mode 100644 index 0000000..b7198c8 --- /dev/null +++ b/src/routes/article/+page.svelte @@ -0,0 +1,17 @@ + + +
+

{i18n`文章`}

+