소스 검색

feat: loading spinner next to artist while loading its albums

Fela Maslen 5 년 전
부모
커밋
7b3f4e0d4b

+ 6 - 6
gmus/src/components/ui/cmus/styled/layout.ts

@@ -11,6 +11,12 @@ export const FlexColumn = styled(FlexRow)`
   flex-flow: column;
 `;
 
+export const NoWrapFill = styled.span`
+  overflow: hidden;
+  text-overflow: ellipsis;
+  width: 100%;
+`;
+
 export const NoWrap = styled.div`
   white-space: nowrap;
 `;
@@ -45,12 +51,6 @@ export const ActiveHighlightRow = styled(FlexRow)<{
 
   white-space: nowrap;
   width: 100%;
-
-  span {
-    overflow: hidden;
-    text-overflow: ellipsis;
-    width: 100%;
-  }
 `;
 
 export const FlexList = styled(FlexColumn)`

+ 28 - 0
gmus/src/components/ui/cmus/styled/spinner.tsx

@@ -0,0 +1,28 @@
+import { rem } from 'polished';
+import styled, { keyframes } from 'styled-components';
+
+const spin = keyframes`
+  0% { content: '⣾' }
+  14.2857% { content: '⣽' }
+  28.5714% { content: '⣻' }
+  42.8571% { content: '⢿' }
+  57.1429% { content: '⡿' }
+  71.4286% { content: '⣟' }
+  85.7143% { content: '⣯' }
+  100% { content: '⣷' }
+`;
+
+export const AsciiSpinner = styled.span`
+  &::after {
+    animation: 1s ${spin} infinite;
+    animation-timing-function: step-end;
+    color: inherit;
+    content: '';
+    display: inline;
+    font-family: Hack, monospace;
+    height: ${rem(16)};
+    line-height: ${rem(16)};
+    margin-right: ${rem(8)};
+    width: auto;
+  }
+`;

+ 1 - 1
gmus/src/components/ui/cmus/views/artists.styles.ts

@@ -11,6 +11,6 @@ export const ArtistTitle = styled(ActiveHighlightRow)``;
 
 export const AlbumTitle = styled(ActiveHighlightRow)`
   span {
-    padding-left: ${rem(16)};
+    padding-left: ${rem(32)};
   }
 `;

+ 9 - 3
gmus/src/components/ui/cmus/views/artists.tsx

@@ -14,6 +14,8 @@ import { NodeComponentProps, NodePublicState } from 'react-vtree/dist/es/Tree';
 import { useArtistsAlbumsAndSongs } from '../../../../hooks/fetch/artists';
 import { artistAlbumsLoaded, artistSongsLoaded } from '../actions';
 import { CmusUIDispatchContext, CmusUIStateContext } from '../reducer';
+import { NoWrapFill } from '../styled/layout';
+import { AsciiSpinner } from '../styled/spinner';
 import { CmusUIState } from '../types';
 import { getScrollIndex } from '../utils/scroll';
 
@@ -28,6 +30,7 @@ type TreeNode = {
   id: string;
   focused: boolean;
   active: boolean;
+  loading?: boolean;
   shouldBeOpen?: boolean;
   children?: TreeNode[];
 };
@@ -53,6 +56,7 @@ function useTreeWalker(
         focused,
         active: activeArtist === artist && activeAlbum === null,
         shouldBeOpen: expandedArtists.includes(artist),
+        loading: !(artist in artistAlbums),
         children:
           artistAlbums[artist]?.map<TreeNode>((album) => ({
             name: album,
@@ -73,6 +77,7 @@ function useTreeWalker(
         active: node.active,
         shouldBeOpen: node.shouldBeOpen,
         isOpenByDefault: !!node.shouldBeOpen,
+        loading: node.loading,
         isArtist,
       },
       node,
@@ -105,7 +110,7 @@ function useTreeWalker(
 }
 
 const Node: React.FC<NodeComponentProps<TreeData, NodePublicState<TreeData>>> = ({
-  data: { name, isArtist, focused, active, shouldBeOpen },
+  data: { name, isArtist, focused, active, shouldBeOpen, loading },
   isOpen,
   setOpen,
   style,
@@ -119,14 +124,15 @@ const Node: React.FC<NodeComponentProps<TreeData, NodePublicState<TreeData>>> =
   if (isArtist) {
     return (
       <Styled.ArtistTitle active={active} parentActive={focused} style={style as CSSProperties}>
-        <span>{name || 'Unknown Artist'}</span>
+        {loading && shouldBeOpen ? <AsciiSpinner /> : <>&nbsp;&nbsp;</>}
+        <NoWrapFill>{name || 'Unknown Artist'}</NoWrapFill>
       </Styled.ArtistTitle>
     );
   }
 
   return (
     <Styled.AlbumTitle active={active} parentActive={focused} style={style as CSSProperties}>
-      <span>{name || 'Unknown Album'}</span>
+      <NoWrapFill>{name || 'Unknown Album'}</NoWrapFill>
     </Styled.AlbumTitle>
   );
 };