Skip to content

Commit

Permalink
feat: feed icon
Browse files Browse the repository at this point in the history
  • Loading branch information
Xstoudi committed Jul 17, 2023
1 parent 3fa1736 commit 35d2fd3
Show file tree
Hide file tree
Showing 22 changed files with 239 additions and 100 deletions.
9 changes: 9 additions & 0 deletions src-tauri/src/commands/fetcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::structs::sync_response::SyncResponse;
use feed_rs::parser;
use crate::enums::feed_type::FeedType;
use crate::structs::article::Article;
use crate::structs::image::Image;

#[tauri::command]
pub fn sync(sync_request: SyncRequest) -> Result<SyncResponse, String> {
Expand All @@ -20,6 +21,14 @@ pub fn sync(sync_request: SyncRequest) -> Result<SyncResponse, String> {
identifier,
feed_type: FeedType::from(feed.feed_type),
articles: feed.entries.into_iter().map(Article::from).collect(),
// image is either feed.logo, feed.icon, or None
image: match feed.logo {
Some(l) => Some(Image::from(l)),
None => match feed.icon {
Some(i) => Some(Image::from(i)),
None => None,
}
}
}),
Err(e) => Err(format!("Error parsing feed: {}", e)),
}
Expand Down
3 changes: 2 additions & 1 deletion src-tauri/src/structs/article.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use chrono::Utc;
use feed_rs::model::Entry;
use serde::Serialize;

