├── documents
├── .gitignore
├── en-US
│ └── images
│ │ └── name.png
├── zh-Hans
│ ├── images
│ │ └── name.png
│ └── README.md
└── assets
│ ├── download_from_appstore.png
│ └── download_from_google_play.png
├── app
├── android
│ ├── settings_aar.gradle
│ ├── gradle.properties
│ ├── app
│ │ └── src
│ │ │ ├── main
│ │ │ ├── res
│ │ │ │ ├── mipmap-hdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-mdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── values
│ │ │ │ │ ├── colors.xml
│ │ │ │ │ └── styles.xml
│ │ │ │ ├── drawable-hdpi
│ │ │ │ │ └── ic_launcher_foreground.png
│ │ │ │ ├── drawable-mdpi
│ │ │ │ │ └── ic_launcher_foreground.png
│ │ │ │ ├── drawable-xhdpi
│ │ │ │ │ └── ic_launcher_foreground.png
│ │ │ │ ├── drawable-xxhdpi
│ │ │ │ │ └── ic_launcher_foreground.png
│ │ │ │ ├── drawable-xxxhdpi
│ │ │ │ │ └── ic_launcher_foreground.png
│ │ │ │ ├── mipmap-anydpi-v26
│ │ │ │ │ └── ic_launcher.xml
│ │ │ │ └── drawable
│ │ │ │ │ └── launch_background.xml
│ │ │ └── java
│ │ │ │ └── com
│ │ │ │ └── bangumin
│ │ │ │ └── munin
│ │ │ │ └── MainActivity.java
│ │ │ ├── profile
│ │ │ └── AndroidManifest.xml
│ │ │ └── debug
│ │ │ └── AndroidManifest.xml
│ ├── gradle
│ │ └── wrapper
│ │ │ └── gradle-wrapper.properties
│ ├── settings.gradle
│ └── build.gradle
├── test
│ ├── integration
│ │ └── http
│ │ │ ├── .gitignore
│ │ │ ├── readme.md
│ │ │ └── config.json.example
│ ├── Widget_test.dart
│ ├── shared
│ │ └── utils
│ │ │ ├── common_test.dart
│ │ │ └── misc
│ │ │ └── DeviceInfo_test.dart
│ └── AppCast_test.dart
├── ios
│ ├── Flutter
│ │ ├── .last_build_id
│ │ ├── Uat.xcconfig
│ │ ├── Debug.xcconfig
│ │ ├── Release.xcconfig
│ │ └── AppFrameworkInfo.plist
│ ├── Runner
│ │ ├── Runner-Bridging-Header.h
│ │ ├── Assets.xcassets
│ │ │ ├── Contents.json
│ │ │ ├── LaunchImage.imageset
│ │ │ │ ├── launch_128x128.png
│ │ │ │ ├── launch_256x256.png
│ │ │ │ └── launch_384x384.png
│ │ │ ├── AppIcon.appiconset
│ │ │ │ ├── Icon-App-20x20@1x.png
│ │ │ │ ├── Icon-App-20x20@2x.png
│ │ │ │ ├── Icon-App-20x20@3x.png
│ │ │ │ ├── Icon-App-29x29@1x.png
│ │ │ │ ├── Icon-App-29x29@2x.png
│ │ │ │ ├── Icon-App-29x29@3x.png
│ │ │ │ ├── Icon-App-40x40@1x.png
│ │ │ │ ├── Icon-App-40x40@2x.png
│ │ │ │ ├── Icon-App-40x40@3x.png
│ │ │ │ ├── Icon-App-60x60@2x.png
│ │ │ │ ├── Icon-App-60x60@3x.png
│ │ │ │ ├── Icon-App-76x76@1x.png
│ │ │ │ ├── Icon-App-76x76@2x.png
│ │ │ │ ├── Icon-App-1024x1024@1x.png
│ │ │ │ └── Icon-App-83.5x83.5@2x.png
│ │ │ └── SplashScreenBackgroundColor.colorset
│ │ │ │ └── Contents.json
│ │ ├── AppDelegate.swift
│ │ └── GoogleService-Info.plist
│ ├── build
│ │ └── Runner.build
│ │ │ ├── Debug-iphoneos
│ │ │ └── Runner.build
│ │ │ │ └── dgph
│ │ │ ├── Profile-iphoneos
│ │ │ └── Runner.build
│ │ │ │ └── dgph
│ │ │ ├── Debug-iphonesimulator
│ │ │ └── Runner.build
│ │ │ │ └── dgph
│ │ │ └── Release-production-iphoneos
│ │ │ └── Runner.build
│ │ │ └── dgph
│ ├── Runner.xcodeproj
│ │ └── project.xcworkspace
│ │ │ ├── contents.xcworkspacedata
│ │ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ └── Runner.xcworkspace
│ │ ├── xcshareddata
│ │ ├── WorkspaceSettings.xcsettings
│ │ └── IDEWorkspaceChecks.plist
│ │ └── contents.xcworkspacedata
├── lib
│ ├── widgets
│ │ ├── timeline
│ │ │ ├── message
│ │ │ │ └── Common.dart
│ │ │ └── item
│ │ │ │ ├── UnknownTimelineActivityWidget.dart
│ │ │ │ ├── PublicMessageNoReplyWidget.dart
│ │ │ │ └── common
│ │ │ │ └── Actions.dart
│ │ ├── shared
│ │ │ ├── text
│ │ │ │ ├── editor
│ │ │ │ │ ├── common.dart
│ │ │ │ │ ├── showBangumiStickersBottomSheet.dart
│ │ │ │ │ └── sticker
│ │ │ │ │ │ ├── utils.dart
│ │ │ │ │ │ └── BangumiStickers.dart
│ │ │ │ └── MuninTextSpans.dart
│ │ │ ├── common
│ │ │ │ ├── Divider.dart
│ │ │ │ ├── SingleChildExpandedRow.dart
│ │ │ │ ├── HorizontalScrollableWidget.dart
│ │ │ │ ├── ScaffoldWithRegularAppBar.dart
│ │ │ │ └── SnackBar.dart
│ │ │ ├── utils
│ │ │ │ ├── ExpandedEmpty.dart
│ │ │ │ └── Scroll.dart
│ │ │ ├── bottomsheet
│ │ │ │ └── showMinHeightModalBottomSheet.dart
│ │ │ ├── icons
│ │ │ │ └── MuninSmallTriangle.dart
│ │ │ ├── button
│ │ │ │ ├── customization.dart
│ │ │ │ ├── FilledFlatButton.dart
│ │ │ │ ├── RoundedInkWell.dart
│ │ │ │ └── MuninOutlineButton.dart
│ │ │ ├── background
│ │ │ │ ├── GreyRoundedBorderContainer.dart
│ │ │ │ └── RoundedConcreteBackgroundWithChild.dart
│ │ │ ├── cover
│ │ │ │ └── ClickableCachedRoundedCover.dart
│ │ │ └── chips
│ │ │ │ └── StrokeChip.dart
│ │ ├── discussion
│ │ │ └── thread
│ │ │ │ └── shared
│ │ │ │ ├── Constants.dart
│ │ │ │ └── CopyPostContent.dart
│ │ ├── subject
│ │ │ ├── info
│ │ │ │ └── SubjectInfoBottomSheet.dart
│ │ │ ├── common
│ │ │ │ └── Common.dart
│ │ │ ├── mainpage
│ │ │ │ ├── CharactersPreview.dart
│ │ │ │ └── RelatedSubjectsPreview.dart
│ │ │ └── management
│ │ │ │ └── StarRatingFormField.dart
│ │ ├── initial
│ │ │ └── splash.dart
│ │ ├── search
│ │ │ └── UserSearchResultWidget.dart
│ │ ├── setting
│ │ │ ├── Common.dart
│ │ │ ├── theme
│ │ │ │ └── Common.dart
│ │ │ ├── mute
│ │ │ │ └── HowToAdd.dart
│ │ │ └── general
│ │ │ │ └── BrowserSettingWidget.dart
│ │ ├── home
│ │ │ └── HomePageAppBarTitle.dart
│ │ └── user
│ │ │ └── UserHome.dart
│ ├── models
│ │ └── bangumi
│ │ │ ├── discussion
│ │ │ ├── enums
│ │ │ │ ├── base.dart
│ │ │ │ └── DiscussionType.dart
│ │ │ ├── DiscussionItem.dart
│ │ │ ├── thread
│ │ │ │ └── common
│ │ │ │ │ ├── BangumiThread.g.dart
│ │ │ │ │ ├── GetThreadRequest.dart
│ │ │ │ │ ├── OriginalPost.dart
│ │ │ │ │ └── Post.g.dart
│ │ │ ├── GroupDiscussionItem.dart
│ │ │ ├── GeneralDiscussionItem.dart
│ │ │ └── DiscussionItem.g.dart
│ │ │ ├── common
│ │ │ ├── ChineseNameOwner.dart
│ │ │ └── ItemMetaInfo.dart
│ │ │ ├── timeline
│ │ │ ├── common
│ │ │ │ ├── HyperItem.dart
│ │ │ │ ├── FeedLoadType.dart
│ │ │ │ ├── FeedMetaInfo.dart
│ │ │ │ ├── TimelineFeed.dart
│ │ │ │ ├── HyperImage.dart
│ │ │ │ ├── Mono.dart
│ │ │ │ ├── TimelineFeed.g.dart
│ │ │ │ └── HyperBangumiItem.dart
│ │ │ ├── WikiCreationSingle.dart
│ │ │ ├── ProgressUpdateEpisodeUntil.dart
│ │ │ ├── GroupJoinSingle.dart
│ │ │ ├── BlogCreationSingle.dart
│ │ │ ├── PublicMessageNoReply.dart
│ │ │ ├── IndexFavoriteSingle.dart
│ │ │ ├── UnknownTimelineActivity.dart
│ │ │ ├── ProgressUpdateEpisodeSingle.dart
│ │ │ ├── FriendshipCreationSingle.dart
│ │ │ ├── PublicMessageNormal.dart
│ │ │ ├── MonoFavoriteSingle.dart
│ │ │ ├── CollectionUpdateSingle.dart
│ │ │ └── message
│ │ │ │ └── PublicMessageReply.dart
│ │ │ ├── mono
│ │ │ ├── MonoBase.dart
│ │ │ ├── Actor.dart
│ │ │ └── MonoBase.g.dart
│ │ │ ├── search
│ │ │ ├── result
│ │ │ │ ├── SearchResultItem.dart
│ │ │ │ ├── BangumiSearchResponse.dart
│ │ │ │ ├── SearchResultItem.g.dart
│ │ │ │ ├── BangumiSearchResponse.g.dart
│ │ │ │ └── MonoSearchResult.dart
│ │ │ └── SearchRequest.dart
│ │ │ ├── progress
│ │ │ ├── common
│ │ │ │ ├── InProgressCollection.dart
│ │ │ │ ├── InProgressCollection.g.dart
│ │ │ │ ├── BaseEpisode.dart
│ │ │ │ └── BaseEpisode.g.dart
│ │ │ └── html
│ │ │ │ └── SubjectEpisodes.dart
│ │ │ ├── BangumiOauthCredentials.dart
│ │ │ ├── user
│ │ │ ├── social
│ │ │ │ ├── NetworkServiceTag.dart
│ │ │ │ ├── NetworkServiceTag.g.dart
│ │ │ │ ├── NetworkServiceTagLink.dart
│ │ │ │ └── NetworkServiceTagPlainText.dart
│ │ │ ├── notification
│ │ │ │ ├── BaseNotificationItem.dart
│ │ │ │ ├── BaseNotificationItem.g.dart
│ │ │ │ └── GeneralNotificationItem.dart
│ │ │ └── timeline
│ │ │ │ └── TimelinePreview.dart
│ │ │ ├── subject
│ │ │ ├── review
│ │ │ │ ├── ReviewMetaInfo.dart
│ │ │ │ └── SubjectReview.dart
│ │ │ ├── common
│ │ │ │ ├── SujectBase.dart
│ │ │ │ ├── SubjectStatus.dart
│ │ │ │ ├── SujectBase.g.dart
│ │ │ │ ├── ParentSubject.dart
│ │ │ │ └── SubjectBaseWithCover.dart
│ │ │ ├── RelatedSubject.dart
│ │ │ └── info
│ │ │ │ └── InfoBoxItem.dart
│ │ │ ├── setting
│ │ │ ├── theme
│ │ │ │ └── ThemeSwitchMode.dart
│ │ │ └── general
│ │ │ │ └── browser
│ │ │ │ └── BrowserSetting.dart
│ │ │ ├── BangumiCookieCredentials.dart
│ │ │ └── BangumiUserIdentity.dart
│ ├── config
│ │ ├── .gitignore
│ │ ├── production_with_update_detector_example.dart
│ │ ├── environment_example.dart
│ │ └── upgrader
│ │ │ ├── readme.md
│ │ │ └── Utils.dart
│ ├── providers
│ │ ├── bangumi
│ │ │ ├── readme.md
│ │ │ ├── search
│ │ │ │ └── parser
│ │ │ │ │ └── isolate.dart
│ │ │ ├── util
│ │ │ │ ├── DioInterceptors.dart
│ │ │ │ └── parser
│ │ │ │ │ ├── ParseBangumiUser.dart
│ │ │ │ │ └── Spoiler.dart
│ │ │ └── progress
│ │ │ │ └── parser
│ │ │ │ └── isolate.dart
│ │ ├── README.md
│ │ └── storage
│ │ │ └── SecureStorageService.dart
│ ├── redux
│ │ ├── setting
│ │ │ └── Common.dart
│ │ ├── shared
│ │ │ ├── utils.dart
│ │ │ └── RequestStatus.dart
│ │ ├── oauth
│ │ │ ├── OauthReducer.dart
│ │ │ ├── OauthState.dart
│ │ │ └── OauthActions.dart
│ │ ├── app
│ │ │ └── AppActions.dart
│ │ ├── readme.md
│ │ ├── search
│ │ │ ├── SearchState.dart
│ │ │ └── SearchReducer.dart
│ │ └── progress
│ │ │ └── common.dart
│ ├── styles
│ │ └── theme
│ │ │ ├── CommonThemeData.dart
│ │ │ ├── NightDeepGreyBlue.dart
│ │ │ ├── BrightIonBrownBlue.dart
│ │ │ ├── BrightNatoriPinkBrown.dart
│ │ │ ├── BrightBlueBangumiPink.dart
│ │ │ └── NightPureDarkBlue.dart
│ └── shared
│ │ └── utils
│ │ ├── a11y
│ │ └── common.dart
│ │ ├── analytics
│ │ └── Constants.dart
│ │ ├── http
│ │ └── common.dart
│ │ ├── ui
│ │ └── completers.dart
│ │ └── misc
│ │ ├── async.dart
│ │ └── constants.dart
├── assets
│ ├── stickers
│ │ ├── bangumi
│ │ │ ├── 01.png
│ │ │ ├── 02.png
│ │ │ ├── 03.png
│ │ │ ├── 04.png
│ │ │ ├── 05.png
│ │ │ ├── 06.png
│ │ │ ├── 07.png
│ │ │ ├── 08.png
│ │ │ ├── 09.png
│ │ │ ├── 10.png
│ │ │ ├── 100.gif
│ │ │ ├── 101.gif
│ │ │ ├── 102.gif
│ │ │ ├── 103.gif
│ │ │ ├── 104.gif
│ │ │ ├── 105.gif
│ │ │ ├── 106.gif
│ │ │ ├── 107.gif
│ │ │ ├── 108.gif
│ │ │ ├── 109.gif
│ │ │ ├── 11.gif
│ │ │ ├── 110.gif
│ │ │ ├── 111.gif
│ │ │ ├── 112.gif
│ │ │ ├── 113.gif
│ │ │ ├── 114.gif
│ │ │ ├── 115.gif
│ │ │ ├── 116.gif
│ │ │ ├── 117.gif
│ │ │ ├── 118.gif
│ │ │ ├── 119.gif
│ │ │ ├── 12.png
│ │ │ ├── 120.gif
│ │ │ ├── 121.gif
│ │ │ ├── 122.gif
│ │ │ ├── 123.gif
│ │ │ ├── 13.png
│ │ │ ├── 14.png
│ │ │ ├── 15.png
│ │ │ ├── 16.png
│ │ │ ├── 17.png
│ │ │ ├── 18.png
│ │ │ ├── 19.png
│ │ │ ├── 20.png
│ │ │ ├── 21.png
│ │ │ ├── 22.png
│ │ │ ├── 23.gif
│ │ │ ├── 24.gif
│ │ │ ├── 25.gif
│ │ │ ├── 26.gif
│ │ │ ├── 27.gif
│ │ │ ├── 28.gif
│ │ │ ├── 29.gif
│ │ │ ├── 30.gif
│ │ │ ├── 31.gif
│ │ │ ├── 32.gif
│ │ │ ├── 33.gif
│ │ │ ├── 34.gif
│ │ │ ├── 35.gif
│ │ │ ├── 36.gif
│ │ │ ├── 37.gif
│ │ │ ├── 38.gif
│ │ │ ├── 39.gif
│ │ │ ├── 40.gif
│ │ │ ├── 41.gif
│ │ │ ├── 42.gif
│ │ │ ├── 43.gif
│ │ │ ├── 44.gif
│ │ │ ├── 45.gif
│ │ │ ├── 46.gif
│ │ │ ├── 47.gif
│ │ │ ├── 48.gif
│ │ │ ├── 49.gif
│ │ │ ├── 50.gif
│ │ │ ├── 51.gif
│ │ │ ├── 52.gif
│ │ │ ├── 53.gif
│ │ │ ├── 54.gif
│ │ │ ├── 55.gif
│ │ │ ├── 56.gif
│ │ │ ├── 57.gif
│ │ │ ├── 58.gif
│ │ │ ├── 59.gif
│ │ │ ├── 60.gif
│ │ │ ├── 61.gif
│ │ │ ├── 62.gif
│ │ │ ├── 63.gif
│ │ │ ├── 64.gif
│ │ │ ├── 65.gif
│ │ │ ├── 66.gif
│ │ │ ├── 67.gif
│ │ │ ├── 68.gif
│ │ │ ├── 69.gif
│ │ │ ├── 70.gif
│ │ │ ├── 71.gif
│ │ │ ├── 72.gif
│ │ │ ├── 73.gif
│ │ │ ├── 74.gif
│ │ │ ├── 75.gif
│ │ │ ├── 76.gif
│ │ │ ├── 77.gif
│ │ │ ├── 78.gif
│ │ │ ├── 79.gif
│ │ │ ├── 80.gif
│ │ │ ├── 81.gif
│ │ │ ├── 82.gif
│ │ │ ├── 83.gif
│ │ │ ├── 84.gif
│ │ │ ├── 85.gif
│ │ │ ├── 86.gif
│ │ │ ├── 87.gif
│ │ │ ├── 88.gif
│ │ │ ├── 89.gif
│ │ │ ├── 90.gif
│ │ │ ├── 91.gif
│ │ │ ├── 92.gif
│ │ │ ├── 93.gif
│ │ │ ├── 94.gif
│ │ │ ├── 95.gif
│ │ │ ├── 96.gif
│ │ │ ├── 97.gif
│ │ │ ├── 98.gif
│ │ │ └── 99.gif
│ │ └── readme.md
│ └── logo
│ │ ├── munin_logo_full_2048x2048.png
│ │ ├── munin_logo_full_512x512.png
│ │ └── munin_logo_padding_rounded_2048x2048.png
├── fonts
│ └── icons
│ │ └── munin
│ │ └── MuninIcons.ttf
├── document
│ └── release
│ │ ├── 0.3.1
│ │ └── images
│ │ │ ├── card.jpg
│ │ │ ├── mute.jpg
│ │ │ ├── theme.jpg
│ │ │ ├── progress.jpg
│ │ │ ├── timeline.jpg
│ │ │ ├── discussion.jpg
│ │ │ ├── launch_switch.jpg
│ │ │ └── general_setting.jpg
│ │ └── 0.4.2
│ │ └── images
│ │ ├── sort.jpg
│ │ └── notification.jpg
└── .metadata
├── .gitignore
├── LICENSE
└── README.md
/documents/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 |
--------------------------------------------------------------------------------
/app/android/settings_aar.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/app/test/integration/http/.gitignore:
--------------------------------------------------------------------------------
1 | config.json
2 |
--------------------------------------------------------------------------------
/app/ios/Flutter/.last_build_id:
--------------------------------------------------------------------------------
1 | 9cf3cfc1a955eae8e1fc249bd5d89508
--------------------------------------------------------------------------------
/app/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
--------------------------------------------------------------------------------
/app/lib/widgets/timeline/message/Common.dart:
--------------------------------------------------------------------------------
1 | const onReplySuccessText = '回复成功';
2 |
--------------------------------------------------------------------------------
/app/lib/widgets/shared/text/editor/common.dart:
--------------------------------------------------------------------------------
1 | const editorMinLines = 10;
2 | const editorMaxLines = 100;
3 |
--------------------------------------------------------------------------------
/documents/en-US/images/name.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/documents/en-US/images/name.png
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/01.png
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/02.png
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/03.png
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/04.png
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/05.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/05.png
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/06.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/06.png
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/07.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/07.png
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/08.png
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/09.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/09.png
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/10.png
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/100.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/100.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/101.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/101.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/102.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/102.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/103.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/103.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/104.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/104.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/105.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/105.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/106.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/106.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/107.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/107.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/108.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/108.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/109.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/109.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/11.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/11.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/110.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/110.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/111.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/111.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/112.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/112.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/113.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/113.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/114.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/114.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/115.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/115.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/116.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/116.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/117.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/117.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/118.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/118.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/119.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/119.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/12.png
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/120.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/120.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/121.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/121.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/122.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/122.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/123.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/123.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/13.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/13.png
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/14.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/14.png
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/15.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/15.png
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/16.png
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/17.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/17.png
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/18.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/18.png
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/19.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/19.png
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/20.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/20.png
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/21.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/21.png
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/22.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/22.png
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/23.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/23.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/24.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/24.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/25.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/25.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/26.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/26.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/27.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/27.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/28.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/28.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/29.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/29.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/30.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/30.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/31.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/31.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/32.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/32.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/33.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/33.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/34.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/34.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/35.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/35.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/36.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/36.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/37.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/37.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/38.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/38.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/39.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/39.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/40.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/40.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/41.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/41.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/42.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/42.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/43.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/43.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/44.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/44.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/45.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/45.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/46.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/46.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/47.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/47.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/48.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/48.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/49.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/49.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/50.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/50.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/51.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/51.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/52.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/52.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/53.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/53.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/54.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/54.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/55.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/55.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/56.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/56.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/57.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/57.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/58.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/58.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/59.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/59.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/60.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/60.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/61.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/61.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/62.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/62.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/63.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/63.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/64.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/64.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/65.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/65.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/66.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/66.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/67.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/67.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/68.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/68.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/69.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/69.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/70.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/70.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/71.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/71.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/72.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/72.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/73.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/73.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/74.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/74.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/75.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/75.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/76.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/76.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/77.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/77.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/78.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/78.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/79.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/79.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/80.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/80.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/81.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/81.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/82.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/82.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/83.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/83.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/84.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/84.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/85.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/85.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/86.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/86.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/87.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/87.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/88.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/88.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/89.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/89.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/90.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/90.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/91.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/91.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/92.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/92.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/93.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/93.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/94.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/94.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/95.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/95.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/96.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/96.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/97.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/97.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/98.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/98.gif
--------------------------------------------------------------------------------
/app/assets/stickers/bangumi/99.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/stickers/bangumi/99.gif
--------------------------------------------------------------------------------
/app/ios/Runner/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info": {
3 | "version": 1,
4 | "author": "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/app/lib/models/bangumi/discussion/enums/base.dart:
--------------------------------------------------------------------------------
1 | abstract class DiscussionFilter {
2 | String get chineseName;
3 | }
4 |
--------------------------------------------------------------------------------
/documents/zh-Hans/images/name.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/documents/zh-Hans/images/name.png
--------------------------------------------------------------------------------
/app/fonts/icons/munin/MuninIcons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/fonts/icons/munin/MuninIcons.ttf
--------------------------------------------------------------------------------
/app/lib/widgets/discussion/thread/shared/Constants.dart:
--------------------------------------------------------------------------------
1 | const userWithDefaultAvatarCannotPostReplyLabel = '未设置过头像的用户无法正常发表回复';
2 |
--------------------------------------------------------------------------------
/app/document/release/0.3.1/images/card.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/document/release/0.3.1/images/card.jpg
--------------------------------------------------------------------------------
/app/document/release/0.3.1/images/mute.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/document/release/0.3.1/images/mute.jpg
--------------------------------------------------------------------------------
/app/document/release/0.4.2/images/sort.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/document/release/0.4.2/images/sort.jpg
--------------------------------------------------------------------------------
/app/assets/logo/munin_logo_full_2048x2048.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/logo/munin_logo_full_2048x2048.png
--------------------------------------------------------------------------------
/app/assets/logo/munin_logo_full_512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/logo/munin_logo_full_512x512.png
--------------------------------------------------------------------------------
/app/document/release/0.3.1/images/theme.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/document/release/0.3.1/images/theme.jpg
--------------------------------------------------------------------------------
/documents/assets/download_from_appstore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/documents/assets/download_from_appstore.png
--------------------------------------------------------------------------------
/app/document/release/0.3.1/images/progress.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/document/release/0.3.1/images/progress.jpg
--------------------------------------------------------------------------------
/app/document/release/0.3.1/images/timeline.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/document/release/0.3.1/images/timeline.jpg
--------------------------------------------------------------------------------
/app/ios/build/Runner.build/Debug-iphoneos/Runner.build/dgph:
--------------------------------------------------------------------------------
1 | DGPH1.04Oct 21 202221:59:43 / Users edward code BangumiN app ios
--------------------------------------------------------------------------------
/app/ios/build/Runner.build/Profile-iphoneos/Runner.build/dgph:
--------------------------------------------------------------------------------
1 | DGPH1.04Dec 14 202110:05:20 / Users edward code BangumiN app ios
--------------------------------------------------------------------------------
/documents/assets/download_from_google_play.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/documents/assets/download_from_google_play.png
--------------------------------------------------------------------------------
/app/android/gradle.properties:
--------------------------------------------------------------------------------
1 | android.enableJetifier=true
2 | android.useAndroidX=true
3 | org.gradle.jvmargs=-Xmx1536M
4 | android.enableR8=true
5 |
--------------------------------------------------------------------------------
/app/document/release/0.3.1/images/discussion.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/document/release/0.3.1/images/discussion.jpg
--------------------------------------------------------------------------------
/app/document/release/0.4.2/images/notification.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/document/release/0.4.2/images/notification.jpg
--------------------------------------------------------------------------------
/app/ios/build/Runner.build/Debug-iphonesimulator/Runner.build/dgph:
--------------------------------------------------------------------------------
1 | DGPH1.04Oct 21 202221:59:43 / Users edward code BangumiN app ios
--------------------------------------------------------------------------------
/app/document/release/0.3.1/images/launch_switch.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/document/release/0.3.1/images/launch_switch.jpg
--------------------------------------------------------------------------------
/app/ios/build/Runner.build/Release-production-iphoneos/Runner.build/dgph:
--------------------------------------------------------------------------------
1 | DGPH1.04Oct 21 202221:59:43 / Users edward code BangumiN app ios
--------------------------------------------------------------------------------
/app/document/release/0.3.1/images/general_setting.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/document/release/0.3.1/images/general_setting.jpg
--------------------------------------------------------------------------------
/app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/assets/logo/munin_logo_padding_rounded_2048x2048.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/assets/logo/munin_logo_padding_rounded_2048x2048.png
--------------------------------------------------------------------------------
/app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/android/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFF09199
4 |
--------------------------------------------------------------------------------
/app/lib/config/.gitignore:
--------------------------------------------------------------------------------
1 | *.dart
2 | !application.dart
3 | !environment_example.dart
4 | !production_with_update_detector_example.dart
5 | !/upgrader/
6 | !/upgrader/Utils.dart
7 |
--------------------------------------------------------------------------------
/app/assets/stickers/readme.md:
--------------------------------------------------------------------------------
1 | ### Sticker assets
2 |
3 | Note that bangumi stickers have different naming extensions, changing file extensions
4 | will break code that reads stickers.
--------------------------------------------------------------------------------
/app/ios/Flutter/Uat.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
2 | #include "Generated.xcconfig"
3 |
4 | FLUTTER_TARGET=lib/config/uat.dart
5 |
--------------------------------------------------------------------------------
/app/lib/widgets/shared/common/Divider.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | Divider onePixelHeightDivider() {
4 | return Divider(
5 | height: 1,
6 | );
7 | }
8 |
--------------------------------------------------------------------------------
/app/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/ios/Runner/Assets.xcassets/LaunchImage.imageset/launch_128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/ios/Runner/Assets.xcassets/LaunchImage.imageset/launch_128x128.png
--------------------------------------------------------------------------------
/app/ios/Runner/Assets.xcassets/LaunchImage.imageset/launch_256x256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/ios/Runner/Assets.xcassets/LaunchImage.imageset/launch_256x256.png
--------------------------------------------------------------------------------
/app/ios/Runner/Assets.xcassets/LaunchImage.imageset/launch_384x384.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/ios/Runner/Assets.xcassets/LaunchImage.imageset/launch_384x384.png
--------------------------------------------------------------------------------
/app/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
2 | #include "Generated.xcconfig"
3 |
4 | FLUTTER_TARGET=lib/config/development.dart
5 |
--------------------------------------------------------------------------------
/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
--------------------------------------------------------------------------------
/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
--------------------------------------------------------------------------------
/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
--------------------------------------------------------------------------------
/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
--------------------------------------------------------------------------------
/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/app/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
2 | #include "Generated.xcconfig"
3 |
4 | FLUTTER_TARGET=lib/config/production.dart
5 |
--------------------------------------------------------------------------------
/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
--------------------------------------------------------------------------------
/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardez/BangumiN/HEAD/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/app/android/app/src/main/java/com/bangumin/munin/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.bangumin.munin;
2 |
3 | import io.flutter.embedding.android.FlutterActivity;
4 |
5 | public class MainActivity extends FlutterActivity {
6 | }
7 |
--------------------------------------------------------------------------------
/app/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/lib/providers/bangumi/readme.md:
--------------------------------------------------------------------------------
1 | # Html parser naming convention:
2 |
3 | * Expose top level method, all methods that parse sub element should be non `pubic` or
4 | `@VisibleForTesting`
5 |
6 | * Name top level method as `processXXXXXX`
--------------------------------------------------------------------------------
/app/lib/models/bangumi/common/ChineseNameOwner.dart:
--------------------------------------------------------------------------------
1 | /// A mixin that can be used to indicate an entity has both an original
2 | /// name and a Chinese name.
3 | abstract class ChineseNameOwner {
4 | String get name;
5 |
6 | String get chineseName;
7 | }
8 |
--------------------------------------------------------------------------------
/app/test/integration/http/readme.md:
--------------------------------------------------------------------------------
1 | # Http integration test
2 |
3 | The purpose of http integration test is to verify that munin can correctly makes http call.
4 | This is especially important for most cookie-based http call since we're mocking browser actions.
--------------------------------------------------------------------------------
/app/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/lib/providers/README.md:
--------------------------------------------------------------------------------
1 | ### Intro
2 |
3 | A list of providers that provide required data by sending Http Request
4 |
5 | ### Credits
6 |
7 | We are parsing Bangumi WebPage to obtain some data, and some parsers are rewritten based on https://github.com/ekibun/Bangumi
8 |
--------------------------------------------------------------------------------
/app/lib/config/production_with_update_detector_example.dart:
--------------------------------------------------------------------------------
1 | import 'package:munin/config/production.dart';
2 |
3 | void main() => ExampleProductionWithUpdateDetector();
4 |
5 | class ExampleProductionWithUpdateDetector extends Production {
6 | final shouldCheckUpdate = true;
7 | }
8 |
--------------------------------------------------------------------------------
/app/lib/widgets/subject/info/SubjectInfoBottomSheet.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class SubjectInfoBottomSheet extends StatelessWidget {
4 | @override
5 | Widget build(BuildContext context) {
6 | // TODO: implement build
7 | return null;
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/app/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sat Oct 03 01:00:15 PDT 2020
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
7 |
--------------------------------------------------------------------------------
/app/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/app/lib/widgets/initial/splash.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class InitialSplashPage extends StatelessWidget {
4 | @override
5 | Widget build(BuildContext context) {
6 | return Scaffold(
7 | body: Center(
8 | child: Text('加载中...'),
9 | ),
10 | );
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/app/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/lib/widgets/shared/utils/ExpandedEmpty.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | // an empty expanded widget that can be used to fill up space
4 | class ExpandedEmpty extends StatelessWidget {
5 | @override
6 | Widget build(BuildContext context) {
7 | return Expanded(child: Container());
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/app/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/lib/redux/setting/Common.dart:
--------------------------------------------------------------------------------
1 | /// [Screen.brightness] returns a value in range [0,1], it needs to be converted
2 | /// to a int percentage in range [0, 100]
3 | int roundDeviceBrightnessToPercentage(double deviceBrightness) {
4 | assert(deviceBrightness >= 0.0 && deviceBrightness <= 1.0);
5 |
6 | return (deviceBrightness * 100).round();
7 | }
8 |
--------------------------------------------------------------------------------
/app/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: 8661d8aecd626f7f57ccbcb735553edc05a2e713
8 | channel: stable
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/app/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/lib/config/environment_example.dart:
--------------------------------------------------------------------------------
1 | import 'package:munin/config/application.dart';
2 |
3 | void main() => EnvironmentExample();
4 |
5 | class EnvironmentExample extends Application {
6 | final environmentType = EnvironmentType.Development;
7 | final bangumiOauthClientIdentifier = '1';
8 | final bangumiOauthClientSecret = '2';
9 | final bangumiRedirectUrl = '3';
10 | }
11 |
--------------------------------------------------------------------------------
/app/lib/redux/shared/utils.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:flutter_redux/flutter_redux.dart';
3 | import 'package:munin/redux/app/AppState.dart';
4 | import 'package:redux/redux.dart';
5 |
6 | Store findStore(BuildContext context) =>
7 | StoreProvider.of(context);
8 |
9 | AppState findAppState(BuildContext context) => findStore(context).state;
10 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/timeline/common/HyperItem.dart:
--------------------------------------------------------------------------------
1 | import 'package:munin/models/bangumi/timeline/common/BangumiContent.dart';
2 |
3 | /// A base interface for all BangumiContent types that have an id, a type and a page Url
4 | abstract class HyperItem {
5 | /// id of the hyper text
6 | String get id;
7 |
8 | /// content type
9 | BangumiContent get contentType;
10 |
11 | String get pageUrl;
12 | }
13 |
--------------------------------------------------------------------------------
/app/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/mono/MonoBase.dart:
--------------------------------------------------------------------------------
1 | import 'package:built_value/built_value.dart';
2 |
3 | part 'MonoBase.g.dart';
4 |
5 | @BuiltValue(instantiable: false)
6 | abstract class MonoBase {
7 | int get id;
8 |
9 | @nullable
10 | @BuiltValueField(wireName: 'url')
11 | String get pageUrl;
12 |
13 | String get name;
14 |
15 | MonoBase rebuild(void updates(MonoBaseBuilder b));
16 |
17 | MonoBaseBuilder toBuilder();
18 | }
19 |
--------------------------------------------------------------------------------
/app/test/Widget_test.dart:
--------------------------------------------------------------------------------
1 | // This is a basic Flutter widget test.
2 | //
3 | // To perform an interaction with a widget in your test, use the WidgetTester
4 | // utility that Flutter provides. For example, you can send tap and scroll
5 | // gestures. You can also use WidgetTester to find child widgets in the widget
6 | // tree, read text, and verify that the values of widget properties are correct.
7 |
8 |
9 |
10 | void main() {
11 | }
12 |
--------------------------------------------------------------------------------
/app/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/test/integration/http/config.json.example:
--------------------------------------------------------------------------------
1 | {
2 | "bangumiOauthCredentials": {
3 | "accessToken": "",
4 | "refreshToken": "",
5 | "tokenEndpoint": "",
6 | "scopes": [],
7 | "expiration": 1
8 | },
9 | "bangumiCookieCredentials": {
10 | "authCookie": "",
11 | "sessionCookie": "",
12 | "userAgent": "",
13 | "expiresOn": 1
14 | },
15 | "bangumiOauthClientIdentifier": "",
16 | "bangumiOauthClientSecret": ""
17 | }
--------------------------------------------------------------------------------
/app/lib/widgets/search/UserSearchResultWidget.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:munin/models/bangumi/search/SearchRequest.dart';
3 |
4 | class UserSearchResultWidget extends StatelessWidget {
5 | final SearchRequest searchRequest;
6 |
7 | const UserSearchResultWidget({Key key, @required this.searchRequest})
8 | : super(key: key);
9 |
10 | @override
11 | Widget build(BuildContext context) {
12 | return null;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/app/lib/widgets/setting/Common.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:munin/styles/theme/Common.dart';
3 | import 'package:munin/widgets/shared/icons/AdaptiveIcons.dart';
4 |
5 | Icon selectedOptionTrailingIcon(BuildContext context, {IconData iconData}) {
6 | if (iconData == null) {
7 | iconData = AdaptiveIcons.doneIconData;
8 | }
9 |
10 | return Icon(
11 | iconData,
12 | color: lightPrimaryDarkAccentColor(context),
13 | );
14 | }
15 |
--------------------------------------------------------------------------------
/app/ios/Runner/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Flutter
3 |
4 | @UIApplicationMain
5 | @objc class AppDelegate: FlutterAppDelegate {
6 | override func application(
7 | _ application: UIApplication,
8 | didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?
9 | ) -> Bool {
10 | GeneratedPluginRegistrant.register(with: self)
11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/app/lib/styles/theme/CommonThemeData.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | BottomSheetThemeData muninBottomSheetThemeData({pureDartTheme = false}) {
4 | return BottomSheetThemeData(
5 | shape: RoundedRectangleBorder(
6 | borderRadius: BorderRadius.only(
7 | topLeft: Radius.circular(8.0),
8 | topRight: Radius.circular(8.0),
9 | ),
10 | ),
11 | elevation: 5.0,
12 | backgroundColor: pureDartTheme ? Colors.grey[850] : null,
13 | );
14 | }
15 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/timeline/common/FeedLoadType.dart:
--------------------------------------------------------------------------------
1 | enum FeedLoadType {
2 | /// feeds are currently empty, it's the initial load
3 | Initial,
4 |
5 | /// trying to load an older feed
6 | Older,
7 |
8 | /// trying to load feed between a gap(i.e. feed1,feed2....feed12,feed13, wants to load feed3-feed11),
9 | /// currently not in use, supporting Gap loading is non-trivial and might require
10 | /// Bangumi's official API
11 | Gap,
12 |
13 | /// trying to load a newer feed
14 | Newer,
15 | }
16 |
--------------------------------------------------------------------------------
/app/lib/widgets/shared/utils/Scroll.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/widgets.dart';
2 |
3 | void scrollPrimaryScrollControllerToTop(BuildContext context) {
4 | if (PrimaryScrollController.of(context) != null &&
5 | PrimaryScrollController.of(context).hasClients) {
6 | /// eyeballed values from `_handleStatusBarTap` in `scaffold.dart`
7 | PrimaryScrollController.of(context).animateTo(0.0,
8 | duration: const Duration(milliseconds: 500),
9 | curve: Curves.linearToEaseOut);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/app/lib/shared/utils/a11y/common.dart:
--------------------------------------------------------------------------------
1 | import 'dart:math' as math;
2 | import 'dart:ui';
3 |
4 | /// Computes color ratio according to
5 | /// http://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef
6 | double computeContrastRatio(
7 | Color foreground,
8 | Color background,
9 | ) {
10 | final double lightness1 = foreground.computeLuminance() + 0.05;
11 | final double lightness2 = background.computeLuminance() + 0.05;
12 | return math.max(lightness1, lightness2) / math.min(lightness1, lightness2);
13 | }
14 |
--------------------------------------------------------------------------------
/app/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/app/lib/widgets/setting/theme/Common.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:munin/widgets/setting/Common.dart';
3 | import 'package:munin/widgets/shared/icons/AdaptiveIcons.dart';
4 |
5 | Icon buildTrailingIcon(BuildContext context, T t1, T t2,
6 | {IconData iconData}) {
7 | if (iconData == null) {
8 | iconData = AdaptiveIcons.doneIconData;
9 | }
10 | if (t1 == t2) {
11 | return selectedOptionTrailingIcon(context, iconData: iconData);
12 | } else {
13 | return null;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/app/lib/widgets/shared/bottomsheet/showMinHeightModalBottomSheet.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | showMinHeightModalBottomSheet(BuildContext context, List children) {
4 | return showModalBottomSheet(
5 | context: context,
6 | builder: (innerContext) {
7 | return SafeArea(
8 | child: Column(
9 | mainAxisSize: MainAxisSize.min,
10 | crossAxisAlignment: CrossAxisAlignment.start,
11 | children: children,
12 | ),
13 | );
14 | });
15 | }
16 |
--------------------------------------------------------------------------------
/app/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
4 |
5 | def plugins = new Properties()
6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
7 | if (pluginsFile.exists()) {
8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
9 | }
10 |
11 | plugins.each { name, path ->
12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
13 | include ":$name"
14 | project(":$name").projectDir = pluginDirectory
15 | }
16 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/common/ItemMetaInfo.dart:
--------------------------------------------------------------------------------
1 | import 'package:built_value/built_value.dart';
2 | import 'package:munin/models/bangumi/common/BangumiImage.dart';
3 |
4 | /// a base TimelineFeed interface
5 | abstract class ItemMetaInfo {
6 | /// Updated at epoch time in milliseconds
7 | int get updatedAt;
8 |
9 | /// user nick name
10 | String get nickName;
11 |
12 | /// User avatars
13 | BangumiImage get avatar;
14 |
15 | /// user name, can only have digit and alphabetic
16 | String get username;
17 |
18 | @nullable
19 | String get actionName;
20 | }
21 |
--------------------------------------------------------------------------------
/app/lib/widgets/shared/common/SingleChildExpandedRow.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | /// A widget that has a row layout and a single child inside a
4 | /// [Expanded].
5 | class SingleChildExpandedRow extends StatelessWidget {
6 | final Widget child;
7 |
8 | const SingleChildExpandedRow({Key key, @required this.child})
9 | : super(key: key);
10 |
11 | @override
12 | Widget build(BuildContext context) {
13 | return Row(
14 | children: [
15 | Expanded(
16 | child: child,
17 | )
18 | ],
19 | );
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/app/lib/config/upgrader/readme.md:
--------------------------------------------------------------------------------
1 | Upgrade configuration for user who installed the app from a non-store environment.
2 |
3 | To indicate an update is a critical one in xml
4 | ```xml
5 | -
6 | Version 0.3.3
7 | 修复崩溃
8 | 2019/07/11 20:00
9 |
10 |
11 |
12 |
14 |
15 | ```
--------------------------------------------------------------------------------
/app/lib/redux/oauth/OauthReducer.dart:
--------------------------------------------------------------------------------
1 | import 'package:munin/redux/oauth/OauthActions.dart';
2 | import 'package:munin/redux/oauth/OauthState.dart';
3 | import 'package:redux/redux.dart';
4 |
5 | final oauthReducers = combineReducers([
6 | TypedReducer(oAuthLoginFailureReducer),
7 | ]);
8 |
9 | // TODO: figure out whether it's needed to reset error message
10 | OauthState oAuthLoginFailureReducer(
11 | OauthState oauthState, OAuthLoginFailure oAuthLoginFailure) {
12 | return oauthState.rebuild((b) => b
13 | ..showLoginErrorSnackBar = true
14 | ..oauthFailureMessage = oAuthLoginFailure.message);
15 | }
16 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/search/result/SearchResultItem.dart:
--------------------------------------------------------------------------------
1 | import 'package:built_value/built_value.dart';
2 | import 'package:munin/models/bangumi/common/BangumiImage.dart';
3 | import 'package:munin/models/bangumi/search/SearchType.dart';
4 |
5 | part 'SearchResultItem.g.dart';
6 |
7 | @BuiltValue(instantiable: false)
8 | abstract class SearchResultItem {
9 | @BuiltValueField(wireName: 'images')
10 | @nullable
11 | BangumiImage get image;
12 |
13 | String get name;
14 |
15 | int get id;
16 |
17 | SearchType get type;
18 |
19 | SearchResultItem rebuild(void updates(SearchResultItemBuilder b));
20 |
21 | SearchResultItemBuilder toBuilder();
22 | }
23 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/progress/common/InProgressCollection.dart:
--------------------------------------------------------------------------------
1 | import 'package:built_value/built_value.dart';
2 | import 'package:munin/models/bangumi/progress/common/InProgressSubjectInfo.dart';
3 |
4 | part 'InProgressCollection.g.dart';
5 |
6 | @BuiltValue(instantiable: false)
7 | abstract class InProgressCollection {
8 | /// The last time user touched this subject
9 | @BuiltValueField(wireName: 'lasttouch')
10 | int get userUpdatedAt;
11 |
12 | @BuiltValueField(wireName: 'subject')
13 | InProgressSubjectInfo get subject;
14 |
15 | InProgressCollection rebuild(void updates(InProgressCollectionBuilder b));
16 |
17 | InProgressCollectionBuilder toBuilder();
18 | }
19 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/timeline/common/FeedMetaInfo.dart:
--------------------------------------------------------------------------------
1 | import 'package:built_value/built_value.dart';
2 | import 'package:built_value/serializer.dart';
3 | import 'package:munin/models/bangumi/common/BangumiImage.dart';
4 | import 'package:munin/models/bangumi/common/ItemMetaInfo.dart';
5 |
6 | part 'FeedMetaInfo.g.dart';
7 |
8 | abstract class FeedMetaInfo
9 | implements ItemMetaInfo, Built {
10 | /// Bangumi feed id.
11 | int get feedId;
12 |
13 | FeedMetaInfo._();
14 |
15 | factory FeedMetaInfo([updates(FeedMetaInfoBuilder b)]) = _$FeedMetaInfo;
16 |
17 | static Serializer get serializer => _$feedMetaInfoSerializer;
18 | }
19 |
--------------------------------------------------------------------------------
/app/test/shared/utils/common_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:munin/shared/utils/common.dart';
2 | import 'package:test/test.dart';
3 |
4 | void main() {
5 | group('firstNChars', () {
6 | test('Adds trailing string.', () {
7 | expect(firstNChars('12345', 4, trailingOverflowText: '...'), '1...');
8 | });
9 |
10 | test(
11 | 'Skips input if firstN is too small and trailingOverflowText is too large',
12 | () {
13 | expect(firstNChars('12345', 2, trailingOverflowText: '...'), '..');
14 | });
15 |
16 | test('Skips stripping input if firstN is longer than input', () {
17 | expect(firstNChars('12345', 100, trailingOverflowText: '...'), '12345');
18 | });
19 | });
20 | }
21 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/mono/Actor.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:built_value/built_value.dart';
4 | import 'package:built_value/serializer.dart';
5 | import 'package:munin/models/bangumi/mono/MonoBase.dart';
6 | import 'package:munin/shared/utils/serializers.dart';
7 |
8 | part 'Actor.g.dart';
9 |
10 | abstract class Actor implements Built, MonoBase {
11 | Actor._();
12 |
13 | factory Actor([updates(ActorBuilder b)]) = _$Actor;
14 |
15 | static Actor fromJson(String jsonString) {
16 | return serializers.deserializeWith(
17 | Actor.serializer, json.decode(jsonString));
18 | }
19 |
20 | static Serializer get serializer => _$actorSerializer;
21 | }
22 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/BangumiOauthCredentials.dart:
--------------------------------------------------------------------------------
1 | class BangumiOauthCredentials {
2 | // hard-code some pre-defined values for readability
3 | static final String tokenEndpoint = 'https://bgm.tv/oauth/access_token';
4 |
5 | String accessToken;
6 | String refreshToken;
7 | int expiration;
8 |
9 | BangumiOauthCredentials.fromJson(Map json)
10 | : accessToken = json['accessToken'],
11 | refreshToken = json['accessToken'],
12 | expiration = json['accessToken'];
13 |
14 | Map toJson() => {
15 | 'accessToken': accessToken,
16 | 'refreshToken': refreshToken,
17 | 'expiration': expiration,
18 | 'tokenEndpoint': tokenEndpoint,
19 | };
20 | }
21 |
--------------------------------------------------------------------------------
/app/lib/widgets/shared/icons/MuninSmallTriangle.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:munin/widgets/shared/icons/MuninIcons.dart';
3 |
4 | class MuninSmallTriangle extends StatelessWidget {
5 | /// Size of the icon, if unset, a special tiny size is used(see code).
6 | final double size;
7 |
8 | const MuninSmallTriangle({
9 | Key key,
10 | this.size,
11 | }) : super(key: key);
12 |
13 | @override
14 | Widget build(BuildContext context) {
15 | return Icon(
16 | MuninIcons.muninRoundedTriangle,
17 | size: size ??
18 | Theme.of(context).textTheme.subtitle1.fontSize *
19 | MediaQuery.of(context).textScaleFactor /
20 | 4.2,
21 | );
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | /dist
5 | /tmp
6 | /out-tsc
7 |
8 | # dependencies
9 | /node_modules
10 |
11 | # IDEs and editors
12 | /.idea
13 | .project
14 | .classpath
15 | .c9/
16 | *.launch
17 | .settings/
18 | *.sublime-workspace
19 |
20 | # IDE - VSCode
21 | .vscode/*
22 | !.vscode/settings.json
23 | !.vscode/tasks.json
24 | !.vscode/launch.json
25 | !.vscode/extensions.json
26 |
27 | # misc
28 | /.sass-cache
29 | /connect.lock
30 | /coverage
31 | /libpeerconnection.log
32 | npm-debug.log
33 | testem.log
34 | /typings
35 |
36 | # e2e
37 | /e2e/*.js
38 | /e2e/*.map
39 |
40 | # System Files
41 | .DS_Store
42 | Thumbs.db
43 |
44 | /conf
45 |
46 | **/.ruby-version
--------------------------------------------------------------------------------
/app/lib/models/bangumi/user/social/NetworkServiceTag.dart:
--------------------------------------------------------------------------------
1 | import 'package:built_value/built_value.dart';
2 | import 'package:munin/models/bangumi/user/social/NetworkServiceType.dart';
3 |
4 | part 'NetworkServiceTag.g.dart';
5 |
6 | @BuiltValue(instantiable: false)
7 | abstract class NetworkServiceTag {
8 | NetworkServiceType get type;
9 |
10 | /// Content that's after this tag, this content is defined by user
11 | /// i.e. [PSN ID]: abcde
12 | /// Here content is abcde
13 | /// If the tag contains a hyperlink, then content is value of this hyperlink
14 | /// And the value is stored in [NetworkServiceTagLink.link]
15 | String get content;
16 |
17 | /// Whether value of the tag is a link or plain text
18 | bool get isLink;
19 | }
20 |
--------------------------------------------------------------------------------
/app/lib/providers/bangumi/search/parser/isolate.dart:
--------------------------------------------------------------------------------
1 | import 'dart:collection';
2 |
3 | import 'package:meta/meta.dart';
4 | import 'package:munin/models/bangumi/search/SearchType.dart';
5 | import 'package:munin/models/bangumi/search/result/MonoSearchResult.dart';
6 | import 'package:munin/providers/bangumi/search/parser/MonoSearchParser.dart';
7 |
8 | LinkedHashMap processMonoSearch(
9 | ParseMonoSearchMessage message) {
10 | return MonoSearchParser()
11 | .processMonoSearch(message.html, searchType: message.searchType);
12 | }
13 |
14 | class ParseMonoSearchMessage {
15 | final String html;
16 |
17 | final SearchType searchType;
18 |
19 | const ParseMonoSearchMessage(this.html, {@required this.searchType});
20 | }
21 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/user/notification/BaseNotificationItem.dart:
--------------------------------------------------------------------------------
1 | import 'package:built_value/built_value.dart';
2 | import 'package:munin/models/bangumi/common/BangumiUserBasic.dart';
3 |
4 | part 'BaseNotificationItem.g.dart';
5 |
6 | @BuiltValue(instantiable: false)
7 | abstract class BaseNotificationItem {
8 | /// ID of the notification item.
9 | int get id;
10 |
11 | /// User who initiated this notification.
12 | BangumiUserBasic get initiator;
13 |
14 | /// Body content of the notification in html, initiator is not included.
15 | /// For example: "在你的日志 test 中发表了新回复"
16 | String get bodyContentHtml;
17 |
18 | BaseNotificationItem rebuild(void updates(BaseNotificationItemBuilder b));
19 |
20 | BaseNotificationItemBuilder toBuilder();
21 | }
22 |
--------------------------------------------------------------------------------
/app/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.6.10'
3 |
4 | repositories {
5 | google()
6 | jcenter()
7 | }
8 |
9 | dependencies {
10 | classpath 'com.android.tools.build:gradle:4.2.2'
11 | classpath 'com.google.gms:google-services:4.3.3'
12 | classpath 'com.google.firebase:firebase-crashlytics-gradle:2.2.0'
13 | }
14 | }
15 |
16 | allprojects {
17 | repositories {
18 | google()
19 | jcenter()
20 | }
21 | }
22 |
23 | rootProject.buildDir = '../build'
24 | subprojects {
25 | project.buildDir = "${rootProject.buildDir}/${project.name}"
26 | }
27 | subprojects {
28 | project.evaluationDependsOn(':app')
29 | }
30 |
31 | task clean(type: Delete) {
32 | delete rootProject.buildDir
33 | }
34 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/discussion/enums/DiscussionType.dart:
--------------------------------------------------------------------------------
1 | import 'package:built_collection/built_collection.dart';
2 | import 'package:built_value/built_value.dart';
3 | import 'package:built_value/serializer.dart';
4 |
5 | part 'DiscussionType.g.dart';
6 |
7 | class DiscussionType extends EnumClass {
8 | /// web: https://bgm.tv/rakuen/topiclist
9 | static const DiscussionType Rakuen = _$Rakuen;
10 |
11 | /// web: https://bgm.tv/group
12 | static const DiscussionType Group = _$Group;
13 |
14 | const DiscussionType._(String name) : super(name);
15 |
16 | static BuiltSet get values => _$values;
17 |
18 | static DiscussionType valueOf(String name) => _$valueOf(name);
19 |
20 | static Serializer get serializer =>
21 | _$discussionTypeSerializer;
22 | }
23 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/timeline/common/TimelineFeed.dart:
--------------------------------------------------------------------------------
1 | import 'package:built_value/built_value.dart';
2 | import 'package:munin/models/bangumi/timeline/common/BangumiContent.dart';
3 | import 'package:munin/models/bangumi/timeline/common/FeedMetaInfo.dart';
4 |
5 | part 'TimelineFeed.g.dart';
6 |
7 | /// a base TimelineFeed interface
8 | @BuiltValue(instantiable: false)
9 | abstract class TimelineFeed {
10 | FeedMetaInfo get user;
11 |
12 | BangumiContent get bangumiContent;
13 |
14 | /// To keep a complete timeline, muted user feeds will still be stored
15 | /// but not shown to user, this is controlled by `isFromMutedUser` field
16 | @nullable
17 | bool get isFromMutedUser;
18 |
19 | TimelineFeed rebuild(void updates(TimelineFeedBuilder b));
20 |
21 | TimelineFeedBuilder toBuilder();
22 | }
23 |
--------------------------------------------------------------------------------
/app/lib/widgets/setting/mute/HowToAdd.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter/widgets.dart';
3 | import 'package:munin/widgets/shared/dialog/common.dart';
4 |
5 | showHowToAddUserDialog(BuildContext context) {
6 | showMuninSingleActionDialog(context,
7 | title: Text('如何添加想屏蔽的用户'),
8 | content: Text('在app内前往用户主页,点击"屏蔽用户"按钮进行添加。'
9 | '你也可以点击屏蔽页面下方'
10 | '的"导入已绝交用户"来导入已绝交的用户。\n'
11 | '由于Bangumi的限制,目前暂时无法以搜索用户名的方式进行添加。\n\n'
12 | '屏蔽作用于除通知以外的地方(Bangumi已经提供了可以屏蔽通知的"绝交"功能。)'));
13 | }
14 |
15 | showHowToAddGroupDialog(BuildContext context) {
16 | showMuninSingleActionDialog(context,
17 | title: Text('如何添加想屏蔽的小组'),
18 | content: Text('在app内长按帖子图标会弹出"屏蔽此小组"选项,选择后即可屏蔽。\n'
19 | '由于Bangumi的限制,目前暂时无法以搜索小组名的方式进行添加。'));
20 | }
21 |
--------------------------------------------------------------------------------
/app/lib/providers/bangumi/util/DioInterceptors.dart:
--------------------------------------------------------------------------------
1 | import 'package:dio/dio.dart';
2 | import 'package:munin/shared/exceptions/exceptions.dart';
3 |
4 | class BangumiCookieExpirationCheckInterceptor extends InterceptorsWrapper {
5 | /// [DateTime] that current cookie should expire.
6 | DateTime _expiresOn;
7 |
8 | set expiresOn(DateTime expiresOn) {
9 | _expiresOn = expiresOn;
10 | }
11 |
12 | BangumiCookieExpirationCheckInterceptor({DateTime expiresOn}) {
13 | this._expiresOn = expiresOn;
14 | }
15 |
16 | @override
17 | void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
18 | if (_expiresOn != null && DateTime.now().isAfter(_expiresOn)) {
19 | throw AuthenticationExpiredException('认证已过期');
20 | }
21 |
22 | return super.onRequest(options, handler);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/lib/widgets/shared/button/customization.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | /// A theme that specifies a small button
4 | /// Note: it doesn't align with Material standard spec and should be used
5 | /// as less as possible
6 | /// If height is not supplied, default value is twice the height of body1 fontSize
7 | ButtonThemeData smallButtonTheme(
8 | BuildContext context, {
9 | double minWidth = 48.0,
10 | double height,
11 | padding: const EdgeInsets.all(0),
12 | }) {
13 | ButtonThemeData defaultButtonTheme = Theme.of(context).buttonTheme;
14 |
15 | ButtonThemeData modifiedButtonTheme = defaultButtonTheme.copyWith(
16 | minWidth: minWidth,
17 | height: height ?? Theme.of(context).textTheme.bodyText2.fontSize * 2,
18 | padding: padding,
19 | );
20 |
21 | return modifiedButtonTheme;
22 | }
23 |
--------------------------------------------------------------------------------
/app/lib/shared/utils/analytics/Constants.dart:
--------------------------------------------------------------------------------
1 | class LoginElapsedTimeEvent {
2 | static const name = 'login_elapsed_time';
3 | static const afterPostLoginCredentials =
4 | 'after_post_login_credentials_milliseconds';
5 | static const afterPostOauthCredentials =
6 | 'after_post_oauth_credentials_milliseconds';
7 | static const afterGetUserProfile = 'after_get_user_profile_milliseconds';
8 | static const totalMilliSeconds = 'total_millisecondss';
9 | }
10 |
11 | class LoginErrorEvent {
12 | static const name = 'login_error';
13 | }
14 |
15 | class InstallUpdatePromptEvent {
16 | static const name = 'install_critical_update_prompt';
17 | static const agreeToInstall = 'agree_to_install';
18 | static const refuseToInstall = 'refuse_to_install';
19 | static const criticalUpdateVersion = 'critical_update_version';
20 | }
21 |
--------------------------------------------------------------------------------
/app/lib/providers/bangumi/util/parser/ParseBangumiUser.dart:
--------------------------------------------------------------------------------
1 | import 'package:html/dom.dart';
2 | import 'package:munin/models/bangumi/common/BangumiImage.dart';
3 | import 'package:munin/models/bangumi/common/BangumiUserBasic.dart';
4 | import 'package:munin/providers/bangumi/util/utils.dart';
5 |
6 | BangumiUserBasic parseBangumiUserBasic(Element element) {
7 | final imageUrl = imageUrlFromBackgroundImage(element);
8 |
9 | final userNameElement = element.querySelector('a[href*="/user/"].l');
10 | final nickname = userNameElement.text;
11 |
12 | final username = parseHrefId(userNameElement);
13 | return BangumiUserBasic(
14 | (b) => b
15 | ..nickname = nickname
16 | ..username = username
17 | ..avatar.replace(BangumiImage.fromImageUrl(
18 | imageUrl, ImageSize.Unknown, ImageType.UserAvatar)),
19 | );
20 | }
21 |
--------------------------------------------------------------------------------
/app/lib/widgets/shared/text/editor/showBangumiStickersBottomSheet.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:munin/widgets/shared/common/MuninPadding.dart';
3 | import 'package:munin/widgets/shared/text/editor/sticker/BangumiStickers.dart';
4 |
5 | showBangumiStickersBottomSheet(
6 | BuildContext context, Function(int id) onStickerTapped) {
7 | return showModalBottomSheet(
8 | context: context,
9 | builder: (innerContext) {
10 | return ListView(
11 | children: [
12 | ListTile(
13 | title: Text('插入一个表情'),
14 | ),
15 | MuninPadding.vertical1xOffset(
16 | child: BangumiStickers(onStickerTapped: onStickerTapped),
17 | denseHorizontal: true,
18 | )
19 | ],
20 | );
21 | });
22 | }
23 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/subject/review/ReviewMetaInfo.dart:
--------------------------------------------------------------------------------
1 | import 'package:built_value/built_value.dart';
2 | import 'package:built_value/serializer.dart';
3 | import 'package:munin/models/bangumi/collection/CollectionStatus.dart';
4 | import 'package:munin/models/bangumi/common/BangumiImage.dart';
5 | import 'package:munin/models/bangumi/common/ItemMetaInfo.dart';
6 |
7 | part 'ReviewMetaInfo.g.dart';
8 |
9 | abstract class ReviewMetaInfo
10 | implements ItemMetaInfo, Built {
11 | @nullable
12 | double get score;
13 |
14 | @nullable
15 | CollectionStatus get collectionStatus;
16 |
17 | ReviewMetaInfo._();
18 |
19 | factory ReviewMetaInfo([updates(ReviewMetaInfoBuilder b)]) = _$ReviewMetaInfo;
20 |
21 | static Serializer get serializer =>
22 | _$reviewMetaInfoSerializer;
23 | }
24 |
--------------------------------------------------------------------------------
/app/lib/providers/bangumi/util/parser/Spoiler.dart:
--------------------------------------------------------------------------------
1 | import 'package:html/dom.dart';
2 | import 'package:munin/shared/utils/common.dart';
3 |
4 | /// Removes bangumi spoiler text style then adds a
5 | /// [MuninCustomHtmlClasses.muninSpoiler] to it.
6 | ///
7 | /// Bangumi defines spoiler as a span that has `background-color:#555`, then
8 | /// uses js event listener to add hover effect, it won't work for us since we
9 | /// don't have event listener, hence this workaround is needed.
10 | DocumentFragment addSpoilerAttribute(
11 | DocumentFragment document,
12 | ) {
13 | final elements =
14 | document.querySelectorAll('span[style^="background-color:#555"]');
15 | for (var element in elements) {
16 | element.attributes.remove('style');
17 | element.classes.add(MuninCustomHtmlClasses.muninSpoiler);
18 | }
19 |
20 | return document;
21 | }
22 |
--------------------------------------------------------------------------------
/app/lib/widgets/shared/button/FilledFlatButton.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:munin/styles/theme/Common.dart';
3 |
4 | class FilledFlatButton extends StatelessWidget {
5 | final VoidCallback onPressed;
6 |
7 | final Widget child;
8 |
9 | const FilledFlatButton({
10 | Key key,
11 | @required this.onPressed,
12 | this.child,
13 | }) : super(key: key);
14 |
15 | @override
16 | Widget build(BuildContext context) {
17 | return TextButton(
18 | style: TextButton.styleFrom(
19 | foregroundColor: Colors.white,
20 | backgroundColor: lightPrimaryDarkAccentColor(context),
21 | disabledBackgroundColor:
22 | lightPrimaryDarkAccentColor(context).withOpacity(0.1),
23 | ),
24 | onPressed: this.onPressed,
25 | child: child,
26 | );
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/subject/common/SujectBase.dart:
--------------------------------------------------------------------------------
1 | import 'package:built_value/built_value.dart';
2 | import 'package:munin/models/bangumi/common/ChineseNameOwner.dart';
3 |
4 | part 'SujectBase.g.dart';
5 |
6 | @BuiltValue(instantiable: false)
7 | abstract class SubjectBase with ChineseNameOwner {
8 | @nullable
9 | @BuiltValueField(wireName: 'id')
10 | int get id;
11 |
12 | /// This value is from Bangumi API, it might be null if data is obtained by
13 | /// html parser. And even api might not return this value.
14 | @nullable
15 | @BuiltValueField(wireName: 'url')
16 | String get pageUrlFromApi;
17 |
18 | String get name;
19 |
20 | @nullable
21 | @BuiltValueField(wireName: 'name_cn')
22 | String get chineseName;
23 |
24 | SubjectBase rebuild(void updates(SubjectBaseBuilder b));
25 |
26 | SubjectBaseBuilder toBuilder();
27 | }
28 |
--------------------------------------------------------------------------------
/app/lib/widgets/shared/text/editor/sticker/utils.dart:
--------------------------------------------------------------------------------
1 | /// Utils for calculating sticker attributes based on sticker id.
2 |
3 | /// Gets sticker file extension.
4 | /// The first 22 stickers(except 11) are png, others are gif.
5 | String _idToStickerExtension(int id) {
6 | if (id <= 22 && id != 11) {
7 | return 'png';
8 | }
9 |
10 | return 'gif';
11 | }
12 |
13 | /// Bangumi names all stickers with id<10 as '01', '02'.. hence they need a
14 | /// padding, otherwise returns its original number in string format.
15 | String _idToBangumiName(int id) {
16 | return '${id.toString().padLeft(2, '0')}';
17 | }
18 |
19 | String idToFullFileName(int id) {
20 | return '${_idToBangumiName(id)}.${_idToStickerExtension(id)}';
21 | }
22 |
23 | /// Sticker code: (bgm01) ... (bgm23)
24 | String idToBangumiStickerCode(int id) {
25 | return '(bgm${_idToBangumiName(id)})';
26 | }
27 |
--------------------------------------------------------------------------------
/app/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | MinimumOSVersion
24 | 14.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/app/lib/config/upgrader/Utils.dart:
--------------------------------------------------------------------------------
1 | import 'package:upgrader/upgrader.dart';
2 |
3 | appCastToString(AppcastItem appCastItem) {
4 | return 'AppcastItem{title: ${appCastItem.title}, \n'
5 | 'dateString: ${appCastItem.dateString}, \n'
6 | 'itemDescription: ${appCastItem.itemDescription}, \n'
7 | 'releaseNotesURL: ${appCastItem.releaseNotesURL}, \n'
8 | 'minimumSystemVersion: ${appCastItem.minimumSystemVersion}, \n'
9 | 'maximumSystemVersion: ${appCastItem.maximumSystemVersion}, \n'
10 | 'fileURL: ${appCastItem.fileURL}, \n'
11 | 'contentLength: ${appCastItem.contentLength}, \n'
12 | 'versionString: ${appCastItem.versionString}, \n'
13 | 'osString: ${appCastItem.osString}, \n'
14 | 'displayVersionString: ${appCastItem.displayVersionString}, \n'
15 | 'infoURL: ${appCastItem.infoURL}, \n'
16 | 'tags: ${appCastItem.tags}}';
17 | }
18 |
--------------------------------------------------------------------------------
/app/ios/Runner/Assets.xcassets/SplashScreenBackgroundColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info": {
3 | "version": 1,
4 | "author": "xcode"
5 | },
6 | "colors": [
7 | {
8 | "idiom": "universal",
9 | "color": {
10 | "color-space": "srgb",
11 | "components": {
12 | "red": "1.000",
13 | "alpha": "1.000",
14 | "blue": "1.000",
15 | "green": "1.000"
16 | }
17 | }
18 | },
19 | {
20 | "idiom": "universal",
21 | "appearances": [
22 | {
23 | "appearance": "luminosity",
24 | "value": "dark"
25 | }
26 | ],
27 | "color": {
28 | "color-space": "srgb",
29 | "components": {
30 | "red": "0.000",
31 | "alpha": "1.000",
32 | "blue": "0.000",
33 | "green": "0.000"
34 | }
35 | }
36 | }
37 | ]
38 | }
--------------------------------------------------------------------------------
/app/lib/widgets/shared/common/HorizontalScrollableWidget.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class HorizontalScrollableWidget extends StatelessWidget {
4 | final List horizontalList;
5 |
6 | final double listHeight;
7 |
8 | final ScrollPhysics physics;
9 |
10 | const HorizontalScrollableWidget({
11 | Key key,
12 | @required this.horizontalList,
13 | @required this.listHeight,
14 | this.physics,
15 | }) : super(key: key);
16 |
17 | @override
18 | Widget build(BuildContext context) {
19 | return Container(
20 | height: listHeight,
21 | child: ListView.builder(
22 | physics: physics,
23 | scrollDirection: Axis.horizontal,
24 | itemBuilder: (BuildContext context, index) {
25 | return horizontalList[index];
26 | },
27 | itemCount: horizontalList.length,
28 | ),
29 | );
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/lib/widgets/home/HomePageAppBarTitle.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:munin/widgets/shared/button/FlatButtonWithTrailingIcon.dart';
3 | import 'package:munin/widgets/shared/icons/MuninSmallTriangle.dart';
4 |
5 | /// A common appbar title displayed across all home screens.
6 | class HomePageAppBarTitle extends StatelessWidget {
7 | final String titleText;
8 |
9 | /// The callback that is called when the button is tapped or otherwise activated.
10 | final VoidCallback onPressed;
11 |
12 | const HomePageAppBarTitle({
13 | Key key,
14 | @required this.titleText,
15 | @required this.onPressed,
16 | }) : super(key: key);
17 |
18 | @override
19 | Widget build(BuildContext context) {
20 | return FlatButtonWithTrailingIcon(
21 | onPressed: onPressed,
22 | label: Text(titleText),
23 | icon: MuninSmallTriangle(),
24 | );
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/lib/shared/utils/http/common.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io' show ContentType;
2 |
3 | /// Determines whether the status code is a canonical http status 2xx code
4 | bool is2xxCode(int code) {
5 | return code != null && code >= 200 && code < 300;
6 | }
7 |
8 | /// Bangumi sometimes doesn't use regular http status code
9 | /// Instead it always returns http status code 200, and in the json there is a
10 | /// `code` field which contains the actual status code
11 | /// if this `code` field is null, we assume it also means successful
12 | bool isBangumi2xxCode(int code) {
13 | return code != null && code >= 200 && code < 300;
14 | }
15 |
16 | bool isBangumiWebPageOkResponse(dynamic decodedResponse) {
17 | return decodedResponse is Map && decodedResponse['status'] == 'ok';
18 | }
19 |
20 | class ExtraContentType {
21 | static final xWwwFormUrlencoded =
22 | ContentType.parse("application/x-www-form-urlencoded");
23 | }
24 |
--------------------------------------------------------------------------------
/app/lib/shared/utils/ui/completers.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:munin/widgets/shared/common/SnackBar.dart';
5 |
6 | /// a completer helper util to show snack bar with a error dialog if this completer fails
7 | /// Note: even shouldPop is set to true, pop may not happen since `maybePop` is used
8 | Completer snackBarCompleter(BuildContext context, String message,
9 | {bool shouldPop = false}) {
10 | final Completer completer = Completer();
11 |
12 | completer.future.then((_) {
13 | if (shouldPop) {
14 | Navigator.of(context).maybePop();
15 | }
16 | showTextOnSnackBar(context, message);
17 | }).catchError((Object error) {
18 | showDialog(
19 | context: context,
20 | builder: (BuildContext context) {
21 | return error;
22 | });
23 | });
24 |
25 | return completer;
26 | }
27 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/search/result/BangumiSearchResponse.dart:
--------------------------------------------------------------------------------
1 | import 'package:built_collection/built_collection.dart';
2 | import 'package:built_value/built_value.dart';
3 | import 'package:munin/models/bangumi/search/result/SearchResultItem.dart';
4 |
5 | part 'BangumiSearchResponse.g.dart';
6 |
7 | @BuiltValue(instantiable: false)
8 | abstract class BangumiSearchResponse {
9 | int get totalCount;
10 |
11 | @nullable
12 | int get requestedResults;
13 |
14 | @nullable
15 | BuiltMap get results;
16 |
17 | /// seems like there is no better way in built_value to specify a getter
18 | /// in interface
19 | @memoized
20 | bool get hasReachedEnd {
21 | throw UnsupportedError('Sub concrete class need to implement this getter');
22 | }
23 |
24 | BangumiSearchResponse rebuild(void updates(BangumiSearchResponseBuilder b));
25 |
26 | BangumiSearchResponseBuilder toBuilder();
27 | }
28 |
--------------------------------------------------------------------------------
/app/lib/redux/app/AppActions.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:munin/redux/app/AppState.dart';
3 | import 'package:munin/redux/app/BasicAppState.dart';
4 |
5 | class PersistAppStateAction {
6 | /// Whether only [BasicAppState] should be persisted
7 | final bool basicAppStateOnly;
8 |
9 | /// An optional pass-ed in [AppState]. If not set, current [AppState] in
10 | /// [Store] will be used.
11 | final AppState appState;
12 |
13 | PersistAppStateAction({
14 | this.basicAppStateOnly = false,
15 | this.appState,
16 | });
17 | }
18 |
19 | class HandleErrorAction {
20 | final BuildContext context;
21 | final Object error;
22 | final StackTrace stack;
23 |
24 | final bool showErrorMessageSnackBar;
25 |
26 | HandleErrorAction({
27 | @required this.context,
28 | @required this.error,
29 | this.stack,
30 | this.showErrorMessageSnackBar = true,
31 | });
32 | }
33 |
--------------------------------------------------------------------------------
/app/lib/shared/utils/misc/async.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:munin/shared/exceptions/utils.dart';
4 |
5 | Completer immediateFinishCompleter() {
6 | Completer completer = Completer();
7 | completer.complete();
8 | return completer;
9 | }
10 |
11 | /// Completes [completer] if it's not completed it.
12 | ///
13 | /// It's useful as a sanity check to complete dangling completer since Calling
14 | /// [Completer.complete] or [Completer.completeError] must be done at most once.
15 | completeDanglingCompleter(Completer completer) {
16 | if (completer != null && !completer.isCompleted) {
17 | completer.complete();
18 | }
19 | }
20 |
21 | completeWithErrorAndReport(error,
22 | Completer completer, {
23 | StackTrace stack,
24 | }) {
25 | print(error.toString());
26 | print(stack);
27 | reportError(error, stack: stack);
28 | completer.completeError(error, stack);
29 | }
30 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/subject/common/SubjectStatus.dart:
--------------------------------------------------------------------------------
1 | import 'package:built_collection/built_collection.dart';
2 | import 'package:built_value/built_value.dart';
3 | import 'package:built_value/serializer.dart';
4 |
5 | part 'SubjectStatus.g.dart';
6 |
7 | /// Status of a subject on bangumi.
8 | class SubjectStatus extends EnumClass {
9 | static const SubjectStatus Unknown = _$Unknown;
10 |
11 | static const SubjectStatus Normal = _$Normal;
12 |
13 | /// Subject is locked. User is not allowed to modify collection status
14 | /// for this subject.
15 | static const SubjectStatus LockedForCollection = _$LockedForCollection;
16 |
17 | const SubjectStatus._(String name) : super(name);
18 |
19 | static BuiltSet get values => _$values;
20 |
21 | static SubjectStatus valueOf(String name) => _$valueOf(name);
22 |
23 | static Serializer get serializer => _$subjectStatusSerializer;
24 | }
25 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/timeline/WikiCreationSingle.dart:
--------------------------------------------------------------------------------
1 | import 'package:built_value/built_value.dart';
2 | import 'package:built_value/serializer.dart';
3 | import 'package:munin/models/bangumi/timeline/common/BangumiContent.dart';
4 | import 'package:munin/models/bangumi/timeline/common/FeedMetaInfo.dart';
5 | import 'package:munin/models/bangumi/timeline/common/TimelineFeed.dart';
6 |
7 | part 'WikiCreationSingle.g.dart';
8 |
9 | abstract class WikiCreationSingle
10 | implements
11 | Built,
12 | TimelineFeed {
13 | FeedMetaInfo get user;
14 |
15 | String get newItemName;
16 |
17 | String get newItemId;
18 |
19 | WikiCreationSingle._();
20 |
21 | factory WikiCreationSingle([updates(WikiCreationSingleBuilder b)]) =
22 | _$WikiCreationSingle;
23 |
24 | static Serializer get serializer =>
25 | _$wikiCreationSingleSerializer;
26 | }
27 |
--------------------------------------------------------------------------------
/app/lib/widgets/shared/background/GreyRoundedBorderContainer.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:munin/styles/theme/Common.dart';
3 |
4 | class GreyRoundedBorderContainer extends StatelessWidget {
5 | final Widget child;
6 |
7 | /// The amount of space by which to inset the child.
8 | final EdgeInsetsGeometry childPadding;
9 |
10 | const GreyRoundedBorderContainer({
11 | Key key,
12 | @required this.child,
13 | this.childPadding = const EdgeInsets.all(baseOffset),
14 | }) : super(key: key);
15 |
16 | @override
17 | Widget build(BuildContext context) {
18 | return Container(
19 | decoration: BoxDecoration(
20 | border: Border.all(color: Theme.of(context).textTheme.caption.color),
21 | borderRadius: defaultContainerCircularRadius),
22 | child: Padding(
23 | child: child,
24 | padding: childPadding,
25 | ),
26 | );
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/app/lib/widgets/user/UserHome.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:munin/models/bangumi/setting/general/PreferredLaunchNavTab.dart';
3 | import 'package:munin/redux/shared/utils.dart';
4 | import 'package:munin/widgets/shared/appbar/OneMuninBar.dart';
5 |
6 | import 'UserProfileWidget.dart';
7 |
8 | class UserHome extends StatelessWidget {
9 | const UserHome({Key key}) : super(key: key);
10 |
11 | @override
12 | Widget build(BuildContext context) {
13 | return UserProfileWidget(
14 | username: findAppState(context).currentAuthenticatedUserBasicInfo
15 | .username,
16 | appBar: OneMuninBar(
17 | title: Text(
18 | PreferredLaunchNavTab.HomePage.generalSettingPageChineseName,
19 | style: Theme
20 | .of(context)
21 | .textTheme
22 | .button,
23 | ),
24 | addNotificationWidget: true,
25 | ),
26 | );
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/mono/MonoBase.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'MonoBase.dart';
4 |
5 | // **************************************************************************
6 | // BuiltValueGenerator
7 | // **************************************************************************
8 |
9 | abstract class MonoBaseBuilder {
10 | void replace(MonoBase other);
11 | void update(void Function(MonoBaseBuilder) updates);
12 | int get id;
13 | set id(int id);
14 |
15 | String get pageUrl;
16 | set pageUrl(String pageUrl);
17 |
18 | String get name;
19 | set name(String name);
20 | }
21 |
22 | // ignore_for_file: always_put_control_body_on_new_line,always_specify_types,annotate_overrides,avoid_annotating_with_dynamic,avoid_as,avoid_catches_without_on_clauses,avoid_returning_this,lines_longer_than_80_chars,omit_local_variable_types,prefer_expression_function_bodies,sort_constructors_first,test_types_in_equals,unnecessary_const,unnecessary_new
23 |
--------------------------------------------------------------------------------
/app/lib/providers/bangumi/progress/parser/isolate.dart:
--------------------------------------------------------------------------------
1 | import 'dart:collection';
2 |
3 | import 'package:munin/models/bangumi/progress/api/EpisodeProgress.dart';
4 | import 'package:munin/models/bangumi/progress/common/EpisodeStatus.dart';
5 | import 'package:munin/models/bangumi/progress/html/SubjectEpisodes.dart';
6 | import 'package:munin/providers/bangumi/progress/parser/ProgressParser.dart';
7 |
8 | LinkedHashMap> processProgressPreview(
9 | String rawHtml) {
10 | return ProgressParser().processProgressPreview(rawHtml);
11 | }
12 |
13 | SubjectEpisodes processSubjectEpisodes(ParseSubjectEpisodesMessage message) {
14 | return ProgressParser()
15 | .processSubjectEpisodes(message.html, message.touchedEpisodes);
16 | }
17 |
18 | class ParseSubjectEpisodesMessage {
19 | final String html;
20 |
21 | final Map touchedEpisodes;
22 |
23 | const ParseSubjectEpisodesMessage(this.html, this.touchedEpisodes);
24 | }
25 |
--------------------------------------------------------------------------------
/app/lib/styles/theme/NightDeepGreyBlue.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:munin/styles/theme/Common.dart';
3 | import 'package:munin/styles/theme/CommonThemeData.dart';
4 |
5 | final ThemeData nightDeepGreyBlueThemeData = ThemeData(
6 | brightness: Brightness.dark,
7 | bottomSheetTheme: muninBottomSheetThemeData(),
8 | buttonTheme: ButtonThemeData(
9 | shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)),
10 | ),
11 | dialogTheme: DialogTheme(
12 | shape: RoundedRectangleBorder(borderRadius: defaultContainerCircularRadius),
13 | ),
14 | sliderTheme: SliderThemeData.fromPrimaryColors(
15 | primaryColor: Colors.lightBlueAccent,
16 | primaryColorDark: Colors.lightBlueAccent.shade700,
17 | valueIndicatorTextStyle: ThemeData().textTheme.bodyText1,
18 | primaryColorLight: Colors.lightBlueAccent.shade100,
19 | ),
20 | accentColor: Colors.lightBlueAccent,
21 | toggleableActiveColor: Colors.lightBlueAccent,
22 | );
23 |
--------------------------------------------------------------------------------
/app/lib/widgets/timeline/item/UnknownTimelineActivityWidget.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:munin/models/bangumi/timeline/UnknownTimelineActivity.dart';
3 | import 'package:munin/shared/utils/common.dart';
4 |
5 | /// an unknown timeline activity, only truncated plain text will be shown
6 | class UnknownTimelineActivityWidget extends StatelessWidget {
7 | final UnknownTimelineActivity unknownTimelineActivity;
8 |
9 | final int maxUnknownTimelineActivityLength = 500;
10 |
11 | const UnknownTimelineActivityWidget(
12 | {Key key, @required this.unknownTimelineActivity})
13 | : super(key: key);
14 |
15 | @override
16 | Widget build(BuildContext context) {
17 | return Row(
18 | children: [
19 | Flexible(
20 | child: Text(firstNChars(
21 | unknownTimelineActivity.content, maxUnknownTimelineActivityLength,
22 | fallbackValue: null)),
23 | )
24 | ],
25 | );
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/search/SearchRequest.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:built_value/built_value.dart';
4 | import 'package:built_value/serializer.dart';
5 | import 'package:munin/models/bangumi/search/SearchType.dart';
6 | import 'package:munin/shared/utils/serializers.dart';
7 |
8 | part 'SearchRequest.g.dart';
9 |
10 | abstract class SearchRequest
11 | implements Built {
12 | SearchRequest._();
13 |
14 | String get query;
15 |
16 | SearchType get searchType;
17 |
18 | factory SearchRequest([updates(SearchRequestBuilder b)]) = _$SearchRequest;
19 |
20 | String toJson() {
21 | return json
22 | .encode(serializers.serializeWith(SearchRequest.serializer, this));
23 | }
24 |
25 | static SearchRequest fromJson(String jsonString) {
26 | return serializers.deserializeWith(
27 | SearchRequest.serializer, json.decode(jsonString));
28 | }
29 |
30 | static Serializer get serializer => _$searchRequestSerializer;
31 | }
32 |
--------------------------------------------------------------------------------
/app/lib/redux/oauth/OauthState.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:built_value/built_value.dart';
4 | import 'package:built_value/serializer.dart';
5 | import 'package:munin/shared/utils/serializers.dart';
6 |
7 | part 'OauthState.g.dart';
8 |
9 | abstract class OauthState implements Built {
10 | @nullable
11 | bool get showLoginErrorSnackBar;
12 |
13 | @nullable
14 | String get oauthFailureMessage;
15 |
16 | @nullable
17 | String get error;
18 |
19 | OauthState._();
20 |
21 | factory OauthState([updates(OauthStateBuilder b)]) =>
22 | _$OauthState((b) => b..update(updates));
23 |
24 | String toJson() {
25 | return json.encode(serializers.serializeWith(OauthState.serializer, this));
26 | }
27 |
28 | static OauthState fromJson(String jsonString) {
29 | return serializers.deserializeWith(
30 | OauthState.serializer, json.decode(jsonString));
31 | }
32 |
33 | static Serializer get serializer => _$oauthStateSerializer;
34 | }
35 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/timeline/ProgressUpdateEpisodeUntil.dart:
--------------------------------------------------------------------------------
1 | import 'package:built_value/built_value.dart';
2 | import 'package:built_value/serializer.dart';
3 | import 'package:munin/models/bangumi/timeline/common/BangumiContent.dart';
4 | import 'package:munin/models/bangumi/timeline/common/FeedMetaInfo.dart';
5 | import 'package:munin/models/bangumi/timeline/common/TimelineFeed.dart';
6 |
7 | part 'ProgressUpdateEpisodeUntil.g.dart';
8 |
9 | abstract class ProgressUpdateEpisodeUntil
10 | implements
11 | Built,
12 | TimelineFeed {
13 | FeedMetaInfo get user;
14 |
15 | String get subjectName;
16 |
17 | String get subjectId;
18 |
19 | ProgressUpdateEpisodeUntil._();
20 |
21 | factory ProgressUpdateEpisodeUntil(
22 | [updates(ProgressUpdateEpisodeUntilBuilder b)]) =
23 | _$ProgressUpdateEpisodeUntil;
24 |
25 | static Serializer get serializer =>
26 | _$progressUpdateEpisodeUntilSerializer;
27 | }
28 |
--------------------------------------------------------------------------------
/app/lib/widgets/shared/button/RoundedInkWell.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class RoundedInkWell extends StatelessWidget {
4 | final Widget child;
5 |
6 | final GestureTapCallback onTap;
7 |
8 | /// Text that describes the action that will occur when the button is pressed.
9 | ///
10 | /// This text is displayed when the user long-presses on the button and is
11 | /// used for accessibility.
12 | final String tooltip;
13 |
14 | const RoundedInkWell({
15 | Key key,
16 | @required this.child,
17 | this.onTap,
18 | this.tooltip,
19 | }) : super(key: key);
20 |
21 | @override
22 | Widget build(BuildContext context) {
23 | final roundedInkWell = InkWell(
24 | child: child,
25 | borderRadius: BorderRadius.all(Radius.circular(2.0)),
26 | onTap: onTap,
27 | );
28 |
29 | if (tooltip != null) {
30 | return Tooltip(
31 | child: roundedInkWell,
32 | message: tooltip,
33 | );
34 | }
35 |
36 | return roundedInkWell;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/timeline/GroupJoinSingle.dart:
--------------------------------------------------------------------------------
1 | import 'package:built_value/built_value.dart';
2 | import 'package:built_value/serializer.dart';
3 | import 'package:munin/models/bangumi/common/BangumiImage.dart';
4 | import 'package:munin/models/bangumi/timeline/common/BangumiContent.dart';
5 | import 'package:munin/models/bangumi/timeline/common/FeedMetaInfo.dart';
6 | import 'package:munin/models/bangumi/timeline/common/TimelineFeed.dart';
7 |
8 | part 'GroupJoinSingle.g.dart';
9 |
10 | abstract class GroupJoinSingle
11 | implements Built, TimelineFeed {
12 | FeedMetaInfo get user;
13 |
14 | BangumiImage get groupIcon;
15 |
16 | String get groupName;
17 |
18 | @nullable
19 | String get groupDescription;
20 |
21 | String get groupId;
22 |
23 | GroupJoinSingle._();
24 |
25 | factory GroupJoinSingle([updates(GroupJoinSingleBuilder b)]) =
26 | _$GroupJoinSingle;
27 |
28 | static Serializer get serializer =>
29 | _$groupJoinSingleSerializer;
30 | }
31 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/timeline/BlogCreationSingle.dart:
--------------------------------------------------------------------------------
1 | import 'package:built_value/built_value.dart';
2 | import 'package:built_value/serializer.dart';
3 | import 'package:munin/models/bangumi/timeline/common/BangumiContent.dart';
4 | import 'package:munin/models/bangumi/timeline/common/FeedMetaInfo.dart';
5 | import 'package:munin/models/bangumi/timeline/common/TimelineFeed.dart';
6 |
7 | part 'BlogCreationSingle.g.dart';
8 |
9 | abstract class BlogCreationSingle
10 | implements
11 | Built,
12 | TimelineFeed {
13 | /// due to the limitation of bangumi, this has to be a string
14 | FeedMetaInfo get user;
15 |
16 | String get title;
17 |
18 | @nullable
19 | String get summary;
20 |
21 | /// blog id
22 | String get id;
23 |
24 | BlogCreationSingle._();
25 |
26 | factory BlogCreationSingle([updates(BlogCreationSingleBuilder b)]) =
27 | _$BlogCreationSingle;
28 |
29 | static Serializer get serializer =>
30 | _$blogCreationSingleSerializer;
31 | }
32 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/progress/common/InProgressCollection.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'InProgressCollection.dart';
4 |
5 | // **************************************************************************
6 | // BuiltValueGenerator
7 | // **************************************************************************
8 |
9 | abstract class InProgressCollectionBuilder {
10 | void replace(InProgressCollection other);
11 | void update(void Function(InProgressCollectionBuilder) updates);
12 | int get userUpdatedAt;
13 | set userUpdatedAt(int userUpdatedAt);
14 |
15 | InProgressSubjectInfoBuilder get subject;
16 | set subject(InProgressSubjectInfoBuilder subject);
17 | }
18 |
19 | // ignore_for_file: always_put_control_body_on_new_line,always_specify_types,annotate_overrides,avoid_annotating_with_dynamic,avoid_as,avoid_catches_without_on_clauses,avoid_returning_this,lines_longer_than_80_chars,omit_local_variable_types,prefer_expression_function_bodies,sort_constructors_first,test_types_in_equals,unnecessary_const,unnecessary_new
20 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/timeline/PublicMessageNoReply.dart:
--------------------------------------------------------------------------------
1 | import 'package:built_value/built_value.dart';
2 | import 'package:built_value/serializer.dart';
3 | import 'package:munin/models/bangumi/timeline/common/BangumiContent.dart';
4 | import 'package:munin/models/bangumi/timeline/common/FeedMetaInfo.dart';
5 | import 'package:munin/models/bangumi/timeline/common/TimelineFeed.dart';
6 |
7 | part 'PublicMessageNoReply.g.dart';
8 |
9 | /// a special public message that's published by system and un-deletable
10 | abstract class PublicMessageNoReply
11 | implements
12 | Built,
13 | TimelineFeed {
14 | /// due to the limitation of bangumi, this has to be a string
15 | FeedMetaInfo get user;
16 |
17 | String get content;
18 |
19 | PublicMessageNoReply._();
20 |
21 | factory PublicMessageNoReply([updates(PublicMessageNoReplyBuilder b)]) =
22 | _$PublicMessageNoReply;
23 |
24 | static Serializer get serializer =>
25 | _$publicMessageNoReplySerializer;
26 | }
27 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/timeline/common/HyperImage.dart:
--------------------------------------------------------------------------------
1 | import 'package:built_value/built_value.dart';
2 | import 'package:built_value/serializer.dart';
3 | import 'package:munin/models/bangumi/common/BangumiImage.dart';
4 | import 'package:munin/models/bangumi/timeline/common/BangumiContent.dart';
5 | import 'package:munin/models/bangumi/timeline/common/HyperItem.dart';
6 |
7 | part 'HyperImage.g.dart';
8 |
9 | /// HyperImage: image with a clickable link
10 | abstract class HyperImage
11 | implements Built, HyperItem {
12 | /// id of the hyper text
13 | String get id;
14 |
15 | /// content type
16 | BangumiContent get contentType;
17 |
18 | BangumiImage get image;
19 |
20 | /// if we cannot parse content, a fallback webview might be used
21 | /// hence an optional link is needed
22 | @nullable
23 | String get pageUrl;
24 |
25 | HyperImage._();
26 |
27 | factory HyperImage([updates(HyperImageBuilder b)]) = _$HyperImage;
28 |
29 | static Serializer get serializer => _$hyperImageSerializer;
30 | }
31 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/user/social/NetworkServiceTag.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'NetworkServiceTag.dart';
4 |
5 | // **************************************************************************
6 | // BuiltValueGenerator
7 | // **************************************************************************
8 |
9 | abstract class NetworkServiceTagBuilder {
10 | void replace(NetworkServiceTag other);
11 | void update(void Function(NetworkServiceTagBuilder) updates);
12 | NetworkServiceType get type;
13 | set type(NetworkServiceType type);
14 |
15 | String get content;
16 | set content(String content);
17 |
18 | bool get isLink;
19 | set isLink(bool isLink);
20 | }
21 |
22 | // ignore_for_file: always_put_control_body_on_new_line,always_specify_types,annotate_overrides,avoid_annotating_with_dynamic,avoid_as,avoid_catches_without_on_clauses,avoid_returning_this,lines_longer_than_80_chars,omit_local_variable_types,prefer_expression_function_bodies,sort_constructors_first,test_types_in_equals,unnecessary_const,unnecessary_new
23 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/subject/review/SubjectReview.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:built_value/built_value.dart';
4 | import 'package:built_value/serializer.dart';
5 | import 'package:munin/models/bangumi/subject/review/ReviewMetaInfo.dart';
6 | import 'package:munin/shared/utils/serializers.dart';
7 |
8 | part 'SubjectReview.g.dart';
9 |
10 | abstract class SubjectReview
11 | implements Built {
12 | SubjectReview._();
13 |
14 | factory SubjectReview([updates(SubjectReviewBuilder b)]) = _$SubjectReview;
15 |
16 | ReviewMetaInfo get metaInfo;
17 |
18 | @nullable
19 | String get content;
20 |
21 | String toJson() {
22 | return json
23 | .encode(serializers.serializeWith(SubjectReview.serializer, this));
24 | }
25 |
26 | static SubjectReview fromJson(String jsonString) {
27 | return serializers.deserializeWith(
28 | SubjectReview.serializer, json.decode(jsonString));
29 | }
30 |
31 | static Serializer get serializer => _$subjectReviewSerializer;
32 | }
33 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/timeline/IndexFavoriteSingle.dart:
--------------------------------------------------------------------------------
1 | import 'package:built_value/built_value.dart';
2 | import 'package:built_value/serializer.dart';
3 | import 'package:munin/models/bangumi/timeline/common/BangumiContent.dart';
4 | import 'package:munin/models/bangumi/timeline/common/FeedMetaInfo.dart';
5 | import 'package:munin/models/bangumi/timeline/common/TimelineFeed.dart';
6 |
7 | part 'IndexFavoriteSingle.g.dart';
8 |
9 | abstract class IndexFavoriteSingle
10 | implements
11 | Built,
12 | TimelineFeed {
13 | /// due to the limitation of bangumi, this has to be a string
14 | FeedMetaInfo get user;
15 |
16 | String get title;
17 |
18 | /// id of the index
19 | String get id;
20 |
21 | @nullable
22 | String get summary;
23 |
24 | IndexFavoriteSingle._();
25 |
26 | factory IndexFavoriteSingle([updates(IndexFavoriteSingleBuilder b)]) =
27 | _$IndexFavoriteSingle;
28 |
29 | static Serializer get serializer =>
30 | _$indexFavoriteSingleSerializer;
31 | }
32 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/timeline/UnknownTimelineActivity.dart:
--------------------------------------------------------------------------------
1 | import 'package:built_value/built_value.dart';
2 | import 'package:built_value/serializer.dart';
3 | import 'package:munin/models/bangumi/timeline/common/BangumiContent.dart';
4 | import 'package:munin/models/bangumi/timeline/common/FeedMetaInfo.dart';
5 | import 'package:munin/models/bangumi/timeline/common/TimelineFeed.dart';
6 |
7 | part 'UnknownTimelineActivity.g.dart';
8 |
9 | abstract class UnknownTimelineActivity
10 | implements
11 | Built,
12 | TimelineFeed {
13 | /// no-op, this should always be null
14 | @nullable
15 | FeedMetaInfo get user;
16 |
17 | String get content;
18 |
19 | @nullable
20 | BangumiContent get bangumiContent;
21 |
22 | UnknownTimelineActivity._();
23 |
24 | factory UnknownTimelineActivity([updates(UnknownTimelineActivityBuilder b)]) =
25 | _$UnknownTimelineActivity;
26 |
27 | static Serializer get serializer =>
28 | _$unknownTimelineActivitySerializer;
29 | }
30 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/timeline/common/Mono.dart:
--------------------------------------------------------------------------------
1 | import 'package:built_collection/built_collection.dart';
2 | import 'package:built_value/built_value.dart';
3 | import 'package:built_value/serializer.dart';
4 | import 'package:munin/models/bangumi/timeline/common/BangumiContent.dart';
5 |
6 | part 'Mono.g.dart';
7 |
8 | class Mono extends EnumClass {
9 | static const Mono Character = _$Character;
10 | static const Mono Person = _$Person;
11 |
12 | BangumiContent get bangumiContent {
13 | switch (this) {
14 | case Mono.Character:
15 | return BangumiContent.Character;
16 | case Mono.Person:
17 | return BangumiContent.Person;
18 | default:
19 | assert(false, '$this doesn\'t have a valid bangumiContent type');
20 | return BangumiContent.CharacterOrPerson;
21 | }
22 | }
23 |
24 | const Mono._(String name) : super(name);
25 |
26 | static BuiltSet get values => _$values;
27 |
28 | static Mono valueOf(String name) => _$valueOf(name);
29 |
30 | static Serializer get serializer => _$monoSerializer;
31 | }
32 |
--------------------------------------------------------------------------------
/app/lib/widgets/shared/common/ScaffoldWithRegularAppBar.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:munin/styles/theme/Common.dart';
3 |
4 | /// a simple layout with a scaffold, an AppBat and a inner SafeArea
5 | /// where AppBar is the built-in app bar for [Scaffold]
6 | class ScaffoldWithRegularAppBar extends StatelessWidget {
7 | final Widget safeAreaChild;
8 | final PreferredSizeWidget appBar;
9 | final double safeAreaChildHorizontalPadding;
10 |
11 | const ScaffoldWithRegularAppBar({
12 | Key key,
13 | @required this.safeAreaChild,
14 | @required this.appBar,
15 | this.safeAreaChildHorizontalPadding = defaultPortraitHorizontalOffset,
16 | }) : super(key: key);
17 |
18 | @override
19 | Widget build(BuildContext context) {
20 | return Scaffold(
21 | key: key,
22 | appBar: appBar,
23 | body: SafeArea(
24 | child: Padding(
25 | padding:
26 | EdgeInsets.symmetric(horizontal: safeAreaChildHorizontalPadding),
27 | child: safeAreaChild,
28 | )),
29 | );
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/lib/widgets/shared/text/MuninTextSpans.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:flutter/material.dart';
3 |
4 | /// A short cut for [RichText] that displays multiple text spans.
5 | ///
6 | /// This is a simple wrapper around the official widget to reduce boilerplate.
7 | class MuninTextSpans extends StatelessWidget {
8 | final List children;
9 |
10 | const MuninTextSpans({Key key, @required this.children}) : super(key: key);
11 |
12 | @override
13 | Widget build(BuildContext context) {
14 | return RichText(
15 | text: TextSpan(
16 | children: [
17 | for (var textConfig in children)
18 | TextSpan(
19 | text: textConfig.text,
20 | style:
21 | textConfig.textStyle ?? Theme.of(context).textTheme.bodyText2,
22 | )
23 | ],
24 | ),
25 | );
26 | }
27 | }
28 |
29 | class MuninTextSpanConfig {
30 | final String text;
31 | final TextStyle textStyle;
32 |
33 | MuninTextSpanConfig(this.text, [this.textStyle]);
34 | }
35 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/discussion/DiscussionItem.dart:
--------------------------------------------------------------------------------
1 | import 'package:built_value/built_value.dart';
2 | import 'package:munin/models/bangumi/common/BangumiImage.dart';
3 | import 'package:munin/models/bangumi/timeline/common/BangumiContent.dart';
4 |
5 | part 'DiscussionItem.g.dart';
6 |
7 | /// a base Discussion interface
8 | @BuiltValue(instantiable: false)
9 | abstract class DiscussionItem {
10 | int get id;
11 |
12 | BangumiContent get bangumiContent;
13 |
14 | BangumiImage get image;
15 |
16 | /// For person/character, it's person/character name
17 | /// For episode, it's episode name
18 | /// For group topic/subject topic, it's postName
19 | String get title;
20 |
21 | /// For person/character, it's a fixed string: person/character
22 | /// For episode/subject topic, it's subject name
23 | /// For group topic, it's group name
24 | String get subTitle;
25 |
26 | int get replyCount;
27 |
28 | @nullable
29 | int get updatedAt;
30 |
31 | DiscussionItem rebuild(void updates(DiscussionItemBuilder b));
32 |
33 | DiscussionItemBuilder toBuilder();
34 | }
35 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/discussion/thread/common/BangumiThread.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'BangumiThread.dart';
4 |
5 | // **************************************************************************
6 | // BuiltValueGenerator
7 | // **************************************************************************
8 |
9 | abstract class BangumiThreadBuilder {
10 | void replace(BangumiThread other);
11 | void update(void Function(BangumiThreadBuilder) updates);
12 | int get id;
13 | set id(int id);
14 |
15 | String get title;
16 | set title(String title);
17 |
18 | ListBuilder get mainPostReplies;
19 | set mainPostReplies(ListBuilder mainPostReplies);
20 | }
21 |
22 | // ignore_for_file: always_put_control_body_on_new_line,always_specify_types,annotate_overrides,avoid_annotating_with_dynamic,avoid_as,avoid_catches_without_on_clauses,avoid_returning_this,lines_longer_than_80_chars,omit_local_variable_types,prefer_expression_function_bodies,sort_constructors_first,test_types_in_equals,unnecessary_const,unnecessary_new
23 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/subject/common/SujectBase.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'SujectBase.dart';
4 |
5 | // **************************************************************************
6 | // BuiltValueGenerator
7 | // **************************************************************************
8 |
9 | abstract class SubjectBaseBuilder {
10 | void replace(SubjectBase other);
11 | void update(void Function(SubjectBaseBuilder) updates);
12 | int get id;
13 | set id(int id);
14 |
15 | String get pageUrlFromApi;
16 | set pageUrlFromApi(String pageUrlFromApi);
17 |
18 | String get name;
19 | set name(String name);
20 |
21 | String get chineseName;
22 | set chineseName(String chineseName);
23 | }
24 |
25 | // ignore_for_file: always_put_control_body_on_new_line,always_specify_types,annotate_overrides,avoid_annotating_with_dynamic,avoid_as,avoid_catches_without_on_clauses,avoid_returning_this,lines_longer_than_80_chars,omit_local_variable_types,prefer_expression_function_bodies,sort_constructors_first,test_types_in_equals,unnecessary_const,unnecessary_new
26 |
--------------------------------------------------------------------------------
/app/lib/widgets/shared/background/RoundedConcreteBackgroundWithChild.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:munin/styles/theme/Common.dart';
3 | import 'package:munin/widgets/shared/background/RoundedConcreteBackground.dart';
4 | import 'package:munin/widgets/shared/text/WrappableText.dart';
5 |
6 | class RoundedConcreteBackgroundWithChild extends StatelessWidget {
7 | final Widget child;
8 |
9 | const RoundedConcreteBackgroundWithChild({
10 | @required this.child,
11 | Key key,
12 | }) : super(key: key);
13 |
14 | RoundedConcreteBackgroundWithChild.fromText(
15 | String text,
16 | BuildContext outerContext, {
17 | Key key,
18 | int maxLines,
19 | }) : child = WrappableText(
20 | text,
21 | textStyle: captionTextWithHigherOpacity(outerContext),
22 | outerWrapper: OuterWrapper.Row,
23 | maxLines: maxLines,
24 | ),
25 | super(key: key);
26 |
27 | @override
28 | Widget build(BuildContext context) {
29 | return RoundedConcreteBackground(
30 | child: child,
31 | );
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/timeline/common/TimelineFeed.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'TimelineFeed.dart';
4 |
5 | // **************************************************************************
6 | // BuiltValueGenerator
7 | // **************************************************************************
8 |
9 | abstract class TimelineFeedBuilder {
10 | void replace(TimelineFeed other);
11 | void update(void Function(TimelineFeedBuilder) updates);
12 | FeedMetaInfoBuilder get user;
13 | set user(FeedMetaInfoBuilder user);
14 |
15 | BangumiContent get bangumiContent;
16 | set bangumiContent(BangumiContent bangumiContent);
17 |
18 | bool get isFromMutedUser;
19 | set isFromMutedUser(bool isFromMutedUser);
20 | }
21 |
22 | // ignore_for_file: always_put_control_body_on_new_line,always_specify_types,annotate_overrides,avoid_annotating_with_dynamic,avoid_as,avoid_catches_without_on_clauses,avoid_returning_this,lines_longer_than_80_chars,omit_local_variable_types,prefer_expression_function_bodies,sort_constructors_first,test_types_in_equals,unnecessary_const,unnecessary_new
23 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/search/result/SearchResultItem.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'SearchResultItem.dart';
4 |
5 | // **************************************************************************
6 | // BuiltValueGenerator
7 | // **************************************************************************
8 |
9 | abstract class SearchResultItemBuilder {
10 | void replace(SearchResultItem other);
11 | void update(void Function(SearchResultItemBuilder) updates);
12 | BangumiImageBuilder get image;
13 | set image(BangumiImageBuilder image);
14 |
15 | String get name;
16 | set name(String name);
17 |
18 | int get id;
19 | set id(int id);
20 |
21 | SearchType get type;
22 | set type(SearchType type);
23 | }
24 |
25 | // ignore_for_file: always_put_control_body_on_new_line,always_specify_types,annotate_overrides,avoid_annotating_with_dynamic,avoid_as,avoid_catches_without_on_clauses,avoid_returning_this,lines_longer_than_80_chars,omit_local_variable_types,prefer_expression_function_bodies,sort_constructors_first,test_types_in_equals,unnecessary_const,unnecessary_new
26 |
--------------------------------------------------------------------------------
/app/lib/widgets/shared/common/SnackBar.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:quiver/time.dart';
3 |
4 | const Duration shortSnackBarDisplayDuration = aSecond;
5 | const Duration snackBarDisplayDuration = Duration(milliseconds: 4000);
6 |
7 | /// Shows a snackbar on success of [future].
8 | void showSnackBarOnSuccess(
9 | BuildContext context,
10 | Future future,
11 | String successText,
12 | ) async {
13 | final hasSucceeded = await future;
14 | if (hasSucceeded == true) {
15 | showTextOnSnackBar(context, successText);
16 | }
17 | }
18 |
19 | /// Show text on snackbar, if [shortDuration] is set to true, value in [duration]
20 | /// will be ignored and [shortSnackBarDisplayDuration] will always be used.
21 | void showTextOnSnackBar(BuildContext context, String text,
22 | {duration = snackBarDisplayDuration, shortDuration = false,}) async {
23 | ScaffoldMessenger.of(context).showSnackBar(SnackBar(
24 | content: Text(text),
25 | duration: shortDuration ? shortSnackBarDisplayDuration : duration,
26 | behavior: SnackBarBehavior.floating,
27 | ));
28 | }
29 |
--------------------------------------------------------------------------------
/app/lib/widgets/timeline/item/PublicMessageNoReplyWidget.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:munin/models/bangumi/timeline/PublicMessageNoReply.dart';
3 | import 'package:munin/widgets/shared/common/UserActionTile.dart';
4 |
5 | /// a special public message that's published by system and un-deletable
6 | /// [PublicMessageNameChange] and [PublicMessageSignChange] currently shares this widget
7 | class PublicMessageNoReplyWidget extends StatelessWidget {
8 | final PublicMessageNoReply publicMessageNoReply;
9 |
10 | const PublicMessageNoReplyWidget(
11 | {Key key, @required this.publicMessageNoReply})
12 | : super(key: key);
13 |
14 | @override
15 | Widget build(BuildContext context) {
16 | return Column(
17 | children: [
18 | UserActionTile.fromUser(
19 | user: publicMessageNoReply.user,
20 | ),
21 | Row(
22 | children: [
23 | Flexible(
24 | child: Text(publicMessageNoReply.content),
25 | )
26 | ],
27 | ),
28 | ],
29 | );
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/user/notification/BaseNotificationItem.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'BaseNotificationItem.dart';
4 |
5 | // **************************************************************************
6 | // BuiltValueGenerator
7 | // **************************************************************************
8 |
9 | abstract class BaseNotificationItemBuilder {
10 | void replace(BaseNotificationItem other);
11 | void update(void Function(BaseNotificationItemBuilder) updates);
12 | int get id;
13 | set id(int id);
14 |
15 | BangumiUserBasicBuilder get initiator;
16 | set initiator(BangumiUserBasicBuilder initiator);
17 |
18 | String get bodyContentHtml;
19 | set bodyContentHtml(String bodyContentHtml);
20 | }
21 |
22 | // ignore_for_file: always_put_control_body_on_new_line,always_specify_types,annotate_overrides,avoid_annotating_with_dynamic,avoid_as,avoid_catches_without_on_clauses,avoid_returning_this,lines_longer_than_80_chars,omit_local_variable_types,prefer_expression_function_bodies,sort_constructors_first,test_types_in_equals,unnecessary_const,unnecessary_new
23 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/timeline/ProgressUpdateEpisodeSingle.dart:
--------------------------------------------------------------------------------
1 | import 'package:built_value/built_value.dart';
2 | import 'package:built_value/serializer.dart';
3 | import 'package:munin/models/bangumi/timeline/common/BangumiContent.dart';
4 | import 'package:munin/models/bangumi/timeline/common/FeedMetaInfo.dart';
5 | import 'package:munin/models/bangumi/timeline/common/TimelineFeed.dart';
6 |
7 | part 'ProgressUpdateEpisodeSingle.g.dart';
8 |
9 | abstract class ProgressUpdateEpisodeSingle
10 | implements
11 | Built,
12 | TimelineFeed {
13 | FeedMetaInfo get user;
14 |
15 | String get episodeName;
16 |
17 | String get episodeId;
18 |
19 | String get subjectName;
20 |
21 | String get subjectId;
22 |
23 | ProgressUpdateEpisodeSingle._();
24 |
25 | factory ProgressUpdateEpisodeSingle(
26 | [updates(ProgressUpdateEpisodeSingleBuilder b)]) =
27 | _$ProgressUpdateEpisodeSingle;
28 |
29 | static Serializer get serializer =>
30 | _$progressUpdateEpisodeSingleSerializer;
31 | }
32 |
--------------------------------------------------------------------------------
/app/lib/redux/oauth/OauthActions.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:munin/models/bangumi/BangumiUserSmall.dart';
3 |
4 | class UpdateLoginDataAction {
5 | final BuildContext context;
6 | final BangumiUserSmall userInfo;
7 |
8 | UpdateLoginDataAction({
9 | @required this.userInfo,
10 | @required this.context,
11 | });
12 | }
13 |
14 | class OAuthLoginCancel {
15 | final BuildContext context;
16 |
17 | OAuthLoginCancel(this.context);
18 | }
19 |
20 | class OAuthLoginFailure {
21 | final BuildContext context;
22 | final String message;
23 |
24 | OAuthLoginFailure(this.context, this.message);
25 | }
26 |
27 | class OAuthLoginSuccess {
28 | final BangumiUserSmall userInfo;
29 |
30 | OAuthLoginSuccess(this.userInfo);
31 | }
32 |
33 | class LogoutRequest {
34 | final BuildContext context;
35 |
36 | LogoutRequest(this.context);
37 | }
38 |
39 | class LogoutSuccess {
40 | final BuildContext context;
41 |
42 | LogoutSuccess(this.context);
43 | }
44 |
45 | class LogoutFailure {
46 | final BuildContext context;
47 |
48 | LogoutFailure(this.context);
49 | }
50 |
--------------------------------------------------------------------------------
/app/lib/widgets/subject/common/Common.dart:
--------------------------------------------------------------------------------
1 | import 'package:fluro/fluro.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:munin/config/application.dart';
4 | import 'package:munin/models/bangumi/collection/CollectionStatus.dart';
5 | import 'package:munin/models/bangumi/subject/BangumiSubject.dart';
6 | import 'package:munin/router/routes.dart';
7 | import 'package:munin/shared/utils/misc/constants.dart';
8 | import 'package:munin/widgets/shared/common/SnackBar.dart';
9 |
10 | String collectionActionText(BangumiSubject subject) =>
11 | subject.userSubjectCollectionInfoPreview.status == CollectionStatus.Pristine
12 | ? '加入收藏'
13 | : '编辑收藏';
14 |
15 | void navigateToSubjectCollection(BuildContext context, int subjectId) {
16 | showSnackBarOnSuccess(
17 | context,
18 | Application.router.navigateTo(
19 | context,
20 | Routes.subjectCollectionManagementRoute
21 | .replaceFirst(RoutesVariable.subjectIdParam, subjectId?.toString()),
22 | transition: TransitionType.nativeModal,
23 | ),
24 | hasSuccessfullyUpdatedCollectionLabel);
25 | }
26 |
--------------------------------------------------------------------------------
/app/lib/widgets/subject/mainpage/CharactersPreview.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:munin/models/bangumi/subject/BangumiSubject.dart';
3 | import 'package:munin/shared/utils/misc/Launch.dart';
4 | import 'package:munin/widgets/shared/utils/common.dart';
5 | import 'package:munin/widgets/subject/common/HorizontalCharacters.dart';
6 | import 'package:munin/widgets/subject/mainpage/SubjectMoreItemsEntry.dart';
7 |
8 | class CharactersPreview extends StatelessWidget {
9 | final BangumiSubject subject;
10 |
11 | const CharactersPreview({Key key, @required this.subject}) : super(key: key);
12 |
13 | @override
14 | Widget build(BuildContext context) {
15 | return Column(
16 | children: [
17 | SubjectMoreItemsEntry(
18 | moreItemsText: '角色介绍',
19 | onTap: () {
20 | launchByPreference(context,
21 | '${httpsBangumiMainSite()}/subject/${subject.id}/characters');
22 | },
23 | ),
24 | HorizontalCharacters(
25 | characters: subject.characters,
26 | )
27 | ],
28 | );
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/timeline/FriendshipCreationSingle.dart:
--------------------------------------------------------------------------------
1 | import 'package:built_value/built_value.dart';
2 | import 'package:built_value/serializer.dart';
3 | import 'package:munin/models/bangumi/common/BangumiImage.dart';
4 | import 'package:munin/models/bangumi/timeline/common/BangumiContent.dart';
5 | import 'package:munin/models/bangumi/timeline/common/FeedMetaInfo.dart';
6 | import 'package:munin/models/bangumi/timeline/common/TimelineFeed.dart';
7 |
8 | part 'FriendshipCreationSingle.g.dart';
9 |
10 | abstract class FriendshipCreationSingle
11 | implements
12 | Built,
13 | TimelineFeed {
14 | FeedMetaInfo get user;
15 |
16 | String get friendNickName;
17 |
18 | BangumiImage get friendAvatar;
19 |
20 | String get friendId;
21 |
22 | FriendshipCreationSingle._();
23 |
24 | factory FriendshipCreationSingle(
25 | [updates(FriendshipCreationSingleBuilder b)]) =
26 | _$FriendshipCreationSingle;
27 |
28 | static Serializer get serializer =>
29 | _$friendshipCreationSingleSerializer;
30 | }
31 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/timeline/PublicMessageNormal.dart:
--------------------------------------------------------------------------------
1 | import 'package:built_value/built_value.dart';
2 | import 'package:built_value/serializer.dart';
3 | import 'package:munin/models/bangumi/timeline/common/BangumiContent.dart';
4 | import 'package:munin/models/bangumi/timeline/common/FeedMetaInfo.dart';
5 | import 'package:munin/models/bangumi/timeline/common/TimelineFeed.dart';
6 |
7 | part 'PublicMessageNormal.g.dart';
8 |
9 | abstract class PublicMessageNormal
10 | implements
11 | Built,
12 | TimelineFeed {
13 | /// due to the limitation of bangumi, this has to be a string
14 | FeedMetaInfo get user;
15 |
16 | /// Content in raw html.
17 | String get contentHtml;
18 |
19 | /// Content in raw text, by parsing html.
20 | String get contentText;
21 |
22 | int get replyCount;
23 |
24 | PublicMessageNormal._();
25 |
26 | factory PublicMessageNormal([updates(PublicMessageNormalBuilder b)]) =
27 | _$PublicMessageNormal;
28 |
29 | static Serializer get serializer =>
30 | _$publicMessageNormalSerializer;
31 | }
32 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 edwardez
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/setting/theme/ThemeSwitchMode.dart:
--------------------------------------------------------------------------------
1 | import 'package:built_collection/built_collection.dart';
2 | import 'package:built_value/built_value.dart';
3 | import 'package:built_value/serializer.dart';
4 | import 'package:munin/shared/utils/serializers.dart';
5 |
6 | part 'ThemeSwitchMode.g.dart';
7 |
8 | class ThemeSwitchMode extends EnumClass {
9 | @BuiltValueEnumConst(fallback: true)
10 | static const ThemeSwitchMode Manual = _$Manual;
11 |
12 | static const ThemeSwitchMode FollowScreenBrightness =
13 | _$FollowScreenBrightness;
14 |
15 | static const ThemeSwitchMode FollowSystemThemeSetting =
16 | _$FollowSystemThemeSetting;
17 |
18 | const ThemeSwitchMode._(String name) : super(name);
19 |
20 | static BuiltSet get values => _$values;
21 |
22 | static ThemeSwitchMode valueOf(String name) => _$valueOf(name);
23 |
24 | static Serializer get serializer =>
25 | _$themeSwitchModeSerializer;
26 |
27 | static ThemeSwitchMode fromWiredName(String wiredName) {
28 | return serializers.deserializeWith(ThemeSwitchMode.serializer, wiredName);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/documents/zh-Hans/README.md:
--------------------------------------------------------------------------------
1 | # BangumiN
2 | BangumiN - 使用Angular,Node.js和NoSQL构建的PWA Bangumi客户端。
3 |
4 | 
5 |
6 | # 注意☠
7 |
8 | 这个项目仍处于初期开发阶段,功能随时可能在没有通知的情况下发生变化。
9 |
10 | 如果您发现了任何错误,安全漏洞或任何其他异常,欢迎告知!
11 |
12 |
13 | # 安装
14 |
15 |
16 | * 前端
17 | * `npm install`
18 | * `npm run start-dev`
19 | * 后端
20 | * `npm install`
21 | * `npm run start-dev`
22 |
23 |
24 | # 功能
25 | * 作品的搜索,展示,进度管理,状态修改
26 | * 用户/作品评分数据可视化
27 | * 剧透箱:安全的发表剧透
28 |
29 | # 路线图
30 |
31 | | 计划 | 状态/截止日期 | 备注 |
32 | | --- | --- | --- |
33 | | 基本功能:进度管理 | 进行中,2018 Q3 | 基础功能已写完,更新话数进度需要用Virtual Scroll重写 |
34 | | 基础功能:搜索 | 尚未开始, 2018 Q3 | 需要从头重写 |
35 | | 基础功能:作品信息 | 基本完成 | 需要进一步微调css |
36 | | 用户数据可视化 | 2018 Q3 | 进行中 |
37 | | 条目数据可视化 | 2018 Q3 | 尚未开始 |
38 | | 数据管道 | 2018 Q3 | 从Bangumi抓取数据并存到我们的数据库中:用户和条目数据有API,用户的具体条目记录需要使用爬虫 |
39 | | 多主题 | 完成 | 目前有:蓝色,粉色,夜间主题 |
40 | | PWA 优化 | 2018 Q3 | 需要进一步优化 |
41 | | 无障碍支持 | 2018 Q3-Q4 | 尚未开始(aira-label,键盘操控,颜色对比度,语义化标签...) |
42 | | 测试 | 2018 Q4 | 精力不足且网站功能经常变化,等待网站稳定后开始写 |
43 | | 自动化构建,持续集成 | 2018 Q3-Q4 | 网站上线前完成基本构建 |
44 | | 网站部件可拖拽 | 未定 | 等待 https://github.com/angular/material2/issues/8963 |
45 |
--------------------------------------------------------------------------------
/app/lib/redux/readme.md:
--------------------------------------------------------------------------------
1 | ### Playbook
2 |
3 | * How to handle redux error and send them back to Widget?
4 |
5 | Unfortunately currently there is no such widely accepted best practice to handle this issue.
6 | So far, the best way seems to be(considering reducing boiler plate / maintainability, but sacrificing some flexibility):
7 | 1. Initialize a `Completer` for evert request action
8 | 2. At the end of each action(i.e. end in the epic, or if necessary, end in the reducer), `complete`
9 | the `Completer`, or `completeError` if an error is thrown.
10 | 3. In the code, now widget gets informed an error is occlude, do what ever you want.
11 | 4. You can use `RequestInProgressIndicatorWidget`, which contains logic to handle most
12 | common error handling cases. It specially useful for `Get` request.
13 | 5. Or you can send the error through `HandleErrorAction`, let relevant epic handle it.
14 |
15 |
16 | * Naming convention
17 |
18 | All reducers, actions, epics..etc should end with `xxxReduer`/`xxxAction`/`xxxEpic`
19 |
20 | * Store
21 |
22 | All variables in store must use `built_value`, otherwise it simply won't work.
--------------------------------------------------------------------------------
/app/lib/widgets/shared/text/editor/sticker/BangumiStickers.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:munin/widgets/shared/text/editor/sticker/BangumiSticker.dart';
3 |
4 | class BangumiStickers extends StatelessWidget {
5 | static const minId = 1;
6 | static const maxId = 123;
7 |
8 | /// A callback function that's called when a specific sticker is tapped.
9 | final Function(int id) onStickerTapped;
10 |
11 | const BangumiStickers({
12 | Key key,
13 | this.onStickerTapped,
14 | }) : super(key: key);
15 |
16 | @override
17 | Widget build(BuildContext context) {
18 | final scaleFactor = MediaQuery.of(context).textScaleFactor;
19 |
20 | return Wrap(
21 | children: [
22 | for (var i = minId; i < maxId; i++)
23 | InkWell(
24 | child: BangumiSticker(
25 | id: i,
26 | scaleFactor: scaleFactor,
27 | ),
28 | onTap: () {
29 | if (onStickerTapped != null) {
30 | onStickerTapped(i);
31 | }
32 | },
33 | )
34 | ],
35 | );
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/search/result/BangumiSearchResponse.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'BangumiSearchResponse.dart';
4 |
5 | // **************************************************************************
6 | // BuiltValueGenerator
7 | // **************************************************************************
8 |
9 | abstract class BangumiSearchResponseBuilder {
10 | void replace(BangumiSearchResponse other);
11 | void update(void Function(BangumiSearchResponseBuilder) updates);
12 | int get totalCount;
13 | set totalCount(int totalCount);
14 |
15 | int get requestedResults;
16 | set requestedResults(int requestedResults);
17 |
18 | MapBuilder get results;
19 | set results(MapBuilder results);
20 | }
21 |
22 | // ignore_for_file: always_put_control_body_on_new_line,always_specify_types,annotate_overrides,avoid_annotating_with_dynamic,avoid_as,avoid_catches_without_on_clauses,avoid_returning_this,lines_longer_than_80_chars,omit_local_variable_types,prefer_expression_function_bodies,sort_constructors_first,test_types_in_equals,unnecessary_const,unnecessary_new
23 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/discussion/thread/common/GetThreadRequest.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:built_value/built_value.dart';
4 | import 'package:built_value/serializer.dart';
5 | import 'package:munin/models/bangumi/discussion/thread/common/ThreadType.dart';
6 | import 'package:munin/shared/utils/serializers.dart';
7 |
8 | part 'GetThreadRequest.g.dart';
9 |
10 | abstract class GetThreadRequest
11 | implements Built {
12 | ThreadType get threadType;
13 |
14 | /// Id of the thread
15 | int get id;
16 |
17 | GetThreadRequest._();
18 |
19 | factory GetThreadRequest([void Function(GetThreadRequestBuilder) updates]) =
20 | _$GetThreadRequest;
21 |
22 | String toJson() {
23 | return json
24 | .encode(serializers.serializeWith(GetThreadRequest.serializer, this));
25 | }
26 |
27 | static GetThreadRequest fromJson(String jsonString) {
28 | return serializers.deserializeWith(
29 | GetThreadRequest.serializer, json.decode(jsonString));
30 | }
31 |
32 | static Serializer get serializer =>
33 | _$getThreadRequestSerializer;
34 | }
35 |
--------------------------------------------------------------------------------
/app/lib/styles/theme/BrightIonBrownBlue.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:munin/styles/theme/Colors.dart';
3 | import 'package:munin/styles/theme/Common.dart';
4 | import 'package:munin/styles/theme/CommonThemeData.dart';
5 |
6 | final ThemeData brightIonBrownBlueThemeData = ThemeData(
7 | canvasColor: Colors.white,
8 | brightness: Brightness.light,
9 | bottomSheetTheme: muninBottomSheetThemeData(),
10 | buttonTheme: ButtonThemeData(
11 | shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)),
12 | ),
13 | dialogTheme: DialogTheme(
14 | shape: RoundedRectangleBorder(borderRadius: defaultContainerCircularRadius),
15 | ),
16 | appBarTheme: AppBarTheme(
17 | elevation: 0,
18 | brightness: Brightness.light,
19 | color: Colors.white,
20 | iconTheme: IconThemeData(color: Colors.black54),
21 | ),
22 | primaryColor: ionBrown,
23 | // TODO: flutter by default calculates abd set it to [Brightness.dark]
24 | // verify whether [Brightness.light] meets color contrast requirement
25 | primaryColorBrightness: Brightness.light,
26 | accentColor: ionBlue,
27 | toggleableActiveColor: ionBlue,
28 | );
29 |
--------------------------------------------------------------------------------
/app/lib/shared/utils/misc/constants.dart:
--------------------------------------------------------------------------------
1 | import 'package:munin/config/application.dart';
2 |
3 | const String bangumiAssetsServer = 'lain.bgm.tv';
4 | final bangumiHostUriForDio =
5 | Uri.parse('https://${Application.environmentValue.bangumiHostForDio}');
6 |
7 | final String bangumiHomePageUrl =
8 | 'https://$bangumiMainHost';
9 | final String bangumiTimelineUrl =
10 | 'https://$bangumiMainHost/timeline';
11 | final String rakuenMobileUrl =
12 | 'https://$bangumiMainHost/m';
13 | const String bangumiAnonymousUserMediumAvatar =
14 | 'https://$bangumiAssetsServer/pic/user/m/icon.jpg';
15 |
16 | const String bangumiTextOnlySubjectCover =
17 | 'https://bgm.tv/img/no_icon_subject.png';
18 | final String bangumiTextOnlyGroupIcon =
19 | 'https://$bangumiAssetsServer/pic/icon/m/no_icon.jpg';
20 |
21 | const String checkWebVersionLabel = '查看网页版';
22 | const String goToForsetiLabel = '在bangumin.tv';
23 | const String appOrBangumiHasAnErrorLabel = '应用或bangumi出错';
24 | const String openInBrowserLabel = '在浏览器中打开';
25 | const String hasSuccessfullyUpdatedCollectionLabel = '收藏更新成功';
26 |
27 | const double avatarSize = 48.0;
28 | const double defaultSliverAppBarElevation = 4.0;
29 |
--------------------------------------------------------------------------------
/app/lib/styles/theme/BrightNatoriPinkBrown.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:munin/styles/theme/Colors.dart';
3 | import 'package:munin/styles/theme/Common.dart';
4 | import 'package:munin/styles/theme/CommonThemeData.dart';
5 |
6 | final ThemeData brightNatoriPinkBrownThemeData = ThemeData(
7 | canvasColor: Colors.white,
8 | brightness: Brightness.light,
9 | bottomSheetTheme: muninBottomSheetThemeData(),
10 | buttonTheme: ButtonThemeData(
11 | shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)),
12 | ),
13 | dialogTheme: DialogTheme(
14 | shape: RoundedRectangleBorder(borderRadius: defaultContainerCircularRadius),
15 | ),
16 | appBarTheme: AppBarTheme(
17 | elevation: 0,
18 | brightness: Brightness.light,
19 | color: Colors.white,
20 | iconTheme: IconThemeData(color: Colors.black54),
21 | ),
22 | primaryColor: natoriPink,
23 | // TODO: flutter by default calculates abd set it to [Brightness.dark]
24 | // verify whether [Brightness.light] meets color contrast requirement
25 | primaryColorBrightness: Brightness.light,
26 | accentColor: natoriBrown,
27 | toggleableActiveColor: natoriBrown,
28 | );
29 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/user/timeline/TimelinePreview.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:built_value/built_value.dart';
4 | import 'package:built_value/serializer.dart';
5 | import 'package:munin/shared/utils/serializers.dart';
6 |
7 | part 'TimelinePreview.g.dart';
8 |
9 | /// a Timeline that's listed as related Timeline('关联条目') on Timeline main page
10 | abstract class TimelinePreview
11 | implements Built {
12 | /// Content of this timeline in plain text
13 | String get content;
14 |
15 | /// Time when user published this timeline
16 | int get userUpdatedAt;
17 |
18 | TimelinePreview._();
19 |
20 | factory TimelinePreview([updates(TimelinePreviewBuilder b)]) =
21 | _$TimelinePreview;
22 |
23 | String toJson() {
24 | return json
25 | .encode(serializers.serializeWith(TimelinePreview.serializer, this));
26 | }
27 |
28 | static TimelinePreview fromJson(String jsonString) {
29 | return serializers.deserializeWith(
30 | TimelinePreview.serializer, json.decode(jsonString));
31 | }
32 |
33 | static Serializer get serializer =>
34 | _$timelinePreviewSerializer;
35 | }
36 |
--------------------------------------------------------------------------------
/app/lib/styles/theme/BrightBlueBangumiPink.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:munin/styles/theme/Colors.dart';
3 | import 'package:munin/styles/theme/Common.dart';
4 | import 'package:munin/styles/theme/CommonThemeData.dart';
5 |
6 | final ThemeData brightBangumiPinkBlueThemeData = ThemeData(
7 | canvasColor: Colors.white,
8 | brightness: Brightness.light,
9 | buttonTheme: ButtonThemeData(
10 | shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)),
11 | ),
12 | bottomSheetTheme: muninBottomSheetThemeData(),
13 | dialogTheme: DialogTheme(
14 | shape: RoundedRectangleBorder(borderRadius: defaultContainerCircularRadius),
15 | ),
16 | appBarTheme: AppBarTheme(
17 | elevation: 0,
18 | brightness: Brightness.light,
19 | color: Colors.white,
20 | iconTheme: IconThemeData(color: Colors.black54),
21 | ),
22 | primaryColor: Colors.blue,
23 | // TODO: flutter by default calculates abd set it to [Brightness.dark], need to
24 | // verify whether [Brightness.light] meets color contrast requirement
25 | primaryColorBrightness: Brightness.light,
26 | accentColor: bangumiPink,
27 | toggleableActiveColor: bangumiPink,
28 | );
29 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/setting/general/browser/BrowserSetting.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:built_value/built_value.dart';
4 | import 'package:built_value/serializer.dart';
5 | import 'package:munin/models/bangumi/setting/general/browser/LaunchBrowserPreference.dart';
6 | import 'package:munin/shared/utils/serializers.dart';
7 |
8 | part 'BrowserSetting.g.dart';
9 |
10 | abstract class BrowserSetting
11 | implements Built {
12 | LaunchBrowserPreference get launchBrowserPreference;
13 |
14 | BrowserSetting._();
15 |
16 | factory BrowserSetting([void Function(BrowserSettingBuilder) updates]) =>
17 | _$BrowserSetting((b) =>
18 | b.launchBrowserPreference = LaunchBrowserPreference.DefaultInApp);
19 |
20 | String toJson() {
21 | return json
22 | .encode(serializers.serializeWith(BrowserSetting.serializer, this));
23 | }
24 |
25 | static BrowserSetting fromJson(String jsonString) {
26 | return serializers.deserializeWith(
27 | BrowserSetting.serializer, json.decode(jsonString));
28 | }
29 |
30 | static Serializer get serializer =>
31 | _$browserSettingSerializer;
32 | }
33 |
--------------------------------------------------------------------------------
/app/lib/widgets/shared/button/MuninOutlineButton.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:munin/styles/theme/Common.dart';
3 |
4 | /// A customized outline button which uses primary color for text and outline
5 | /// for bright theme, and accent color for dark theme
6 | class MuninOutlineButton extends StatelessWidget {
7 | /// The button's label.
8 | ///
9 | /// Often a [Text] widget in all caps.
10 | final Widget child;
11 |
12 | /// The callback that is called when the button is tapped or otherwise activated.
13 | ///
14 | /// If this is set to null, the button will be disabled.
15 | final VoidCallback onPressed;
16 |
17 | const MuninOutlineButton(
18 | {Key key, @required this.onPressed, @required this.child})
19 | : super(key: key);
20 |
21 | @override
22 | Widget build(BuildContext context) {
23 | Color mainColor = lightPrimaryDarkAccentColor(context);
24 | return OutlinedButton(
25 | child: child,
26 | // style: ,
27 | // textColor: mainColor,
28 | // borderSide: BorderSide(color: mainColor, width: 1.0),
29 | // highlightedBorderColor: mainColor,
30 | onPressed: onPressed,
31 | );
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/test/AppCast_test.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'package:pub_semver/pub_semver.dart';
4 | import 'package:test/test.dart';
5 | import 'package:upgrader/upgrader.dart';
6 |
7 | void main() {
8 | group('AppCast', () {
9 | String appCastXml;
10 |
11 | setUp(() async {
12 | appCastXml =
13 | File('lib/config/upgrader/production_appcast.xml').readAsStringSync();
14 | });
15 |
16 | test('verifies production upgarder xml is valid', () {
17 | final items = Appcast().parseItemsFromXMLString(appCastXml);
18 |
19 | Version newerVersion;
20 | for (var item in items) {
21 | expect(item.fileURL, isNotNull);
22 | expect(item.isCriticalUpdate, isNotNull);
23 | expect(item.itemDescription, isNotNull);
24 |
25 | final currentVersion = Version.parse(item.versionString);
26 |
27 | expect(currentVersion.isPreRelease, isFalse);
28 | if (newerVersion != null) {
29 | // In production, newer version must be higher than older one.
30 | expect(newerVersion.compareTo(currentVersion), isPositive);
31 | }
32 |
33 | newerVersion = currentVersion;
34 | }
35 | });
36 | });
37 | }
38 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/timeline/MonoFavoriteSingle.dart:
--------------------------------------------------------------------------------
1 | import 'package:built_value/built_value.dart';
2 | import 'package:built_value/serializer.dart';
3 | import 'package:munin/models/bangumi/common/BangumiImage.dart';
4 | import 'package:munin/models/bangumi/timeline/common/BangumiContent.dart';
5 | import 'package:munin/models/bangumi/timeline/common/FeedMetaInfo.dart';
6 | import 'package:munin/models/bangumi/timeline/common/Mono.dart';
7 | import 'package:munin/models/bangumi/timeline/common/TimelineFeed.dart';
8 |
9 | part 'MonoFavoriteSingle.g.dart';
10 |
11 | abstract class MonoFavoriteSingle
12 | implements
13 | Built,
14 | TimelineFeed {
15 | FeedMetaInfo get user;
16 |
17 | String get monoName;
18 |
19 | BangumiImage get avatar;
20 |
21 | /// id on bangumi page, i.e. for https://bgm.tv/character/1 , id is 1
22 | String get id;
23 |
24 | Mono get monoType;
25 |
26 | MonoFavoriteSingle._();
27 |
28 | factory MonoFavoriteSingle([updates(MonoFavoriteSingleBuilder b)]) =
29 | _$MonoFavoriteSingle;
30 |
31 | static Serializer get serializer =>
32 | _$monoFavoriteSingleSerializer;
33 | }
34 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/timeline/common/HyperBangumiItem.dart:
--------------------------------------------------------------------------------
1 | import 'package:built_value/built_value.dart';
2 | import 'package:built_value/serializer.dart';
3 | import 'package:munin/models/bangumi/timeline/common/BangumiContent.dart';
4 | import 'package:munin/models/bangumi/timeline/common/HyperItem.dart';
5 |
6 | part 'HyperBangumiItem.g.dart';
7 |
8 | /// A hyper link that directs to a [BangumiContent] page
9 | abstract class HyperBangumiItem
10 | implements Built, HyperItem {
11 | /// id of the hyper text
12 | String get id;
13 |
14 | BangumiContent get contentType;
15 |
16 | /// Item name, i.e. user name, subject name...
17 | String get name;
18 |
19 | /// if we cannot parse content, a fallback webview might be used
20 | /// hence an optional link is needed
21 | @nullable
22 | String get pageUrl;
23 |
24 | /// a item may or may not have an image
25 | @nullable
26 | String get imageUrl;
27 |
28 | HyperBangumiItem._();
29 |
30 | factory HyperBangumiItem([updates(HyperBangumiItemBuilder b)]) =
31 | _$HyperBangumiItem;
32 |
33 | static Serializer get serializer =>
34 | _$hyperBangumiItemSerializer;
35 | }
36 |
--------------------------------------------------------------------------------
/app/lib/widgets/timeline/item/common/Actions.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:munin/models/bangumi/timeline/common/GetTimelineRequest.dart';
3 | import 'package:munin/models/bangumi/timeline/common/TimelineFeed.dart';
4 | import 'package:munin/redux/app/AppState.dart';
5 | import 'package:munin/redux/timeline/TimelineActions.dart';
6 | import 'package:munin/shared/exceptions/utils.dart';
7 | import 'package:munin/widgets/shared/common/SnackBar.dart';
8 | import 'package:redux/redux.dart';
9 |
10 | /// A commonly used helper that deletes a feed.
11 | void deleteFeedHelper(
12 | Store store,
13 | BuildContext context,
14 | GetTimelineRequest getTimelineRequest,
15 | TimelineFeed feed, {
16 | popContextOnSuccess = false,
17 | }) async {
18 | final action =
19 | DeleteTimelineAction(feed: feed, getTimelineRequest: getTimelineRequest);
20 | store.dispatch(action);
21 |
22 | try {
23 | await action.completer.future;
24 | showTextOnSnackBar(context, '时间线删除成功');
25 | if (popContextOnSuccess) {
26 | Navigator.pop(context);
27 | }
28 | } catch (error) {
29 | showTextOnSnackBar(context, '时间线删除失败: ${formatErrorMessage(error)}');
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/BangumiCookieCredentials.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:built_value/built_value.dart';
4 | import 'package:built_value/serializer.dart';
5 | import 'package:munin/shared/utils/serializers.dart';
6 |
7 | part 'BangumiCookieCredentials.g.dart';
8 |
9 | abstract class BangumiCookieCredentials
10 | implements
11 | Built {
12 | BangumiCookieCredentials._();
13 |
14 | factory BangumiCookieCredentials(
15 | [updates(BangumiCookieCredentialsBuilder b)]) =
16 | _$BangumiCookieCredentials;
17 |
18 | String get authCookie;
19 |
20 | String get sessionCookie;
21 |
22 | String get userAgent;
23 |
24 | DateTime get expiresOn;
25 |
26 | String toJson() {
27 | return json.encode(
28 | serializers.serializeWith(BangumiCookieCredentials.serializer, this));
29 | }
30 |
31 | static BangumiCookieCredentials fromJson(String jsonString) {
32 | return serializers.deserializeWith(
33 | BangumiCookieCredentials.serializer, json.decode(jsonString));
34 | }
35 |
36 | static Serializer get serializer =>
37 | _$bangumiCookieCredentialsSerializer;
38 | }
39 |
--------------------------------------------------------------------------------
/app/lib/redux/search/SearchState.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:built_collection/built_collection.dart';
4 | import 'package:built_value/built_value.dart';
5 | import 'package:built_value/serializer.dart';
6 | import 'package:munin/models/bangumi/search/SearchRequest.dart';
7 | import 'package:munin/models/bangumi/search/result/BangumiSearchResponse.dart';
8 | import 'package:munin/shared/utils/serializers.dart';
9 |
10 | part 'SearchState.g.dart';
11 |
12 | abstract class SearchState implements Built {
13 | BuiltMap get results;
14 |
15 | factory SearchState([updates(SearchStateBuilder b)]) => _$SearchState((b) => b
16 | ..results.replace(BuiltMap())
17 | ..update(updates));
18 |
19 | SearchState._();
20 |
21 | String toJson() {
22 | return json.encode(serializers.serializeWith(SearchState.serializer, this));
23 | }
24 |
25 | static SearchState fromJson(String jsonString) {
26 | return serializers.deserializeWith(
27 | SearchState.serializer, json.decode(jsonString));
28 | }
29 |
30 | static Serializer get serializer => _$searchStateSerializer;
31 | }
32 |
--------------------------------------------------------------------------------
/app/lib/styles/theme/NightPureDarkBlue.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:munin/styles/theme/Common.dart';
3 | import 'package:munin/styles/theme/CommonThemeData.dart';
4 |
5 | final ThemeData nightPureDarkBlueThemeData = ThemeData(
6 | brightness: Brightness.dark,
7 | canvasColor: Colors.black,
8 | backgroundColor: Colors.black,
9 | scaffoldBackgroundColor: Colors.black,
10 | bottomSheetTheme: muninBottomSheetThemeData(pureDartTheme: true),
11 | buttonTheme: ButtonThemeData(
12 | shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)),
13 | ),
14 | dialogTheme: DialogTheme(
15 | shape: RoundedRectangleBorder(borderRadius: defaultContainerCircularRadius),
16 | ),
17 | appBarTheme: AppBarTheme(
18 | brightness: Brightness.dark,
19 | ),
20 | sliderTheme: SliderThemeData.fromPrimaryColors(
21 | primaryColor: Colors.lightBlueAccent,
22 | primaryColorDark: Colors.lightBlueAccent.shade700,
23 | valueIndicatorTextStyle: ThemeData().textTheme.bodyText1,
24 | primaryColorLight: Colors.lightBlueAccent.shade100,
25 | ),
26 | dividerColor: Colors.grey,
27 | accentColor: Colors.lightBlueAccent,
28 | toggleableActiveColor: Colors.lightBlueAccent,
29 | );
30 |
--------------------------------------------------------------------------------
/app/lib/widgets/discussion/thread/shared/CopyPostContent.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:html/parser.dart';
3 | import 'package:munin/widgets/shared/icons/AdaptiveIcons.dart';
4 | import 'package:munin/widgets/shared/services/Clipboard.dart';
5 |
6 | class CopyPostContent extends StatelessWidget {
7 | final String contentHtml;
8 |
9 | /// An additional context that's under a scaffold. If non-null, copy snackbar
10 | /// messages are shown under [contextWithScaffold].
11 | /// It's useful if [CopyPostContent] itself is put in a place with no scaffold.
12 | final BuildContext contextWithScaffold;
13 |
14 | const CopyPostContent(
15 | {Key key, @required this.contentHtml, this.contextWithScaffold})
16 | : super(key: key);
17 |
18 | @override
19 | Widget build(BuildContext context) {
20 | return ListTile(
21 | leading: Icon(AdaptiveIcons.clipBoardIconData),
22 | title: Text('复制内容'),
23 | onTap: () {
24 | Navigator.pop(context);
25 | var postContent = (parseFragment(contentHtml).text ?? '').trim();
26 | ClipboardService.copyAsPlainText(
27 | contextWithScaffold ?? context, postContent);
28 | },
29 | );
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/lib/redux/progress/common.dart:
--------------------------------------------------------------------------------
1 | import 'package:munin/models/bangumi/progress/api/EpisodeProgress.dart';
2 | import 'package:munin/models/bangumi/progress/common/BaseEpisode.dart';
3 | import 'package:munin/models/bangumi/progress/common/EpisodeStatus.dart';
4 | import 'package:munin/models/bangumi/progress/common/EpisodeType.dart';
5 | import 'package:munin/models/bangumi/progress/common/EpisodeUpdateType.dart';
6 |
7 | /// Checks if episode will be affected by [EpisodeUpdateType.CollectUntil]
8 | bool isEpisodeAffectedByCollectUntilOperation(BaseEpisode episode) {
9 | return episode.episodeType == EpisodeType.Regular &&
10 | episode.userEpisodeStatus != EpisodeStatus.Dropped;
11 | }
12 |
13 | /// Watch until only affects previous episodes with smaller sequential number that
14 | /// are regular episodes and status is not [EpisodeStatus.Dropped].
15 | /// [EpisodeProgress] has an additional [sequentialNumber] which can be used to
16 | /// check which episodes are behind new EpisodeNumber.
17 | bool isEpisodeProgressAffectedByCollectUntilOperation(
18 | EpisodeProgress episode, int newEpisodeNumber) {
19 | return isEpisodeAffectedByCollectUntilOperation(episode) &&
20 | episode.sequentialNumber <= newEpisodeNumber;
21 | }
22 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/progress/common/BaseEpisode.dart:
--------------------------------------------------------------------------------
1 | import 'package:built_value/built_value.dart';
2 | import 'package:munin/models/bangumi/progress/common/AirStatus.dart';
3 | import 'package:munin/models/bangumi/progress/common/EpisodeStatus.dart';
4 | import 'package:munin/models/bangumi/progress/common/EpisodeType.dart';
5 |
6 | part 'BaseEpisode.g.dart';
7 |
8 | @BuiltValue(instantiable: false)
9 | abstract class BaseEpisode {
10 | /// id of this episode. This id can be used to uniquely identify an episode
11 | /// across all bangumi subjects
12 | int get id;
13 |
14 | String get name;
15 |
16 | @BuiltValueField(wireName: 'name_cn')
17 | String get chineseName;
18 |
19 | /// Current air status of this episode(has been aired or not)
20 | @BuiltValueField(wireName: 'status')
21 | AirStatus get airStatus;
22 |
23 | /// Current user episode status of this episode(has been watched or not)
24 | /// Note: user can mark an episode as 'watched' regardless its airStatus
25 | @BuiltValueField(wireName: 'user_episode_status')
26 | EpisodeStatus get userEpisodeStatus;
27 |
28 | EpisodeType get episodeType;
29 |
30 | BaseEpisode rebuild(void updates(BaseEpisodeBuilder b));
31 |
32 | BaseEpisodeBuilder toBuilder();
33 | }
34 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/discussion/GroupDiscussionItem.dart:
--------------------------------------------------------------------------------
1 | import 'package:built_value/built_value.dart';
2 | import 'package:built_value/serializer.dart';
3 | import 'package:munin/models/bangumi/common/BangumiImage.dart';
4 | import 'package:munin/models/bangumi/discussion/DiscussionItem.dart';
5 | import 'package:munin/models/bangumi/timeline/common/BangumiContent.dart';
6 |
7 | part 'GroupDiscussionItem.g.dart';
8 |
9 | abstract class GroupDiscussionItem
10 | implements
11 | Built,
12 | DiscussionItem {
13 | @nullable
14 | String get originalPosterUsername;
15 |
16 | /// Secondary user identifier, for Rakuen posts, `originalPosterUsername` is
17 | /// not available, instead, there is `originalPosterUserId`
18 | /// User id is parsed from path in user icon
19 | /// meaning for user with default icon, this will be null
20 | @nullable
21 | int get originalPosterUserId;
22 |
23 | String get postedGroupId;
24 |
25 | GroupDiscussionItem._();
26 |
27 | factory GroupDiscussionItem([updates(GroupDiscussionItemBuilder b)]) =
28 | _$GroupDiscussionItem;
29 |
30 | static Serializer get serializer =>
31 | _$groupDiscussionItemSerializer;
32 | }
33 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/discussion/thread/common/OriginalPost.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:built_value/built_value.dart';
4 | import 'package:built_value/serializer.dart';
5 | import 'package:munin/models/bangumi/common/BangumiUserBasic.dart';
6 | import 'package:munin/models/bangumi/discussion/thread/common/Post.dart';
7 | import 'package:munin/shared/utils/serializers.dart';
8 |
9 | part 'OriginalPost.g.dart';
10 |
11 | /// OriginalPost post in a thread that's posted by the original author.
12 | abstract class OriginalPost
13 | implements Post, Built {
14 | @override
15 | String get sequentialName {
16 | return '#$mainSequentialNumber';
17 | }
18 |
19 | OriginalPost._();
20 |
21 | factory OriginalPost([void Function(OriginalPostBuilder) updates]) =
22 | _$OriginalPost;
23 |
24 | String toJson() {
25 | return json
26 | .encode(serializers.serializeWith(OriginalPost.serializer, this));
27 | }
28 |
29 | static OriginalPost fromJson(String jsonString) {
30 | return serializers.deserializeWith(
31 | OriginalPost.serializer, json.decode(jsonString));
32 | }
33 |
34 | static Serializer get serializer => _$originalPostSerializer;
35 | }
36 |
--------------------------------------------------------------------------------
/app/lib/widgets/subject/management/StarRatingFormField.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:munin/styles/theme/Common.dart';
3 | import 'package:munin/widgets/subject/management/StarRating.dart';
4 |
5 | class StarRatingFormField extends FormField {
6 | StarRatingFormField(
7 | {FormFieldSetter onSaved,
8 | FormFieldValidator validator,
9 | int initialValue = 0,
10 | double horizontalPadding = defaultPortraitHorizontalOffset,
11 | AutovalidateMode autovalidate = AutovalidateMode.disabled})
12 | : super(
13 | onSaved: onSaved,
14 | validator: validator,
15 | initialValue: initialValue,
16 | autovalidateMode: autovalidate,
17 | builder: (FormFieldState state) {
18 | return StarRating(
19 | rating: state.value.toDouble(),
20 | onRatingChanged: (double rating) =>
21 | state.didChange(rating.toInt()),
22 | horizontalPadding: horizontalPadding);
23 | });
24 |
25 | @override
26 | _StarRatingFormFieldState createState() => _StarRatingFormFieldState();
27 | }
28 |
29 | class _StarRatingFormFieldState extends FormFieldState {}
30 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/timeline/CollectionUpdateSingle.dart:
--------------------------------------------------------------------------------
1 | import 'package:built_value/built_value.dart';
2 | import 'package:built_value/serializer.dart';
3 | import 'package:munin/models/bangumi/common/BangumiImage.dart';
4 | import 'package:munin/models/bangumi/timeline/common/BangumiContent.dart';
5 | import 'package:munin/models/bangumi/timeline/common/FeedMetaInfo.dart';
6 | import 'package:munin/models/bangumi/timeline/common/TimelineFeed.dart';
7 |
8 | part 'CollectionUpdateSingle.g.dart';
9 |
10 | abstract class CollectionUpdateSingle
11 | implements
12 | Built,
13 | TimelineFeed {
14 | /// due to the limitation of bangumi, this has to be a string
15 | FeedMetaInfo get user;
16 |
17 | @nullable
18 | String get subjectComment;
19 |
20 | String get subjectId;
21 |
22 | BangumiImage get subjectCover;
23 |
24 | @nullable
25 | double get subjectScore;
26 |
27 | String get subjectName;
28 |
29 | CollectionUpdateSingle._();
30 |
31 | factory CollectionUpdateSingle([updates(CollectionUpdateSingleBuilder b)]) =
32 | _$CollectionUpdateSingle;
33 |
34 | static Serializer get serializer =>
35 | _$collectionUpdateSingleSerializer;
36 | }
37 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/discussion/GeneralDiscussionItem.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:built_value/built_value.dart';
4 | import 'package:built_value/serializer.dart';
5 | import 'package:munin/models/bangumi/common/BangumiImage.dart';
6 | import 'package:munin/models/bangumi/discussion/DiscussionItem.dart';
7 | import 'package:munin/models/bangumi/timeline/common/BangumiContent.dart';
8 | import 'package:munin/shared/utils/serializers.dart';
9 |
10 | part 'GeneralDiscussionItem.g.dart';
11 |
12 | abstract class GeneralDiscussionItem
13 | implements
14 | Built,
15 | DiscussionItem {
16 | factory GeneralDiscussionItem([updates(GeneralDiscussionItemBuilder b)]) =
17 | _$GeneralDiscussionItem;
18 |
19 | GeneralDiscussionItem._();
20 |
21 | String toJson() {
22 | return json.encode(
23 | serializers.serializeWith(GeneralDiscussionItem.serializer, this));
24 | }
25 |
26 | static GeneralDiscussionItem fromJson(String jsonString) {
27 | return serializers.deserializeWith(
28 | GeneralDiscussionItem.serializer, json.decode(jsonString));
29 | }
30 |
31 | static Serializer get serializer =>
32 | _$generalDiscussionItemSerializer;
33 | }
34 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/user/social/NetworkServiceTagLink.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:built_value/built_value.dart';
4 | import 'package:built_value/serializer.dart';
5 | import 'package:munin/models/bangumi/user/social/NetworkServiceTag.dart';
6 | import 'package:munin/models/bangumi/user/social/NetworkServiceType.dart';
7 | import 'package:munin/shared/utils/serializers.dart';
8 |
9 | part 'NetworkServiceTagLink.g.dart';
10 |
11 | abstract class NetworkServiceTagLink
12 | implements
13 | NetworkServiceTag,
14 | Built {
15 | NetworkServiceTagLink._();
16 |
17 | String get link;
18 |
19 | factory NetworkServiceTagLink(
20 | [void Function(NetworkServiceTagLinkBuilder) updates]) =
21 | _$NetworkServiceTagLink;
22 |
23 | String toJson() {
24 | return json.encode(
25 | serializers.serializeWith(NetworkServiceTagLink.serializer, this));
26 | }
27 |
28 | static NetworkServiceTagLink fromJson(String jsonString) {
29 | return serializers.deserializeWith(
30 | NetworkServiceTagLink.serializer, json.decode(jsonString));
31 | }
32 |
33 | static Serializer get serializer =>
34 | _$networkServiceTagLinkSerializer;
35 | }
36 |
--------------------------------------------------------------------------------
/app/lib/widgets/subject/mainpage/RelatedSubjectsPreview.dart:
--------------------------------------------------------------------------------
1 | import 'package:built_collection/built_collection.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:munin/models/bangumi/setting/general/PreferredSubjectInfoLanguage.dart';
4 | import 'package:munin/models/bangumi/subject/RelatedSubject.dart';
5 | import 'package:munin/widgets/subject/common/HorizontalRelatedSubjects.dart';
6 | import 'package:munin/widgets/subject/mainpage/SubjectMoreItemsEntry.dart';
7 |
8 | class RelatedSubjectsPreview extends StatelessWidget {
9 | final BuiltListMultimap relatedSubjects;
10 |
11 | final PreferredSubjectInfoLanguage preferredSubjectInfoLanguage;
12 |
13 | const RelatedSubjectsPreview({Key key,
14 | @required this.relatedSubjects,
15 | @required this.preferredSubjectInfoLanguage})
16 | : super(key: key);
17 |
18 | @override
19 | Widget build(BuildContext context) {
20 | return Column(
21 | children: [
22 | SubjectMoreItemsEntry(
23 | moreItemsText: '关联条目',
24 | ),
25 | HorizontalRelatedSubjects(
26 | relatedSubjects: relatedSubjects.values,
27 | preferredSubjectInfoLanguage: preferredSubjectInfoLanguage,
28 | ),
29 | ],
30 | );
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/progress/common/BaseEpisode.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'BaseEpisode.dart';
4 |
5 | // **************************************************************************
6 | // BuiltValueGenerator
7 | // **************************************************************************
8 |
9 | abstract class BaseEpisodeBuilder {
10 | void replace(BaseEpisode other);
11 | void update(void Function(BaseEpisodeBuilder) updates);
12 | int get id;
13 | set id(int id);
14 |
15 | String get name;
16 | set name(String name);
17 |
18 | String get chineseName;
19 | set chineseName(String chineseName);
20 |
21 | AirStatus get airStatus;
22 | set airStatus(AirStatus airStatus);
23 |
24 | EpisodeStatus get userEpisodeStatus;
25 | set userEpisodeStatus(EpisodeStatus userEpisodeStatus);
26 |
27 | EpisodeType get episodeType;
28 | set episodeType(EpisodeType episodeType);
29 | }
30 |
31 | // ignore_for_file: always_put_control_body_on_new_line,always_specify_types,annotate_overrides,avoid_annotating_with_dynamic,avoid_as,avoid_catches_without_on_clauses,avoid_returning_this,lines_longer_than_80_chars,omit_local_variable_types,prefer_expression_function_bodies,sort_constructors_first,test_types_in_equals,unnecessary_const,unnecessary_new
32 |
--------------------------------------------------------------------------------
/app/test/shared/utils/misc/DeviceInfo_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:munin/shared/utils/misc/DeviceInfo.dart';
2 | import 'package:pub_semver/pub_semver.dart';
3 | import 'package:test/test.dart';
4 |
5 | void main() {
6 | group('parseIOSVersion', () {
7 | test('Parses 13', () {
8 | expect(VersionExtension.parseIOSVersion('13'), Version(13, 0, 0));
9 | });
10 |
11 | test('Parses 13.0', () {
12 | expect(VersionExtension.parseIOSVersion('13.0'), Version(13, 0, 0));
13 | });
14 |
15 | test('Parses 13.1', () {
16 | expect(VersionExtension.parseIOSVersion('13.1'), Version(13, 1, 0));
17 | });
18 |
19 | test('Parses 13.1.2', () {
20 | expect(VersionExtension.parseIOSVersion('13.1.2'), Version(13, 1, 2));
21 | });
22 |
23 | test('Throws error on 13.1.2.1', () {
24 | expect(VersionExtension.parseIOSVersion('13.1.2'), Version(13, 1, 2));
25 | });
26 |
27 | test('Parses 13.1.2.1 and ignores version string after last minor', () {
28 | expect(VersionExtension.parseIOSVersion('13.1.2.1'), Version(13, 1, 2));
29 | });
30 |
31 | test('Throws error on invalidVersion', () {
32 | expect(() => VersionExtension.parseIOSVersion('invalidVersion'),
33 | throwsA(TypeMatcher()));
34 | });
35 | });
36 | }
37 |
--------------------------------------------------------------------------------
/app/lib/widgets/shared/cover/ClickableCachedRoundedCover.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:munin/models/bangumi/timeline/common/BangumiContent.dart';
3 | import 'package:munin/widgets/shared/cover/CachedRoundedCover.dart';
4 | import 'package:munin/widgets/shared/utils/common.dart';
5 |
6 | class ClickableCachedRoundedCover extends StatelessWidget {
7 | final String imageUrl;
8 | final String id;
9 | final double width;
10 | final double height;
11 | final BangumiContent contentType;
12 |
13 | ClickableCachedRoundedCover(
14 | {@required this.imageUrl,
15 | @required this.contentType,
16 | @required this.width,
17 | @required this.height,
18 | @required this.id});
19 |
20 | ClickableCachedRoundedCover.asGridSize({
21 | @required this.imageUrl,
22 | @required this.contentType,
23 | @required this.id,
24 | this.width = 48.0,
25 | this.height = 48.0,
26 | });
27 |
28 | @override
29 | Widget build(BuildContext context) {
30 | return InkWell(
31 | child: CachedRoundedCover(
32 | imageUrl: this.imageUrl, width: this.width, height: this.height),
33 | onTap: generateOnTapCallbackForBangumiContent(
34 | contentType: contentType, id: id, context: context),
35 | );
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/timeline/message/PublicMessageReply.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:built_value/built_value.dart';
4 | import 'package:built_value/serializer.dart';
5 | import 'package:munin/models/bangumi/common/BangumiUserBasic.dart';
6 | import 'package:munin/shared/utils/serializers.dart';
7 |
8 | part 'PublicMessageReply.g.dart';
9 |
10 | abstract class PublicMessageReply
11 | implements Built {
12 | /// author of the reply.
13 | BangumiUserBasic get author;
14 |
15 | /// Reply content in html.
16 | String get contentHtml;
17 |
18 | /// Reply content in plainText.
19 | String get contentText;
20 |
21 | PublicMessageReply._();
22 |
23 | factory PublicMessageReply(
24 | [void Function(PublicMessageReplyBuilder) updates]) =
25 | _$PublicMessageReply;
26 |
27 | String toJson() {
28 | return json
29 | .encode(serializers.serializeWith(PublicMessageReply.serializer, this));
30 | }
31 |
32 | static PublicMessageReply fromJson(String jsonString) {
33 | return serializers.deserializeWith(
34 | PublicMessageReply.serializer, json.decode(jsonString));
35 | }
36 |
37 | static Serializer get serializer =>
38 | _$publicMessageReplySerializer;
39 | }
40 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/subject/common/ParentSubject.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:built_value/built_value.dart';
4 | import 'package:built_value/serializer.dart';
5 | import 'package:munin/models/bangumi/common/BangumiImage.dart';
6 | import 'package:munin/models/bangumi/subject/common/SujectBase.dart';
7 | import 'package:munin/shared/utils/serializers.dart';
8 |
9 | part 'ParentSubject.g.dart';
10 |
11 | /// A subject as seen on subject page.
12 | /// It's called a 'parent' subject because it's typically shown on subject episode,
13 | /// subject thread pages where these items are linked to a parent subject.
14 | abstract class ParentSubject
15 | implements SubjectBase, Built {
16 | BangumiImage get cover;
17 |
18 | ParentSubject._();
19 |
20 | factory ParentSubject([void Function(ParentSubjectBuilder) updates]) =
21 | _$ParentSubject;
22 |
23 | String toJson() {
24 | return json
25 | .encode(serializers.serializeWith(ParentSubject.serializer, this));
26 | }
27 |
28 | static ParentSubject fromJson(String jsonString) {
29 | return serializers.deserializeWith(
30 | ParentSubject.serializer, json.decode(jsonString));
31 | }
32 |
33 | static Serializer get serializer => _$parentSubjectSerializer;
34 | }
35 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/discussion/thread/common/Post.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'Post.dart';
4 |
5 | // **************************************************************************
6 | // BuiltValueGenerator
7 | // **************************************************************************
8 |
9 | abstract class PostBuilder {
10 | void replace(Post other);
11 | void update(void Function(PostBuilder) updates);
12 | BangumiUserBasicBuilder get author;
13 | set author(BangumiUserBasicBuilder author);
14 |
15 | int get id;
16 | set id(int id);
17 |
18 | String get contentHtml;
19 | set contentHtml(String contentHtml);
20 |
21 | String get authorPostedText;
22 | set authorPostedText(String authorPostedText);
23 |
24 | int get postTimeInMilliSeconds;
25 | set postTimeInMilliSeconds(int postTimeInMilliSeconds);
26 |
27 | int get mainSequentialNumber;
28 | set mainSequentialNumber(int mainSequentialNumber);
29 | }
30 |
31 | // ignore_for_file: always_put_control_body_on_new_line,always_specify_types,annotate_overrides,avoid_annotating_with_dynamic,avoid_as,avoid_catches_without_on_clauses,avoid_returning_this,lines_longer_than_80_chars,omit_local_variable_types,prefer_expression_function_bodies,sort_constructors_first,test_types_in_equals,unnecessary_const,unnecessary_new
32 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/user/social/NetworkServiceTagPlainText.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:built_value/built_value.dart';
4 | import 'package:built_value/serializer.dart';
5 | import 'package:munin/models/bangumi/user/social/NetworkServiceTag.dart';
6 | import 'package:munin/models/bangumi/user/social/NetworkServiceType.dart';
7 | import 'package:munin/shared/utils/serializers.dart';
8 |
9 | part 'NetworkServiceTagPlainText.g.dart';
10 |
11 | abstract class NetworkServiceTagPlainText
12 | implements
13 | NetworkServiceTag,
14 | Built {
15 | NetworkServiceTagPlainText._();
16 |
17 | factory NetworkServiceTagPlainText(
18 | [void Function(NetworkServiceTagPlainTextBuilder) updates]) =
19 | _$NetworkServiceTagPlainText;
20 |
21 | String toJson() {
22 | return json.encode(
23 | serializers.serializeWith(NetworkServiceTagPlainText.serializer, this));
24 | }
25 |
26 | static NetworkServiceTagPlainText fromJson(String jsonString) {
27 | return serializers.deserializeWith(
28 | NetworkServiceTagPlainText.serializer, json.decode(jsonString));
29 | }
30 |
31 | static Serializer get serializer =>
32 | _$networkServiceTagPlainTextSerializer;
33 | }
34 |
--------------------------------------------------------------------------------
/app/lib/widgets/shared/chips/StrokeChip.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | /// A [ChoiceChip] that has a background which is by default same as
4 | /// CanvasColor of the Theme, textColor/borderColor is the same, as set by the
5 | /// pass-in parameter.
6 | ///
7 | class StrokeChip extends StatelessWidget {
8 | /// Callback when the chip is pressed.
9 | /// If [onPressed] is null, a [RawChip] instead of [ActionChip] will be used.
10 | final VoidCallback onPressed;
11 |
12 | final Color borderColor;
13 |
14 | /// Label on the chip.
15 | final Widget label;
16 |
17 | const StrokeChip({
18 | Key key,
19 | @required this.borderColor,
20 | @required this.label,
21 | this.onPressed,
22 | }) : super(key: key);
23 |
24 | @override
25 | Widget build(BuildContext context) {
26 | final shape = StadiumBorder(
27 | side: BorderSide(
28 | color: borderColor,
29 | ));
30 |
31 | if (onPressed == null) {
32 | return RawChip(
33 | label: label,
34 | backgroundColor: Theme.of(context).canvasColor,
35 | shape: shape,
36 | );
37 | }
38 |
39 | return ActionChip(
40 | onPressed: onPressed,
41 | label: label,
42 | backgroundColor: Theme.of(context).canvasColor,
43 | shape: shape,
44 | );
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/user/notification/GeneralNotificationItem.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:built_value/built_value.dart';
4 | import 'package:built_value/serializer.dart';
5 | import 'package:munin/models/bangumi/common/BangumiUserBasic.dart';
6 | import 'package:munin/models/bangumi/user/notification/BaseNotificationItem.dart';
7 | import 'package:munin/shared/utils/serializers.dart';
8 |
9 | part 'GeneralNotificationItem.g.dart';
10 |
11 | /// A general-purpose notification item.
12 | abstract class GeneralNotificationItem
13 | implements
14 | Built,
15 | BaseNotificationItem {
16 | GeneralNotificationItem._();
17 |
18 | factory GeneralNotificationItem(
19 | [void Function(GeneralNotificationItemBuilder) updates]) =
20 | _$GeneralNotificationItem;
21 |
22 | String toJson() {
23 | return json.encode(
24 | serializers.serializeWith(GeneralNotificationItem.serializer, this));
25 | }
26 |
27 | static GeneralNotificationItem fromJson(String jsonString) {
28 | return serializers.deserializeWith(
29 | GeneralNotificationItem.serializer, json.decode(jsonString));
30 | }
31 |
32 | static Serializer get serializer =>
33 | _$generalNotificationItemSerializer;
34 | }
35 |
--------------------------------------------------------------------------------
/app/lib/widgets/setting/general/BrowserSettingWidget.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:munin/models/bangumi/setting/general/browser/LaunchBrowserPreference.dart';
3 | import 'package:munin/widgets/shared/selection/MuninExpansionSelection.dart';
4 |
5 | class BrowserSettingWidget extends StatelessWidget {
6 | final LaunchBrowserPreference currentLaunchBrowserPreference;
7 |
8 | final Function(LaunchBrowserPreference launchBrowserPreference) onTapOption;
9 |
10 | const BrowserSettingWidget({
11 | Key key,
12 | @required this.currentLaunchBrowserPreference,
13 | @required this.onTapOption,
14 | }) : super(key: key);
15 |
16 | @override
17 | Widget build(BuildContext context) {
18 | return MuninExpansionSelection(
19 | expansionKey:
20 | PageStorageKey('general-setting-LaunchBrowserPreference'),
21 | title: Text('打开链接时使用'),
22 | optionTitleBuilder: (selection) => Text(selection.chineseName),
23 | optionSubTitleBuilder: (selection) {
24 | return Text(selection.subChineseName);
25 | },
26 | options: LaunchBrowserPreference.values,
27 | currentSelection: currentLaunchBrowserPreference ??
28 | LaunchBrowserPreference.DefaultInApp,
29 | onTapOption: onTapOption,
30 | );
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/app/ios/Runner/GoogleService-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CLIENT_ID
6 | 674801028596-rae532fpdlshhba5at0bkudfh0d2dbj6.apps.googleusercontent.com
7 | REVERSED_CLIENT_ID
8 | com.googleusercontent.apps.674801028596-rae532fpdlshhba5at0bkudfh0d2dbj6
9 | API_KEY
10 | AIzaSyCT23Jxa4PcW7JO8jkh9eWeRg8wcIUkbAQ
11 | GCM_SENDER_ID
12 | 674801028596
13 | PLIST_VERSION
14 | 1
15 | BUNDLE_ID
16 | com.bangumin.munin.ios
17 | PROJECT_ID
18 | bangumin-app
19 | STORAGE_BUCKET
20 | bangumin-app.appspot.com
21 | IS_ADS_ENABLED
22 |
23 | IS_ANALYTICS_ENABLED
24 |
25 | IS_APPINVITE_ENABLED
26 |
27 | IS_GCM_ENABLED
28 |
29 | IS_SIGNIN_ENABLED
30 |
31 | GOOGLE_APP_ID
32 | 1:674801028596:ios:3e039bc7540951cb
33 | DATABASE_URL
34 | https://bangumin-app.firebaseio.com
35 |
36 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/discussion/DiscussionItem.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'DiscussionItem.dart';
4 |
5 | // **************************************************************************
6 | // BuiltValueGenerator
7 | // **************************************************************************
8 |
9 | abstract class DiscussionItemBuilder {
10 | void replace(DiscussionItem other);
11 | void update(void Function(DiscussionItemBuilder) updates);
12 | int get id;
13 | set id(int id);
14 |
15 | BangumiContent get bangumiContent;
16 | set bangumiContent(BangumiContent bangumiContent);
17 |
18 | BangumiImageBuilder get image;
19 | set image(BangumiImageBuilder image);
20 |
21 | String get title;
22 | set title(String title);
23 |
24 | String get subTitle;
25 | set subTitle(String subTitle);
26 |
27 | int get replyCount;
28 | set replyCount(int replyCount);
29 |
30 | int get updatedAt;
31 | set updatedAt(int updatedAt);
32 | }
33 |
34 | // ignore_for_file: always_put_control_body_on_new_line,always_specify_types,annotate_overrides,avoid_annotating_with_dynamic,avoid_as,avoid_catches_without_on_clauses,avoid_returning_this,lines_longer_than_80_chars,omit_local_variable_types,prefer_expression_function_bodies,sort_constructors_first,test_types_in_equals,unnecessary_const,unnecessary_new
35 |
--------------------------------------------------------------------------------
/app/lib/redux/search/SearchReducer.dart:
--------------------------------------------------------------------------------
1 | import 'package:munin/models/bangumi/search/SearchRequest.dart';
2 | import 'package:munin/models/bangumi/search/result/BangumiGeneralSearchResponse.dart';
3 | import 'package:munin/redux/search/SearchActions.dart';
4 | import 'package:munin/redux/search/SearchState.dart';
5 | import 'package:redux/redux.dart';
6 |
7 | final searchReducers = combineReducers([
8 | /// Search Subject
9 | TypedReducer(searchSubjectSuccessReducer),
10 | ]);
11 |
12 | SearchState searchSubjectSuccessReducer(
13 | SearchState searchState, SearchSuccessAction searchSubjectSuccessAction) {
14 | SearchRequest searchRequest = searchSubjectSuccessAction.searchRequest;
15 | BangumiGeneralSearchResponse responseToUpdate =
16 | searchState.results[searchRequest];
17 | BangumiGeneralSearchResponse newResponse =
18 | searchSubjectSuccessAction.searchResponse;
19 | if (responseToUpdate == null) {
20 | responseToUpdate = newResponse;
21 | } else {
22 | responseToUpdate = responseToUpdate.rebuild((b) => b
23 | ..requestedResults = b.requestedResults + newResponse.requestedResults
24 | ..results.addAll(newResponse.results.asMap()));
25 | }
26 |
27 | return searchState.rebuild(
28 | (b) => b..results.addAll({searchRequest: responseToUpdate}),
29 | );
30 | }
31 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # BangumiN
2 |
3 |
4 |
5 |
6 | BangumiN - Yet another Bangumi app.
7 |
8 | BangumiN is a native client written in Flutter, an app that aims to provide essential Bangumi features,
9 | munin runs on modern iOS/Android/(+?) systems.
10 |
11 | Disclaimer: this is a third-party app, we are not affiliated with the Bangumi officials.
12 |
13 |
14 |
15 | # Preview
16 |
17 |
18 |
19 |
20 | # Install
21 |
22 | * For app
23 |
24 | See instructions in app folder
25 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/BangumiUserIdentity.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:built_value/built_value.dart';
4 | import 'package:built_value/serializer.dart';
5 | import 'package:munin/shared/utils/serializers.dart';
6 |
7 | part 'BangumiUserIdentity.g.dart';
8 |
9 | abstract class BangumiUserIdentity
10 | implements Built {
11 | BangumiUserIdentity._();
12 |
13 | factory BangumiUserIdentity([updates(BangumiUserIdentityBuilder b)]) =
14 | _$BangumiUserIdentity;
15 |
16 | @BuiltValueField(wireName: 'access_token')
17 | String get accessToken;
18 |
19 | @BuiltValueField(wireName: 'client_id')
20 | String get clientId;
21 |
22 | @BuiltValueField(wireName: 'expires')
23 | int get expires;
24 |
25 | @BuiltValueField(wireName: 'scope')
26 | @nullable
27 | String get scope;
28 |
29 | @BuiltValueField(wireName: 'user_id')
30 | int get id;
31 |
32 | String toJson() {
33 | return json.encode(
34 | serializers.serializeWith(BangumiUserIdentity.serializer, this));
35 | }
36 |
37 | static BangumiUserIdentity fromJson(String jsonString) {
38 | return serializers.deserializeWith(
39 | BangumiUserIdentity.serializer, json.decode(jsonString));
40 | }
41 |
42 | static Serializer get serializer =>
43 | _$bangumiUserIdentitySerializer;
44 | }
45 |
--------------------------------------------------------------------------------
/app/lib/providers/storage/SecureStorageService.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_secure_storage/flutter_secure_storage.dart';
2 | import 'package:meta/meta.dart';
3 | import 'package:munin/models/bangumi/BangumiCookieCredentials.dart';
4 | import 'package:oauth2/oauth2.dart';
5 |
6 | class SecureStorageService {
7 | final FlutterSecureStorage secureStorage;
8 |
9 | static const String cookieCredentialsKey = 'bangumiCookieCredentials';
10 | static const String oauthCredentialsKey = 'bangumiOauthCredentials';
11 |
12 | SecureStorageService({@required this.secureStorage});
13 |
14 | Future persistBangumiOauthCredentials(Credentials credentials) async {
15 | assert(credentials != null);
16 | return this
17 | .secureStorage
18 | .write(key: oauthCredentialsKey, value: credentials.toJson());
19 | }
20 |
21 | Future clearBangumiOauthCredentials() async {
22 | return this.secureStorage.delete(key: oauthCredentialsKey);
23 | }
24 |
25 | Future persistBangumiCookieCredentials(
26 | BangumiCookieCredentials credentials) async {
27 | assert(credentials != null);
28 | return this
29 | .secureStorage
30 | .write(key: cookieCredentialsKey, value: credentials.toJson());
31 | }
32 |
33 | Future clearBangumiCookieCredentials() async {
34 | return this.secureStorage.delete(key: cookieCredentialsKey);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/progress/html/SubjectEpisodes.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:built_collection/built_collection.dart';
4 | import 'package:built_value/built_value.dart';
5 | import 'package:built_value/serializer.dart';
6 | import 'package:munin/models/bangumi/progress/html/SimpleHtmlBasedEpisode.dart';
7 | import 'package:munin/models/bangumi/subject/common/ParentSubject.dart';
8 | import 'package:munin/shared/utils/serializers.dart';
9 |
10 | part 'SubjectEpisodes.g.dart';
11 |
12 | /// All episodes of a subject, as seen on bangumi web page, like https://bgm.tv/subject/1836/ep.
13 | abstract class SubjectEpisodes
14 | implements Built {
15 | @nullable
16 | ParentSubject get subject;
17 |
18 | BuiltMap get episodes;
19 |
20 | SubjectEpisodes._();
21 |
22 | factory SubjectEpisodes([void Function(SubjectEpisodesBuilder) updates]) =
23 | _$SubjectEpisodes;
24 |
25 | String toJson() {
26 | return json
27 | .encode(serializers.serializeWith(SubjectEpisodes.serializer, this));
28 | }
29 |
30 | static SubjectEpisodes fromJson(String jsonString) {
31 | return serializers.deserializeWith(
32 | SubjectEpisodes.serializer, json.decode(jsonString));
33 | }
34 |
35 | static Serializer get serializer =>
36 | _$subjectEpisodesSerializer;
37 | }
38 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/search/result/MonoSearchResult.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:built_collection/built_collection.dart';
4 | import 'package:built_value/built_value.dart';
5 | import 'package:built_value/serializer.dart';
6 | import 'package:munin/models/bangumi/common/BangumiImage.dart';
7 | import 'package:munin/models/bangumi/search/SearchType.dart';
8 | import 'package:munin/models/bangumi/search/result/SearchResultItem.dart';
9 | import 'package:munin/shared/utils/serializers.dart';
10 |
11 | part 'MonoSearchResult.g.dart';
12 |
13 | abstract class MonoSearchResult
14 | implements
15 | SearchResultItem,
16 | Built {
17 | BuiltList get miscInfo;
18 |
19 | @BuiltValueField(wireName: 'name_cn')
20 | String get chineseName;
21 |
22 | factory MonoSearchResult([updates(MonoSearchResultBuilder b)]) =
23 | _$MonoSearchResult;
24 |
25 | MonoSearchResult._();
26 |
27 | String toJson() {
28 | return json
29 | .encode(serializers.serializeWith(MonoSearchResult.serializer, this));
30 | }
31 |
32 | static MonoSearchResult fromJson(String jsonString) {
33 | return serializers.deserializeWith(
34 | MonoSearchResult.serializer, json.decode(jsonString));
35 | }
36 |
37 | static Serializer get serializer =>
38 | _$monoSearchResultSerializer;
39 | }
40 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/subject/common/SubjectBaseWithCover.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:built_value/built_value.dart';
4 | import 'package:built_value/serializer.dart';
5 | import 'package:munin/models/bangumi/common/BangumiImage.dart';
6 | import 'package:munin/models/bangumi/subject/common/SujectBase.dart';
7 | import 'package:munin/shared/utils/serializers.dart';
8 |
9 | part 'SubjectBaseWithCover.g.dart';
10 |
11 | /// a subject that has a cover.
12 | abstract class SubjectBaseWithCover
13 | implements
14 | SubjectBase,
15 | Built {
16 | /// Images might be intentionally set to null because
17 | /// of [displayedAsPlainText] in [CollectionPreview]
18 | @nullable
19 | BangumiImage get cover;
20 |
21 | SubjectBaseWithCover._();
22 |
23 | factory SubjectBaseWithCover([updates(SubjectBaseWithCoverBuilder b)]) =
24 | _$SubjectBaseWithCover;
25 |
26 | String toJson() {
27 | return json.encode(
28 | serializers.serializeWith(SubjectBaseWithCover.serializer, this));
29 | }
30 |
31 | static SubjectBaseWithCover fromJson(String jsonString) {
32 | return serializers.deserializeWith(
33 | SubjectBaseWithCover.serializer, json.decode(jsonString));
34 | }
35 |
36 | static Serializer get serializer =>
37 | _$subjectBaseWithCoverSerializer;
38 | }
39 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/subject/RelatedSubject.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:built_value/built_value.dart';
4 | import 'package:built_value/serializer.dart';
5 | import 'package:munin/models/bangumi/common/BangumiImage.dart';
6 | import 'package:munin/models/bangumi/subject/common/SujectBase.dart';
7 | import 'package:munin/shared/utils/serializers.dart';
8 |
9 | part 'RelatedSubject.g.dart';
10 |
11 | /// a subject that's listed as related subject('关联条目') on subject main page
12 | abstract class RelatedSubject
13 | implements SubjectBase, Built {
14 | /// i.e. 画集, 原声集, 片头曲...
15 | /// ideally this should be a enum, however due to the nature of parsing and
16 | /// the number of subtype, it's currently a string
17 | String get subjectSubTypeName;
18 |
19 | @nullable
20 | BangumiImage get cover;
21 |
22 | RelatedSubject._();
23 |
24 | factory RelatedSubject([updates(RelatedSubjectBuilder b)]) = _$RelatedSubject;
25 |
26 | String toJson() {
27 | return json
28 | .encode(serializers.serializeWith(RelatedSubject.serializer, this));
29 | }
30 |
31 | static RelatedSubject fromJson(String jsonString) {
32 | return serializers.deserializeWith(
33 | RelatedSubject.serializer, json.decode(jsonString));
34 | }
35 |
36 | static Serializer get serializer =>
37 | _$relatedSubjectSerializer;
38 | }
39 |
--------------------------------------------------------------------------------
/app/lib/models/bangumi/subject/info/InfoBoxItem.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:built_value/built_value.dart';
4 | import 'package:built_value/serializer.dart';
5 | import 'package:munin/models/bangumi/timeline/common/BangumiContent.dart';
6 | import 'package:munin/shared/utils/serializers.dart';
7 |
8 | part 'InfoBoxItem.g.dart';
9 |
10 | /// The smallest element in a info box
11 | /// i.e. a staff under a job
12 | abstract class InfoBoxItem implements Built {
13 | InfoBoxItem._();
14 |
15 | BangumiContent get type;
16 |
17 | /// Item name, i.e. staff name, air date...
18 | String get name;
19 |
20 | @nullable
21 |
22 | /// id of the InfoBoxItem, if there is any. For plain text such id doesn't exist
23 | String get id;
24 |
25 | /// if we cannot parse content, a fallback webview might be used
26 | /// hence an optional link is needed
27 | @nullable
28 | String get pageUrl;
29 |
30 | factory InfoBoxItem([updates(InfoBoxItemBuilder b)]) = _$InfoBoxItem;
31 |
32 | String toJson() {
33 | return json.encode(serializers.serializeWith(InfoBoxItem.serializer, this));
34 | }
35 |
36 | static InfoBoxItem fromJson(String jsonString) {
37 | return serializers.deserializeWith(
38 | InfoBoxItem.serializer, json.decode(jsonString));
39 | }
40 |
41 | static Serializer get serializer => _$infoBoxItemSerializer;
42 | }
43 |
--------------------------------------------------------------------------------
/app/lib/redux/shared/RequestStatus.dart:
--------------------------------------------------------------------------------
1 | import 'package:built_collection/built_collection.dart';
2 | import 'package:built_value/built_value.dart';
3 | import 'package:built_value/serializer.dart';
4 |
5 | part 'RequestStatus.g.dart';
6 |
7 | class RequestStatus extends EnumClass {
8 | /// Request has not started yet
9 | static const RequestStatus Initial = _$Initial;
10 |
11 | static const RequestStatus Loading = _$Loading;
12 | static const RequestStatus Success = _$Success;
13 |
14 | /// New exception/error should be added to [isException]
15 | static const RequestStatus TimeoutException = _$Timeout;
16 | static const RequestStatus UnknownException = _$UnknownError;
17 |
18 | /// If new status is a exception/error, please add it here
19 | @memoized
20 | bool get isException {
21 | return this == RequestStatus.TimeoutException ||
22 | this == RequestStatus.UnknownException;
23 | }
24 |
25 | /// Checks whether next request can be initialized.
26 | @memoized
27 | bool get canInitializeNextRequest {
28 | return this == RequestStatus.Initial || this == RequestStatus.Success;
29 | }
30 |
31 | const RequestStatus._(String name) : super(name);
32 |
33 | static BuiltSet get values => _$values;
34 |
35 | static RequestStatus valueOf(String name) => _$valueOf(name);
36 |
37 | static Serializer get serializer => _$requestStatusSerializer;
38 | }
39 |
--------------------------------------------------------------------------------