diff --git a/.gitignore b/.gitignore index 6186cb870d..0e2850a01c 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ tools/** build/tools/** +fastlane/report.xml # Build results bin/[Dd]ebug/ diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000000..cdd3a6b349 --- /dev/null +++ b/Gemfile @@ -0,0 +1,6 @@ +source "https://rubygems.org" + +gem "fastlane" + +plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile') +eval_gemfile(plugins_path) if File.exist?(plugins_path) diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000000..17c0db12e7 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,173 @@ +GEM + remote: https://rubygems.org/ + specs: + CFPropertyList (3.0.0) + addressable (2.6.0) + public_suffix (>= 2.0.2, < 4.0) + atomos (0.1.3) + babosa (1.0.2) + claide (1.0.2) + colored (1.2) + colored2 (3.1.2) + commander-fastlane (4.4.6) + highline (~> 1.7.2) + declarative (0.0.10) + declarative-option (0.1.0) + digest-crc (0.4.1) + domain_name (0.5.20180417) + unf (>= 0.0.5, < 1.0.0) + dotenv (2.7.1) + emoji_regex (1.0.1) + excon (0.62.0) + faraday (0.15.4) + multipart-post (>= 1.2, < 3) + faraday-cookie_jar (0.0.6) + faraday (>= 0.7.4) + http-cookie (~> 1.0.0) + faraday_middleware (0.13.1) + faraday (>= 0.7.4, < 1.0) + fastimage (2.1.5) + fastlane (2.117.0) + CFPropertyList (>= 2.3, < 4.0.0) + addressable (>= 2.3, < 3.0.0) + babosa (>= 1.0.2, < 2.0.0) + bundler (>= 1.12.0, < 3.0.0) + colored + commander-fastlane (>= 4.4.6, < 5.0.0) + dotenv (>= 2.1.1, < 3.0.0) + emoji_regex (>= 0.1, < 2.0) + excon (>= 0.45.0, < 1.0.0) + faraday (~> 0.9) + faraday-cookie_jar (~> 0.0.6) + faraday_middleware (~> 0.9) + fastimage (>= 2.1.0, < 3.0.0) + gh_inspector (>= 1.1.2, < 2.0.0) + google-api-client (>= 0.21.2, < 0.24.0) + google-cloud-storage (>= 1.15.0, < 2.0.0) + highline (>= 1.7.2, < 2.0.0) + json (< 3.0.0) + mini_magick (~> 4.5.1) + multi_json + multi_xml (~> 0.5) + multipart-post (~> 2.0.0) + plist (>= 3.1.0, < 4.0.0) + public_suffix (~> 2.0.0) + rubyzip (>= 1.2.2, < 2.0.0) + security (= 0.1.3) + simctl (~> 1.6.3) + slack-notifier (>= 2.0.0, < 3.0.0) + terminal-notifier (>= 1.6.2, < 2.0.0) + terminal-table (>= 1.4.5, < 2.0.0) + tty-screen (>= 0.6.3, < 1.0.0) + tty-spinner (>= 0.8.0, < 1.0.0) + word_wrap (~> 1.0.0) + xcodeproj (>= 1.6.0, < 2.0.0) + xcpretty (~> 0.3.0) + xcpretty-travis-formatter (>= 0.0.3) + fastlane-plugin-clean_testflight_testers (0.2.0) + fastlane-plugin-souyuz (0.8.1) + souyuz (>= 0.8.1) + fastlane-plugin-xamarin (0.6.3) + gh_inspector (1.1.3) + google-api-client (0.23.9) + addressable (~> 2.5, >= 2.5.1) + googleauth (>= 0.5, < 0.7.0) + httpclient (>= 2.8.1, < 3.0) + mime-types (~> 3.0) + representable (~> 3.0) + retriable (>= 2.0, < 4.0) + signet (~> 0.9) + google-cloud-core (1.3.0) + google-cloud-env (~> 1.0) + google-cloud-env (1.0.5) + faraday (~> 0.11) + google-cloud-storage (1.16.0) + digest-crc (~> 0.4) + google-api-client (~> 0.23) + google-cloud-core (~> 1.2) + googleauth (>= 0.6.2, < 0.10.0) + googleauth (0.6.7) + faraday (~> 0.12) + jwt (>= 1.4, < 3.0) + memoist (~> 0.16) + multi_json (~> 1.11) + os (>= 0.9, < 2.0) + signet (~> 0.7) + highline (1.7.10) + http-cookie (1.0.3) + domain_name (~> 0.5) + httpclient (2.8.3) + json (2.2.0) + jwt (2.1.0) + memoist (0.16.0) + mime-types (3.2.2) + mime-types-data (~> 3.2015) + mime-types-data (3.2018.0812) + mini_magick (4.5.1) + mini_portile2 (2.4.0) + multi_json (1.13.1) + multi_xml (0.6.0) + multipart-post (2.0.0) + nanaimo (0.2.6) + naturally (2.2.0) + nokogiri (1.10.1) + mini_portile2 (~> 2.4.0) + os (1.0.0) + plist (3.5.0) + public_suffix (2.0.5) + representable (3.0.4) + declarative (< 0.1.0) + declarative-option (< 0.2.0) + uber (< 0.2.0) + retriable (3.1.2) + rouge (2.0.7) + rubyzip (1.2.2) + security (0.1.3) + signet (0.11.0) + addressable (~> 2.3) + faraday (~> 0.9) + jwt (>= 1.5, < 3.0) + multi_json (~> 1.10) + simctl (1.6.5) + CFPropertyList + naturally + slack-notifier (2.3.2) + souyuz (0.8.1) + fastlane (>= 2.29.0) + highline (~> 1.7) + nokogiri (~> 1.7) + terminal-notifier (1.8.0) + terminal-table (1.8.0) + unicode-display_width (~> 1.1, >= 1.1.1) + tty-cursor (0.6.1) + tty-screen (0.6.5) + tty-spinner (0.9.0) + tty-cursor (~> 0.6.0) + uber (0.1.0) + unf (0.1.4) + unf_ext + unf_ext (0.0.7.5) + unicode-display_width (1.4.1) + word_wrap (1.0.0) + xcodeproj (1.8.1) + CFPropertyList (>= 2.3.3, < 4.0) + atomos (~> 0.1.3) + claide (>= 1.0.2, < 2.0) + colored2 (~> 3.1) + nanaimo (~> 0.2.6) + xcpretty (0.3.0) + rouge (~> 2.0.7) + xcpretty-travis-formatter (1.0.0) + xcpretty (~> 0.2, >= 0.0.7) + +PLATFORMS + ruby + +DEPENDENCIES + fastlane + fastlane-plugin-clean_testflight_testers + fastlane-plugin-souyuz + fastlane-plugin-xamarin + +BUNDLED WITH + 2.0.1 diff --git a/fastlane/Appfile b/fastlane/Appfile new file mode 100644 index 0000000000..083de66985 --- /dev/null +++ b/fastlane/Appfile @@ -0,0 +1,2 @@ +app_identifier("sh.ppy.osulazer") # The bundle identifier of your app +apple_id("apple-dev@ppy.sh") # Your Apple email address diff --git a/fastlane/Fastfile b/fastlane/Fastfile new file mode 100644 index 0000000000..3f64bcdf19 --- /dev/null +++ b/fastlane/Fastfile @@ -0,0 +1,65 @@ +update_fastlane + +default_platform(:ios) + +platform :ios do + lane :testflight_prune_dry do + clean_testflight_testers(days_of_inactivity:45, dry_run: true) + end + + # Specify a custom number for what's "inactive" + lane :testflight_prune do + clean_testflight_testers(days_of_inactivity: 45) # 120 days, so about 4 months + end + + lane :update_version do |options| + options[:plist_path] = '../osu.iOS/Info.plist' + app_version(options) + end + + desc 'Deploy to testflight' + lane :beta do |options| + update_version(options) + + provision( + type: 'appstore' + ) + + build( + build_configuration: 'Release', + build_platform: 'iPhone' + ) + + client = HTTPClient.new + changelog = client.get_content 'https://gist.githubusercontent.com/peppy/ab89c29dcc0dce95f39eb218e8fad197/raw' + changelog.gsub!('$BUILD_ID', options[:build]) + + pilot( + wait_processing_interval: 900, + changelog: changelog, + ipa: './osu.iOS/bin/iPhone/Release/osu.iOS.ipa' + ) + end + + desc 'Compile the project' + lane :build do + nuget_restore( + project_path: 'osu.iOS.sln' + ) + + souyuz( + platform: "ios", + build_target: "osu_iOS", + plist_path: "../osu.iOS/Info.plist" + ) + end + + desc 'Install provisioning profiles using match' + lane :provision do |options| + if Helper.is_ci? + options[:readonly] = true + end + + match(options) + end +end diff --git a/fastlane/Matchfile b/fastlane/Matchfile new file mode 100644 index 0000000000..40c974b09e --- /dev/null +++ b/fastlane/Matchfile @@ -0,0 +1 @@ +git_url('https://github.com/peppy/apple-certificates') diff --git a/fastlane/Pluginfile b/fastlane/Pluginfile new file mode 100644 index 0000000000..9f4f47f213 --- /dev/null +++ b/fastlane/Pluginfile @@ -0,0 +1,7 @@ +# Autogenerated by fastlane +# +# Ensure this file is checked in to source control! + +gem 'fastlane-plugin-clean_testflight_testers' +gem 'fastlane-plugin-souyuz' +gem 'fastlane-plugin-xamarin' diff --git a/fastlane/README.md b/fastlane/README.md new file mode 100644 index 0000000000..53bbc62cae --- /dev/null +++ b/fastlane/README.md @@ -0,0 +1,54 @@ +fastlane documentation +================ +# Installation + +Make sure you have the latest version of the Xcode command line tools installed: + +``` +xcode-select --install +``` + +Install _fastlane_ using +``` +[sudo] gem install fastlane -NV +``` +or alternatively using `brew cask install fastlane` + +# Available Actions +## iOS +### ios testflight_prune_dry +``` +fastlane ios testflight_prune_dry +``` + +### ios testflight_prune +``` +fastlane ios testflight_prune +``` + +### ios update_version +``` +fastlane ios update_version +``` + +### ios beta +``` +fastlane ios beta +``` +Deploy to testflight +### ios build +``` +fastlane ios build +``` +Compile the project +### ios provision +``` +fastlane ios provision +``` +Install provisioning profiles using match + +---- + +This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run. +More information about fastlane can be found on [fastlane.tools](https://fastlane.tools). +The documentation of fastlane can be found on [docs.fastlane.tools](https://docs.fastlane.tools). diff --git a/osu.Game/Beatmaps/BeatmapStore.cs b/osu.Game/Beatmaps/BeatmapStore.cs index 6786a780b6..f4b7b1d74f 100644 --- a/osu.Game/Beatmaps/BeatmapStore.cs +++ b/osu.Game/Beatmaps/BeatmapStore.cs @@ -12,7 +12,7 @@ namespace osu.Game.Beatmaps /// /// Handles the storage and retrieval of Beatmaps/BeatmapSets to the database backing /// - public class BeatmapStore : MutableDatabaseBackedStore + public class BeatmapStore : MutableDatabaseBackedStoreWithFileIncludes { public event Action BeatmapHidden; public event Action BeatmapRestored; @@ -64,18 +64,17 @@ namespace osu.Game.Beatmaps protected override IQueryable AddIncludesForDeletion(IQueryable query) => base.AddIncludesForDeletion(query) - .Include(s => s.Beatmaps).ThenInclude(b => b.Metadata) - .Include(s => s.Beatmaps).ThenInclude(b => b.BaseDifficulty) .Include(s => s.Metadata) - .Include(s => s.Beatmaps).ThenInclude(b => b.Scores); + .Include(s => s.Beatmaps).ThenInclude(b => b.Scores) + .Include(s => s.Beatmaps).ThenInclude(b => b.BaseDifficulty) + .Include(s => s.Beatmaps).ThenInclude(b => b.Metadata); protected override IQueryable AddIncludesForConsumption(IQueryable query) => base.AddIncludesForConsumption(query) .Include(s => s.Metadata) .Include(s => s.Beatmaps).ThenInclude(s => s.Ruleset) .Include(s => s.Beatmaps).ThenInclude(b => b.BaseDifficulty) - .Include(s => s.Beatmaps).ThenInclude(b => b.Metadata) - .Include(s => s.Files).ThenInclude(f => f.FileInfo); + .Include(s => s.Beatmaps).ThenInclude(b => b.Metadata); protected override void Purge(List items, OsuDbContext context) { diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 86c97df191..5b4a191682 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -108,7 +108,7 @@ namespace osu.Game.Database a.Invoke(); } - protected ArchiveModelManager(Storage storage, IDatabaseContextFactory contextFactory, MutableDatabaseBackedStore modelStore, IIpcHost importHost = null) + protected ArchiveModelManager(Storage storage, IDatabaseContextFactory contextFactory, MutableDatabaseBackedStoreWithFileIncludes modelStore, IIpcHost importHost = null) { ContextFactory = contextFactory; diff --git a/osu.Game/Database/MutableDatabaseBackedStoreWithFileIncludes.cs b/osu.Game/Database/MutableDatabaseBackedStoreWithFileIncludes.cs new file mode 100644 index 0000000000..5d6ff6b09b --- /dev/null +++ b/osu.Game/Database/MutableDatabaseBackedStoreWithFileIncludes.cs @@ -0,0 +1,27 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using Microsoft.EntityFrameworkCore; +using osu.Framework.Platform; + +namespace osu.Game.Database +{ + public abstract class MutableDatabaseBackedStoreWithFileIncludes : MutableDatabaseBackedStore + where T : class, IHasPrimaryKey, ISoftDelete, IHasFiles + where U : INamedFileInfo + { + protected MutableDatabaseBackedStoreWithFileIncludes(IDatabaseContextFactory contextFactory, Storage storage = null) + : base(contextFactory, storage) + { + } + + protected override IQueryable AddIncludesForConsumption(IQueryable query) => + base.AddIncludesForConsumption(query) + .Include(s => s.Files).ThenInclude(f => f.FileInfo); + + protected override IQueryable AddIncludesForDeletion(IQueryable query) => + base.AddIncludesForDeletion(query) + .Include(s => s.Files); // don't include FileInfo. these are handled by the FileStore itself. + } +} diff --git a/osu.Game/Scoring/ScoreStore.cs b/osu.Game/Scoring/ScoreStore.cs index 745bb6cc29..9627481f4d 100644 --- a/osu.Game/Scoring/ScoreStore.cs +++ b/osu.Game/Scoring/ScoreStore.cs @@ -8,7 +8,7 @@ using osu.Game.Database; namespace osu.Game.Scoring { - public class ScoreStore : MutableDatabaseBackedStore + public class ScoreStore : MutableDatabaseBackedStoreWithFileIncludes { public ScoreStore(IDatabaseContextFactory factory, Storage storage) : base(factory, storage) @@ -17,7 +17,6 @@ namespace osu.Game.Scoring protected override IQueryable AddIncludesForConsumption(IQueryable query) => base.AddIncludesForConsumption(query) - .Include(s => s.Files).ThenInclude(f => f.FileInfo) .Include(s => s.Beatmap) .Include(s => s.Ruleset); } diff --git a/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs b/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs new file mode 100644 index 0000000000..a37327f2c3 --- /dev/null +++ b/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs @@ -0,0 +1,48 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Overlays.Dialog; +using osu.Game.Scoring; +using System; +using System.Linq; +using System.Threading.Tasks; + +namespace osu.Game.Screens.Select +{ + public class BeatmapClearScoresDialog : PopupDialog + { + private ScoreManager scoreManager; + + public BeatmapClearScoresDialog(BeatmapInfo beatmap, Action onCompletion) + { + BodyText = $@"{beatmap.Metadata?.Artist} - {beatmap.Metadata?.Title}"; + Icon = FontAwesome.fa_eraser; + HeaderText = @"Clearing all local scores. Are you sure?"; + Buttons = new PopupDialogButton[] + { + new PopupDialogOkButton + { + Text = @"Yes. Please.", + Action = () => + { + Task.Run(() => scoreManager.Delete(scoreManager.QueryScores(s => !s.DeletePending && s.Beatmap.ID == beatmap.ID).ToList())) + .ContinueWith(_ => onCompletion); + } + }, + new PopupDialogCancelButton + { + Text = @"No, I'm still attached.", + }, + }; + } + + [BackgroundDependencyLoader] + private void load(ScoreManager scoreManager) + { + this.scoreManager = scoreManager; + } + } +} diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs index adf989ee62..61370f7ce3 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -55,7 +55,7 @@ namespace osu.Game.Screens.Select.Leaderboards { if (Scope == BeatmapLeaderboardScope.Local) { - Scores = scoreManager.QueryScores(s => s.Beatmap.ID == Beatmap.ID).ToArray(); + Scores = scoreManager.QueryScores(s => !s.DeletePending && s.Beatmap.ID == Beatmap.ID).ToArray(); PlaceholderState = Scores.Any() ? PlaceholderState.Successful : PlaceholderState.NoScores; return null; } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 0c5871f483..866c7e0926 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -1,11 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; -using System.Collections.Generic; -using System.Linq; -using osuTK; -using osuTK.Input; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; @@ -13,8 +8,8 @@ using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Logging; using osu.Framework.Input.Events; +using osu.Framework.Logging; using osu.Framework.Screens; using osu.Framework.Threading; using osu.Game.Beatmaps; @@ -31,7 +26,12 @@ using osu.Game.Screens.Menu; using osu.Game.Screens.Play; using osu.Game.Screens.Select.Options; using osu.Game.Skinning; +using osuTK; using osuTK.Graphics; +using osuTK.Input; +using System; +using System.Collections.Generic; +using System.Linq; namespace osu.Game.Screens.Select { @@ -226,7 +226,7 @@ namespace osu.Game.Screens.Select BeatmapOptions.AddButton(@"Delete", @"all difficulties", FontAwesome.fa_trash, colours.Pink, () => delete(Beatmap.Value.BeatmapSetInfo), Key.Number4, float.MaxValue); BeatmapOptions.AddButton(@"Remove", @"from unplayed", FontAwesome.fa_times_circle_o, colours.Purple, null, Key.Number1); - BeatmapOptions.AddButton(@"Clear", @"local scores", FontAwesome.fa_eraser, colours.Purple, null, Key.Number2); + BeatmapOptions.AddButton(@"Clear", @"local scores", FontAwesome.fa_eraser, colours.Purple, () => clearScores(Beatmap.Value.BeatmapInfo), Key.Number2); } if (this.beatmaps == null) @@ -621,6 +621,15 @@ namespace osu.Game.Screens.Select dialogOverlay?.Push(new BeatmapDeleteDialog(beatmap)); } + private void clearScores(BeatmapInfo beatmap) + { + if (beatmap == null || beatmap.ID <= 0) return; + + dialogOverlay?.Push(new BeatmapClearScoresDialog(beatmap, () => + // schedule done here rather than inside the dialog as the dialog may fade out and never callback. + Schedule(() => BeatmapDetails.Leaderboard.RefreshScores()))); + } + public override bool OnPressed(GlobalAction action) { if (!this.IsCurrentScreen()) return false; diff --git a/osu.Game/Skinning/SkinStore.cs b/osu.Game/Skinning/SkinStore.cs index e80b03b935..31cadb0a24 100644 --- a/osu.Game/Skinning/SkinStore.cs +++ b/osu.Game/Skinning/SkinStore.cs @@ -1,22 +1,16 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Linq; -using Microsoft.EntityFrameworkCore; using osu.Framework.Platform; using osu.Game.Database; namespace osu.Game.Skinning { - public class SkinStore : MutableDatabaseBackedStore + public class SkinStore : MutableDatabaseBackedStoreWithFileIncludes { public SkinStore(DatabaseContextFactory contextFactory, Storage storage = null) : base(contextFactory, storage) { } - - protected override IQueryable AddIncludesForConsumption(IQueryable query) => - base.AddIncludesForConsumption(query) - .Include(s => s.Files).ThenInclude(f => f.FileInfo); } } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index f8d89ec8a5..8f9a7cd14b 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -16,7 +16,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 16eeb46683..499878347b 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -35,7 +35,7 @@ prompt 4 - iPhone Developer + iPhone Distribution true true Entitlements.plist @@ -105,12 +105,12 @@ - - + + - \ No newline at end of file + diff --git a/osu.iOS/Info.plist b/osu.iOS/Info.plist index fe711fc03c..0627feab78 100644 --- a/osu.iOS/Info.plist +++ b/osu.iOS/Info.plist @@ -2,6 +2,14 @@ + CFBundleIdentifier + sh.ppy.osulazer + CFBundleName + osu! + CFBundleShortVersionString + 0.1 + CFBundleVersion + 0.1.0 LSRequiresIPhoneOS MinimumOSVersion @@ -17,6 +25,10 @@ armv7 + UIRequiresFullScreen + + UIStatusBarHidden + UISupportedInterfaceOrientations UIInterfaceOrientationPortrait @@ -26,17 +38,5 @@ XSAppIconAssets Assets.xcassets/AppIcon.appiconset - UIStatusBarHidden - - UIRequiresFullScreen - - CFBundleIdentifier - sh.ppy.osulazer - CFBundleName - osu! - CFBundleShortVersionString - 0.1 - CFBundleVersion - 0.1.1 diff --git a/osu.iOS/OsuGameIOS.cs b/osu.iOS/OsuGameIOS.cs index b94119af55..6cf18df9a6 100644 --- a/osu.iOS/OsuGameIOS.cs +++ b/osu.iOS/OsuGameIOS.cs @@ -9,6 +9,6 @@ namespace osu.iOS { public class OsuGameIOS : OsuGame { - protected override Version AssemblyVersion => new Version(NSBundle.MainBundle.InfoDictionary["CFBundleVersion"].ToString()); + public override Version AssemblyVersion => new Version(NSBundle.MainBundle.InfoDictionary["CFBundleVersion"].ToString()); } }