-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Learner view audio translations #3681
Conversation
<p> | ||
Pick Language | ||
</p> | ||
<!-- <select2-drdopdown |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice! Took a first pass.
@@ -107,6 +107,18 @@ oppia.factory('StatesObjectFactory', [ | |||
return finalStateNames; | |||
}; | |||
|
|||
States.prototype.getAllAudioLanguageCodes = function() { | |||
var allAudioLanguageCodes = new Set(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Set is relatively recent and isn't supported in some slightly older browsers. Might want to consider using an array instead and checking for existing membership with indexOf().
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
var allAudioLanguageCodes = new Set(); | ||
for (var stateName in this._states) { | ||
var audioTranslationsForState = | ||
this._states[stateName].content.getBindableAudioTranslations() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing semicolon.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
@@ -107,6 +107,18 @@ oppia.factory('StatesObjectFactory', [ | |||
return finalStateNames; | |||
}; | |||
|
|||
States.prototype.getAllAudioLanguageCodes = function() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tests?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added
@@ -838,6 +838,13 @@ oppia.factory('explorationLanguageCodeService', [ | |||
} | |||
} | |||
}; | |||
child.getLanguageDescription = function(languageCode) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this has anything to do with this factory, actually. You might want to define a separate utility factory for handling global language-related operations that aren't exploration-specific.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
moved into LanguageUtilService
|
||
/** | ||
* @fileoverview Service to manage the current language being | ||
* used for audio translations as well as related modals. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a bit unclear -- on first reading I thought the service also managed the language being used to display the text on related modals, and thought, hm, this isn't in the spec. I think it's fine to drop the "as well as related modals" bit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
|
||
<div class="modal-body"> | ||
<p> | ||
Pick Language |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
@@ -30,6 +33,18 @@ oppia.factory('AudioPlayerService', [ | |||
var blobUrl = URL.createObjectURL(audioBlob); | |||
_currentTrack = ngAudio.load(blobUrl); | |||
_currentTrackFilename = filename; | |||
|
|||
// Doesn't seem to be any way of detecing when native |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
detecting (typo)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
// to grab native audio. | ||
// TODO(tjiang11): Find a better way to handle this. | ||
$timeout(function() { | ||
_currentTrack.audio.onended = function() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you elaborate a bit more (in comments) on the ideas behind the workaround here? I'm not sure I fully understand it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
if (!_currentTrack) { | ||
return false; | ||
} | ||
return !_currentTrack.paused; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternative: return (_currentTrack && !_currentTrack.paused);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
} | ||
return !_currentTrack.paused; | ||
}, | ||
trackLoaded: function() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
start with verb. (This one is particularly misleading because "track" is a verb too :P)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
Codecov Report
@@ Coverage Diff @@
## develop #3681 +/- ##
===========================================
- Coverage 45.03% 45.01% -0.02%
===========================================
Files 261 264 +3
Lines 20219 20343 +124
Branches 3157 3168 +11
===========================================
+ Hits 9106 9158 +52
- Misses 11113 11185 +72
Continue to review full report at Codecov.
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nice, thanks @tjiang11! Mostly trivial comments.
@@ -122,6 +122,24 @@ oppia.factory('ExplorationObjectFactory', [ | |||
return this.getState(stateName).content.getHtml(); | |||
}; | |||
|
|||
Exploration.prototype.getAudioTranslations = function(stateName) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tests?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added
var audioTranslationsForState = | ||
this._states[stateName].content.getBindableAudioTranslations(); | ||
for (var languageCode in audioTranslationsForState) { | ||
if (allAudioLanguageCodes.indexOf(languageCode) == -1) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: ===
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
return { | ||
restrict: 'E', | ||
scope: { | ||
audioTranslations: '=' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is intended to be readonly (i.e. not modified from within the directive), then use getAudioTranslations: '&audioTranslations'
and call $scope.getAudioTranslations() instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
'$scope', 'AudioTranslationManagerService', 'AudioPlayerService', | ||
function( | ||
$scope, AudioTranslationManagerService, AudioPlayerService) { | ||
var filenameOfLastStartedAudio; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Initialize to null?
} | ||
} else { | ||
AudioPlayerService.pause(); | ||
if (!isRequestForSameAudioAsLastTime()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still a bit confused by this. Presumably the user has just clicked a button that looks visually like "pause" -- how come we then play?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should only activate when a user has clicked a "play" button on an audio translation that is different from the current audio being played. This way, the audio player pauses the existing audio and immediately starts playing the newly requested audio translation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a bit confused about this scenario: "when a user has clicked a 'play' button on an audio translation that is different from the current audio being played". In what circumstances would it happen? Are there ever multiple play buttons available at once?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, my bad: as @tjiang11 clarified in chat, in the future there may be > 1 play button on a page. This handles that correctly, so I don't think further changes are needed here.
}; | ||
|
||
$scope.arePreviousResponsesShown = false; | ||
|
||
$scope.waitingForOppiaFeedback = false; | ||
|
||
$scope.currentAudioLanguageCode = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't used or modified?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed
@@ -52,14 +52,16 @@ oppia.directive('tutorCard', [ | |||
'$scope', '$timeout', 'oppiaPlayerService', 'HintManagerService', | |||
'playerPositionService', 'playerTranscriptService', | |||
'ExplorationPlayerStateService', 'windowDimensionsService', | |||
'urlService', 'TWO_CARD_THRESHOLD_PX', 'CONTENT_FOCUS_LABEL_PREFIX', | |||
'urlService', 'AudioPlayerService', 'AudioTranslationManagerService', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The added deps aren't used?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed, except for AudioPlayerService which I use now for stopping audio on changing cards
} | ||
|
||
.audio-control-button:hover { | ||
cursor: pointer |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing semicolon.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
focus-on="<[getContentFocusLabel($index)]>"> | ||
<span angular-html-bind="activeCard.contentHtml"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not div? Content may span multiple paragraphs, for instance. Might mean the next bit has to be a div too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
changed
@@ -72,6 +72,10 @@ oppia.factory('AssetsBackendApiService', [ | |||
}; | |||
|
|||
var _getAudioDownloadUrl = function(explorationId, filename) { | |||
if (GLOBALS.DEV_MODE) { | |||
return '/assets/test/' + filename; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we have /assets/test/ be part of constants.js perhaps? Or rather -- have an assets dir prefix that's one thing in dev and another thing (the GCS url prefix) in prod? Better to do the if/else check in just one place if possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not really sure how to go about doing the latter implementation while still avoiding an if/else for the url interpolation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I thought about this. It's not straightforward, unfortunately. Here's the reasoning:
constants.js needs to be a static dict (since the backend interprets it as JSON), so it can't have any logic in it. That's a shame, because that's the file for constants shared between frontend and backend. But we can't have an if/else condition there.
The other place we store constants is in feconf, and we can do "if DEV_MODE ... else ..." there. We can then ship it to the frontend as a GLOBALS property -- see how ALLOWED_GADGETS is sent in editor.py, for example. The logic here should then be:
return UrlInterpolationService.interpolateUrl(GLOBALS.AUDIO_URL_PREFIX, {...});
This would be better than doing a one-off thing here, because that way there'll be a single audio url prefix used everywhere. This becomes important when we work on the editor page because audio files need to be uploaded to that same location as well, so this prefix needs to be somehow global.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh noes -- there's a merge conflict!
Otherwise this looks amazing :) I made a few nit comments and maybe we should change the image files to use material-icons or something but other than that, LGTM!
Feel free to merge once you've fixed the remaining stuff and Travis checks pass. Thanks a lot!
@@ -238,6 +244,10 @@ | |||
top: 14px; | |||
} | |||
|
|||
.audio-controls { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.oppia-player-audio-controls or similar
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
changed to conversation-skin-audio-controls
feconf.py
Outdated
# files. | ||
|
||
if DEV_MODE: | ||
AUDIO_URL_PREFIX = '/assets/test/<exploration_id>/audio/<filename>' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, looks more like AUDIO_URL_TEMPLATE to me?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
changed
function( | ||
$scope, AudioTranslationManagerService, AudioPlayerService) { | ||
var filenameOfLastStartedAudio = null; | ||
var directiveId = Math.random().toString(36).substr(2, 10); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a comment here to explain what this is used for -- it's not obvious from the naming.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added
UrlInterpolationService.getStaticImageUrl( | ||
'/icons/settings.svg')); | ||
|
||
var PLAY_AUDIO_BUTTON_IMAGE_URL = ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Naming: use
IMAGE_URL_AUDIO_SETTINGS
IMAGE_URL_PLAY_AUDIO
IMAGE_URL_PAUSE_AUDIO
IMAGE_URL_REWIND_AUDIO
(basically, start with the type. That way it's easier to see that everything is in a "group of related things".)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
'$scope', 'AudioTranslationManagerService', 'AudioPlayerService', | ||
function( | ||
$scope, AudioTranslationManagerService, AudioPlayerService) { | ||
var filenameOfLastStartedAudio = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you actually still need this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed
Just FYI, there are merge conflicts (which block the Travis tests from running). |
Hi @tjiang11 -- e2e tests are failing rather badly. Any idea why? Feel free to pick up and use any patterns in my PR if you need them, btw. I'm happy to handle the merges. |
* upstream/develop: (39 commits) Add a one-off job to list which explorations use gadgets; remove old fallbacks job. (oppia#3717) Fix oppia#2998 implement collection skills update commands (oppia#3710) Improvements and bug fixes to changes introduced by PR oppia#3671 (oppia#3684) Fix oppia#3688: refactoring ExplorationStatusHandler to remove unpublish and publicize functionality as it is not called from frontend. (oppia#3715) fix oppia#3698: Shift exception handling for NotLoggedInException so that it does not generate log errors. (oppia#3714) Revert "Fix oppia#3707 Change text of collection learner view "start here"" (oppia#3713) Fix oppia#3701 Fixed Continue button name issue (oppia#3711) Fix oppia#3467: Introduce re-training and add_to_training_queue methods (oppia#3650) Fix oppia#3707 Change text of collection learner view "start here" (oppia#3700) Audio fixes: make audio download URL safe; don't show flagging modal if all translations are flagged; use templateUrl for modals. (oppia#3709) Learner audio updates (oppia#3705) Move hint button to left corner for supplemental cards (oppia#3702) Make the representation of the learner's 'Continue' answer more conversational. (oppia#3675) Add editor for audio translations (oppia#3692) Fix oppia#3686 fixed text overflow for contributors list (oppia#3697) Fix oppia#3551: Manually set cookie value during language change (oppia#3622) Learner view audio translations (oppia#3681) The sixth milestone of the learner dashboard. (oppia#3680) Routine update of translations. (oppia#3685) SiteWide ACL Refactor: Milestone 3.1 (oppia#3682) ...
This PR implements minimum viable functionality for audio in the learner view.