#[derive(Debug, serde::Serialize)]
#[derive(Debug, Serialize)]
pub struct Article {
pub id: String,
pub title: String,
Expand Down
22 changes: 22 additions & 0 deletions src-tauri/src/structs/image.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use serde::Serialize;

#[derive(Debug, Serialize)]
pub struct Image {
pub uri: String,
pub title: Option<String>,
pub width: Option<u32>,
pub height: Option<u32>,
pub description: Option<String>,
}

impl From<feed_rs::model::Image> for Image {
fn from(image: feed_rs::model::Image) -> Self {
Image {
uri: image.uri,
title: image.title,
width: image.width,
height: image.height,
description: image.description,
}
}
}
1 change: 1 addition & 0 deletions src-tauri/src/structs/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod article;
pub mod sync_response;
pub mod sync_request;
pub mod image;
7 changes: 5 additions & 2 deletions src-tauri/src/structs/sync_response.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
use serde::Serialize;
use crate::enums::feed_type::FeedType;
use crate::structs::article::Article;
use crate::structs::image::Image;

#[derive(Debug, serde::Serialize)]
#[derive(Debug, Serialize)]
pub struct SyncResponse {
pub identifier: String,
#[serde(rename = "type")]
pub feed_type: FeedType,
pub articles: Vec<Article>
pub articles: Vec<Article>,
pub image: Option<Image>
}
21 changes: 7 additions & 14 deletions src/components/layout/Feed.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import clsx from 'clsx';
import { memo, useCallback, useMemo, MouseEvent } from 'react';
import { FaAtom, FaEdit, FaQuestion, FaRss } from 'react-icons/fa';
import { VscJson } from 'react-icons/vsc';
import { FaEdit } from 'react-icons/fa';
import { useNavigate } from 'react-router-dom';

import useEditMode from '../../hooks/useEditMode';
Expand All @@ -13,16 +12,13 @@ import IFeed from '../../types/Feed';
import Button from '../form/Button';
import { ModalFormContent } from '../modal/AddFeedModal';

import FeedIcon from './FeedIcon';

interface FeedProps extends IFeed {}

function Feed({
identifier,
displayName,
link,
articles,
type,
interval,
}: FeedProps) {
function Feed(props: FeedProps) {
const { identifier, displayName, link, articles, interval } = props;

const view = useView();
const viewDispatch = useViewDispatch();
const navigate = useNavigate();
Expand Down Expand Up @@ -79,10 +75,7 @@ function Feed({
</Button>
)}

{type === 'rss' && <FaRss className="w-6 h-6" />}
{type === 'atom' && <FaAtom className="w-6 h-6" />}
{type === 'json' && <VscJson className="w-6 h-6" />}
{type === null && <FaQuestion className="w-6 h-6" />}
<FeedIcon feed={props} />

<div>{displayName}</div>
</div>
Expand Down
38 changes: 38 additions & 0 deletions src/components/layout/FeedIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import styled from '@emotion/styled';
import { memo } from 'react';
import { FaAtom, FaQuestion, FaRss } from 'react-icons/fa';
import { VscJson } from 'react-icons/vsc';

import usePreference from '../../hooks/usePreference';
import Feed from '../../types/Feed';

interface FeedIconProps {
feed: Feed;
}

const ImageIcon = styled.img`
width: 2rem;
height: 2rem;
border-radius: 50%;
object-fit: cover;
object-position: center;
`;

function FeedIcon({ feed }: FeedIconProps) {
const { type, image } = feed;
const { showFeedIcons } = usePreference();

if (showFeedIcons && image !== null) {
return <ImageIcon src={image.uri} alt={image.description || ''} />;
}

return (
<>
{type === 'rss' && <FaRss className="w-8 h-6" />}
{type === 'atom' && <FaAtom className="w-8 h-6" />}
{type === 'json' && <VscJson className="w-8 h-6" />}
{type === null && <FaQuestion className="w-8 h-6" />}
</>
);
}
export default memo(FeedIcon);
8 changes: 7 additions & 1 deletion src/components/modal/PreferenceModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ function PreferenceModal() {
useModal<PreferenceState>(modalIdentifier);

const defaultForm = useMemo(
() => (isStateEmpty ? { darkMode: false } : state),
() => (isStateEmpty ? { darkMode: false, showFeedIcons: false } : state),
[isStateEmpty, state],
);

Expand Down Expand Up @@ -72,6 +72,12 @@ function PreferenceModal() {
value={form.darkMode}
onChange={(darkMode) => setForm({ ...form, darkMode })}
/>
<Switch
label="Show feed icons"
name="showFeedIcons"
value={form.showFeedIcons}
onChange={(showFeedIcons) => setForm({ ...form, showFeedIcons })}
/>

<div className={clsx('mt-4 flex justify-between flex-row-reverse')}>
<Form.Submit asChild>
Expand Down
8 changes: 3 additions & 5 deletions src/components/utils/BackupManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { invoke } from '@tauri-apps/api';
import { memo, useEffect } from 'react';

import { DataBackup, PreferenceBackup } from '../../data/Backup';
import * as Backup from '../../data/Backup';
import { loadData, loadPreference } from '../../data/load';
import save from '../../data/save';
import useData from '../../hooks/useData';
Expand Down Expand Up @@ -31,10 +32,9 @@ function BackupManager() {

const backupStructure: DataBackup = {
type: 'data',
version: 1,
version: Backup.VERSION,
state: dataState,
};
console.log('save data use effect', JSON.stringify(backupStructure));
save(backupStructure).catch((error) => console.error(error));
}, [dataState]);

Expand All @@ -44,15 +44,13 @@ function BackupManager() {

const backupStructure: PreferenceBackup = {
type: 'preference',
version: 1,
version: Backup.VERSION,
state: preferenceState,
};
console.log('save preference use effect', JSON.stringify(backupStructure));
save(backupStructure).catch((error) => console.error(error));
}, [preferenceState]);

useEffect(() => {
console.log('load use effect');
Promise.all([
loadData().then((backup) => {
if (backup !== null) {
Expand Down
2 changes: 2 additions & 0 deletions src/data/Backup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ export type DataBackup = Backup<'data', DataState>;
export type PreferenceBackup = Backup<'preference', PreferenceState>;

export type AnyBackup = DataBackup | PreferenceBackup;

export const VERSION = 2;
13 changes: 11 additions & 2 deletions src/data/load.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import JSZip from 'jszip';

import { BackupType, DataBackup, PreferenceBackup } from './Backup';
import init from './init';
import save from './save';
import transform from './transformers/transform';

async function load<T>(type: BackupType) {
const db = await init();
Expand All @@ -23,11 +25,18 @@ async function load<T>(type: BackupType) {
export async function loadData() {
const data = await load<DataBackup>('data');
if (data === null) return null;
return parseDataBackup(data);
const parsedData = parseDataBackup(data);
const transformed = transform<DataBackup>(parsedData);
await save(transformed);
return transformed;
}

export async function loadPreference() {
return load<PreferenceBackup>('preference');
const preference = await load<PreferenceBackup>('preference');
if (preference === null) return null;
const transformed = transform<PreferenceBackup>(preference);
await save(transformed);
return transformed;
}

function parseDataBackup(backup: DataBackup) {
Expand Down
20 changes: 20 additions & 0 deletions src/data/transformers/transform.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { AnyBackup, DataBackup, PreferenceBackup } from '../Backup';

import v2 from './v2';

export interface Transformer {
data: (backup: DataBackup) => DataBackup;
preference: (backup: PreferenceBackup) => PreferenceBackup;
}

const transformers: Record<number, Transformer> = {
1: v2,
};

export default function transform<T extends AnyBackup>(backup: T) {
const transformer = transformers[backup.version];
if (!transformer) return backup;
const func = transformer[backup.type] as (backup: AnyBackup) => AnyBackup;
if (!func) throw new Error(`Invalid backup type ${backup.type}`);
return func(backup) as T;
}
25 changes: 25 additions & 0 deletions src/data/transformers/v2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { DataBackup, PreferenceBackup } from '../Backup';

export default {
data: (backup: DataBackup) => {
return {
...backup,
state: {
...backup.state,
feeds: backup.state.feeds.map((feed) => ({
...feed,
image: null,
})),
},
};
},
preference: (backup: PreferenceBackup) => {
return {
...backup,
state: {
...backup.state,
showFeedIcons: true,
},
};
},
};
16 changes: 3 additions & 13 deletions src/hooks/useSync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useCallback } from 'react';

import {
UPDATE_ARTICLES,
UPDATE_CONTENT,
UPDATE_FEED_TYPE,
UPDATE_MULTIPLE_ARTICLES,
UPDATE_MULTIPLE_FEED_TYPE,
Expand Down Expand Up @@ -41,18 +42,8 @@ export default function useSync() {
})
.then((response) => {
dataDispatch({
type: UPDATE_FEED_TYPE,
payload: {
identifier: feed.identifier,
type: response.type,
},
});
dataDispatch({
type: UPDATE_ARTICLES,
payload: {
identifier: feed.identifier,
articles: response.articles.map(articleMapper),
},
type: UPDATE_CONTENT,
payload: response,
});
})
.catch((error) => {
Expand Down Expand Up @@ -87,7 +78,6 @@ export default function useSync() {
type,
})),
});

dataDispatch({
type: UPDATE_MULTIPLE_ARTICLES,
payload: response.map(({ identifier, articles }) => ({
Expand Down
4 changes: 4 additions & 0 deletions src/state/data/DataActionType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ export type DataActionType<Action, Payload = void> = Payload extends void
export const LOAD = 'LOAD';
export const ADD_FEED = 'ADD_FEED';
export const UPDATE_FEED = 'UPDATE_FEED';

export const UPDATE_CONTENT = 'UPDATE_CONTENT';
export const UPDATE_MULTIPLE_CONTENT = 'UPDATE_MULTIPLE_CONTENT';

export const UPDATE_ARTICLES = 'UPDATE_ARTICLES';
export const UPDATE_MULTIPLE_ARTICLES = 'UPDATE_MULTIPLE_ARTICLES';
export const UPDATE_FEED_TYPE = 'UPDATE_FEED_TYPE';
Expand Down

0 comments on commit 35d2fd3

Please sign in to comment.